diff --git a/Makefile b/Makefile index 44dc95bf..8b4bcede 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,7 @@ BUILD := build SOURCES := source source/libwiigui source/images source/fonts source/sounds \ source/libwbfs source/unzip source/language source/mload source/patches \ source/usbloader source/xml source/network source/settings source/prompts \ - source/ramdisc source/wad + source/ramdisc source/wad source/banner INCLUDES := source SVNDEV := -D'SVN_REV="$(shell svnversion -n ..)"' diff --git a/source/banner/MD5.c b/source/banner/MD5.c new file mode 100644 index 00000000..582ae8b1 --- /dev/null +++ b/source/banner/MD5.c @@ -0,0 +1,592 @@ +/* ========================================================================== ** + * + * MD5.c + * + * Copyright: + * Copyright (C) 2003-2005 by Christopher R. Hertel + * + * Email: crh@ubiqx.mn.org + * + * $Id: MD5.c,v 0.6 2005/06/08 18:35:59 crh Exp $ + * + * + * Modified by dimok + * + * -------------------------------------------------------------------------- ** + * + * Description: + * Implements the MD5 hash algorithm, as described in RFC 1321. + * + * -------------------------------------------------------------------------- ** + * + * License: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * -------------------------------------------------------------------------- ** + * + * Notes: + * + * None of this will make any sense unless you're studying RFC 1321 as you + * read the code. + * + * MD5 is described in RFC 1321. + * The MD*4* algorithm is described in RFC 1320 (that's 1321 - 1). + * MD5 is very similar to MD4, but not quite similar enough to justify + * putting the two into a single module. Besides, I wanted to add a few + * extra functions to this one to expand its usability. + * + * There are three primary motivations for this particular implementation. + * 1) Programmer's pride. I wanted to be able to say I'd done it, and I + * wanted to learn from the experience. + * 2) Portability. I wanted an implementation that I knew to be portable + * to a reasonable number of platforms. In particular, the algorithm is + * designed with little-endian platforms in mind, but I wanted an + * endian-agnostic implementation. + * 3) Compactness. While not an overriding goal, I thought it worth-while + * to see if I could reduce the overall size of the result. This is in + * keeping with my hopes that this library will be suitable for use in + * some embedded environments. + * Beyond that, cleanliness and clarity are always worth pursuing. + * + * As mentioned above, the code really only makes sense if you are familiar + * with the MD5 algorithm or are using RFC 1321 as a guide. This code is + * quirky, however, so you'll want to be reading carefully. + * + * Yeah...most of the comments are cut-and-paste from my MD4 implementation. + * + * -------------------------------------------------------------------------- ** + * + * References: + * IETF RFC 1321: The MD5 Message-Digest Algorithm + * Ron Rivest. IETF, April, 1992 + * + * ========================================================================== ** + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "MD5.h" + + +/* -------------------------------------------------------------------------- ** + * Static Constants: + * + * K[][] - In round one, the values of k (which are used to index + * particular four-byte sequences in the input) are simply + * sequential. In later rounds, however, they are a bit more + * varied. Rather than calculate the values of k (which may + * or may not be possible--I haven't though about it) the + * values are stored in this array. + * + * S[][] - In each round there is a left rotate operation performed as + * part of the 16 permutations. The number of bits varies in + * a repeating patter. This array keeps track of the patterns + * used in each round. + * + * T[][] - There are four rounds of 16 permutations for a total of 64. + * In each of these 64 permutation operations, a different + * constant value is added to the mix. The constants are + * based on the sine function...read RFC 1321 for more detail. + * In any case, the correct constants are stored in the T[][] + * array. They're divided up into four groups of 16. + */ + +static const uint8_t K[3][16] = + { + /* Round 1: skipped (since it is simply sequential). */ + { 1, 6, 11, 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12 }, /* R2 */ + { 5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2 }, /* R3 */ + { 0, 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9 } /* R4 */ + }; + +static const uint8_t S[4][4] = + { + { 7, 12, 17, 22 }, /* Round 1 */ + { 5, 9, 14, 20 }, /* Round 2 */ + { 4, 11, 16, 23 }, /* Round 3 */ + { 6, 10, 15, 21 } /* Round 4 */ + }; + + +static const uint32_t T[4][16] = + { + { 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, /* Round 1 */ + 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, + 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, + 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821 }, + + { 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, /* Round 2 */ + 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, + 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, + 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a }, + + { 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, /* Round 3 */ + 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, + 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, + 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665 }, + + { 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, /* Round 4 */ + 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, + 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, + 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 }, + }; + + +/* -------------------------------------------------------------------------- ** + * Macros: + * md5F(), md5G(), md5H(), and md5I() are described in RFC 1321. + * All of these operations are bitwise, and so not impacted by endian-ness. + * + * GetLongByte() + * Extract one byte from a (32-bit) longword. A value of 0 for + * indicates the lowest order byte, while 3 indicates the highest order + * byte. + * + */ + +#define md5F( X, Y, Z ) ( ((X) & (Y)) | ((~(X)) & (Z)) ) +#define md5G( X, Y, Z ) ( ((X) & (Z)) | ((Y) & (~(Z))) ) +#define md5H( X, Y, Z ) ( (X) ^ (Y) ^ (Z) ) +#define md5I( X, Y, Z ) ( (Y) ^ ((X) | (~(Z))) ) + +#define GetLongByte( L, idx ) ((unsigned char)(( L >> (((idx) & 0x03) << 3) ) & 0xFF)) + + +/* -------------------------------------------------------------------------- ** + * Static Functions: + */ + +static void Permute( uint32_t ABCD[4], const unsigned char block[64] ) + /* ------------------------------------------------------------------------ ** + * Permute the ABCD "registers" using the 64-byte as a driver. + * + * Input: ABCD - Pointer to an array of four unsigned longwords. + * block - An array of bytes, 64 bytes in size. + * + * Output: none. + * + * Notes: The MD5 algorithm operates on a set of four longwords stored + * (conceptually) in four "registers". It is easy to imagine a + * simple MD4/5 chip that would operate this way. In any case, + * the mangling of the contents of those registers is driven by + * the input message. The message is chopped and finally padded + * into 64-byte chunks and each chunk is used to manipulate the + * contents of the registers. + * + * The MD5 Algorithm calls for padding the input to ensure that + * it is a multiple of 64 bytes in length. The last 16 bytes + * of the padding space are used to store the message length + * (the length of the original message, before padding, expressed + * in terms of bits). If there is not enough room for 16 bytes + * worth of bitcount (eg., if the original message was 122 bytes + * long) then the block is padded to the end with zeros and + * passed to this function. Then *another* block is filled with + * zeros except for the last 16 bytes which contain the length. + * + * Oh... and the algorithm requires that there be at least one + * padding byte. The first padding byte has a value of 0x80, + * and any others are 0x00. + * + * ------------------------------------------------------------------------ ** + */ + { + int round; + int i, j; + uint8_t s; + uint32_t a, b, c, d; + uint32_t KeepABCD[4]; + uint32_t X[16]; + + /* Store the current ABCD values for later re-use. + */ + for( i = 0; i < 4; i++ ) + KeepABCD[i] = ABCD[i]; + + /* Convert the input block into an array of unsigned longs, taking care + * to read the block in Little Endian order (the algorithm assumes this). + * The uint32_t values are then handled in host order. + */ + for( i = 0, j = 0; i < 16; i++ ) + { + X[i] = (uint32_t)block[j++]; + X[i] |= ((uint32_t)block[j++] << 8); + X[i] |= ((uint32_t)block[j++] << 16); + X[i] |= ((uint32_t)block[j++] << 24); + } + + /* This loop performs the four rounds of permutations. + * The rounds are each very similar. The differences are in three areas: + * - The function (F, G, H, or I) used to perform bitwise permutations + * on the registers, + * - The order in which values from X[] are chosen. + * - Changes to the number of bits by which the registers are rotated. + * This implementation uses a switch statement to deal with some of the + * differences between rounds. Other differences are handled by storing + * values in arrays and using the round number to select the correct set + * of values. + * + * (My implementation appears to be a poor compromise between speed, size, + * and clarity. Ugh. [crh]) + */ + for( round = 0; round < 4; round++ ) + { + for( i = 0; i < 16; i++ ) + { + j = (4 - (i % 4)) & 0x3; /* handles the rotation of ABCD. */ + s = S[round][i%4]; /* is the bit shift for this iteration. */ + + b = ABCD[(j+1) & 0x3]; /* Copy the b,c,d values per ABCD rotation. */ + c = ABCD[(j+2) & 0x3]; /* This isn't really necessary, it just looks */ + d = ABCD[(j+3) & 0x3]; /* clean & will hopefully be optimized away. */ + + /* The actual perumation function. + * This is broken out to minimize the code within the switch(). + */ + switch( round ) + { + case 0: + /* round 1 */ + a = md5F( b, c, d ) + X[i]; + break; + case 1: + /* round 2 */ + a = md5G( b, c, d ) + X[ K[0][i] ]; + break; + case 2: + /* round 3 */ + a = md5H( b, c, d ) + X[ K[1][i] ]; + break; + default: + /* round 4 */ + a = md5I( b, c, d ) + X[ K[2][i] ]; + break; + } + a = 0xFFFFFFFF & ( ABCD[j] + a + T[round][i] ); + ABCD[j] = b + (0xFFFFFFFF & (( a << s ) | ( a >> (32 - s) ))); + } + } + + /* Use the stored original A, B, C, D values to perform + * one last convolution. + */ + for( i = 0; i < 4; i++ ) + ABCD[i] = 0xFFFFFFFF & ( ABCD[i] + KeepABCD[i] ); + + } /* Permute */ + + +/* -------------------------------------------------------------------------- ** + * Functions: + */ + +auth_md5Ctx *auth_md5InitCtx( auth_md5Ctx *ctx ) + /* ------------------------------------------------------------------------ ** + * Initialize an MD5 context. + * + * Input: ctx - A pointer to the MD5 context structure to be initialized. + * Contexts are typically created thusly: + * ctx = (auth_md5Ctx *)malloc( sizeof(auth_md5Ctx) ); + * + * Output: A pointer to the initialized context (same as ). + * + * Notes: The purpose of the context is to make it possible to generate + * an MD5 Message Digest in stages, rather than having to pass a + * single large block to a single MD5 function. The context + * structure keeps track of various bits of state information. + * + * Once the context is initialized, the blocks of message data + * are passed to the function. Once the + * final bit of data has been handed to the + * context can be closed out by calling , + * which also calculates the final MD5 result. + * + * Don't forget to free an allocated context structure when + * you've finished using it. + * + * See Also: , + * + * ------------------------------------------------------------------------ ** + */ + { + ctx->len = 0; + ctx->b_used = 0; + + ctx->ABCD[0] = 0x67452301; /* The array ABCD[] contains the four 4-byte */ + ctx->ABCD[1] = 0xefcdab89; /* "registers" that are manipulated to */ + ctx->ABCD[2] = 0x98badcfe; /* produce the MD5 digest. The input acts */ + ctx->ABCD[3] = 0x10325476; /* upon the registers, not the other way */ + /* 'round. The initial values are those */ + /* given in RFC 1321 (pg. 4). Note, however, that RFC 1321 */ + /* provides these values as bytes, not as longwords, and the */ + /* bytes are arranged in little-endian order as if they were */ + /* the bytes of (little endian) 32-bit ints. That's */ + /* confusing as all getout (to me, anyway). The values given */ + /* here are provided as 32-bit values in C language format, */ + /* so they are endian-agnostic. */ + return( ctx ); + } /* auth_md5InitCtx */ + + +auth_md5Ctx *auth_md5SumCtx( auth_md5Ctx *ctx, + const unsigned char *src, + const int len ) + /* ------------------------------------------------------------------------ ** + * Build an MD5 Message Digest within the given context. + * + * Input: ctx - Pointer to the context in which the MD5 sum is being + * built. + * src - A chunk of source data. This will be used to drive + * the MD5 algorithm. + * len - The number of bytes in . + * + * Output: A pointer to the updated context (same as ). + * + * See Also: , , + * + * ------------------------------------------------------------------------ ** + */ + { + int i; + + /* Add the new block's length to the total length. + */ + ctx->len += (uint32_t)len; + + /* Copy the new block's data into the context block. + * Call the Permute() function whenever the context block is full. + */ + for( i = 0; i < len; i++ ) + { + ctx->block[ ctx->b_used ] = src[i]; + (ctx->b_used)++; + if( 64 == ctx->b_used ) + { + Permute( ctx->ABCD, ctx->block ); + ctx->b_used = 0; + } + } + + /* Return the updated context. + */ + return( ctx ); + } /* auth_md5SumCtx */ + + +auth_md5Ctx *auth_md5CloseCtx( auth_md5Ctx *ctx, unsigned char *dst ) + /* ------------------------------------------------------------------------ ** + * Close an MD5 Message Digest context and generate the final MD5 sum. + * + * Input: ctx - Pointer to the context in which the MD5 sum is being + * built. + * dst - A pointer to at least 16 bytes of memory, which will + * receive the finished MD5 sum. + * + * Output: A pointer to the closed context (same as ). + * You might use this to free a malloc'd context structure. :) + * + * Notes: The context () is returned in an undefined state. + * It must be re-initialized before re-use. + * + * See Also: , + * + * ------------------------------------------------------------------------ ** + */ + { + int i; + uint32_t l; + + /* Add the required 0x80 padding initiator byte. + * The auth_md5SumCtx() function always permutes and resets the context + * block when it gets full, so we know that there must be at least one + * free byte in the context block. + */ + ctx->block[ctx->b_used] = 0x80; + (ctx->b_used)++; + + /* Zero out any remaining free bytes in the context block. + */ + for( i = ctx->b_used; i < 64; i++ ) + ctx->block[i] = 0; + + /* We need 8 bytes to store the length field. + * If we don't have 8, call Permute() and reset the context block. + */ + if( 56 < ctx->b_used ) + { + Permute( ctx->ABCD, ctx->block ); + for( i = 0; i < 64; i++ ) + ctx->block[i] = 0; + } + + /* Add the total length and perform the final perumation. + * Note: The 60'th byte is read from the *original* len> value + * and shifted to the correct position. This neatly avoids + * any MAXINT numeric overflow issues. + */ + l = ctx->len << 3; + for( i = 0; i < 4; i++ ) + ctx->block[56+i] |= GetLongByte( l, i ); + ctx->block[60] = ((GetLongByte( ctx->len, 3 ) & 0xE0) >> 5); /* See Above! */ + Permute( ctx->ABCD, ctx->block ); + + /* Now copy the result into the output buffer and we're done. + */ + for( i = 0; i < 4; i++ ) + { + dst[ 0+i] = GetLongByte( ctx->ABCD[0], i ); + dst[ 4+i] = GetLongByte( ctx->ABCD[1], i ); + dst[ 8+i] = GetLongByte( ctx->ABCD[2], i ); + dst[12+i] = GetLongByte( ctx->ABCD[3], i ); + } + + /* Return the context. + * This is done for compatibility with the other auth_md5*Ctx() functions. + */ + return( ctx ); + } /* auth_md5CloseCtx */ + + +unsigned char * MD5(unsigned char *dst, const unsigned char *src, const int len ) + /* ------------------------------------------------------------------------ ** + * Compute an MD5 message digest. + * + * Input: dst - Destination buffer into which the result will be written. + * Must be 16 bytes, minimum. + * src - Source data block to be MD5'd. + * len - The length, in bytes, of the source block. + * (Note that the length is given in bytes, not bits.) + * + * Output: A pointer to , which will contain the calculated 16-byte + * MD5 message digest. + * + * Notes: This function is a shortcut. It takes a single input block. + * For more drawn-out operations, see . + * + * This function is interface-compatible with the + * function in the MD4 module. + * + * The MD5 algorithm is designed to work on data with an + * arbitrary *bit* length. Most implementations, this one + * included, handle the input data in byte-sized chunks. + * + * The MD5 algorithm does much of its work using four-byte + * words, and so can be tuned for speed based on the endian-ness + * of the host. This implementation is intended to be + * endian-neutral, which may make it a teeny bit slower than + * others. ...maybe. + * + * See Also: + * + * ------------------------------------------------------------------------ ** + */ + { + auth_md5Ctx ctx[1]; + + (void)auth_md5InitCtx( ctx ); /* Open a context. */ + (void)auth_md5SumCtx( ctx, src, len ); /* Pass only one block. */ + (void)auth_md5CloseCtx( ctx, dst ); /* Close the context. */ + + return( dst ); /* Makes life easy. */ + } /* auth_md5Sum */ + + + +unsigned char * MD5fromFile(unsigned char *dst, const char *src) + /* ------------------------------------------------------------------------ ** + * Compute an MD5 message digest. + * + * Input: dst - Destination buffer into which the result will be written. + * Must be 16 bytes, minimum. + * src - filepath of the file to be checked + * + * Output: A pointer to , which will contain the calculated 16-byte + * MD5 message digest. + * + * Notes: This function is a shortcut. It takes a single input block. + * For more drawn-out operations, see . + * + * This function is interface-compatible with the + * function in the MD4 module. + * + * The MD5 algorithm is designed to work on data with an + * arbitrary *bit* length. Most implementations, this one + * included, handle the input data in byte-sized chunks. + * + * The MD5 algorithm does much of its work using four-byte + * words, and so can be tuned for speed based on the endian-ness + * of the host. This implementation is intended to be + * endian-neutral, which may make it a teeny bit slower than + * others. ...maybe. + * + * See Also: + * + * ------------------------------------------------------------------------ ** + */ + { + auth_md5Ctx ctx[1]; + + FILE * file; + u32 blksize = 0; + u32 read = 0; + + file = fopen(src, "rb"); + + if (file==NULL){ + return NULL; + } + + (void)auth_md5InitCtx( ctx ); /* Open a context. */ + + fseek (file , 0 , SEEK_END); + u64 filesize = ftell(file); + rewind (file); + + if(filesize < 1048576) //1MB cache for files bigger than 1 MB + blksize = filesize; + else + blksize = 1048576; + + unsigned char * buffer = malloc(blksize); + + if(buffer == NULL){ + //no memory + fclose(file); + return NULL; + } + + do + { + read = fread(buffer, 1, blksize, file); + (void)auth_md5SumCtx( ctx, buffer, read ); /* Pass only one block. */ + + } while(read > 0); + + fclose(file); + free(buffer); + + (void)auth_md5CloseCtx( ctx, dst ); /* Close the context. */ + + return( dst ); /* Makes life easy. */ + } /* auth_md5Sum */ + + +/* ========================================================================== */ diff --git a/source/banner/MD5.h b/source/banner/MD5.h new file mode 100644 index 00000000..f1c5bd3c --- /dev/null +++ b/source/banner/MD5.h @@ -0,0 +1,242 @@ +#ifndef MD5_H +#define MD5_H + +#ifdef __cplusplus +extern "C" +{ +#endif +/* ========================================================================== ** + * + * MD5.h + * + * Copyright: + * Copyright (C) 2003-2005 by Christopher R. Hertel + * + * Email: crh@ubiqx.mn.org + * + * $Id: MD5.h,v 0.6 2005/06/08 18:35:59 crh Exp $ + * + * -------------------------------------------------------------------------- ** + * + * Description: + * Implements the MD5 hash algorithm, as described in RFC 1321. + * + * -------------------------------------------------------------------------- ** + * + * License: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * -------------------------------------------------------------------------- ** + * + * Notes: + * + * None of this will make any sense unless you're studying RFC 1321 as you + * read the code. + * + * MD5 is described in RFC 1321. + * The MD*4* algorithm is described in RFC 1320 (that's 1321 - 1). + * MD5 is very similar to MD4, but not quite similar enough to justify + * putting the two into a single module. Besides, I wanted to add a few + * extra functions to this one to expand its usability. + * + * There are three primary motivations for this particular implementation. + * 1) Programmer's pride. I wanted to be able to say I'd done it, and I + * wanted to learn from the experience. + * 2) Portability. I wanted an implementation that I knew to be portable + * to a reasonable number of platforms. In particular, the algorithm is + * designed with little-endian platforms in mind, but I wanted an + * endian-agnostic implementation. + * 3) Compactness. While not an overriding goal, I thought it worth-while + * to see if I could reduce the overall size of the result. This is in + * keeping with my hopes that this library will be suitable for use in + * some embedded environments. + * Beyond that, cleanliness and clarity are always worth pursuing. + * + * As mentioned above, the code really only makes sense if you are familiar + * with the MD5 algorithm or are using RFC 1321 as a guide. This code is + * quirky, however, so you'll want to be reading carefully. + * + * Yeah...most of the comments are cut-and-paste from my MD4 implementation. + * + * -------------------------------------------------------------------------- ** + * + * References: + * IETF RFC 1321: The MD5 Message-Digest Algorithm + * Ron Rivest. IETF, April, 1992 + * + * ========================================================================== ** + */ +/* -------------------------------------------------------------------------- ** + * Typedefs: + */ + +typedef struct + { + uint32_t len; + uint32_t ABCD[4]; + int b_used; + unsigned char block[64]; + } auth_md5Ctx; + + +/* -------------------------------------------------------------------------- ** + * Functions: + */ + +auth_md5Ctx *auth_md5InitCtx( auth_md5Ctx *ctx ); + /* ------------------------------------------------------------------------ ** + * Initialize an MD5 context. + * + * Input: ctx - A pointer to the MD5 context structure to be initialized. + * Contexts are typically created thusly: + * ctx = (auth_md5Ctx *)malloc( sizeof(auth_md5Ctx) ); + * + * Output: A pointer to the initialized context (same as ). + * + * Notes: The purpose of the context is to make it possible to generate + * an MD5 Message Digest in stages, rather than having to pass a + * single large block to a single MD5 function. The context + * structure keeps track of various bits of state information. + * + * Once the context is initialized, the blocks of message data + * are passed to the function. Once the + * final bit of data has been handed to the + * context can be closed out by calling , + * which also calculates the final MD5 result. + * + * Don't forget to free an allocated context structure when + * you've finished using it. + * + * See Also: , + * + * ------------------------------------------------------------------------ ** + */ + + +auth_md5Ctx *auth_md5SumCtx( auth_md5Ctx *ctx, + const unsigned char *src, + const int len ); + /* ------------------------------------------------------------------------ ** + * Build an MD5 Message Digest within the given context. + * + * Input: ctx - Pointer to the context in which the MD5 sum is being + * built. + * src - A chunk of source data. This will be used to drive + * the MD5 algorithm. + * len - The number of bytes in . + * + * Output: A pointer to the updated context (same as ). + * + * See Also: , , + * + * ------------------------------------------------------------------------ ** + */ + + +auth_md5Ctx *auth_md5CloseCtx( auth_md5Ctx *ctx, unsigned char *dst ); + /* ------------------------------------------------------------------------ ** + * Close an MD5 Message Digest context and generate the final MD5 sum. + * + * Input: ctx - Pointer to the context in which the MD5 sum is being + * built. + * dst - A pointer to at least 16 bytes of memory, which will + * receive the finished MD5 sum. + * + * Output: A pointer to the closed context (same as ). + * You might use this to free a malloc'd context structure. :) + * + * Notes: The context () is returned in an undefined state. + * It must be re-initialized before re-use. + * + * See Also: , + * + * ------------------------------------------------------------------------ ** + */ + + +unsigned char * MD5(unsigned char * hash, const unsigned char *src, const int len ); + /* ------------------------------------------------------------------------ ** + * Compute an MD5 message digest. + * + * Input: dst - Destination buffer into which the result will be written. + * Must be 16 bytes, minimum. + * src - Source data block to be MD5'd. + * len - The length, in bytes, of the source block. + * (Note that the length is given in bytes, not bits.) + * + * Output: A pointer to , which will contain the calculated 16-byte + * MD5 message digest. + * + * Notes: This function is a shortcut. It takes a single input block. + * For more drawn-out operations, see . + * + * This function is interface-compatible with the + * function in the MD4 module. + * + * The MD5 algorithm is designed to work on data with an + * arbitrary *bit* length. Most implementations, this one + * included, handle the input data in byte-sized chunks. + * + * The MD5 algorithm does much of its work using four-byte + * words, and so can be tuned for speed based on the endian-ness + * of the host. This implementation is intended to be + * endian-neutral, which may make it a teeny bit slower than + * others. ...maybe. + * + * See Also: + * + * ------------------------------------------------------------------------ ** + */ + +unsigned char * MD5fromFile(unsigned char *dst, const char *src); + /* ------------------------------------------------------------------------ ** + * Compute an MD5 message digest. + * + * Input: dst - Destination buffer into which the result will be written. + * Must be 16 bytes, minimum. + * src - filepath to the file to be MD5'd. + * + * Output: A pointer to , which will contain the calculated 16-byte + * MD5 message digest. + * + * Notes: This function is a shortcut. It takes a single input block. + * For more drawn-out operations, see . + * + * This function is interface-compatible with the + * function in the MD4 module. + * + * The MD5 algorithm is designed to work on data with an + * arbitrary *bit* length. Most implementations, this one + * included, handle the input data in byte-sized chunks. + * + * The MD5 algorithm does much of its work using four-byte + * words, and so can be tuned for speed based on the endian-ness + * of the host. This implementation is intended to be + * endian-neutral, which may make it a teeny bit slower than + * others. ...maybe. + * + * See Also: + * + * ------------------------------------------------------------------------ ** + */ + + +/* ========================================================================== */ + +#ifdef __cplusplus +} +#endif +#endif /* AUTH_MD5_H */ diff --git a/source/banner/banner.c b/source/banner/banner.c new file mode 100644 index 00000000..3e497c95 --- /dev/null +++ b/source/banner/banner.c @@ -0,0 +1,140 @@ +/**************************************************************************** + * USB Loader GX Team + * banner.c + * + * Dump opening.bnr thanks to Wiipower + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fatmounter.h" +#include "usbloader/wdvd.h" +#include "usbloader/disc.h" +#include "banner.h" +#include "patches/fst.h" + + +char *fstfilename(FST_ENTRY *fst, u32 index) +{ + u32 count = fst[0].filelen; + u32 stringoffset; + if (index < count) + { + stringoffset = *(u32 *)&(fst[index]) % (256*256*256); + return (char *)((u32)fst + count*12 + stringoffset); + } else + { + return NULL; + } +} + +s32 dump_banner(const char * discid,const char * dest) +{ + // Mount the disc + //Disc_SetWBFS(1, (u8*)discid); + Disc_SetUSB((u8*)discid); + + Disc_Open(); + + u64 offset; + s32 ret; + + ret = __Disc_FindPartition(&offset); + if (ret < 0) + return ret; + + ret = WDVD_OpenPartition(offset); + + if (ret < 0) { + //printf("ERROR: OpenPartition(0x%llx) %d\n", offset, ret); + return ret; + } + + // Read where to find the fst.bin + u32 *buffer = memalign(32, 0x20); + + if (buffer == NULL) + { + //Out of memory + return -1; + } + + ret = WDVD_Read(buffer, 0x20, 0x420); + if (ret < 0) + return ret; + + // Read fst.bin + void *fstbuffer = memalign(32, buffer[2]*4); + FST_ENTRY *fst = (FST_ENTRY *)fstbuffer; + + if (fst == NULL) + { + //Out of memory + free(buffer); + return -1; + } + + ret = WDVD_Read(fstbuffer, buffer[2]*4, buffer[1]*4); + if (ret < 0) + return ret; + + free(buffer); + + // Search the fst.bin + u32 count = fst[0].filelen; + int i; + u32 index = 0; + + for (i=1;i + * Licensed under the terms of the GNU GPL, version 2 + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt + * Version 1.0 Initial release + ***************************************************************************/ + +#define _GNU_SOURCE + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "MD5.h" +#include "banner.h" + +u16 be16(const u8 *p) +{ + return (p[0] << 8) | p[1]; +} + +u32 be32(const u8 *p) +{ + return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; +} + +u64 be64(const u8 *p) +{ + return ((u64)be32(p) << 32) | be32(p + 4); +} + +u64 be34(const u8 *p) +{ + return 4 * (u64)be32(p); +} + +void wbe16(u8 *p, u16 x) +{ + p[0] = x >> 8; + p[1] = x; +} + +void wbe32(u8 *p, u32 x) +{ + wbe16(p, x >> 16); + wbe16(p + 2, x); +} + +void wbe64(u8 *p, u64 x) +{ + wbe32(p, x >> 32); + wbe32(p + 4, x); +} + +void md5(u8 *data, u32 len, u8 *hash) +{ + MD5(hash, data, len); +} + +static FILE *fp; + +typedef struct { + u8 zeroes[0x40]; + u32 imet; // "IMET" + u8 zero_six_zero_three[8]; // fixed, unknown purpose + u32 sizes[3]; + u32 flag1; + u16 name_jp[0x2a]; // might be empty + u16 name_en[0x2a]; + u16 name_de[0x2a]; + u16 name_fr[0x2a]; + u16 name_es[0x2a]; + u16 name_it[0x2a]; + u16 name_nl[0x2a]; + u8 zeroes_2[0x348]; + u8 crypto[0x10]; +} imet_data_t; + +typedef struct { + u32 imd5_tag; // 0x494D4435 "IMD5"; + u32 size; // size of the rest of part B, starting from next field. + u8 zeroes[8]; + u8 md5[16]; + u32 payload_tag; // 0x4C5A3737 "LZ77" if this is lz77 + u32 payload_data; +} imd5_header_t; + +typedef struct +{ + u16 type; + u16 name_offset; + u32 data_offset; // == absolut offset från U.8- headerns början + u32 size; // last included file num for directories +} U8_node; + +typedef struct +{ + u32 tag; // 0x55AA382D "U.8-" + u32 rootnode_offset; // offset to root_node, always 0x20. + u32 header_size; // size of header from root_node to end of string table. + u32 data_offset; // offset to data -- this is rootnode_offset + header_size, aligned to 0x40. + u8 zeroes[16]; +} U8_archive_header; + +static void write_file(void* data, size_t size, char* name) +{ + FILE *out; + out = fopen(name, "wb"); + fwrite(data, 1, size, out); + fclose(out); +} + +u8* decompress_lz77(u8 *data, size_t data_size, size_t* decompressed_size) +{ + u8 *data_end; + u8 *decompressed_data; + size_t unpacked_size; + u8 *in_ptr; + u8 *out_ptr; + u8 *out_end; + + in_ptr = data; + data_end = data + data_size; + + // Assume this for now and grow when needed + unpacked_size = data_size; + + decompressed_data = malloc(unpacked_size); + out_end = decompressed_data + unpacked_size; + + out_ptr = decompressed_data; + + while (in_ptr < data_end) { + int bit; + u8 bitmask = *in_ptr; + + in_ptr++; + for (bit = 0x80; bit != 0; bit >>= 1) { + if (bitmask & bit) { + // Next section is compressed + u8 rep_length; + u16 rep_offset; + + rep_length = (*in_ptr >> 4) + 3; + rep_offset = *in_ptr & 0x0f; + in_ptr++; + rep_offset = *in_ptr | (rep_offset << 8); + in_ptr++; + if (out_ptr-decompressed_data < rep_offset) { + return NULL; + } + + for ( ; rep_length > 0; rep_length--) { + *out_ptr = out_ptr[-rep_offset-1]; + out_ptr++; + if (out_ptr >= out_end) { + // Need to grow buffer + decompressed_data = realloc(decompressed_data, unpacked_size*2); + out_ptr = decompressed_data + unpacked_size; + unpacked_size *= 2; + out_end = decompressed_data + unpacked_size; + } + } + } else { + // Just copy byte + *out_ptr = *in_ptr; + out_ptr++; + if (out_ptr >= out_end) { + // Need to grow buffer + decompressed_data = realloc(decompressed_data, unpacked_size*2); + out_ptr = decompressed_data + unpacked_size; + unpacked_size *= 2; + out_end = decompressed_data + unpacked_size; + } + in_ptr++; + } + } + } + + *decompressed_size = (out_ptr - decompressed_data); + return decompressed_data; +} + +static int write_imd5_lz77(u8* data, size_t size, char* outname) +{ + imd5_header_t* header = (imd5_header_t*) data; + u32 tag; + u32 size_in_imd5; + u8 md5_calc[16]; + u8 *decompressed_data; + size_t decompressed_size; + + tag = be32((u8*) &header->imd5_tag); + if (tag != 0x494D4435) { + return -4; + } + + md5(data+32, size-32, md5_calc); + if (memcmp(&header->md5, md5_calc, 0x10)) { + return -5; + } + + size_in_imd5 = be32((u8*) &header->size); + if (size_in_imd5 != size - 32) { + return -6; + } + + tag = be32((u8*) &header->payload_tag); + if (tag == 0x4C5A3737) { + // "LZ77" - uncompress + decompressed_data = decompress_lz77(data + sizeof(imd5_header_t), size - sizeof(imd5_header_t), &decompressed_size); + if(decompressed_data == NULL) + return -7; + write_file(decompressed_data, decompressed_size, outname); + //printf(", uncompressed %d bytes, md5 ok", decompressed_size); + + free(decompressed_data); + } else { + write_file(&header->payload_tag, size-32, outname); + //printf(", md5 ok"); + } + return 0; +} + +static int do_U8_archive(void) +{ + U8_archive_header header; + U8_node root_node; + u32 tag; + u32 num_nodes; + U8_node* nodes; + u8* string_table; + size_t rest_size; + unsigned int i; + u32 data_offset; + u32 current_offset; + u16 dir_stack[16]; + int dir_index = 0; + + fread(&header, 1, sizeof header, fp); + tag = be32((u8*) &header.tag); + if (tag != 0x55AA382D) { + return -1; + } + + fread(&root_node, 1, sizeof(root_node), fp); + num_nodes = be32((u8*) &root_node.size) - 1; + //printf("Number of files: %d\n", num_nodes); + + nodes = malloc(sizeof(U8_node) * (num_nodes)); + fread(nodes, 1, num_nodes * sizeof(U8_node), fp); + + data_offset = be32((u8*) &header.data_offset); + rest_size = data_offset - sizeof(header) - (num_nodes+1)*sizeof(U8_node); + + string_table = malloc(rest_size); + fread(string_table, 1, rest_size, fp); + current_offset = data_offset; + + for (i = 0; i < num_nodes; i++) { + U8_node* node = &nodes[i]; + u16 type = be16((u8*)&node->type); + u16 name_offset = be16((u8*)&node->name_offset); + u32 my_data_offset = be32((u8*)&node->data_offset); + u32 size = be32((u8*)&node->size); + char* name = (char*) &string_table[name_offset]; + u8* file_data; + + if (type == 0x0100) { + // Directory + mkdir(name, 0777); + chdir(name); + dir_stack[++dir_index] = size; + //printf("%*s%s/\n", dir_index, "", name); + } else { + // Normal file + u8 padding[32]; + + if (type != 0x0000) { + return -2; + } + + if (current_offset < my_data_offset) { + int diff = my_data_offset - current_offset; + + if (diff > 32) { + return -3; + } + fread(padding, 1, diff, fp); + current_offset += diff; + } + + file_data = malloc(size); + fread(file_data, 1, size, fp); + //printf("%*s %s (%d bytes", dir_index, "", name, size); + int result; + result = write_imd5_lz77(file_data, size, name); + if(result < 0) + return result; + //printf(")\n"); + current_offset += size; + } + + while (dir_stack[dir_index] == i+2 && dir_index > 0) { + chdir(".."); + dir_index--; + } + } + return 0; +} + +static void do_imet_header(void) +{ + imet_data_t header; + + fread(&header, 1, sizeof header, fp); + + write_file(&header, sizeof(header), "header.imet"); +} + +void do_U8_archivebanner(FILE *fp) +{ + U8_archive_header header; + U8_node root_node; + u32 tag; + u32 num_nodes; + U8_node* nodes; + u8* string_table; + size_t rest_size; + unsigned int i; + u32 data_offset; + u16 dir_stack[16]; + int dir_index = 0; + + fread(&header, 1, sizeof header, fp); + tag = be32((u8*) &header.tag); + if (tag != 0x55AA382D) { + //printf("No U8 tag"); + exit(0); + } + + fread(&root_node, 1, sizeof(root_node), fp); + num_nodes = be32((u8*) &root_node.size) - 1; + printf("Number of files: %d\n", num_nodes); + + nodes = malloc(sizeof(U8_node) * (num_nodes)); + fread(nodes, 1, num_nodes * sizeof(U8_node), fp); + + data_offset = be32((u8*) &header.data_offset); + rest_size = data_offset - sizeof(header) - (num_nodes+1)*sizeof(U8_node); + + string_table = malloc(rest_size); + fread(string_table, 1, rest_size, fp); + + for (i = 0; i < num_nodes; i++) { + U8_node* node = &nodes[i]; + u16 type = be16((u8*)&node->type); + u16 name_offset = be16((u8*)&node->name_offset); + u32 my_data_offset = be32((u8*)&node->data_offset); + u32 size = be32((u8*)&node->size); + char* name = (char*) &string_table[name_offset]; + u8* file_data; + + if (type == 0x0100) { + // Directory + mkdir(name, 0777); + chdir(name); + dir_stack[++dir_index] = size; + //printf("%*s%s/\n", dir_index, "", name); + } else { + // Normal file + + if (type != 0x0000) { + printf("Unknown type"); + exit(0); + } + + fseek(fp, my_data_offset, SEEK_SET); + file_data = malloc(size); + fread(file_data, 1, size, fp); + write_file(file_data, size, name); + free(file_data); + //printf("%*s %s (%d bytes)\n", dir_index, "", name, size); + } + + while (dir_stack[dir_index] == i+2 && dir_index > 0) { + chdir(".."); + dir_index--; + } + } +} + +int extractbnrfile(const char * filepath, const char * destpath) +{ + int ret; + + fp = fopen(filepath, "rb"); + + mkdir(destpath, 0777); + chdir(destpath); + + do_imet_header(); + ret = do_U8_archive(); + + fclose(fp); + + return ret; +} + +int unpackBin(const char * filename,const char * outdir) +{ +FILE *fp; +fp = fopen(filename,"rb"); + +if(fp!=NULL) +{ +mkdir(outdir, 0777); +chdir(outdir); + +do_U8_archivebanner(fp); +fclose(fp); +return 1; +} +return 0; +} + +int unpackBanner(char * gameid, char * outdir) +{ +s32 ret = dump_banner(gameid,"SD:/opening.bnr"); +if (ret != 1) return -1; + +ret = extractbnrfile("SD:/opening.bnr","SD:/neu"); +if (ret != 0) return -1; +remove("SD:/opening.bnr"); +char iconpath[60]; +snprintf(iconpath,sizeof(iconpath),"%s/meta/icon.bin",outdir); +ret = unpackBin(iconpath,"SD:/icon"); +if (ret != 1) return -1; + +if (unlink("/neu/meta/banner.bin") == -1) return -1; +if (unlink("/neu/meta/icon.bin") == -1) return -1; +if (unlink("/neu/meta/sound.bin") == -1) return -1; +if (unlink("/neu/header.imet") == -1) return -1; +if (unlink("/neu/meta") == -1) return -1; +if (unlink("/neu") == -1) return -1; + +return 1; +} diff --git a/source/banner/openingbnr.h b/source/banner/openingbnr.h new file mode 100644 index 00000000..64eedac2 --- /dev/null +++ b/source/banner/openingbnr.h @@ -0,0 +1,41 @@ +/**************************************************************************** + * USB Loader GX Team + * openingbnr + * + * Extract opening.bnr/banner.bin/sound.bin/icon.bin + ***************************************************************************/ + +#ifndef _OPENINGBNR_H_ +#define _OPENINGBNR_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*********************************************************** +* Error description: +* 0 Successfully extracted +* -1 No U8 tag +* -2 Unknown type +* -3 Archive inconsistency, too much padding +* -4 No IMD5 tag +* -5 MD5 mismatch +* -6 Size mismatch +* -7 Inconsistency in LZ77 encoding +************************************************************/ + +//! Extract opening.bnr from filepath to destpath +//! Files extracted: banner.bin icon.bin and sound.bin +int extractbnrfile(char * filepath, char * destpath); +//int unpackBanner(const char * filename,const char * outdir); +int unpackBanner(const char * gameid, const char * outdir); +//! Extract the lz77 compressed banner, icon and sound .bin +u8* decompress_lz77(u8 *data, size_t data_size, size_t* decompressed_size); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source/libwiigui/gui_banner.h b/source/libwiigui/gui_banner.h deleted file mode 100644 index 006c2d0c..00000000 --- a/source/libwiigui/gui_banner.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef _GUIBANNER_H_ -#define _GUIBANNER_H_ - -#include "gui.h" - -class GuiBanner : public GuiImage -{ -public: - GuiBanner(); - GuiBanner(const char *tplfilepath); - GuiBanner(void *mem,u32 len,u16 w, u16 h); - ~GuiBanner(); - - void Draw(); -private: - f32 deg_beta; - const char *filepath; - const void *memory; - bool filecheck; -}; - -#endif /* _GUIDISCCOVER_H_ */ diff --git a/source/usbloader/disc.h b/source/usbloader/disc.h index 5c9d200c..d7d8a06d 100644 --- a/source/usbloader/disc.h +++ b/source/usbloader/disc.h @@ -48,7 +48,8 @@ s32 Disc_ReadHeader(void *); s32 Disc_IsWii(void); s32 Disc_BootPartition(u64, u8, u8, u8, u8, u8, u8); s32 Disc_WiiBoot(u8, u8, u8, u8, u8, u8); -void PatchCountryStrings(void *Address, int Size); +void PatchCountryStrings(void *Address, int Size); +s32 __Disc_FindPartition(u64 *outbuf); #ifdef __cplusplus }