public CriAfs2Archive(FileStream fs, long offset) { ushort previousCueId = ushort.MaxValue; if (IsCriAfs2Archive(fs, offset)) { this.SourceFile = fs.Name; long afs2FileSize = fs.Length; this.MagicBytes = ParseFile.ParseSimpleOffset(fs, offset, SIGNATURE.Length); this.Version = ParseFile.ParseSimpleOffset(fs, offset + 4, 4); this.FileCount = ParseFile.ReadUintLE(fs, offset + 8); // setup offset field size int offsetFieldSize = this.Version[1]; // known values: 2 and 4. 4 is most common. I've only seen 2 in 'se_enemy_gurdon_galaga_bee.acb' from Sonic Lost World. uint offsetMask = 0; for (int j = 0; j < offsetFieldSize; j++) { offsetMask |= (uint)((byte)0xFF << (j * 8)); } if (this.FileCount > ushort.MaxValue) { throw new FormatException(String.Format("ERROR, file count exceeds max value for ushort. Please report this at official feedback forums (see 'Other' menu item).", fs.Name)); } this.ByteAlignment = ParseFile.ReadUintLE(fs, offset + 0xC); this.Files = new Dictionary <ushort, CriAfs2File>((int)this.FileCount); CriAfs2File dummy; for (ushort i = 0; i < this.FileCount; i++) { dummy = new CriAfs2File(); dummy.CueId = ParseFile.ReadUshortLE(fs, offset + (0x10 + (2 * i))); dummy.FileOffsetRaw = ParseFile.ReadUintLE(fs, offset + (0x10 + (this.FileCount * 2) + (offsetFieldSize * i))); // mask off unneeded info dummy.FileOffsetRaw &= offsetMask; // add offset dummy.FileOffsetRaw += offset; // for AFS2 files inside of other files (ACB, etc.) // set file offset to byte alignment if ((dummy.FileOffsetRaw % this.ByteAlignment) != 0) { dummy.FileOffsetByteAligned = MathUtil.RoundUpToByteAlignment(dummy.FileOffsetRaw, this.ByteAlignment); } else { dummy.FileOffsetByteAligned = dummy.FileOffsetRaw; } //--------------- // set file size //--------------- // last file will use final offset entry if (i == this.FileCount - 1) { dummy.FileLength = (ParseFile.ReadUintLE(fs, offset + (0x10 + (this.FileCount * 2) + ((offsetFieldSize) * i)) + offsetFieldSize) + offset) - dummy.FileOffsetByteAligned; } // else set length for previous cue id if (previousCueId != ushort.MaxValue) { this.Files[previousCueId].FileLength = dummy.FileOffsetRaw - this.Files[previousCueId].FileOffsetByteAligned; } this.Files.Add(dummy.CueId, dummy); previousCueId = dummy.CueId; } // for (uint i = 0; i < this.FileCount; i++) } else { throw new FormatException(String.Format("AFS2 magic bytes not found at offset: 0x{0}.", offset.ToString("X8"))); } }
public CriCpkDirectory GetDirectoryForItoc(FileStream fs, CriUtfTable cpkUtf, string BaseDirectoryName, long volumeBaseOffset) { ulong currentOffset = 0; CriUtfTocFileInfo fileInfo = new CriUtfTocFileInfo(); CriAfs2File afs2File = new CriAfs2File(); // get content offset and align ulong contentOffset = (ulong)CriUtfTable.GetUtfFieldForRow(cpkUtf, 0, "ContentOffset"); ushort align = (ushort)CriUtfTable.GetUtfFieldForRow(cpkUtf, 0, "Align"); // build direcory path CriCpkDirectory baseDirectory = new CriCpkDirectory(this.SourceFileName, BaseDirectoryName, String.Empty); CriCpkFile tempFile; // read file groups uint filesH = 0; uint filesL = 0; object filesHObj = CriUtfTable.GetUtfFieldForRow(this.ItocUtf, 0, "FilesH"); //count of files in DataH object filesLObj = CriUtfTable.GetUtfFieldForRow(this.ItocUtf, 0, "FilesL"); // count of files in DataL if (filesHObj != null) { filesH = (uint)filesHObj; } if (filesHObj != null) { filesL = (uint)filesLObj; } if ((filesH > 0) || (filesL > 0)) { Dictionary <string, CriUtfTocFileInfo> fileList = new Dictionary <string, CriUtfTocFileInfo>(); if (filesH > 0) { // read DataH group CriUtfTable dataH = new CriUtfTable(); dataH.Initialize(fs, (long)CriUtfTable.GetOffsetForUtfFieldForRow(this.ItocUtf, 0, "DataH")); for (int i = 0; i < dataH.Rows.GetLength(0); i++) { fileInfo = GetUtfItocFileInfo(dataH, i); fileList.Add(fileInfo.FileName, fileInfo); } } if (filesL > 0) { // read DataL group CriUtfTable dataL = new CriUtfTable(); dataL.Initialize(fs, (long)CriUtfTable.GetOffsetForUtfFieldForRow(this.ItocUtf, 0, "DataL")); for (int i = 0; i < dataL.Rows.GetLength(0); i++) { fileInfo = GetUtfItocFileInfo(dataL, i); fileList.Add(fileInfo.FileName, fileInfo); } } // initialize current offset currentOffset = contentOffset; // populate offsets for files var keys = fileList.Keys.ToList(); keys.Sort(); foreach (string key in keys) { // afs2 file for ACB extraction afs2File = new CriAfs2File(); afs2File.CueId = Convert.ToUInt16(fileList[key].FileName); // ??? @TODO maybe key is enough? need to check. afs2File.FileOffsetRaw = volumeBaseOffset + (long)currentOffset; // align offset if (currentOffset % align != 0) { currentOffset = MathUtil.RoundUpToByteAlignment(currentOffset, align); } // update file info fileList[key].FileOffset = (ulong)volumeBaseOffset + currentOffset; fileList[key].FileName += ".bin"; // afs2 file for ACB extraction afs2File.FileOffsetByteAligned = (long)fileList[key].FileOffset; afs2File.FileLength = fileList[key].FileSize; // increment current offset currentOffset += fileList[key].FileSize; // create file and add to base directory tempFile = new CriCpkFile(BaseDirectoryName, this.SourceFileName, fileList[key].FileName, (long)fileList[key].FileOffset, this.VolumeBaseOffset, (long)fileList[key].FileOffset, fileList[key].FileSize, fileList[key].ExtractSize); baseDirectory.FileArray.Add(tempFile); // add afs2 file to ItocFiles for ACB extraction this.ItocFiles.Add(afs2File.CueId, afs2File); } // foreach (string key in keys) } // if ((filesH > 0) || (filesL > 0)) return(baseDirectory); }