// initialize from existing CPK public CpkBuilder(DuplicatableStream stream, Endianness endian = Endianness.BigEndian) { // should start with CPK and some unknown header bytes long cpkHeaderOffset = stream.Position; uint cpkMagic = stream.ReadUInt32(Endianness.LittleEndian); if (cpkMagic != 0x204B5043) { throw new Exception("wrong CPK magic"); } UnknownCpkHeaderBytes = stream.ReadBytes(12); // main UTF table should follow MainUtfTable = new UtfBuilder(stream, endian); if (MainUtfTable.Rows.Count != 1) { throw new Exception("wrong rowcount in main UTF table"); } UnknownPostCpkHeaderBytes = stream.ReadBytes(4); while (stream.ReadByte() == 0) { ; } stream.Position = stream.Position - 1; CopyrightText = stream.ReadBytes(stream.Position.Align(0x800, cpkHeaderOffset) - stream.Position); // or something ulong tocOffset = (ulong)MainUtfTable.Rows[0].Cells[MainUtfTable.FindColumnIndex("TocOffset")].Data; //ulong tocSize = (ulong)MainUtfTable.Rows[0].Cells[MainUtfTable.FindColumnIndex("TocSize")].Data; if (tocOffset != 0) { stream.Position = cpkHeaderOffset + (long)tocOffset; uint magic = stream.ReadUInt32(Endianness.LittleEndian); if (magic != 0x20434F54) { throw new Exception("wrong TOC magic"); } UnknownTocHeaderBytes = stream.ReadBytes(12); TocUtfTable = new UtfBuilder(stream, endian); } ulong itocOffset = (ulong)MainUtfTable.Rows[0].Cells[MainUtfTable.FindColumnIndex("ItocOffset")].Data; //ulong itocSize = (ulong)MainUtfTable.Rows[0].Cells[MainUtfTable.FindColumnIndex("ItocSize")].Data; if (itocOffset != 0) { stream.Position = cpkHeaderOffset + (long)itocOffset; uint magic = stream.ReadUInt32(Endianness.LittleEndian); if (magic != 0x434F5449) { throw new Exception("wrong ITOC magic"); } UnknownItocHeaderBytes = stream.ReadBytes(12); ItocUtfTable = new UtfBuilder(stream, endian); } ulong etocOffset = (ulong)MainUtfTable.Rows[0].Cells[MainUtfTable.FindColumnIndex("EtocOffset")].Data; //ulong etocSize = (ulong)MainUtfTable.Rows[0].Cells[MainUtfTable.FindColumnIndex("EtocSize")].Data; if (etocOffset != 0) { stream.Position = cpkHeaderOffset + (long)etocOffset; uint magic = stream.ReadUInt32(Endianness.LittleEndian); if (magic != 0x434F5445) { throw new Exception("wrong ETOC magic"); } UnknownEtocHeaderBytes = stream.ReadBytes(12); EtocUtfTable = new UtfBuilder(stream, endian); } ulong gtocOffset = (ulong)MainUtfTable.Rows[0].Cells[MainUtfTable.FindColumnIndex("GtocOffset")].Data; //ulong gtocSize = (ulong)MainUtfTable.Rows[0].Cells[MainUtfTable.FindColumnIndex("GtocSize")].Data; if (gtocOffset != 0) { // haven't actually seen this, but would make sense... stream.Position = cpkHeaderOffset + (long)gtocOffset; uint magic = stream.ReadUInt32(Endianness.LittleEndian); if (magic != 0x434F5447) { throw new Exception("wrong GTOC magic"); } UnknownGtocHeaderBytes = stream.ReadBytes(12); GtocUtfTable = new UtfBuilder(stream, endian); } ulong contentOffset = (ulong)MainUtfTable.Rows[0].Cells[MainUtfTable.FindColumnIndex("ContentOffset")].Data; //ulong contentSize = (ulong)MainUtfTable.Rows[0].Cells[MainUtfTable.FindColumnIndex("ContentSize")].Data; uint fileCount = (uint)MainUtfTable.Rows[0].Cells[MainUtfTable.FindColumnIndex("Files")].Data; if (TocUtfTable == null || TocUtfTable.Rows.Count != fileCount) { throw new Exception("invalid TOC?"); } Files = new List <CpkFile>((int)fileCount); int columnDirName = TocUtfTable.FindColumnIndex("DirName"); int columnFileName = TocUtfTable.FindColumnIndex("FileName"); int columnFileSize = TocUtfTable.FindColumnIndex("FileSize"); int columnExtractSize = TocUtfTable.FindColumnIndex("ExtractSize"); int columnFileOffset = TocUtfTable.FindColumnIndex("FileOffset"); int columnFileIndexId = TocUtfTable.FindColumnIndex("ID"); long baseFileOffset = cpkHeaderOffset + ((contentOffset < tocOffset) ? (long)contentOffset : (long)tocOffset); for (int i = 0; i < (int)fileCount; ++i) { var f = new CpkFile(); f.Directory = (string)TocUtfTable.Rows[i].Cells[columnDirName].Data; f.Name = (string)TocUtfTable.Rows[i].Cells[columnFileName].Data; uint fileSize = (uint)TocUtfTable.Rows[i].Cells[columnFileSize].Data; f.DecompressedSize = (uint)TocUtfTable.Rows[i].Cells[columnExtractSize].Data; ulong fileOffset = (ulong)TocUtfTable.Rows[i].Cells[columnFileOffset].Data; f.FileStream = new HyoutaUtils.Streams.PartialStream(stream, baseFileOffset + (long)fileOffset, fileSize); f.ID = (uint)TocUtfTable.Rows[i].Cells[columnFileIndexId].Data; Files.Add(f); } return; }