using System; using System.IO; using System.Text; using Force.Crc32; namespace UnityFileApi { // This class can be used to read typed data from a UnityFile. Is uses a buffer for better performance. public class UnityFileReader : IDisposable { UnityFile m_File; byte[] m_Buffer; long m_BufferStartInFile; long m_BufferEndInFile; public long Length { get; } public UnityFileReader(string path, int bufferSize) { m_Buffer = new byte[bufferSize]; m_BufferStartInFile = 0; m_BufferEndInFile = 0; m_File = UnityFileSystem.OpenFile(path); Length = m_File.GetSize(); } int GetBufferOffset(long fileOffset, int count) { // Should we update the buffer? if (fileOffset < m_BufferStartInFile || fileOffset + count > m_BufferEndInFile) { if (count > m_Buffer.Length) throw new IOException("Requested size is larger than cache size"); m_BufferStartInFile = m_File.Seek(fileOffset); if (m_BufferStartInFile != fileOffset) throw new IOException("Invalid file offset"); m_BufferEndInFile = m_File.Read(m_Buffer.Length, m_Buffer); m_BufferEndInFile += m_BufferStartInFile; } return (int)(fileOffset - m_BufferStartInFile); } public void ReadArray(long fileOffset, int size, Array dest) { var offset = GetBufferOffset(fileOffset, size); Buffer.BlockCopy(m_Buffer, offset, dest, 0, size); } public string ReadString(long fileOffset, int size) { var offset = GetBufferOffset(fileOffset, size); return Encoding.Default.GetString(m_Buffer, offset, size); } public float ReadFloat(long fileOffset) { var offset = GetBufferOffset(fileOffset, 4); return BitConverter.ToSingle(m_Buffer, offset); } public double ReadDouble(long fileOffset) { var offset = GetBufferOffset(fileOffset, 8); return BitConverter.ToDouble(m_Buffer, offset); } public long ReadInt64(long fileOffset) { var offset = GetBufferOffset(fileOffset, 8); return BitConverter.ToInt64(m_Buffer, offset); } public ulong ReadUInt64(long fileOffset) { var offset = GetBufferOffset(fileOffset, 8); return BitConverter.ToUInt64(m_Buffer, offset); } public int ReadInt32(long fileOffset) { var offset = GetBufferOffset(fileOffset, 4); return BitConverter.ToInt32(m_Buffer, offset); } public uint ReadUInt32(long fileOffset) { var offset = GetBufferOffset(fileOffset, 4); return BitConverter.ToUInt32(m_Buffer, offset); } public short ReadInt16(long fileOffset) { var offset = GetBufferOffset(fileOffset, 2); return BitConverter.ToInt16(m_Buffer, offset); } public ushort ReadUInt16(long fileOffset) { var offset = GetBufferOffset(fileOffset, 2); return BitConverter.ToUInt16(m_Buffer, offset); } public sbyte ReadInt8(long fileOffset) { var offset = GetBufferOffset(fileOffset, 1); return (sbyte)m_Buffer[offset]; } public byte ReadUInt8(long fileOffset) { var offset = GetBufferOffset(fileOffset, 1); return m_Buffer[offset]; } public uint ComputeCRC(long fileOffset, int size, uint crc32 = 0) { var readSize = size > m_Buffer.Length ? m_Buffer.Length : size; var readBytes = 0; while (readBytes < size) { var offset = GetBufferOffset(fileOffset, readSize); crc32 = Crc32Algorithm.Append(crc32, m_Buffer, offset, readSize); readBytes += readSize; } return crc32; } public void Dispose() { m_File.Dispose(); } } }