public override void Flush() { const int blockSize = 64; var offsetList = new int[entries.Count]; destination.WriteByte((byte)'T'); destination.WriteByte((byte)'X'); destination.WriteByte((byte)'A'); destination.WriteByte((byte)'G'); PTStream.WriteInt32BE(destination, entries.Count); var offset = PTMethods.RoundUp(0x8 + (entries.Count * 0x28), blockSize); for (int i = 0; i < entries.Count; i++) { var length = entries[i].Length; offsetList[i] = offset; PTStream.WriteInt32BE(destination, offset); PTStream.WriteInt32BE(destination, length); PTStream.WriteCString(destination, Path.GetFileNameWithoutExtension(entries[i].Name), 32); offset += PTMethods.RoundUp(length, blockSize); } for (int i = 0; i < entries.Count; i++) { destination.Position = offsetList[i]; PTStream.CopyToPadded(entries[i].Open(), destination, blockSize, 0); OnFileAdded(EventArgs.Empty); } }
public override void Flush() { // The start of the archive long offset = destination.Position; // Magic code "MRG0" destination.WriteByte((byte)'M'); destination.WriteByte((byte)'R'); destination.WriteByte((byte)'G'); destination.WriteByte((byte)'0'); // Number of entries in the archive PTStream.WriteInt32(destination, entries.Count); destination.Position += 8; // Write out the header for the archive int entryOffset = 16 + (entries.Count * 48); for (int i = 0; i < entries.Count; i++) { // Write out the file extension string fileExtension = Path.GetExtension(entries[i].Name); if (fileExtension != String.Empty) { fileExtension = fileExtension.Substring(1); } PTStream.WriteCString(destination, fileExtension, 4, Encoding.GetEncoding("Shift_JIS")); // Write out the offset, length, and filename (without the extension) PTStream.WriteInt32(destination, entryOffset); PTStream.WriteInt32(destination, entries[i].Length); destination.Position += 4; PTStream.WriteCString(destination, Path.GetFileNameWithoutExtension(entries[i].Name), 32, Encoding.GetEncoding("Shift_JIS")); entryOffset += PTMethods.RoundUp(entries[i].Length, 16); } // Write out the file data for each entry for (int i = 0; i < entries.Count; i++) { PTStream.CopyToPadded(entries[i].Open(), destination, 16, 0); // Call the file added event OnFileAdded(EventArgs.Empty); } }
public override void Flush() { const int tableStartPtr = 0x10; int dataStartPtr = PTMethods.RoundUp(tableStartPtr + (entries.Count * 0x30), 0x10); // just to be safe PTStream.WriteInt32BE(destination, entries.Count); PTStream.WriteInt32BE(destination, tableStartPtr); // pointer to table start PTStream.WriteInt32BE(destination, dataStartPtr); // pointer to data start PTStream.WriteInt32BE(destination, 0); // not 100% sure on this one int offset = dataStartPtr; using (var compressedStream = new MemoryStream()) { for (int i = 0; i < entries.Count; i++) { var entry = entries[i]; var entryStream = entry.Open(); _prsCompression.Compress(entryStream, compressedStream); compressedStream.Position = 0; PTStream.WriteCString(destination, entry.Name, 0x20); // name PTStream.WriteInt32BE(destination, i); // index PTStream.WriteInt32BE(destination, offset); // offset PTStream.WriteInt32BE(destination, (int)compressedStream.Length); // compressed length PTStream.WriteInt32BE(destination, (int)entryStream.Length); // uncompressed length Debug.Assert(destination.Position <= dataStartPtr, "Table overrun!"); var currentPos = destination.Position; destination.Position = offset; PTStream.CopyTo(compressedStream, destination); destination.Position = currentPos; offset += (int)compressedStream.Length; compressedStream.Position = 0; compressedStream.SetLength(0); OnFileAdded(EventArgs.Empty); } } }
public override void Flush() { // The start of the archive long offset = destination.Position; // Magic code "\0\0\0\0" destination.WriteByte(0); destination.WriteByte(0); destination.WriteByte(0); destination.WriteByte(0); // Number of entries in the archive PTStream.WriteInt32BE(destination, entries.Count); // Write out the header for the archive int entryOffset = PTMethods.RoundUp(8 + (entries.Count * 8), blockSize); int firstEntryOffset = entryOffset; for (int i = 0; i < entries.Count; i++) { PTStream.WriteInt32BE(destination, entryOffset); PTStream.WriteInt32BE(destination, entries[i].Length); entryOffset += PTMethods.RoundUp(entries[i].Length, blockSize); } // Pad before writing out the file data while ((destination.Position - offset) % blockSize != 0) { destination.WriteByte(0); } // Write out the file data for each entry for (int i = 0; i < entries.Count; i++) { PTStream.CopyToPadded(entries[i].Open(), destination, blockSize, 0); // Call the file added event OnFileAdded(EventArgs.Empty); } }
public override void Flush() { // The start of the archive long offset = destination.Position; // Magic code "AFS\0" destination.WriteByte((byte)'A'); destination.WriteByte((byte)'F'); destination.WriteByte((byte)'S'); destination.WriteByte(0); // Number of entries in the archive PTStream.WriteInt32(destination, entries.Count); // Write out the header for the archive int entryOffset = PTMethods.RoundUp(12 + (entries.Count * 8), blockSize); int firstEntryOffset = entryOffset; for (int i = 0; i < entries.Count; i++) { PTStream.WriteInt32(destination, entryOffset); PTStream.WriteInt32(destination, entries[i].Length); entryOffset += PTMethods.RoundUp(entries[i].Length, blockSize); } // If this is AFS v1, then the metadata offset is stored at 8 bytes before // the first entry offset. if (version == AfsVersion.Version1) { destination.Position = offset + firstEntryOffset - 8; } // Write out the metadata offset and length PTStream.WriteInt32(destination, entryOffset); PTStream.WriteInt32(destination, entries.Count * 48); destination.Position = offset + firstEntryOffset; // Write out the file data for each entry for (int i = 0; i < entries.Count; i++) { PTStream.CopyToPadded(entries[i].Open(), destination, blockSize, 0); // Call the file added event OnFileAdded(EventArgs.Empty); } // Write out the footer for the archive for (int i = 0; i < entries.Count; i++) { PTStream.WriteCString(destination, entries[i].Name, 32); // File timestamp if (HasTimestamps && !String.IsNullOrEmpty(entries[i].Path) && File.Exists(entries[i].Path)) { // File exists, let's read in the file timestamp FileInfo fileInfo = new FileInfo(entries[i].Path); PTStream.WriteInt16(destination, (short)fileInfo.LastWriteTime.Year); PTStream.WriteInt16(destination, (short)fileInfo.LastWriteTime.Month); PTStream.WriteInt16(destination, (short)fileInfo.LastWriteTime.Day); PTStream.WriteInt16(destination, (short)fileInfo.LastWriteTime.Hour); PTStream.WriteInt16(destination, (short)fileInfo.LastWriteTime.Minute); PTStream.WriteInt16(destination, (short)fileInfo.LastWriteTime.Second); } else { // File does not exist, just store all 0s PTStream.WriteInt16(destination, 0); PTStream.WriteInt16(destination, 0); PTStream.WriteInt16(destination, 0); PTStream.WriteInt16(destination, 0); PTStream.WriteInt16(destination, 0); PTStream.WriteInt16(destination, 0); } // Write out this data that I have no idea what its purpose is long oldPosition = destination.Position; byte[] buffer = new byte[4]; if (version == AfsVersion.Version1) { destination.Position = offset + 8 + (i * 8); } else { destination.Position = offset + 4 + (i * 4); } destination.Read(buffer, 0, 4); destination.Position = oldPosition; destination.Write(buffer, 0, 4); } // Finish padding out the archive while ((destination.Position - offset) % blockSize != 0) { destination.WriteByte(0); } }
public override void Flush() { // Determine the length of each entry in the header // and the flags that indicate what is stored in the header int entryLength = 2; ushort flags = 0; if (HasFilenames) { entryLength += 28; flags |= 0x8; } if (HasFormats) { entryLength += 2; flags |= 0x4; } if (HasDimensions) { entryLength += 2; flags |= 0x2; } if (HasGlobalIndexes) { entryLength += 4; flags |= 0x1; } // Write the start of the header destination.WriteByte((byte)'P'); destination.WriteByte((byte)'V'); destination.WriteByte((byte)'M'); destination.WriteByte((byte)'H'); // Offset of the first texture in the archive long entryOffset = PTMethods.RoundUp(12 + (entries.Count * entryLength), 16); PTStream.WriteInt32(destination, (int)entryOffset - 8); // Write out the flags PTStream.WriteUInt16(destination, flags); // Write out the number of entries PTStream.WriteUInt16(destination, (ushort)entries.Count); // We're going to be using this a few times. Might as well do this here long oldPosition; // Now, let's add the entries for (int i = 0; i < entries.Count; i++) { Stream entryData = entries[i].Open(); // We need to get some information about the texture. // We already checked to make sure this texture is a PVR. // No need to check it again. oldPosition = entryData.Position; VrSharp.PvrTexture.PvrTexture texture = new VrSharp.PvrTexture.PvrTexture(entryData); entryData.Position = oldPosition; // Write out the entry number PTStream.WriteUInt16(destination, (ushort)i); // Write the information for this entry in the header if (HasFilenames) { PTStream.WriteCString(destination, Path.GetFileNameWithoutExtension(entries[i].Name), 28); } if (HasFormats) { destination.WriteByte(0); destination.WriteByte((byte)texture.DataFormat); } if (HasDimensions) { ushort dimensions = 0; dimensions |= (ushort)(((byte)Math.Log(texture.TextureWidth, 2) - 2) & 0xF); dimensions |= (ushort)((((byte)Math.Log(texture.TextureHeight, 2) - 2) & 0xF) << 4); PTStream.WriteUInt16(destination, dimensions); } if (HasGlobalIndexes) { PTStream.WriteUInt32(destination, texture.GlobalIndex); } // Now write out the entry information oldPosition = destination.Position; destination.Position = entryOffset; entryData.Position += texture.PvrtOffset; PTStream.CopyToPadded(entryData, destination, 16, 0); entryOffset = destination.Position; destination.Position = oldPosition; // Call the file added event OnFileAdded(EventArgs.Empty); } }
public override void Flush() { // The start of the archive long offset = destination.Position; // Magic code "NSIF/NUIF" if (platform == SntPlatform.Ps2) { destination.WriteByte((byte)'N'); destination.WriteByte((byte)'S'); destination.WriteByte((byte)'I'); destination.WriteByte((byte)'F'); } else { destination.WriteByte((byte)'N'); destination.WriteByte((byte)'U'); destination.WriteByte((byte)'I'); destination.WriteByte((byte)'F'); } PTStream.WriteInt32(destination, 24); // Unknown PTStream.WriteInt32(destination, 1); // Unknown PTStream.WriteInt32(destination, 32); // Offset of the NSTL/NUTL chunk? // Calculate the size of the NSTL chunk int NSTLLength = 0; for (int i = 0; i < entries.Count; i++) { NSTLLength += PTMethods.RoundUp(entries[i].Length, 8); } PTStream.WriteInt32(destination, PTMethods.RoundUp(28 + (entries.Count * 28), 8) + NSTLLength); PTStream.WriteInt32(destination, PTMethods.RoundUp(60 + (entries.Count * 28), 8) + NSTLLength); PTStream.WriteInt32(destination, 24 + (entries.Count * 4)); PTStream.WriteInt32(destination, 1); // NSTL/NUTL chunk if (platform == SntPlatform.Ps2) { destination.WriteByte((byte)'N'); destination.WriteByte((byte)'S'); destination.WriteByte((byte)'T'); destination.WriteByte((byte)'L'); } else { destination.WriteByte((byte)'N'); destination.WriteByte((byte)'U'); destination.WriteByte((byte)'T'); destination.WriteByte((byte)'L'); } PTStream.WriteInt32(destination, PTMethods.RoundUp(20 + (entries.Count * 28), 8) + NSTLLength); PTStream.WriteInt32(destination, 16); PTStream.WriteInt32(destination, 0); PTStream.WriteInt32(destination, entries.Count); PTStream.WriteInt32(destination, 28); PTStream.WriteInt32(destination, 28 + (entries.Count * 20)); // Write out crap bytes for (int i = 0; i < entries.Count; i++) { PTStream.WriteInt32(destination, 1); PTStream.WriteInt32(destination, 0); destination.Write(new byte[] { 1, 0, 1, 0 }, 0, 4); PTStream.WriteInt32(destination, i); PTStream.WriteInt32(destination, 0); } // Write out the header for the archive int entryOffset = 60 + (entries.Count * 28); for (int i = 0; i < entries.Count; i++) { PTStream.WriteInt32(destination, entries[i].Length); PTStream.WriteInt32(destination, entryOffset - 32); entryOffset += PTMethods.RoundUp(entries[i].Length, 4); } // Write out the file data for each entry for (int i = 0; i < entries.Count; i++) { PTStream.CopyToPadded(entries[i].Open(), destination, 4, 0); // Call the file added event OnFileAdded(EventArgs.Empty); } // Pad before writing out the NOF0 chunk while ((destination.Position - offset) % 8 != 0) { destination.WriteByte(0); } // NOF0 chunk destination.WriteByte((byte)'N'); destination.WriteByte((byte)'O'); destination.WriteByte((byte)'F'); destination.WriteByte((byte)'0'); // Write out crap bytes PTStream.WriteInt32(destination, PTMethods.RoundUp(28 + (entries.Count * 4), 8)); PTStream.WriteInt32(destination, entries.Count + 2); PTStream.WriteInt32(destination, 0); PTStream.WriteInt32(destination, 20); // Write out more unknown stuff for (int i = 0; i < entries.Count; i++) { PTStream.WriteInt32(destination, 32 + (entries.Count * 20) + (i * 8)); } PTStream.WriteInt32(destination, 24); // Pad before we write NEND // Finish padding out the archive while ((destination.Position - offset) % 16 != 0) { destination.WriteByte(0); } destination.WriteByte((byte)'N'); destination.WriteByte((byte)'E'); destination.WriteByte((byte)'N'); destination.WriteByte((byte)'D'); while ((destination.Position - offset) % 16 != 0) { destination.WriteByte(0); } }
public override void Flush() { // The start of the archive long offset = destination.Position; // Puyo Tools is only capable of building U8 archives that do not contain directories. // It's just very difficult to do with the way Puyo Tools is structured. // First things first, let's get the header size int headerSize = ((entries.Count + 1) * 12) + 1; for (int i = 0; i < entries.Count; i++) { headerSize += entries[i].Name.Length + 1; } // Get the name and data offset int nameOffset = 0; int dataOffset = PTMethods.RoundUp(0x20 + headerSize, 32); // Start writing out the header destination.WriteByte((byte)'U'); destination.WriteByte(0xAA); destination.WriteByte((byte)'8'); destination.WriteByte((byte)'-'); PTStream.WriteUInt32BE(destination, 0x20); // Root node offset (always 0x20) PTStream.WriteInt32BE(destination, headerSize); // Header size PTStream.WriteInt32BE(destination, dataOffset); // Data offset // Pad while ((destination.Position - offset) % 32 != 0) { destination.WriteByte(0); } // Write the root node destination.WriteByte(1); destination.WriteByte((byte)(nameOffset >> 16)); PTStream.WriteUInt16BE(destination, (ushort)(nameOffset & 0xFFFF)); PTStream.WriteInt32BE(destination, 0); PTStream.WriteInt32BE(destination, entries.Count + 1); nameOffset++; // Write out the file nodes for (int i = 0; i < entries.Count; i++) { destination.WriteByte(0); destination.WriteByte((byte)(nameOffset >> 16)); PTStream.WriteUInt16BE(destination, (ushort)(nameOffset & 0xFFFF)); PTStream.WriteInt32BE(destination, dataOffset); PTStream.WriteInt32BE(destination, entries[i].Length); nameOffset += entries[i].Name.Length + 1; dataOffset += PTMethods.RoundUp(entries[i].Length, 32); } // Write out the filename table PTStream.WriteCString(destination, String.Empty, 1); for (int i = 0; i < entries.Count; i++) { PTStream.WriteCString(destination, entries[i].Name, entries[i].Name.Length + 1); } // Pad while ((destination.Position - offset) % 32 != 0) { destination.WriteByte(0); } // Write the file data for (int i = 0; i < entries.Count; i++) { PTStream.CopyToPadded(entries[i].Open(), destination, 32, 0); // Call the file added event OnFileAdded(EventArgs.Empty); } }
// Build GAME.DAT using files from the specified directory public static void Build(string executable, string inDir, string gameDat) { Console.Write("\nBuilding ..."); using (FileStream exeStream = File.Open(executable, FileMode.Open, FileAccess.ReadWrite)) { // Determine the game version GameVersion gameVersion = GameVersion.Unknown; if (exeStream.Length == 2678816) // Wii version { gameVersion = GameVersion.Wii; } else if (exeStream.Length == 2716853) // PSP version { gameVersion = GameVersion.PSP; } else { return; } uint numFiles = 0; if (gameVersion == GameVersion.Wii) { exeStream.Position = 0x18A058; } else if (gameVersion == GameVersion.PSP) { exeStream.Position = 0x1978FC; } // Get the number of files in the file entry table exeStream.Position += 20; if (gameVersion == GameVersion.Wii) { numFiles = PTStream.ReadUInt32BE(exeStream); exeStream.Position += 152; // Now go to the position of the first file in the file entry table } else if (gameVersion == GameVersion.PSP) { numFiles = PTStream.ReadUInt32(exeStream); exeStream.Position += 136; // Now go to the position of the first file in the file entry table } string[] fileList = new string[numFiles]; Console.WriteLine(" " + numFiles + " files detected."); // Now let's make sure all the files exist for (uint i = 0; i < numFiles; i++) { string[] file = Directory.GetFiles(inDir, i.ToString("D4") + ".*"); if (file.Length == 0) { Console.WriteLine("{0} does not exist. Terminating.", i.ToString("D4")); return; } else if (file.Length > 1) { Console.WriteLine("Multiple copies of {0} exist. Terminating.", i.ToString("D4")); return; } fileList[i] = file[0]; } // Ok, looks like we're good. Let's build GAME.DAT using (FileStream gameDatStream = File.Create(gameDat)) { uint offset = 0; for (uint i = 0; i < numFiles; i++) { Console.Write("Adding file " + Path.GetFileName(fileList[i]) + " ... "); // Go to the next file entry NextFileEntry(exeStream); exeStream.Position += 4; using (FileStream inStream = File.OpenRead(fileList[i])) { if (gameVersion == GameVersion.Wii) { PTStream.WriteUInt32BE(exeStream, offset); PTStream.WriteUInt32BE(exeStream, (uint)inStream.Length); } else if (gameVersion == GameVersion.PSP) { PTStream.WriteUInt32(exeStream, offset); PTStream.WriteUInt32(exeStream, (uint)inStream.Length); } PTStream.CopyPartToPadded(inStream, gameDatStream, (int)inStream.Length, 2048, 0); offset += (uint)PTMethods.RoundUp((int)inStream.Length, 2048); } exeStream.Position += 12; Console.WriteLine("OK"); } } } }