Last active
April 24, 2022 20:42
-
-
Save pleonex/6265017 to your computer and use it in GitHub Desktop.
GBA and DS copyright logo decrypt and encrypt classes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ---------------------------------------------------------------------- | |
// <copyright file="LogoGBA.cs" company="none"> | |
// Copyright (C) 2013 | |
// | |
// This program is free software: you can redistribute it and/or modify | |
// it under the terms of the GNU General Public License as published by | |
// the Free Software Foundation, either version 3 of the License, or | |
// (at your option) any later version. | |
// | |
// This program is distributed in the hope that it will be useful, | |
// but WITHOUT ANY WARRANTY; without even the implied warranty of | |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
// GNU General Public License for more details. | |
// | |
// You should have received a copy of the GNU General Public License | |
// along with this program. If not, see <http://www.gnu.org/licenses/>. | |
// | |
// </copyright> | |
// <author>pleoNeX</author> | |
// <email>benito356@gmail.com</email> | |
// <date>19/08/2013</date> | |
// ----------------------------------------------------------------------- | |
using System; | |
using System.Collections.Generic; | |
using System.Drawing; | |
using System.IO; | |
namespace NinLogo | |
{ | |
/// <summary> | |
/// Logo from GBA/NDS encrypted header data | |
/// </summary> | |
public class LogoGBA | |
{ | |
// Image constants | |
private const int TileWidth = 8; | |
private const int TileHeight = 8; | |
private static Color[] Palette = { | |
Color.FromArgb(255, 255, 255, 255), // White (background) | |
Color.FromArgb(255, 248, 0, 248) // Magenta (chars) | |
}; | |
private const int LogoLength = 0x9C; // Length of the original logo encrypted. | |
// Huffman header to decoded the logo, it's in the BIOS | |
private static byte[] BiosHeader = { | |
0x24, 0xD4, 0x00, 0x00, 0x0F, 0x40, 0x00, 0x00, 0x00, 0x01, 0x81, 0x82, | |
0x82, 0x83, 0x0F, 0x83, 0x0C, 0xC3, 0x03, 0x83, 0x01, 0x83, 0x04, 0xC3, | |
0x08, 0x0E, 0x02, 0xC2, 0x0D, 0xC2, 0x07, 0x0B, 0x06, 0x0A, 0x05, 0x09 | |
}; | |
// Codewords of the BIOS Huffman header | |
private static string[] Codewords = { | |
"1", "0110", "01010", "0100", "00010", "011110", | |
"010110", "000110", "00110", "011111", "010111", "000111", | |
"0010", "01110", "00111", "0000" | |
}; | |
private byte[] logo; | |
private LogoGBA() | |
{ | |
this.logo = new byte[0]; | |
} | |
/// <summary> | |
/// Initializes a new instance of the <see cref="NinLogo.LogoGBA"/> class. | |
/// </summary> | |
/// <param name="file">File with the encrypted logo data.</param> | |
/// <param name="offset">Offset to the logo data.</param> | |
public LogoGBA(string file, int offset) | |
{ | |
this.Read(File.ReadAllBytes(file), offset); | |
} | |
/// <summary> | |
/// Create a new GBA logo from an image. | |
/// </summary> | |
/// <returns>The new GBA logo.</returns> | |
/// <param name="imgFile">Image file.</param> | |
public static LogoGBA FromImage(string imgFile) | |
{ | |
LogoGBA lgba = new LogoGBA(); | |
lgba.SetImage((Bitmap)Image.FromFile(imgFile)); | |
return lgba; | |
} | |
/// <summary> | |
/// Gets the width of the logo. | |
/// </summary> | |
/// <value>The width.</value> | |
public int Width { | |
get { return 104; } | |
} | |
/// <summary> | |
/// Gets the height of the logo. | |
/// </summary> | |
/// <value>The height.</value> | |
public int Height { | |
get { return 16; } | |
} | |
/// <summary> | |
/// Gets the bits per pixel of the logo. | |
/// </summary> | |
/// <value>The bpp.</value> | |
public int Bpp { | |
get { return 1; } | |
} | |
/// <summary> | |
/// Writes the encrypted logo to a file. | |
/// </summary> | |
/// <param name="fileOut">File out.</param> | |
public void Write(string fileOut, int offset) | |
{ | |
// Encrypt using Addition algorithm | |
byte[] encrypted = Addition.Encrypt(this.logo); | |
// Encode data with huffman | |
byte[] encoded = Huffman.Encrypt(encrypted, LogoLength, Codewords); | |
// Add the debug bits | |
encoded[0x98] = 0x21; | |
encoded[0x99] = 0xD4; | |
// Write to a file at the given offset | |
FileStream fs = new FileStream(fileOut, FileMode.Create); | |
fs.Position = offset; | |
fs.Write(encoded, 0, encoded.Length); | |
fs.Flush(); | |
fs.Close(); | |
} | |
/// <summary> | |
/// Get the final image from the decoded bytes. | |
/// </summary> | |
/// <returns>Logo</returns> | |
public Bitmap GetImage() | |
{ | |
Bitmap bmp = new Bitmap(this.Width, this.Height); | |
for (int y = 0; y < this.Height; y++) { | |
for (int x = 0; x < this.Width; x++) { | |
// Get index of pixel of a tiled structure from (x, y) coordinates | |
int index = this.GetTiledPixel(x, y); | |
// Get the bit related to that pixel | |
int bit = (this.logo[index / 8] >> (index % 8)) & 1; | |
// Get color from that bit using the palette | |
bmp.SetPixel(x, y, Palette[bit]); | |
} | |
} | |
return bmp; | |
} | |
/// <summary> | |
/// Set the logo from an image | |
/// </summary> | |
/// <param name="image">New logo image</param> | |
private void SetImage(Bitmap image) | |
{ | |
if (image.Width != this.Width) | |
throw new ArgumentException("The width of the image is not valid."); | |
if (image.Height != this.Height) | |
throw new ArgumentException("The height of the image is not valid."); | |
int numPixels = this.Width * this.Height; | |
this.logo = new byte[numPixels / 8]; | |
for (int y = 0; y < this.Height; y++) { | |
for (int x = 0; x < this.Width; x++) { | |
// Get color | |
Color color = image.GetPixel(x, y); | |
// Get color index in palette | |
int colorIndex = Array.FindIndex<Color>(Palette, c => c == color); | |
if (colorIndex == -1) | |
throw new ArgumentException(string.Format("Invalid color found at ({0},{1})", x, y)); | |
// Get index of pixel of a tiled structure | |
int index = this.GetTiledPixel(x, y); | |
// Set the colorIndex | |
int value = this.logo[index / 8]; | |
value |= colorIndex << (index % 8); | |
this.logo[index / 8] = (byte)value; | |
} | |
} | |
} | |
/// <summary> | |
/// Read the specified data at the offset to get the decrypted logo data. | |
/// </summary> | |
/// <param name="data">Encrypted data.</param> | |
/// <param name="offset">Offset to the logo data.</param> | |
private void Read(byte[] data, int offset) | |
{ | |
// Add to the encoded bytes the huffman header | |
byte[] encoded = new byte[LogoLength + BiosHeader.Length]; | |
BiosHeader.CopyTo(encoded, 0); | |
Array.Copy(data, offset, encoded, BiosHeader.Length, LogoLength); | |
// Decode the data with Huffman | |
byte[] encrypted = Huffman.Decrypt(encoded); | |
// Finally, decrypt it | |
this.logo = Addition.Decrypt(encrypted); | |
} | |
/// <summary> | |
/// Gets the pixel index of a tiled structure. | |
/// </summary> | |
/// <returns>The tiled pixel index.</returns> | |
/// <param name="x">The x coordinate.</param> | |
/// <param name="y">The y coordinate.</param> | |
private int GetTiledPixel(int x, int y) | |
{ | |
int numTilesX = this.Width / TileWidth; | |
int tileLength = TileWidth * TileHeight; | |
Point tilePos = new Point(x / TileWidth, y / TileHeight); | |
Point pixelPos = new Point(x % TileWidth, y % TileHeight); | |
int index = tilePos.Y * numTilesX * tileLength + tilePos.X * tileLength; // Start tile index | |
index += pixelPos.Y * TileWidth + pixelPos.X; // Pixel index | |
return index; | |
} | |
} | |
/// <summary> | |
/// Encryption used in the Logo. | |
/// </summary> | |
public static class Addition | |
{ | |
/// <summary> | |
/// Decrypt data. | |
/// </summary> | |
/// <param name="encrypted">Encrypted data.</param> | |
public static byte[] Decrypt(byte[] encrypted) | |
{ | |
uint header = BitConverter.ToUInt32(encrypted, 0); | |
uint size = header >> 8; // Number of bytes to decrypt | |
byte[] buffer = new byte[size]; | |
ushort sum = 0; | |
for (int i = 0, j = 4; i < size; i += 2, j += 2) { | |
sum += BitConverter.ToUInt16(encrypted, j); // Add the current 16bits value | |
BitConverter.GetBytes(sum).CopyTo(buffer, i); // Store the addition | |
} | |
return buffer; | |
} | |
/// <summary> | |
/// Encrypt data. | |
/// </summary> | |
/// <param name="decoded">Data to encrypt</param> | |
public static byte[] Encrypt(byte[] decoded) | |
{ | |
byte[] encoded = new byte[decoded.Length + 4]; | |
// Create the header | |
uint header = (uint)(decoded.Length << 8) | 0x82; | |
BitConverter.GetBytes(header).CopyTo(encoded, 0); | |
for (int i = 2, j = 6; i < decoded.Length - 1; i += 2, j += 2) { | |
ushort valuePrev = BitConverter.ToUInt16(decoded, i - 2); | |
ushort valueNext = BitConverter.ToUInt16(decoded, i); | |
ushort encrypted = (ushort)(valueNext - valuePrev); | |
BitConverter.GetBytes(encrypted).CopyTo(encoded, j); | |
} | |
return encoded; | |
} | |
} | |
/// <summary> | |
/// Huffman encoding | |
/// </summary> | |
public static class Huffman | |
{ | |
// Huffman constant | |
private const byte IncrMask = 0x3F; // (0xFF & ~(0x80 | 0x40)) used in huffman | |
private const int MaxEncBits = 0x4CE; | |
/// <summary> | |
/// Decrypt encoded bytes using Huffman algorithm. | |
/// </summary> | |
public static byte[] Decrypt(byte[] encoded) | |
{ | |
int pos_code = 4; // Position to read the codeworks | |
int pos_tree = 5; // Position to read the tree | |
int pos_buffer = 0; // Position in the output buffer | |
uint code = 0; // Current codeworks | |
byte node = 0; // Current node byte | |
byte code_shift = 0; // Shift value for the codework | |
byte bit_code = 0; // Current bit code | |
int incr = 0; // Jump to the next node / child | |
byte dec_bits = 0; // Decoded bits | |
// Read header | |
uint header = BitConverter.ToUInt32(encoded, 0); | |
byte type = (byte)(header & 0xF); | |
uint decompressed_size = header >> 8; | |
byte[] decoded = new byte[decompressed_size]; | |
// Get the pos_code initial value skipping the tree | |
int num_nodes = encoded[pos_code] + 1; | |
pos_code = pos_code + (num_nodes << 1); | |
while (pos_buffer < decompressed_size) | |
{ | |
if (code_shift == 0x00) // If we've already read all the uint, get another | |
{ | |
code = BitConverter.ToUInt32(encoded, pos_code); pos_code += 4; | |
code_shift = 0x20; // 32 bits | |
} | |
code_shift--; // Update the mask | |
bit_code = (byte)((code >> code_shift) & 1); // Get the bit code that indicates the branch, 0-> right, 1-> left | |
node = encoded[pos_tree]; // Get node byte | |
incr = (node & IncrMask) + 1; // Get the increment to the next node | |
incr <<= 1; | |
node <<= bit_code; // Activate the child flag | |
if ((pos_tree & 1) != 0) incr--; // If we're in the right branch, go back to increment it | |
pos_tree += (int)(incr + bit_code); // Increment the position in the tree to the next node | |
if ((node & 0x80) != 0) // If it's a child, get the decoded byte | |
{ | |
decoded[pos_buffer] |= (byte)(encoded[pos_tree] << dec_bits); // Store the child | |
dec_bits += type; // Increment the decoded bits | |
if (dec_bits == 0x8) // If it's a byte, reset it and update the position | |
{ | |
pos_buffer++; | |
dec_bits = 0; | |
} | |
pos_tree = 5; // Reset the position in the tree | |
} | |
} | |
return decoded; | |
} | |
/// <summary> | |
/// Encrypt the data with huffman compression | |
/// </summary> | |
/// <param name="data">Data to encrypt</param> | |
public static byte[] Encrypt(byte[] data, int outputSize, string[] codewords) | |
{ | |
byte[] encoded = new byte[outputSize]; | |
uint enc_value = 0; | |
int pos_enc = 0; | |
for (int i = 0; i < data.Length; i++) | |
{ | |
byte value = data[i]; | |
string[] codeword = { codewords[value & 0xF], codewords[value >> 4] }; | |
for (int c = 0; c < codeword.Length; c++) | |
{ | |
for (int b = 0; b < codeword[c].Length; b++) | |
{ | |
if (pos_enc == MaxEncBits) | |
{ | |
Console.WriteLine("ERROR encoded file is bigger than permitted!"); | |
return null; | |
} | |
int bit = (codeword[c][b] == '0' ? 0 : 1); | |
enc_value |= (uint)(bit << (31 - (pos_enc % 32))); | |
pos_enc++; | |
if (pos_enc % 32 == 0) | |
{ | |
BitConverter.GetBytes(enc_value).CopyTo(encoded, (pos_enc / 8) - 4); | |
enc_value = 0; | |
} | |
} | |
} | |
} | |
if (pos_enc % 32 != 0) | |
BitConverter.GetBytes(enc_value).CopyTo(encoded, (pos_enc / 8) - (pos_enc % 32 / 8)); | |
return encoded; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment