mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Don't use IStorage for reading the romfs file table.
It's at least 2-3x faster reading it all from a byte array
This commit is contained in:
parent
7004b22958
commit
34e926f2a4
6 changed files with 182 additions and 153 deletions
|
@ -5,12 +5,12 @@ namespace LibHac.IO
|
||||||
{
|
{
|
||||||
public ref struct PathParser
|
public ref struct PathParser
|
||||||
{
|
{
|
||||||
private ReadOnlySpan<char> _path;
|
private ReadOnlySpan<byte> _path;
|
||||||
private int _offset;
|
private int _offset;
|
||||||
private int _length;
|
private int _length;
|
||||||
private bool _finished;
|
private bool _finished;
|
||||||
|
|
||||||
public PathParser(ReadOnlySpan<char> path)
|
public PathParser(ReadOnlySpan<byte> path)
|
||||||
{
|
{
|
||||||
Debug.Assert(PathTools.IsNormalized(path));
|
Debug.Assert(PathTools.IsNormalized(path));
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ namespace LibHac.IO
|
||||||
_finished = false;
|
_finished = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryGetNext(out ReadOnlySpan<char> name)
|
public bool TryGetNext(out ReadOnlySpan<byte> name)
|
||||||
{
|
{
|
||||||
bool success = MoveNext();
|
bool success = MoveNext();
|
||||||
name = GetCurrent();
|
name = GetCurrent();
|
||||||
|
@ -50,7 +50,7 @@ namespace LibHac.IO
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReadOnlySpan<char> GetCurrent()
|
public ReadOnlySpan<byte> GetCurrent()
|
||||||
{
|
{
|
||||||
return _path.Slice(_offset, _length);
|
return _path.Slice(_offset, _length);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,10 +7,16 @@ namespace LibHac.IO
|
||||||
{
|
{
|
||||||
public static readonly char DirectorySeparator = '/';
|
public static readonly char DirectorySeparator = '/';
|
||||||
|
|
||||||
|
public static string Normalize(string inPath)
|
||||||
|
{
|
||||||
|
if (IsNormalized(inPath.AsSpan())) return inPath;
|
||||||
|
return NormalizeInternal(inPath);
|
||||||
|
}
|
||||||
|
|
||||||
// Licensed to the .NET Foundation under one or more agreements.
|
// Licensed to the .NET Foundation under one or more agreements.
|
||||||
// The .NET Foundation licenses this file to you under the MIT license.
|
// The .NET Foundation licenses this file to you under the MIT license.
|
||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
public static string Normalize(string inPath)
|
public static string NormalizeInternal(string inPath)
|
||||||
{
|
{
|
||||||
// Relative paths aren't a thing for IFileSystem, so assume all paths are absolute
|
// Relative paths aren't a thing for IFileSystem, so assume all paths are absolute
|
||||||
// and add a '/' to the beginning of the path if it doesn't already begin with one
|
// and add a '/' to the beginning of the path if it doesn't already begin with one
|
||||||
|
@ -111,7 +117,36 @@ namespace LibHac.IO
|
||||||
case NormalizeState.Dot when c == '/': return false;
|
case NormalizeState.Dot when c == '/': return false;
|
||||||
case NormalizeState.Dot when c == '.': state = NormalizeState.DoubleDot; break;
|
case NormalizeState.Dot when c == '.': state = NormalizeState.DoubleDot; break;
|
||||||
case NormalizeState.Dot: state = NormalizeState.Normal; break;
|
case NormalizeState.Dot: state = NormalizeState.Normal; break;
|
||||||
|
|
||||||
|
case NormalizeState.DoubleDot when c == '/': return false;
|
||||||
|
case NormalizeState.DoubleDot: state = NormalizeState.Normal; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return state == NormalizeState.Normal || state == NormalizeState.Delimiter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsNormalized(ReadOnlySpan<byte> path)
|
||||||
|
{
|
||||||
|
var state = NormalizeState.Initial;
|
||||||
|
|
||||||
|
foreach (byte c in path)
|
||||||
|
{
|
||||||
|
switch (state)
|
||||||
|
{
|
||||||
|
case NormalizeState.Initial when c == '/': state = NormalizeState.Delimiter; break;
|
||||||
|
case NormalizeState.Initial: return false;
|
||||||
|
|
||||||
|
case NormalizeState.Normal when c == '/': state = NormalizeState.Delimiter; break;
|
||||||
|
|
||||||
|
case NormalizeState.Delimiter when c == '/': return false;
|
||||||
|
case NormalizeState.Delimiter when c == '.': state = NormalizeState.Dot; break;
|
||||||
|
case NormalizeState.Delimiter: state = NormalizeState.Normal; break;
|
||||||
|
|
||||||
|
case NormalizeState.Dot when c == '/': return false;
|
||||||
|
case NormalizeState.Dot when c == '.': state = NormalizeState.DoubleDot; break;
|
||||||
|
case NormalizeState.Dot: state = NormalizeState.Normal; break;
|
||||||
|
|
||||||
case NormalizeState.DoubleDot when c == '/': return false;
|
case NormalizeState.DoubleDot when c == '/': return false;
|
||||||
case NormalizeState.DoubleDot: state = NormalizeState.Normal; break;
|
case NormalizeState.DoubleDot: state = NormalizeState.Normal; break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Text;
|
||||||
|
|
||||||
namespace LibHac.IO.RomFs
|
namespace LibHac.IO.RomFs
|
||||||
{
|
{
|
||||||
|
@ -27,11 +27,11 @@ namespace LibHac.IO.RomFs
|
||||||
|
|
||||||
public bool OpenFile(string path, out RomFileInfo fileInfo)
|
public bool OpenFile(string path, out RomFileInfo fileInfo)
|
||||||
{
|
{
|
||||||
FindFileRecursive(path.AsSpan(), out RomEntryKey key);
|
FindFileRecursive(GetUtf8Bytes(path), out RomEntryKey key);
|
||||||
|
|
||||||
if (FileTable.TryGetValue(ref key, out FileRomEntry entry, out int _))
|
if (FileTable.TryGetValue(ref key, out RomKeyValuePair<FileRomEntry> keyValuePair))
|
||||||
{
|
{
|
||||||
fileInfo = entry.Info;
|
fileInfo = keyValuePair.Value.Info;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,9 +41,9 @@ namespace LibHac.IO.RomFs
|
||||||
|
|
||||||
public bool OpenFile(int offset, out RomFileInfo fileInfo)
|
public bool OpenFile(int offset, out RomFileInfo fileInfo)
|
||||||
{
|
{
|
||||||
if (FileTable.TryGetValue(offset, out FileRomEntry entry))
|
if (FileTable.TryGetValue(offset, out RomKeyValuePair<FileRomEntry> keyValuePair))
|
||||||
{
|
{
|
||||||
fileInfo = entry.Info;
|
fileInfo = keyValuePair.Value.Info;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,11 +53,11 @@ namespace LibHac.IO.RomFs
|
||||||
|
|
||||||
public bool OpenDirectory(string path, out FindPosition position)
|
public bool OpenDirectory(string path, out FindPosition position)
|
||||||
{
|
{
|
||||||
FindDirectoryRecursive(path.AsSpan(), out RomEntryKey key);
|
FindDirectoryRecursive(GetUtf8Bytes(path), out RomEntryKey key);
|
||||||
|
|
||||||
if (DirectoryTable.TryGetValue(ref key, out DirectoryRomEntry entry, out int _))
|
if (DirectoryTable.TryGetValue(ref key, out RomKeyValuePair<DirectoryRomEntry> keyValuePair))
|
||||||
{
|
{
|
||||||
position = entry.Pos;
|
position = keyValuePair.Value.Pos;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,9 +67,9 @@ namespace LibHac.IO.RomFs
|
||||||
|
|
||||||
public bool OpenDirectory(int offset, out FindPosition position)
|
public bool OpenDirectory(int offset, out FindPosition position)
|
||||||
{
|
{
|
||||||
if (DirectoryTable.TryGetValue(offset, out DirectoryRomEntry entry))
|
if (DirectoryTable.TryGetValue(offset, out RomKeyValuePair<DirectoryRomEntry> keyValuePair))
|
||||||
{
|
{
|
||||||
position = entry.Pos;
|
position = keyValuePair.Value.Pos;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,121 +77,68 @@ namespace LibHac.IO.RomFs
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static ReadOnlySpan<byte> GetUtf8Bytes(string value)
|
||||||
|
{
|
||||||
|
return Encoding.UTF8.GetBytes(value).AsSpan();
|
||||||
|
}
|
||||||
|
|
||||||
public bool FindNextFile(ref FindPosition position, out RomFileInfo info, out string name)
|
public bool FindNextFile(ref FindPosition position, out RomFileInfo info, out string name)
|
||||||
{
|
{
|
||||||
if (FileTable.TryGetValue(position.NextFile, out FileRomEntry entry, out name))
|
if (FileTable.TryGetValue(position.NextFile, out RomKeyValuePair<FileRomEntry> keyValuePair))
|
||||||
{
|
{
|
||||||
position.NextFile = entry.NextSibling;
|
position.NextFile = keyValuePair.Value.NextSibling;
|
||||||
info = entry.Info;
|
info = keyValuePair.Value.Info;
|
||||||
|
name = Encoding.UTF8.GetString(keyValuePair.Key.Name.ToArray());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
info = default;
|
info = default;
|
||||||
|
name = default;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool FindNextDirectory(ref FindPosition position, out string name)
|
public bool FindNextDirectory(ref FindPosition position, out string name)
|
||||||
{
|
{
|
||||||
if (DirectoryTable.TryGetValue(position.NextDirectory, out DirectoryRomEntry entry, out name))
|
if (DirectoryTable.TryGetValue(position.NextDirectory, out RomKeyValuePair<DirectoryRomEntry> keyValuePair))
|
||||||
{
|
{
|
||||||
position.NextDirectory = entry.NextSibling;
|
position.NextDirectory = keyValuePair.Value.NextSibling;
|
||||||
|
name = Encoding.UTF8.GetString(keyValuePair.Key.Name.ToArray());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
name = default;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void FindFileRecursive(ReadOnlySpan<char> path, out RomEntryKey key)
|
private void FindFileRecursive(ReadOnlySpan<byte> path, out RomEntryKey key)
|
||||||
{
|
{
|
||||||
var parser = new PathParser(path);
|
var parser = new PathParser(path);
|
||||||
FindParentDirectoryRecursive(ref parser, out DirectoryRomEntry _, out int parentOffset);
|
FindParentDirectoryRecursive(ref parser, out RomKeyValuePair<DirectoryRomEntry> keyValuePair);
|
||||||
|
|
||||||
key = new RomEntryKey(parser.GetCurrent(), parentOffset);
|
key = keyValuePair.Key;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void FindDirectoryRecursive(ReadOnlySpan<char> path, out RomEntryKey key)
|
private void FindDirectoryRecursive(ReadOnlySpan<byte> path, out RomEntryKey key)
|
||||||
{
|
{
|
||||||
var parser = new PathParser(path);
|
var parser = new PathParser(path);
|
||||||
FindParentDirectoryRecursive(ref parser, out DirectoryRomEntry _, out int parentOffset);
|
FindParentDirectoryRecursive(ref parser, out RomKeyValuePair<DirectoryRomEntry> keyValuePair);
|
||||||
|
|
||||||
ReadOnlySpan<char> name = parser.GetCurrent();
|
ReadOnlySpan<byte> name = parser.GetCurrent();
|
||||||
if (name.Length == 0) parentOffset = 0;
|
int parentOffset = name.Length == 0 ? 0 : keyValuePair.Offset;
|
||||||
|
|
||||||
key = new RomEntryKey(name, parentOffset);
|
key = new RomEntryKey(name, parentOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void FindParentDirectoryRecursive(ref PathParser parser, out DirectoryRomEntry parentEntry, out int parentOffset)
|
private void FindParentDirectoryRecursive(ref PathParser parser, out RomKeyValuePair<DirectoryRomEntry> keyValuePair)
|
||||||
{
|
{
|
||||||
parentEntry = default;
|
keyValuePair = default;
|
||||||
parentOffset = default;
|
|
||||||
RomEntryKey key = default;
|
RomEntryKey key = default;
|
||||||
|
|
||||||
while (parser.TryGetNext(out key.Name) && !parser.IsFinished())
|
while (parser.TryGetNext(out key.Name) && !parser.IsFinished())
|
||||||
{
|
{
|
||||||
DirectoryTable.TryGetValue(ref key, out parentEntry, out parentOffset);
|
DirectoryTable.TryGetValue(ref key, out keyValuePair);
|
||||||
key.Parent = parentOffset;
|
key.Parent = keyValuePair.Offset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal ref struct RomEntryKey
|
|
||||||
{
|
|
||||||
public ReadOnlySpan<char> Name;
|
|
||||||
public int Parent;
|
|
||||||
|
|
||||||
public RomEntryKey(ReadOnlySpan<char> name, int parent)
|
|
||||||
{
|
|
||||||
Name = name;
|
|
||||||
Parent = parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
public uint GetRomHashCode()
|
|
||||||
{
|
|
||||||
uint hash = 123456789 ^ (uint)Parent;
|
|
||||||
|
|
||||||
foreach (char c in Name)
|
|
||||||
{
|
|
||||||
hash = c ^ ((hash << 27) | (hash >> 5));
|
|
||||||
}
|
|
||||||
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
public struct RomFsEntry<T> where T : unmanaged
|
|
||||||
{
|
|
||||||
public int Parent;
|
|
||||||
public T Value;
|
|
||||||
public int Next;
|
|
||||||
public int KeyLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
|
||||||
public struct FileRomEntry
|
|
||||||
{
|
|
||||||
public int NextSibling;
|
|
||||||
public RomFileInfo Info;
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
|
||||||
public struct RomFileInfo
|
|
||||||
{
|
|
||||||
public long Offset;
|
|
||||||
public long Length;
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
public struct DirectoryRomEntry
|
|
||||||
{
|
|
||||||
public int NextSibling;
|
|
||||||
public FindPosition Pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
public struct FindPosition
|
|
||||||
{
|
|
||||||
public int NextDirectory;
|
|
||||||
public int NextFile;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,36 +1,32 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace LibHac.IO.RomFs
|
namespace LibHac.IO.RomFs
|
||||||
{
|
{
|
||||||
internal class RomFsDictionary<T> where T : unmanaged
|
internal class RomFsDictionary<T> where T : unmanaged
|
||||||
{
|
{
|
||||||
private int HashBucketCount { get; }
|
private int[] BucketTable { get; }
|
||||||
private IStorage BucketStorage { get; }
|
private byte[] EntryTable { get; }
|
||||||
private IStorage EntryStorage { get; }
|
|
||||||
|
|
||||||
// Hack around not being able to get the size of generic structures
|
// Hack around not being able to get the size of generic structures
|
||||||
private readonly int _sizeOfEntry = 12 + Marshal.SizeOf<T>();
|
private readonly int _sizeOfEntry = 12 + Marshal.SizeOf<T>();
|
||||||
|
|
||||||
public RomFsDictionary(IStorage bucketStorage, IStorage entryStorage)
|
public RomFsDictionary(IStorage bucketStorage, IStorage entryStorage)
|
||||||
{
|
{
|
||||||
BucketStorage = bucketStorage;
|
BucketTable = bucketStorage.ToArray<int>();
|
||||||
EntryStorage = entryStorage;
|
EntryTable = entryStorage.ToArray();
|
||||||
HashBucketCount = (int)(bucketStorage.Length / 4);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryGetValue(ref RomEntryKey key, out T value, out int offset)
|
public bool TryGetValue(ref RomEntryKey key, out RomKeyValuePair<T> value)
|
||||||
{
|
{
|
||||||
int i = FindEntry(ref key);
|
int i = FindEntry(ref key);
|
||||||
offset = i;
|
|
||||||
|
|
||||||
if (i >= 0)
|
if (i >= 0)
|
||||||
{
|
{
|
||||||
GetEntryInternal(i, out RomFsEntry<T> entry);
|
GetEntryInternal(i, out RomFsEntry<T> entry);
|
||||||
value = entry.Value;
|
|
||||||
|
value = new RomKeyValuePair<T> { Key = key, Value = entry.Value, Offset = i };
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,43 +34,32 @@ namespace LibHac.IO.RomFs
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryGetValue(int offset, out T value, out string entryName)
|
public bool TryGetValue(int offset, out RomKeyValuePair<T> value)
|
||||||
{
|
{
|
||||||
if (offset < 0 || offset + _sizeOfEntry >= EntryStorage.Length)
|
if (offset < 0 || offset + _sizeOfEntry >= EntryTable.Length)
|
||||||
{
|
|
||||||
value = default;
|
|
||||||
entryName = default;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
GetEntryInternal(offset, out RomFsEntry<T> entry, out entryName);
|
|
||||||
value = entry.Value;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool TryGetValue(int offset, out T value)
|
|
||||||
{
|
|
||||||
if (offset < 0 || offset + _sizeOfEntry >= EntryStorage.Length)
|
|
||||||
{
|
{
|
||||||
value = default;
|
value = default;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
GetEntryInternal(offset, out RomFsEntry<T> entry);
|
value = new RomKeyValuePair<T>();
|
||||||
value = entry.Value;
|
|
||||||
|
GetEntryInternal(offset, out RomFsEntry<T> entry, out value.Key.Name);
|
||||||
|
value.Value = entry.Value;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int FindEntry(ref RomEntryKey key)
|
private int FindEntry(ref RomEntryKey key)
|
||||||
{
|
{
|
||||||
uint hashCode = key.GetRomHashCode();
|
uint hashCode = key.GetRomHashCode();
|
||||||
int i = GetBucket((int)(hashCode % HashBucketCount));
|
int index = (int)(hashCode % BucketTable.Length);
|
||||||
|
int i = BucketTable[index];
|
||||||
|
|
||||||
while (i != -1)
|
while (i != -1)
|
||||||
{
|
{
|
||||||
GetEntryInternal(i, out RomFsEntry<T> entry);
|
GetEntryInternal(i, out RomFsEntry<T> entry, out ReadOnlySpan<byte> name);
|
||||||
|
|
||||||
if (IsEqual(ref key, ref entry, i))
|
if (key.Parent == entry.Parent && key.Name.SequenceEqual(name))
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -85,24 +70,12 @@ namespace LibHac.IO.RomFs
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsEqual(ref RomEntryKey key, ref RomFsEntry<T> entry, int entryOffset)
|
|
||||||
{
|
|
||||||
if (key.Parent != entry.Parent) return false;
|
|
||||||
if (key.Name.Length != entry.KeyLength) return false;
|
|
||||||
|
|
||||||
GetEntryInternal(entryOffset, out RomFsEntry<T> _, out string name);
|
|
||||||
|
|
||||||
return key.Name.Equals(name.AsSpan(), StringComparison.Ordinal);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void GetEntryInternal(int offset, out RomFsEntry<T> outEntry)
|
private void GetEntryInternal(int offset, out RomFsEntry<T> outEntry)
|
||||||
{
|
{
|
||||||
Span<byte> b = stackalloc byte[_sizeOfEntry];
|
outEntry = MemoryMarshal.Read<RomFsEntry<T>>(EntryTable.AsSpan(offset));
|
||||||
EntryStorage.Read(b, offset);
|
|
||||||
outEntry = MemoryMarshal.Read<RomFsEntry<T>>(b);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GetEntryInternal(int offset, out RomFsEntry<T> outEntry, out string entryName)
|
private void GetEntryInternal(int offset, out RomFsEntry<T> outEntry, out ReadOnlySpan<byte> entryName)
|
||||||
{
|
{
|
||||||
GetEntryInternal(offset, out outEntry);
|
GetEntryInternal(offset, out outEntry);
|
||||||
|
|
||||||
|
@ -111,18 +84,7 @@ namespace LibHac.IO.RomFs
|
||||||
throw new InvalidDataException("Rom entry name is too long.");
|
throw new InvalidDataException("Rom entry name is too long.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var buf = new byte[outEntry.KeyLength];
|
entryName = EntryTable.AsSpan(offset + _sizeOfEntry, outEntry.KeyLength);
|
||||||
EntryStorage.Read(buf, offset + _sizeOfEntry);
|
|
||||||
entryName = Encoding.ASCII.GetString(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int GetBucket(int index)
|
|
||||||
{
|
|
||||||
Debug.Assert(index < HashBucketCount);
|
|
||||||
|
|
||||||
Span<byte> buf = stackalloc byte[4];
|
|
||||||
BucketStorage.Read(buf, index * 4);
|
|
||||||
return MemoryMarshal.Read<int>(buf);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
73
src/LibHac/IO/RomFs/RomFsEntries.cs
Normal file
73
src/LibHac/IO/RomFs/RomFsEntries.cs
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace LibHac.IO.RomFs
|
||||||
|
{
|
||||||
|
internal ref struct RomEntryKey
|
||||||
|
{
|
||||||
|
public ReadOnlySpan<byte> Name;
|
||||||
|
public int Parent;
|
||||||
|
|
||||||
|
public RomEntryKey(ReadOnlySpan<byte> name, int parent)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
Parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public uint GetRomHashCode()
|
||||||
|
{
|
||||||
|
uint hash = 123456789 ^ (uint)Parent;
|
||||||
|
|
||||||
|
foreach (byte c in Name)
|
||||||
|
{
|
||||||
|
hash = c ^ ((hash << 27) | (hash >> 5));
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal ref struct RomKeyValuePair<T> where T : unmanaged
|
||||||
|
{
|
||||||
|
public RomEntryKey Key;
|
||||||
|
public int Offset;
|
||||||
|
public T Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
|
public struct RomFsEntry<T> where T : unmanaged
|
||||||
|
{
|
||||||
|
public int Parent;
|
||||||
|
public T Value;
|
||||||
|
public int Next;
|
||||||
|
public int KeyLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
|
public struct FileRomEntry
|
||||||
|
{
|
||||||
|
public int NextSibling;
|
||||||
|
public RomFileInfo Info;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct RomFileInfo
|
||||||
|
{
|
||||||
|
public long Offset;
|
||||||
|
public long Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
|
public struct DirectoryRomEntry
|
||||||
|
{
|
||||||
|
public int NextSibling;
|
||||||
|
public FindPosition Pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct FindPosition
|
||||||
|
{
|
||||||
|
public int NextDirectory;
|
||||||
|
public int NextFile;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace LibHac.IO
|
namespace LibHac.IO
|
||||||
{
|
{
|
||||||
|
@ -111,6 +112,17 @@ namespace LibHac.IO
|
||||||
return arr;
|
return arr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static T[] ToArray<T>(this IStorage storage) where T : unmanaged
|
||||||
|
{
|
||||||
|
if (storage == null) return new T[0];
|
||||||
|
|
||||||
|
var arr = new T[storage.Length / Marshal.SizeOf<T>()];
|
||||||
|
Span<byte> dest = MemoryMarshal.Cast<T, byte>(arr.AsSpan());
|
||||||
|
|
||||||
|
storage.Read(dest, 0);
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
public static void CopyToStream(this IStorage input, Stream output, long length, IProgressReport progress = null)
|
public static void CopyToStream(this IStorage input, Stream output, long length, IProgressReport progress = null)
|
||||||
{
|
{
|
||||||
const int bufferSize = 0x8000;
|
const int bufferSize = 0x8000;
|
||||||
|
|
Loading…
Reference in a new issue