public override byte[] Encode(byte[] input, int width, int height, VrPixelCodec PixelCodec) { int offset = 0; byte[] output = new byte[width * height * 2]; for (int y = 0; y < height; y += 4) { for (int x = 0; x < width; x += 4) { for (int y2 = 0; y2 < 4; y2++) { for (int x2 = 0; x2 < 4; x2++) { ushort pixel = 0x0000; if (input[((((y + y2) * width) + (x + x2)) * 4) + 3] <= 0xDA) // Argb3444 { pixel |= (ushort)((input[((((y + y2) * width) + (x + x2)) * 4) + 3] >> 5) << 12); pixel |= (ushort)((input[((((y + y2) * width) + (x + x2)) * 4) + 2] >> 4) << 8); pixel |= (ushort)((input[((((y + y2) * width) + (x + x2)) * 4) + 1] >> 4) << 4); pixel |= (ushort)((input[((((y + y2) * width) + (x + x2)) * 4) + 0] >> 4) << 0); //pixel |= (ushort)(((input[((((y + y2) * width) + (x + x2)) * 4) + 3] * 0x07 / 0xFF) & 0x07) << 12); //pixel |= (ushort)(((input[((((y + y2) * width) + (x + x2)) * 4) + 2] * 0x0F / 0xFF) & 0x0F) << 8); //pixel |= (ushort)(((input[((((y + y2) * width) + (x + x2)) * 4) + 1] * 0x0F / 0xFF) & 0x0F) << 4); //pixel |= (ushort)(((input[((((y + y2) * width) + (x + x2)) * 4) + 0] * 0x0F / 0xFF) & 0x0F) << 0); } else // Rgb555 { pixel |= 0x8000; pixel |= (ushort)((input[((((y + y2) * width) + (x + x2)) * 4) + 2] >> 3) << 10); pixel |= (ushort)((input[((((y + y2) * width) + (x + x2)) * 4) + 1] >> 3) << 5); pixel |= (ushort)((input[((((y + y2) * width) + (x + x2)) * 4) + 0] >> 3) << 0); //pixel |= (ushort)(((input[((((y + y2) * width) + (x + x2)) * 4) + 2] * 0x1F / 0xFF) & 0x1F) << 10); //pixel |= (ushort)(((input[((((y + y2) * width) + (x + x2)) * 4) + 1] * 0x1F / 0xFF) & 0x1F) << 5); //pixel |= (ushort)(((input[((((y + y2) * width) + (x + x2)) * 4) + 0] * 0x1F / 0xFF) & 0x1F) << 0); } PTMethods.GetBytesBE(pixel).CopyTo(output, offset); offset += 2; } } } } return(output); }
public override void Flush() { // The start of the archive long offset = destination.Position; // Magic code "TEX0" destination.WriteByte((byte)'T'); destination.WriteByte((byte)'E'); destination.WriteByte((byte)'X'); 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 * 32); 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); PTStream.WriteCString(destination, Path.GetFileNameWithoutExtension(entries[i].Name), 20, 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); } }
/// <summary> /// Determines if this is a PVR texture. /// </summary> /// <param name="source">Byte array containing the data.</param> /// <param name="offset">The offset in the byte array to start at.</param> /// <param name="length">Length of the data (in bytes).</param> /// <returns>True if this is a PVR texture, false otherwise.</returns> public static bool Is(byte[] source, int offset, int length) { // GBIX and PVRT //if (length >= 0x20 && // PTMethods.Contains(source, offset + 0x00, Encoding.UTF8.GetBytes("GBIX")) && // PTMethods.Contains(source, offset + 0x10, Encoding.UTF8.GetBytes("PVRT")) && // source[offset + 0x19] < 0x60 && // BitConverter.ToUInt32(source, offset + 0x14) == length - 24) if (length >= 0x20 && PTMethods.Contains(source, offset + 0x00, Encoding.UTF8.GetBytes("GBIX")) && PTMethods.Contains(source, offset + 0x10, Encoding.UTF8.GetBytes("PVRT"))) { return(true); } // PVRT (and no GBIX chunk) else if (length >= 0x10 && PTMethods.Contains(source, offset + 0x00, Encoding.UTF8.GetBytes("PVRT")) && source[offset + 0x09] < 0x60 && BitConverter.ToUInt32(source, offset + 0x04) == length - 8) { return(true); } // GBIX and PVRT with RLE compression else if (length >= 0x24 && PTMethods.Contains(source, offset + 0x04, Encoding.UTF8.GetBytes("GBIX")) && PTMethods.Contains(source, offset + 0x14, Encoding.UTF8.GetBytes("PVRT")) && source[offset + 0x1D] < 0x60 && BitConverter.ToUInt32(source, offset + 0x18) == BitConverter.ToUInt32(source, offset + 0x00) - 24) { return(true); } // PVRT (and no GBIX chunk) with RLE compression else if (length >= 0x14 && PTMethods.Contains(source, offset + 0x04, Encoding.UTF8.GetBytes("PVRT")) && source[offset + 0x0D] < 0x60 && BitConverter.ToUInt32(source, offset + 0x08) == BitConverter.ToUInt32(source, offset + 0x00) - 8) { return(true); } return(false); }
public override byte[] Decode(byte[] input, int offset, int width, int height, VrPixelCodec PixelCodec) { byte[] output = new byte[width * height * 4]; for (int y = 0; y < height; y += 4) { for (int x = 0; x < width; x += 4) { for (int y2 = 0; y2 < 4; y2++) { for (int x2 = 0; x2 < 4; x2++) { ushort pixel = PTMethods.ToUInt16BE(input, offset); if ((pixel & 0x8000) != 0) // Rgb555 { output[((((y + y2) * width) + (x + x2)) * 4) + 3] = 0xFF; output[((((y + y2) * width) + (x + x2)) * 4) + 2] = (byte)(((pixel >> 10) & 0x1F) * 0xFF / 0x1F); output[((((y + y2) * width) + (x + x2)) * 4) + 1] = (byte)(((pixel >> 5) & 0x1F) * 0xFF / 0x1F); output[((((y + y2) * width) + (x + x2)) * 4) + 0] = (byte)(((pixel >> 0) & 0x1F) * 0xFF / 0x1F); //output[((((y + y2) * width) + (x + x2)) * 4) + 3] = 0xFF; //output[((((y + y2) * width) + (x + x2)) * 4) + 2] = (byte)(((pixel >> 10) & 0x1F) << 11); //output[((((y + y2) * width) + (x + x2)) * 4) + 1] = (byte)(((pixel >> 5) & 0x1F) << 11); //output[((((y + y2) * width) + (x + x2)) * 4) + 0] = (byte)(((pixel >> 0) & 0x1F) << 11); } else // Argb3444 { output[((((y + y2) * width) + (x + x2)) * 4) + 3] = (byte)(((pixel >> 12) & 0x07) * 0xFF / 0x07); output[((((y + y2) * width) + (x + x2)) * 4) + 2] = (byte)(((pixel >> 8) & 0x0F) * 0xFF / 0x0F); output[((((y + y2) * width) + (x + x2)) * 4) + 1] = (byte)(((pixel >> 4) & 0x0F) * 0xFF / 0x0F); output[((((y + y2) * width) + (x + x2)) * 4) + 0] = (byte)(((pixel >> 0) & 0x0F) * 0xFF / 0x0F); //output[((((y + y2) * width) + (x + x2)) * 4) + 3] = (byte)(((pixel >> 12) & 0x07) << 13); //output[((((y + y2) * width) + (x + x2)) * 4) + 2] = (byte)(((pixel >> 8) & 0x0F) << 12); //output[((((y + y2) * width) + (x + x2)) * 4) + 1] = (byte)(((pixel >> 4) & 0x0F) << 12); //output[((((y + y2) * width) + (x + x2)) * 4) + 0] = (byte)(((pixel >> 0) & 0x0F) << 12); } offset += 2; } } } } return(output); }
public override void DecodePixel(byte[] source, int sourceIndex, byte[] destination, int destinationIndex) { ushort pixel = PTMethods.ToUInt16BE(source, sourceIndex); if ((pixel & 0x8000) != 0) // Rgb555 { destination[destinationIndex + 3] = 0xFF; destination[destinationIndex + 2] = (byte)(((pixel >> 10) & 0x1F) * 0xFF / 0x1F); destination[destinationIndex + 1] = (byte)(((pixel >> 5) & 0x1F) * 0xFF / 0x1F); destination[destinationIndex + 0] = (byte)(((pixel >> 0) & 0x1F) * 0xFF / 0x1F); } else // Argb3444 { destination[destinationIndex + 3] = (byte)(((pixel >> 12) & 0x07) * 0xFF / 0x07); destination[destinationIndex + 2] = (byte)(((pixel >> 8) & 0x0F) * 0xFF / 0x0F); destination[destinationIndex + 1] = (byte)(((pixel >> 4) & 0x0F) * 0xFF / 0x0F); destination[destinationIndex + 0] = (byte)(((pixel >> 0) & 0x0F) * 0xFF / 0x0F); } }
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); } } }
/// <summary> /// Checks for the PVRT header and validates it. /// <para>See also: <seealso cref="IsValidGbix"/></para> /// </summary> /// <param name="source">Byte array containing the data.</param> /// <param name="offset">The offset in the byte array to start at.</param> /// <param name="length">The expected length of the PVR data minus the preceding header sizes.</param> /// <returns>True if the header is PVRT and it passes validation, false otherwise.</returns> private static bool IsValidPvrt(byte[] source, int offset, uint length) { if (!PTMethods.Contains(source, offset + 0x00, pvrtFourCC)) { return(false); } if (source[offset + 0x09] >= 0x60) { return(false); } if (BitConverter.ToUInt32(source, offset + 0x04) != length - 8) { return(false); } return(true); }
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); } }
protected override bool Initalize() { // Check to see if what we are dealing with is a GVP palette if (!Is(encodedData)) { return(false); } // Get the pixel format and the codec and make sure we can decode using them pixelFormat = (PvrPixelFormat)encodedData[0x08]; pixelCodec = PvrPixelCodec.GetPixelCodec(pixelFormat); if (pixelCodec == null) { return(false); } // Get the number of colors contained in the palette paletteEntries = PTMethods.ToUInt16(encodedData, 0x0E, true); return(true); }
/// <summary> /// Determines if this is a SVR texture. /// </summary> /// <param name="source">Byte array containing the data.</param> /// <param name="offset">The offset in the byte array to start at.</param> /// <param name="length">Length of the data (in bytes).</param> /// <returns>True if this is a SVR texture, false otherwise.</returns> public static bool Is(byte[] source, int offset, int length) { // GBIX and PVRT if (length >= 0x20 && PTMethods.Contains(source, offset + 0x00, Encoding.UTF8.GetBytes("GBIX")) && PTMethods.Contains(source, offset + 0x10, Encoding.UTF8.GetBytes("PVRT")) && source[offset + 0x19] >= 0x60 && source[offset + 0x19] < 0x70 && BitConverter.ToUInt32(source, offset + 0x14) == length - 24) { return(true); } // PVRT (and no GBIX chunk) else if (length >= 0x10 && PTMethods.Contains(source, offset + 0x00, Encoding.UTF8.GetBytes("PVRT")) && source[offset + 0x19] >= 0x60 && source[offset + 0x19] < 0x70 && BitConverter.ToUInt32(source, offset + 0x04) == length - 8) { return(true); } return(false); }
/// <summary> /// Checks for the XVRT header and validates it. /// </summary> /// <param name="source">Byte array containing the data.</param> /// <param name="offset">The offset in the byte array to start at.</param> /// <param name="length">The expected length of the PVR data minus the preceding header sizes.</param> /// <returns>True if the header is XVRT and it passes validation, false otherwise.</returns> private static bool IsValidXvrt(byte[] source, int offset, int length) { return(PTMethods.Contains(source, offset, xvrtFourCC) && BitConverter.ToUInt32(source, offset + 0x04) == length - 8); }
/// <summary> /// Checks for the PVRT header and validates it. /// <para>See also: <seealso cref="IsValidGbix"/></para> /// </summary> /// <param name="source">Byte array containing the data.</param> /// <param name="offset">The offset in the byte array to start at.</param> /// <param name="length">The expected length of the PVR data minus the preceding header sizes.</param> /// <returns>True if the header is PVRT and it passes validation, false otherwise.</returns> private static bool IsValidPvrt(byte[] source, int offset, int length) { return(PTMethods.Contains(source, offset, pvrtFourCC) && source[offset + 0x09] < 0x60 && PTMethods.ToUInt32(source, offset + 0x04, true) == length - 8); }
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); } }
protected override void Initalize() { // Check to see if what we are dealing with is a GVR texture if (!Is(encodedData)) { throw new NotAValidTextureException("This is not a valid GVR texture."); } // Determine the offsets of the GBIX/GCIX (if present) and GCIX header chunks. if (PTMethods.Contains(encodedData, 0, Encoding.UTF8.GetBytes("GBIX")) || PTMethods.Contains(encodedData, 0, Encoding.UTF8.GetBytes("GCIX"))) { gbixOffset = 0x00; pvrtOffset = 0x10; } else { gbixOffset = -1; pvrtOffset = 0x00; } // Read the global index (if it is present). If it is not present, just set it to 0. if (gbixOffset != -1) { globalIndex = PTMethods.ToUInt32BE(encodedData, gbixOffset + 0x08); } else { globalIndex = 0; } // Read information about the texture textureWidth = PTMethods.ToUInt16BE(encodedData, pvrtOffset + 0x0C); textureHeight = PTMethods.ToUInt16BE(encodedData, pvrtOffset + 0x0E); pixelFormat = (GvrPixelFormat)(encodedData[pvrtOffset + 0x0A] >> 4); // Only the first 4 bits matter dataFlags = (GvrDataFlags)(encodedData[pvrtOffset + 0x0A] & 0x0F); // Only the last 4 bits matter dataFormat = (GvrDataFormat)encodedData[pvrtOffset + 0x0B]; // Get the codecs and make sure we can decode using them dataCodec = GvrDataCodec.GetDataCodec(dataFormat); // We need a pixel codec if this is a palettized texture if (dataCodec != null && dataCodec.PaletteEntries != 0) { pixelCodec = GvrPixelCodec.GetPixelCodec(pixelFormat); if (pixelCodec != null) { dataCodec.PixelCodec = pixelCodec; canDecode = true; } } else { pixelFormat = GvrPixelFormat.Unknown; if (dataCodec != null) { canDecode = true; } } // Set the palette and data offsets paletteEntries = dataCodec.PaletteEntries; if (!canDecode || paletteEntries == 0 || (paletteEntries != 0 && (dataFlags & GvrDataFlags.ExternalPalette) != 0)) { paletteOffset = -1; dataOffset = pvrtOffset + 0x10; } else { paletteOffset = pvrtOffset + 0x10; dataOffset = paletteOffset + (paletteEntries * (pixelCodec.Bpp >> 3)); } // If the texture contains mipmaps, gets the offsets of them if (canDecode && paletteEntries == 0 && (dataFlags & GvrDataFlags.Mipmaps) != 0) { mipmapOffsets = new int[(int)Math.Log(textureWidth, 2) + 1]; int mipmapOffset = 0; for (int i = 0, size = textureWidth; i < mipmapOffsets.Length; i++, size >>= 1) { mipmapOffsets[i] = mipmapOffset; mipmapOffset += Math.Max(size * size * (dataCodec.Bpp >> 3), 32); } } initalized = true; }
public override byte[] Decode(byte[] input, int offset, int width, int height, VrPixelCodec PixelCodec) { byte[] output = new byte[width * height * 4]; // Palette for each 4x4 block byte[][] palette = new byte[4][]; palette[0] = new byte[4]; palette[1] = new byte[4]; palette[2] = new byte[4]; palette[3] = new byte[4]; // The two colors that determine the palette ushort[] pixel = new ushort[2]; for (int y = 0; y < height; y += 8) { for (int x = 0; x < width; x += 8) { for (int y2 = 0; y2 < 8; y2 += 4) { for (int x2 = 0; x2 < 8; x2 += 4) { // Get the first two colors pixel[0] = PTMethods.ToUInt16BE(input, offset); pixel[1] = PTMethods.ToUInt16BE(input, offset + 2); palette[0][3] = 0xFF; palette[0][2] = (byte)(((pixel[0] >> 11) & 0x1F) * 0xFF / 0x1F); palette[0][1] = (byte)(((pixel[0] >> 5) & 0x3F) * 0xFF / 0x3F); palette[0][0] = (byte)(((pixel[0] >> 0) & 0x1F) * 0xFF / 0x1F); palette[1][3] = 0xFF; palette[1][2] = (byte)(((pixel[1] >> 11) & 0x1F) * 0xFF / 0x1F); palette[1][1] = (byte)(((pixel[1] >> 5) & 0x3F) * 0xFF / 0x3F); palette[1][0] = (byte)(((pixel[1] >> 0) & 0x1F) * 0xFF / 0x1F); // Determine the next two colors based on how the first two are stored if (pixel[0] > pixel[1]) { palette[2][3] = 0xFF; palette[2][2] = (byte)(((palette[0][2] * 2) + palette[1][2]) / 3); palette[2][1] = (byte)(((palette[0][1] * 2) + palette[1][1]) / 3); palette[2][0] = (byte)(((palette[0][0] * 2) + palette[1][0]) / 3); palette[3][3] = 0xFF; palette[3][2] = (byte)(((palette[1][2] * 2) + palette[0][2]) / 3); palette[3][1] = (byte)(((palette[1][1] * 2) + palette[0][1]) / 3); palette[3][0] = (byte)(((palette[1][0] * 2) + palette[0][0]) / 3); } else { palette[2][3] = 0xFF; palette[2][2] = (byte)((palette[0][2] + palette[1][2]) / 2); palette[2][1] = (byte)((palette[0][1] + palette[1][1]) / 2); palette[2][0] = (byte)((palette[0][0] + palette[1][0]) / 2); palette[3][3] = 0x00; palette[3][2] = 0x00; palette[3][1] = 0x00; palette[3][0] = 0x00; } offset += 4; for (int y3 = 0; y3 < 4; y3++) { for (int x3 = 0; x3 < 4; x3++) { output[((((y + y2 + y3) * width) + (x + x2 + x3)) * 4) + 3] = palette[((input[offset] >> (6 - (x3 * 2))) & 0x03)][3]; output[((((y + y2 + y3) * width) + (x + x2 + x3)) * 4) + 2] = palette[((input[offset] >> (6 - (x3 * 2))) & 0x03)][2]; output[((((y + y2 + y3) * width) + (x + x2 + x3)) * 4) + 1] = palette[((input[offset] >> (6 - (x3 * 2))) & 0x03)][1]; output[((((y + y2 + y3) * width) + (x + x2 + x3)) * 4) + 0] = palette[((input[offset] >> (6 - (x3 * 2))) & 0x03)][0]; } offset++; } } } } } return(output); }
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); } }
protected override void Initalize() { // Check to see if what we are dealing with is a PVR texture if (!Is(encodedData)) { throw new NotAValidTextureException("This is not a valid PVR texture."); } // Determine the offsets of the GBIX (if present) and PVRT header chunks. if (PTMethods.Contains(encodedData, 0x00, Encoding.UTF8.GetBytes("GBIX"))) { gbixOffset = 0x00; pvrtOffset = 0x10; } else if (PTMethods.Contains(encodedData, 0x04, Encoding.UTF8.GetBytes("GBIX"))) { gbixOffset = 0x04; pvrtOffset = 0x14; } else if (PTMethods.Contains(encodedData, 0x04, Encoding.UTF8.GetBytes("PVRT"))) { gbixOffset = -1; pvrtOffset = 0x04; } else { gbixOffset = -1; pvrtOffset = 0x00; } // Read the global index (if it is present). If it is not present, just set it to 0. if (gbixOffset != -1) { globalIndex = BitConverter.ToUInt32(encodedData, gbixOffset + 0x08); } else { globalIndex = 0; } // Read information about the texture textureWidth = BitConverter.ToUInt16(encodedData, pvrtOffset + 0x0C); textureHeight = BitConverter.ToUInt16(encodedData, pvrtOffset + 0x0E); pixelFormat = (PvrPixelFormat)encodedData[pvrtOffset + 0x08]; dataFormat = (PvrDataFormat)encodedData[pvrtOffset + 0x09]; // Get the codecs and make sure we can decode using them pixelCodec = PvrPixelCodec.GetPixelCodec(pixelFormat); dataCodec = PvrDataCodec.GetDataCodec(dataFormat); if (dataCodec != null && pixelCodec != null) { dataCodec.PixelCodec = pixelCodec; canDecode = true; } // Set the palette and data offsets if (!canDecode || dataCodec.PaletteEntries == 0 || dataCodec.NeedsExternalPalette) { paletteOffset = -1; dataOffset = pvrtOffset + 0x10; } else { paletteOffset = pvrtOffset + 0x10; dataOffset = paletteOffset + (dataCodec.PaletteEntries * (pixelCodec.Bpp >> 3)); } // Get the compression format and determine if we need to decompress this texture compressionFormat = GetCompressionFormat(encodedData, pvrtOffset, dataOffset); compressionCodec = PvrCompressionCodec.GetCompressionCodec(compressionFormat); if (compressionFormat != PvrCompressionFormat.None && compressionCodec != null) { encodedData = compressionCodec.Decompress(encodedData, dataOffset, pixelCodec, dataCodec); // Now place the offsets in the appropiate area if (compressionFormat == PvrCompressionFormat.Rle) { if (gbixOffset != -1) { gbixOffset -= 4; } pvrtOffset -= 4; if (paletteOffset != -1) { paletteOffset -= 4; } dataOffset -= 4; } } // If the texture contains mipmaps, gets the offsets of them if (canDecode && dataCodec.HasMipmaps) { mipmapOffsets = new int[(int)Math.Log(textureWidth, 2) + 1]; int mipmapOffset = 0; // Calculate the padding for the first mipmap offset if (dataFormat == PvrDataFormat.SquareTwiddledMipmaps) { // A 1x1 mipmap takes up as much space as a 2x1 mipmap mipmapOffset = (dataCodec.Bpp) >> 3; } if (dataFormat == PvrDataFormat.SquareTwiddledMipmapsAlt) { // A 1x1 mipmap takes up as much space as a 2x2 mipmap mipmapOffset = (3 * dataCodec.Bpp) >> 3; } for (int i = mipmapOffsets.Length - 1, size = 1; i >= 0; i--, size <<= 1) { mipmapOffsets[i] = mipmapOffset; mipmapOffset += Math.Max((size * size * dataCodec.Bpp) >> 3, 1); } } initalized = true; }
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); } }
protected override void Initalize() { // Check to see if what we are dealing with is a SVR texture if (!Is(encodedData)) { throw new NotAValidTextureException("This is not a valid GVR texture."); } // Determine the offsets of the GBIX (if present) and PVRT header chunks. if (PTMethods.Contains(encodedData, 0, Encoding.UTF8.GetBytes("GBIX"))) { gbixOffset = 0x00; pvrtOffset = 0x10; } else { gbixOffset = -1; pvrtOffset = 0x00; } // Read the global index (if it is present). If it is not present, just set it to 0. if (gbixOffset != -1) { globalIndex = BitConverter.ToUInt32(encodedData, gbixOffset + 0x08); } else { globalIndex = 0; } // Read information about the texture textureWidth = BitConverter.ToUInt16(encodedData, pvrtOffset + 0x0C); textureHeight = BitConverter.ToUInt16(encodedData, pvrtOffset + 0x0E); pixelFormat = (SvrPixelFormat)encodedData[pvrtOffset + 0x08]; dataFormat = (SvrDataFormat)encodedData[pvrtOffset + 0x09]; // Get the codecs and make sure we can decode using them pixelCodec = SvrPixelCodec.GetPixelCodec(pixelFormat); dataCodec = SvrDataCodec.GetDataCodec(dataFormat); if (dataCodec != null && pixelCodec != null) { dataCodec.PixelCodec = pixelCodec; canDecode = true; } // Set the palette and data offsets paletteEntries = dataCodec.PaletteEntries; if (!canDecode || paletteEntries == 0 || dataCodec.NeedsExternalPalette) { paletteOffset = -1; dataOffset = pvrtOffset + 0x10; } else { paletteOffset = pvrtOffset + 0x10; dataOffset = paletteOffset + (paletteEntries * (pixelCodec.Bpp >> 3)); } initalized = true; }
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); } }
// 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"); } } } }