/*---------------------------------------------------------------------------* Project: RevoEX - tools - chjpedit File: main.c Copyright 2007 Nintendo. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. *---------------------------------------------------------------------------*/ #include #include #include #define CHJUMP_HEADER_SIZE 32 // Size, excluding the data block #define CHJUMP_SIZE_MAX 512 // File size upper limit, including the data block unsigned char gameCode[5]; unsigned char chCode[5]; unsigned char attachData[CHJUMP_SIZE_MAX + 1]; unsigned int dataSize; unsigned int dataBlockNum = 1; // unsigned int addrDataBlock0 = 32; // 0x00000020 enum readMode { RM_DEFAULT = 0, RM_GAMECODE, // -gc RM_CHCODE, RM_DATA // -d }; enum errorCode { ERR_WRONG_GAMECODE = 1, ERR_WRONG_CHCODE, ERR_UNKNOWN_OPTION, ERR_DATASIZE_OVER }; /*---------------------------------------------------------------------------* Name: Usage Description: Output command usage to the console. Arguments None. Returns: None. *---------------------------------------------------------------------------*/ static void Usage( void ) { printf("\n"); printf("usage: makeChjp -gc [-d ]\n"); printf(" ex. makeChjp -gc 0001 -d \"THIS IS EXAMPLE.\"\n"); printf("\n"); printf("-gc : game code. default is 0001 (0x30303031).\n"); printf(" if you specify this option, \n"); printf(" this value will be used for generating the titleId.\n"); printf(" the value must be 4 characters (ex. 0001 or 0x30303031).\n"); /* printf("-pc : publisher code.\n"); printf(" if you specify this option, \n"); printf(" this value will be used for generating the titleId.\n");*/ printf("-h show the message for help.\n"); printf("-----------------------------------------------------------\n"); printf("-d : data postted to an application .\n"); printf(" * all options are ignored after -d.\n"); printf(" * data size must be lower than 512 byte. *\n"); } /*---------------------------------------------------------------------------* Name: OutputError Description: Outputs an error to the console Arguments errcode Returns: None. *---------------------------------------------------------------------------*/ static void OutputError(int errcode) { switch(errcode) { case ERR_WRONG_GAMECODE: fprintf(stderr, "error %d\n", errcode); fprintf(stderr, "wrong game code (-gc)\n"); break; case ERR_WRONG_CHCODE: fprintf(stderr, "error %d\n", errcode); fprintf(stderr, "wrong code (-pc)\n"); break; case ERR_UNKNOWN_OPTION: fprintf(stderr, "error %d\n", errcode); fprintf(stderr, "unknown option\n"); Usage(); break; case ERR_DATASIZE_OVER: fprintf(stderr, "error %d\n", errcode); fprintf(stderr, "data size is over 512 byte..\n"); break; } } /*---------------------------------------------------------------------------* Name: CheckHex Description: Checks whether a character string takes on a hexadecimal value (0-9, a-f, A-F). Arguments: str Character string to check Returns: 1 if the check passes; 0 if the check fails. *---------------------------------------------------------------------------*/ static int CheckHex(const char *str) { const char *p = str; p += 2; while(*p) { if( !('0' <= *p && *p <= '9') && !('a' <= *p && *p <= 'f') && !('A' <= *p && *p <= 'F') ) return 0; p++; } return 1; } /*---------------------------------------------------------------------------* Name: ConvertHexCharToChar Description: Converts a character string that contains a hexadecimal number ("0x...") to a character string that the hexadecimal number points to. If the number of hexadecimal digits to convert is short of the number of characters, align them to the right and fill the remaining with '\0'. Arguments: src Character string that stores the hexadecimal number to convert dst Stores the converted character string dst_size Number of characters in dst Returns: None. *---------------------------------------------------------------------------*/ static void ConvertHexCharToChar(const unsigned char *src, unsigned char *dst, unsigned int dst_size) { const unsigned char *p; unsigned char tmp[dst_size + 1]; // For saving temporarily during conversion; in the end, copy to dst. unsigned int size; int i, j; // Tentative check if( src == '\0') return; size = strlen(src); p = src; p += size - 1; // Properly initialize the end. tmp[dst_size] = '\0'; for(i = dst_size -1; i >= 0; i--) { tmp[i] = 0x00; for(j = 0; j < 2; j++) { if(*p == 'x') { tmp[i] += 0; continue; } if('0' <= *p && *p <= '9') tmp[i] += (*p - '0') << 4 * j; else if('a' <= *p && *p <= 'f') tmp[i] += (*p - 'a' + 10) << 4 * j; else if('A' <= *p && *p <= 'F') tmp[i] += (*p - 'A' + 10) << 4 * j; p--; } } strcpy(dst, tmp); } /*---------------------------------------------------------------------------* Name: OutputIntForBin Description: Output an integer value to chjump.bin. Values equal to or greater than the maximum value expressible in the number of bytes specified by the input value are cut off and output. Arguments: val The positive integer value to output byte The number of bytes to output fp File pointer (assumed to be already opened with fopen) Returns: None. *---------------------------------------------------------------------------*/ static void OutputIntForBin(unsigned int val, unsigned int byte, FILE *fp) { int i, tmp; for( i= byte - 1; i>=0; i--) { tmp = val >> i * 8; fwrite(&tmp, 1, 1 ,fp); val = val - (tmp << i * 8); } } /*---------------------------------------------------------------------------* Name: CreateChJumpFile Description: Create chjump.bin. Arguments: None. Returns: None. *---------------------------------------------------------------------------*/ static void CreateChJumpFile( void ) { int i; FILE *fp; // Prepare and write to the file fp = fopen("chjump.bin", "wb"); fputs("ChJp", fp); // Magic OutputIntForBin(dataSize, 4, fp); // TotalSize OutputIntForBin(dataBlockNum, 4, fp); // NumBlocks OutputIntForBin(0, 4, fp); // Options // Title ID 64 bit OutputIntForBin(1, 2, fp); // (Publisher Code) (16 bit) OutputIntForBin((unsigned int)atoi(chCode), 2, fp); // (Channel Code) (16 bit) for(i=0; i < 4; i++) // (Game Code) (32 bit) { if(gameCode[i] == 0x00) fputc('\0', fp); else fputc(gameCode[i], fp); } OutputIntForBin(addrDataBlock0, 4, fp); // Offset 0 OutputIntForBin(dataSize - CHJUMP_HEADER_SIZE, 4, fp); // Size 0 if(strlen(attachData) != 0) // Block 0 fputs(attachData, fp); fputc('\0', fp); fputc('\0', fp); // Add '\0' twice to the end. (void)fclose(fp); } /*---------------------------------------------------------------------------* Name: main Description: Arguments: argc Total number of arguments argv Argument list Returns: None. *---------------------------------------------------------------------------*/ int main(int argc, char *argv[]) { int i, mode; char *p; mode = RM_DEFAULT; // Initialization strcpy(gameCode, "0001"); strcpy(chCode, "0001"); // Load options for(i = 1; i < argc; i++) { p = argv[i]; if( mode == RM_DEFAULT ) { if(*p == '-') { if(strcmp(p, "-gc") == 0) { mode = RM_GAMECODE; } else if(strcmp(p, "-pc") == 0) { mode = RM_CHCODE; } else if(strcmp(p, "-d") == 0) { mode = RM_DATA; } else if(strcmp(p, "-h") == 0) { Usage(); return 0; } else { OutputError(ERR_UNKNOWN_OPTION); return ERR_UNKNOWN_OPTION; } } else { OutputError(ERR_UNKNOWN_OPTION); return ERR_UNKNOWN_OPTION; } } else if( mode == RM_GAMECODE ) { // Conditionals, check for illegal values if(*p == '0' && *(p + 1) == 'x') { if(strlen(p) < 3 || strlen(p) > 10 || !CheckHex(p)) { OutputError(ERR_WRONG_GAMECODE); return ERR_WRONG_GAMECODE; } ConvertHexCharToChar(p, gameCode, 4); } else { if(strlen(p) != 4) { OutputError(ERR_WRONG_GAMECODE); return ERR_WRONG_GAMECODE; } strcpy(gameCode, p); } mode = RM_DEFAULT; } else if( mode == RM_CHCODE ) { // Check whether a value is illegal or not if(*p == '0' && *(p + 1) == 'x') { if(strlen(p) < 3 || strlen(p) > 6 || !CheckHex(p)) { OutputError(ERR_WRONG_CHCODE); return ERR_WRONG_CHCODE; } // Because it is a number, cut off '0x' and then copy as is strcpy(chCode, p+2); } else { OutputError(ERR_WRONG_CHCODE); return ERR_WRONG_CHCODE; } mode = RM_DEFAULT; } else if( mode == RM_DATA) { // Check the size (the two '\0's at the end are also included in the count) if(strlen(p) >= CHJUMP_SIZE_MAX - 1) { OutputError(ERR_DATASIZE_OVER); return ERR_DATASIZE_OVER; } // Everything after this option is considered to be data; mode is no longer changed. (void)strcat(attachData, p); // All further arguments are ignored. break; } } // Detect the full data size. dataSize = strlen(attachData) + CHJUMP_HEADER_SIZE + 2; // Add the header size and the two '\0's at the end. // Create chjump.bin. CreateChJumpFile(); return 0; } /*---------------------------------------------------------------------------* $Log: main.c,v $ Revision 1.2 2007/07/30 05:46:18 nishimoto_takashi Revised the portion that checks the data size. Revision 1.1 2007/07/30 00:48:20 nishimoto_takashi Created $NoKeywords: $ *---------------------------------------------------------------------------*/