public void ParseToc(TocFileNotify notifyFn) { uint[] offsets = new uint[Files]; // this merry dance is because unsigned things are second-class // citizens when it comes to marshalling byte[] offsetBytes = new byte[offsets.Length * sizeof(uint)]; // offsets start at 0x10 IntPtr offStart = new IntPtr(volMappingAddrAsNum + 0x10); Marshal.Copy(offStart, offsetBytes, 0, offsetBytes.Length); Buffer.BlockCopy(offsetBytes, 0, offsets, 0, offsetBytes.Length); // offsets[0] = offset of the offsets // offsets[1] = toc // offsets[2...n] = files uint unusedLastSectorBytes = 0; uint tocOffset = SanitiseOffset(offsets[1], out unusedLastSectorBytes); uint notNeeded = 0; uint firstFileOffset = SanitiseOffset(offsets[2], out notNeeded); uint tocLen = firstFileOffset - tocOffset - unusedLastSectorBytes; byte[] toc = new byte[tocLen]; IntPtr tocStart = new IntPtr(volMappingAddrAsNum + tocOffset); Marshal.Copy(tocStart, toc, 0, (int)tocLen); embeddedFiles.Capacity = TotalEntries; BinaryReader reader = new BinaryReader(new MemoryStream(toc, false)); ReadEmbeddedFileData(embeddedFiles, TotalEntries, reader, offsets, notifyFn, volMappingAddrAsNum); }
static void ReadEmbeddedFileData( List <EmbeddedFileInfo> files, short numFiles, BinaryReader src, uint[] fileOffsets, TocFileNotify notifyFn, long volStartAddress ) { List <string> directories = new List <string>(); string dirBeingParsed = String.Empty; EmbeddedFileInfo lastFile = null; int dirIndex = 0, dirInsertIndex = 0; short i = 0; for (; i < numFiles; ++i) { EmbeddedFileInfo efi = new EmbeddedFileInfo(); efi.dateTime = src.ReadInt32(); int offsetIndex = src.ReadInt16(); byte flags = src.ReadByte(); uint bytesFromLastSector = 0; // flags // 0x00 = regular file // 0x01 = directory // 0x80 = last entry for this directory if (offsetIndex == 0 || (flags & 0x1) == 0x1) { // '..' entries and directories don't have an offset efi.fileAddress = 0; efi.pFileStart = IntPtr.Zero; } else { efi.fileAddress = SanitiseOffset(fileOffsets[offsetIndex], out bytesFromLastSector); efi.pFileStart = new IntPtr(volStartAddress + efi.fileAddress); } // cache this here for now, it's not the actual size, but it's used in its calculation efi.size = bytesFromLastSector; efi.name = Path.Combine(dirBeingParsed, Encoding.ASCII.GetString(src.ReadBytes(25)).TrimEnd('\0')); if ((flags & 0x1) != 0) { // '..' don't need saving if (!efi.name.EndsWith("..")) { // if we're parsing one, this is a child directory // its' contents are after the one being parsed, not at the end of the list if (dirBeingParsed != String.Empty) { directories.Insert(dirInsertIndex++, efi.name); } else { directories.Add(efi.name); } } } else { if (lastFile != null) { uint realSize = efi.fileAddress - lastFile.fileAddress - lastFile.size; // realsize can be 0 if files are 0 bytes (everything in the replay dir) lastFile.size = realSize; if (notifyFn != null) { notifyFn(lastFile); } } lastFile = efi; } // if this is the last entry for this directory // change which dir we use as the parent if ((flags & 0x80) != 0) { // directories are listed in order of first encounter // so a simple iteration will suffice dirBeingParsed = (dirIndex < directories.Count) ? directories[dirIndex++] : String.Empty; dirInsertIndex = dirIndex; } // '..' entries don't need to be saved if (offsetIndex != 0) { files.Add(efi); } } // the last fileOffset is the length of the whole file lastFile.size = fileOffsets[fileOffsets.Length - 1] - lastFile.fileAddress - lastFile.size; lastFile.size = (lastFile.size > 0) ? lastFile.size : 0x800; if (notifyFn != null) { notifyFn(lastFile); } }