/*---------------------------------------------------------------------------* Project: Revolution THP Converting tool File: THPConv.c Copyright (C)2002-2006 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. $Log: THPConv.c,v $ Revision 1.3 2007/11/26 07:17:29 nakano_yoshinobu Fix for VC2005. Revision 1.2 2006/12/21 07:07:22 aka Removed resampling code. Revision 1.1 2006/02/08 02:55:34 aka Imported from Dolphin Tree. 22 2003/12/24 4:20p Akagi Revised frame rate check. 21 2003/12/24 1:36p Akagi Removed a function copyFile(). Changed calling from copyFile() to THPUtyCopyTHPFile(). Added frame rate check. 20 2003/12/08 2:27p Akagi Revised 2 error messages. 19 2003/09/21 6:48p Akagi Added several file checks. 18 2003/09/18 10:07a Akagi Added option check. 17 2003/09/17 9:59p Akagi Fixed some bugs. 16 2003/07/03 2:58p Akagi Fixed 2 trivial bugs. 15 2003/07/03 11:09a Akagi Renamed some functions. 14 2003/07/02 4:20p Akagi Renamed from THPConv_main.c to THPConv.c. 13 2003/07/02 4:13p Akagi Rewrote with DolphinSDK coding style. 12 2003/07/02 1:02p Akagi Modified appendAudioData(). 11 2003/07/01 2:55p Akagi Modified to divide old THPConv.exe into 2 DLLs and 1 EXE by Ohki-san@NTSC. 10 2003/07/01 9:54a Akagi Moved from securebuild/tools. 9 2002/10/16 11:28a Akagi JPEG & WAV file name bug fixed. (by iRD tsuji) 8 2002/05/30 2:21p Akagi Stopped supporting QuickTime MJPEG By Tsuji (IRD) 7 2002/05/27 9:45a Akagi Modified output error messages in English, by Tsuji (IRD) 6 2002/05/08 2:31p Akagi Modified [-trk] option By Tsuji (IRD) 5 2002/05/01 3:09p Akagi Added JPEG files sort program. By Tsuji (IRD) 4 2002/03/28 11:15a Akagi Modified by Tsuji (IRD) 2 2002/01/22 10:10a Akagi Added error message, when can't load "dsptools.dll" by Tsuji. 1 2002/01/16 4:58p Akagi Initial revision made by Tsuji-san (IRD). $NoKeywords: $ *---------------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include //----------------------------------------------------------------------------- // // // #include // // THPUtyConvertToUnixFmt() // THPUtyHeaderInit() // THPUtyCreateTHP() // THPUtyChangeAudioTrack() // THPUtyReadTHPFileHeader() // THPUtyCopyTHPFile() // // #include // // THPAudioInit() // THPAudioCreateHandle() // THPAudioGetInfo() // THPAudioGetMaxFrameSamples() // THPAudioGetTotalSamples() // THPAudioQuit() // THPAudioFreeHandle() // //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // Define //----------------------------------------------------------------------------- #define TMP_FILENAME "__tmp_VD" #pragma warning (disable: 4996) //for VC2005 //----------------------------------------------------------------------------- // Static Value //----------------------------------------------------------------------------- static char** jpegFilesPtr = NULL; // JPEG File name string array pointer static u32 num_JPGfiles = 0; // Number of JPEG files static char** waveFilesPtr = NULL; // WAV File name string array pointer static u32 num_WAVfiles = 0; // Number of WAV files static char* inFile = NULL; // Input filename static char* outFile = NULL; // Output filename static u32 pack_jpegs = 0; // Convert jpegs files to THP flag (-j) static u32 change_snd_data = 0; // Change sound data flag (-c) static u32 change_alltrack = 0; // Change all track data flag (-s) static u32 change_onetrack = 0; // Change one track data flag (-trk) static u8 change_framerate = 0; // Change framerate (-r) static u8 change_videotype = 0; // Change videotype (-non/odd/even) static u32 offset_mode = 0; // Offset flag (-o) static f32 frame_rate = 29.97F; // Frame rate static u32 videotype = THP_VIDEO_NON_INTERLACE;// Video Type static u8 trackNo = 0; // Track # (-trk) static u32 Convert_Verbose = 0; //----------------------------------------------------------------------------- // Function //----------------------------------------------------------------------------- static void Usage ( const char* progName ); static s32 verifyWriteOverInputData( const char* fileName ); static u32 getFileNameList ( const char *sWildcardPattern, THPFileName* filename ); static int compare ( const void *arg1, const void *arg2 ); static s32 checkArguments ( u32 argument_count, char *argv[] ); static s32 convertJPEGtoTHP ( void ); static s32 changeAudioTrack ( THPFileHeader* fileHeader ); static s32 appendAudioData ( THPFileHeader* fileHeader ); static s32 changeAudioData ( void ); static void logPrintFunc ( const char* format, va_list argptr ); /*---------------------------------------------------------------------------* Name: Usage Description: Command usage display Arguments: progName Pointer to the program name Returns: None *---------------------------------------------------------------------------*/ static void Usage(const char* progName) { fprintf(stderr, "usage: %s [-OPTIONS]\n", progName); fprintf(stderr, "\n"); fprintf(stderr, " < THP>>\n"); fprintf(stderr, " -j [*.jpg] # some JPEGs are packed into one file\n"); fprintf(stderr, " -d [file.thp] # output THP file name\n"); fprintf(stderr, "\n"); fprintf(stderr, " \n"); fprintf(stderr, " -s [file.wav] [file.wav] [file.wav] ...\n"); fprintf(stderr, " # input WAV file names\n"); fprintf(stderr, " -o # put offset data in THP\n"); fprintf(stderr, " -r [f32 number] # set FrameRate (default is 29.97)\n"); fprintf(stderr, "\n"); fprintf(stderr, " (THP Video Type)\n"); fprintf(stderr, " -non # default Video Type NON Interlace\n"); fprintf(stderr, " -odd # set Video Type ODD Interlace\n"); fprintf(stderr, " -even # set Video Type EVEN Interlace\n"); fprintf(stderr, "\n"); fprintf(stderr, " -v # verbose mode\n"); fprintf(stderr, " -h # show this usage\n"); fprintf(stderr, "\n"); fprintf(stderr, " ------------------------------------------------------------------\n"); fprintf(stderr, "\n"); fprintf(stderr, " <>\n"); fprintf(stderr, " -c [file.thp] # input THP file name\n"); fprintf(stderr, " -s [file.wav] [file.wav] [file.wav] ...\n"); fprintf(stderr, " # input WAV file names\n"); fprintf(stderr, " -trk [u8 track No.] [file.wav] \n"); fprintf(stderr, " # change Track No.[0... ] & input WAV file name\n"); fprintf(stderr, "\n"); fprintf(stderr, " \n"); fprintf(stderr, " -d [file.thp] # output THP file name\n"); fprintf(stderr, " -o # put offset data in THP\n"); fprintf(stderr, " -r [f32 number] # set FrameRate (default is 29.97)\n"); fprintf(stderr, "\n"); fprintf(stderr, " (THP Video Type)\n"); fprintf(stderr, " -non # default Video Type NON Interlace \n"); fprintf(stderr, " -odd # set Video Type ODD Interlace \n"); fprintf(stderr, " -even # set Video Type EVEN Interlace \n"); fprintf(stderr, "\n"); fprintf(stderr, " -v # verbose mode\n"); fprintf(stderr, " -h # show this usage\n"); fprintf(stderr, "\n"); } /*---------------------------------------------------------------------------* Name: verifyWriteOverInputData Description: Confirms overwriting of input file Arguments: fileName Pointer to the file name Returns: TRUE Overwrite permitted FALSE Overwrite denied *---------------------------------------------------------------------------*/ static s32 verifyWriteOverInputData(const char* fileName) { char answer = 0; u8 loop_flag = 1; while (loop_flag) { printf("\a>Overwrite the inputfile[%s] by OutputData ? [Y/N] ", fileName); answer = toupper(getchar()); if (answer == 'Y') { loop_flag = 0; } else if (answer == 'N') { printf("\a\n Please try using [-d] option.\n"); return FALSE; } else { // Loop // } } return TRUE; } /*---------------------------------------------------------------------------* Name: getFileNameList Description: Searches for file names matching sWildcardPattern and stores them all in filename. The filename buffer must be allocated in advance. Arguments: sWildcardPattern File name including the wildcard filename Buffer storing the file names Returns: Number of files *---------------------------------------------------------------------------*/ static u32 getFileNameList(const char *sWildcardPattern, THPFileName* filename) { u32 f_count = 0; WIN32_FIND_DATA wfd; HANDLE hFind; struct stat; if ((hFind = FindFirstFile(sWildcardPattern, &wfd)) == INVALID_HANDLE_VALUE) { return f_count; } do { if (filename != NULL) { strcpy(filename[f_count].name, wfd.cFileName); strlwr(filename[f_count].name); filename[f_count].fileSize = wfd.nFileSizeLow; } f_count++; } while (FindNextFile(hFind, &wfd)); FindClose(hFind); if (filename != NULL) { u32 i; char drive[_MAX_DRIVE]; char dir[_MAX_DIR]; char pathCopy[_MAX_PATH]; char path[_MAX_PATH]; qsort(filename, f_count, sizeof(THPFileName), compare); _splitpath(sWildcardPattern, drive, dir, NULL, NULL); _makepath(path, drive, dir, NULL, NULL); for (i = 0; i < f_count; i++) { strcpy(pathCopy, path); strcat(pathCopy, filename[i].name); strcpy(filename[i].name, pathCopy); } } return f_count++; } /*---------------------------------------------------------------------------* Name: compare Description: The comparison function specified in qsort() Arguments: arg1, arg2 The elements to be compared Returns: negative arg1 is less than arg2 0 arg1 = arg2 positive arg1 is greater than arg2 *---------------------------------------------------------------------------*/ static int compare(const void *arg1, const void *arg2) { // Compares two strings until their ends return strcmp(((THPFileName *)arg1)->name, ((THPFileName *)arg2)->name); } /*---------------------------------------------------------------------------* Name: checkArguments Description: Argument checker Arguments: argument_count Number of arguments argv Array of argument strings Returns: TRUE Ended successfully FALSE Ended in an error (when the argument is '-h', immediately use exit(0)) *---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------* [Flags] -j option: Create THP files from serial JPEG file (group) Flag specifying serial JPEG files pack_jpegs : 0 -> 1 Serial JPEG file's file name jpegFilesPtr : NULL -> XXXX Number of serial JPEG files num_JPGfiles : 0 -> XX -c option: Replace audio data in existing THP file Flag specifying the input THP file change_snd_data : 0 -> 1 Input THP file's file name inFile : NULL -> XXXX *** -j and -c are exclusive *** -d option Output THP file name outFile : NULL -> XXXX *** Required when using -j/optional when using -c *** -s option Flag specifying the input WAV file change_alltrack : 0 -> 1 Input WAV file's file name waveFilesPtr : NULL -> XXXX Number of input WAV files num_WAVfiles : 0 -> XX *** Not required when using -j *** -trk option Audio track file exchange flag change_onetrack : 0 -> 1 Exchange WAV file name waveFilesPtr : NULL -> XXXX Exchange track number trackNo : 0 -> XX *** Invalid when using -j/when using -c, although -s and -trk are exclusive, one of the two must be included *** -r option Flag specifying frame rate change_framerate : 0 -> 1 Frame rate frame_rate : 29.97f -> XXXXXX -o option Flag for creating offset table offset_mode : 0 -> 1 -non/odd/even option Flag specifying display start field change_videotype : 0 -> 1 Specified field videotype : THP_VIDEO_NON_INTERLACE -> XXXXX *** Invalid when using -trk *** -v option verbose mode flag Convert_Verbose : 0 -> 1 *---------------------------------------------------------------------------*/ static s32 checkArguments(u32 argument_count, char *argv[]) { u32 i; if (argument_count < 2) { Usage(argv[0]); exit(0); } // Check Options // for (i = 1; i < argument_count; i++) { if (argv[i][0] == '-') { u8* option = argv[i]; // // -j : Specify input JPEG file(s) // if (strcmp(option, "-j") == 0) { // -j (pack_jpegs = 1) and -c (change_snd_data) are mutually exclusive if (change_snd_data) { printf("\aERROR : Can't use both [-j] and [-c] at the same time.\n"); return FALSE; } // -j can only be specified once if (pack_jpegs) { printf("\aERROR : [-j] is specified twice.\n"); return FALSE; } // Check to see if there are subsequent file names i++; if (i >= argument_count) { printf("\aERROR : Must specify input filename after [-j].\n"); return FALSE; } jpegFilesPtr = &argv[i]; // Check the number of specified JPEG files while (i < argument_count && argv[i][0] != '-') { num_JPGfiles++; i++; } if (num_JPGfiles == 0) { printf("\aERROR : There is no jpeg file after [-j].\n"); return FALSE; } i--; pack_jpegs = 1; } // // -c : Specify input THP file // else if (strcmp(option, "-c") == 0) { // -j (pack_jpegs = 1) and -c (change_snd_data) are mutually exclusive if (pack_jpegs) { printf("\aERROR : Can't use both [-j] and [-c] at the same time.\n"); return FALSE; } // -c can only be specified once if (change_snd_data) { printf("\aERROR : [-c] is specified twice.\n"); return FALSE; } // Check to see if there are subsequent file names i++; if (i >= argument_count || argv[i][0] == '-') { printf("\aERROR : Must specify input filename after [-c].\n"); return FALSE; } // Acquire input THP file name inFile = argv[i]; THPUtyConvertToUnixFmt(inFile); change_snd_data = 1; } // // -d : Specify output THP file name // else if (strcmp(option, "-d") == 0) { // -d can only be specified once if (outFile) { printf("\aERROR : [-d] is specified twice.\n"); return FALSE; } // Check to see if there are subsequent file names i++; if (i >= argument_count || argv[i][0] == '-') { printf("\aERROR : Must specify output THP filename after [-d].\n"); return FALSE; } // Get output THP file name outFile = argv[i]; THPUtyConvertToUnixFmt(outFile); } // // -s : Specify input WAV file(s) // else if (strcmp(option, "-s") == 0) { // -s (change_alltrack = 1) and -trk (change_onetrack) are mutually exclusive if (change_onetrack) { printf("\aERROR : Can't use both [-s] and [-trk] at the same time.\n"); return FALSE; } // -s can only be specified once if (change_alltrack) { printf("\aERROR : [-s] is specified twice.\n"); return FALSE; } // Check to see if there are subsequent file names i++; if (i >= argument_count) { printf("\aERROR : Must specify input filename after [-s].\n"); return FALSE; } waveFilesPtr = &argv[i]; // Check the number of specified WAV files while ((i < argument_count) && argv[i][0] != '-') { num_WAVfiles++; i++; } if (num_WAVfiles == 0) { printf("\aERROR : There is no WAV file after [-s].\n"); return FALSE; } i--; change_alltrack = 1; } // // -trk : Specify track number and input WAV file // else if (strcmp(option, "-trk") == 0) { // -s (change_alltrack = 1) and -trk (change_onetrack) are mutually exclusive if (change_alltrack) { printf("\aERROR : Can't use both [-s] and [-trk] at the same time.\n"); return FALSE; } // -trk can only be specified once if (change_onetrack) { printf("\aERROR : [-trk] is specified twice.\n"); return FALSE; } // Check to see if there are subsequent track numbers and file names i++; if (i + 1 >= argument_count) { printf("\aERROR : Must specify trackNo and input filename after [-trk].\n"); return FALSE; } // Get the track number if (argv[i][0] < '0' || argv[i][0] > '9') { printf("\aERROR : Invalid trackNo.\n"); return FALSE; } trackNo = atoi(argv[i]); // Get the input WAV file name i++; if (i >= argument_count || argv[i][0] == '-') { printf("\aERROR : Must specify trackNo and input filename after [-trk].\n"); return FALSE; } waveFilesPtr = &argv[i]; THPUtyConvertToUnixFmt(waveFilesPtr[0]); change_onetrack = 1; } // // -r : Specify frame rate // else if (strcmp(option, "-r") == 0) { // Check to see if there are subsequent specified frame rates i++; if (i >= argument_count) { printf("\aERROR : Must specify output framerate after [-r].\n"); return FALSE; } // Get the frame rate frame_rate = (f32)atof(argv[i]); if (frame_rate > 59.94 || frame_rate < 1.0) { printf("\aERROR : Framarate should be between 1.0 to 59.94.\n"); return FALSE; } change_framerate = 1; } // // -o : Specify making an offset table // else if (strcmp(option, "-o") == 0) { offset_mode = 1; } // // -non : Specify video type non-interlace // else if (strcmp(option, "-non") == 0) { if (change_videotype) { printf("\aERROR : Already used -non/odd/even.\n"); return FALSE; } videotype = THP_VIDEO_NON_INTERLACE; change_videotype = 1; } // // -odd : Specify video type interlace beginning from odd field // else if (strcmp(option, "-odd") == 0) { if (change_videotype) { printf("\aERROR : Already used -non/odd/even.\n"); return FALSE; } videotype = THP_VIDEO_ODD_INTERLACE; change_videotype = 1; } // // -even : Specify video type interlace beginning from even field // else if (strcmp(option, "-even") == 0) { if (change_videotype) { printf("\aERROR : Already used -non/odd/even.\n"); return FALSE; } videotype = THP_VIDEO_EVEN_INTERLACE; change_videotype = 1; } // // -v : Specify setting verbose mode // else if (strcmp(option, "-v") == 0) { Convert_Verbose = 1; } // // -on : old option // else if (strcmp(option, "-on") == 0) { ; } // // -h : Specify displaying usage // else if (strcmp(option, "-h") == 0) { Usage(argv[0]); exit(0); } // // etc. // else { printf("\aERROR : Unknown option <%s>.\n", option); return FALSE; } } // if (argv[i][0] == '-') // Invalid argument else { printf("\aERROR : Invalid arguments!\n"); return FALSE; } // if (argv[i][0] == '-') } // for (i = 1; i < argument_count; i++) // Check if either conversion (-j : pack_jpegs = 1) or audio replacement (-c : change_snd_data = 1) has been specified. // if (!pack_jpegs && !change_snd_data) { printf("\aERROR : Must input either [-j] or [-c].\n"); return FALSE; } // When converting from JPEG to THP (-j : pack_jpegs = 1), check that the output THP files have been specified (-d : outFile != NULL). // if (pack_jpegs) { if (outFile == NULL) { printf("\aERROR : Must specify output THP filename [-d] [file.thp].\n"); return FALSE; } } // When replacing THP audio data (-c: change_snd_data = 1), check if an input WAV file has been specified (-s: change_alltrack = 1 or -trk: change_one_track = 1). // if (change_snd_data) { if (!change_alltrack && !change_onetrack) { printf("\aERROR : Must input [-s] [file.wav] or [-trk] [track No.] [file.wav].\n"); return FALSE; } } return TRUE; } /*---------------------------------------------------------------------------* Name: convertJPEGtoTHP Description: Converts serial JPEG files to THP data Arguments: None Returns: TRUE Ended successfully FALSE Ended in an error *---------------------------------------------------------------------------*/ static s32 convertJPEGtoTHP(void) { s32 rtn; s32 error = TRUE; u32 cnt; FILE* thpFp = NULL; THPFileName* jpegFileList = NULL; THPAudioHandle** audioHandle = NULL; THPFileHeader fileHeader; // // Create a JPEG file list // if (num_JPGfiles == 1) { // When a single JPEG file or wildcard is specified THPUtyConvertToUnixFmt(jpegFilesPtr[0]); // Confirm the number of JPEG files num_JPGfiles = getFileNameList(jpegFilesPtr[0], NULL); if (num_JPGfiles == 0) { printf("\aERROR : There is no jpeg files. Input right argument.\n"); error = FALSE; goto ERROR_END; } jpegFileList = (THPFileName*)malloc(sizeof(THPFileName) * num_JPGfiles); if (jpegFileList == NULL) { printf("\aERROR : Can't alloc memory.\n"); error = FALSE; goto ERROR_END; } // Save the JPEG file name getFileNameList(jpegFilesPtr[0], jpegFileList); } else { // When multiple JPEG files are specified jpegFileList = (THPFileName*)malloc(sizeof(THPFileName) * num_JPGfiles); if (jpegFileList == NULL) { printf("\aERROR : Can't alloc memory.\n"); error = FALSE; goto ERROR_END; } for (cnt = 0; cnt < num_JPGfiles; cnt++) { struct stat statBuf; char ext[_MAX_EXT]; // Save the JPEG file name strcpy(jpegFileList[cnt].name, jpegFilesPtr[cnt]); THPUtyConvertToUnixFmt(jpegFileList[cnt].name); // Confirm the extension _splitpath(jpegFileList[cnt].name, NULL, NULL, NULL, ext); if ((stricmp(ext, ".jpg") != 0) && (stricmp(ext, ".jpeg") != 0)) { printf("\aERROR : Please input filename is [***.jpg] or [***.jpeg].\n"); error = FALSE; goto ERROR_END; } // Get the JPEG file size rtn = stat(jpegFileList[cnt].name, &statBuf); if (rtn != 0) { printf("\aERROR : Can't get JPEG file status!\n"); error = FALSE; goto ERROR_END; } jpegFileList[cnt].fileSize = statBuf.st_size; } } if (Convert_Verbose) { printf("Total Frames = %ld\n", num_JPGfiles); printf("Frame Rate = %f\n", frame_rate); } // Set THPHeader THPUtyHeaderInit(&fileHeader.header); fileHeader.header.frameRate = frame_rate; fileHeader.header.numFrames = num_JPGfiles; fileHeader.header.compInfoDataOffsets = sizeof(THPHeader); fileHeader.header.offsetDataOffsets = offset_mode; // Set THPFrameCompInfo fileHeader.frameCompInfo.frameComp[0] = THP_VIDEO_COMP; fileHeader.frameCompInfo.numComponents = 1; for (cnt = 1; cnt < THP_COMP_MAX; cnt++) { fileHeader.frameCompInfo.frameComp[cnt] = THP_NOCOMP_COMP; } // Set THPVideoInfo fileHeader.videoInfo.videoType = videotype; // Audio data encoding settings if (change_alltrack) { if (Convert_Verbose) { printf("START: Audio Initializing.\n"); } if (THPAudioInit() == FALSE) { printf("\aERROR : Fail to execute THPAudioInit().\n"); error = FALSE; goto ERROR_END; } audioHandle = (THPAudioHandle**)malloc(sizeof(THPAudioHandle*) * num_WAVfiles); if (audioHandle == NULL) { printf("\aERROR : Can't alloc memory.\n"); error = FALSE; goto ERROR_END; } // Create the WAV file list for (cnt = 0; cnt < num_WAVfiles; cnt++) { THPUtyConvertToUnixFmt(waveFilesPtr[cnt]); audioHandle[cnt] = THPAudioCreateHandle(waveFilesPtr[cnt], frame_rate); if (audioHandle[cnt] == NULL) { printf("\aERROR : Fail to execute THPAudioCreateHandle().\n"); error = FALSE; goto ERROR_END; } } // Main Sound rtn = THPAudioGetInfo(audioHandle[0], &fileHeader.audioInfo); if (rtn == FALSE) { printf("\aERROR : Fail to execute THPAudioGetInfo().\n"); error = FALSE; goto ERROR_END; } if (Convert_Verbose) { printf(" [AudioData]:(%2ld Tracks)\n", num_WAVfiles); printf(" >> Channels = %ld\n", fileHeader.audioInfo.sndChannels); printf(" >> Frequency = %ld\n", fileHeader.audioInfo.sndFrequency); printf(" <+> Track%02d <= [%s]\n", 0, waveFilesPtr[0]); } // Sub Sound for (cnt = 1; cnt < num_WAVfiles; cnt++) { THPAudioInfo subAudioInfo; rtn = THPAudioGetInfo(audioHandle[cnt], &subAudioInfo); if (rtn == FALSE) { printf("\aERROR : Fail to execute THPAudioGetInfo().\n"); error = FALSE; goto ERROR_END; } // Check the number of channels in a WAV file if (audioHandle[0]->audioInfo.channel != subAudioInfo.sndChannels) { printf("\aERROR : Channels of [%s] is %d (!= %d).\n", waveFilesPtr[cnt], subAudioInfo.sndChannels, audioHandle[0]->audioInfo.channel); error = FALSE; goto ERROR_END; } // Check the playback frequency in a WAV file if (audioHandle[0]->audioInfo.frequency != subAudioInfo.sndFrequency) { printf("\aERROR : Frequency of [%s] is %d (!= %d).\n", waveFilesPtr[cnt], subAudioInfo.sndFrequency, audioHandle[0]->audioInfo.frequency); error = FALSE; goto ERROR_END; } if (Convert_Verbose) { printf(" <+> Track%02d <= [%s]\n", cnt, waveFilesPtr[cnt]); } } if (Convert_Verbose) { printf("END : Audio Initializing.\n"); } // Update THPHeader fileHeader.header.audioMaxSamples = THPAudioGetMaxFrameSamples(audioHandle[0], num_JPGfiles); // Update THPAudioInfo fileHeader.audioInfo.sndNumTracks = num_WAVfiles; fileHeader.audioInfo.sndNumSamples = THPAudioGetTotalSamples(audioHandle[0], num_JPGfiles); // Update THPFrameCompInfo fileHeader.frameCompInfo.frameComp[1] = THP_AUDIO_COMP; fileHeader.frameCompInfo.numComponents++; } // Open Output File thpFp = fopen(outFile, "wb"); if (thpFp == NULL) { THPPrintError("\aERROR : Can't output [%s].\n", outFile); THPPrintError(" Please input valid directory & file name.\n"); error = FALSE; goto ERROR_END; } // Convert serial JPEGs to THP rtn = THPUtyCreateTHP(thpFp, THP_CREATETHP_FILEFLAG_JPEGS, (void*)jpegFileList, &fileHeader, audioHandle); if (rtn != THP_ERROR_NOERROR) { printf("\aERROR : Fail to execute THPUtyCreateTHP().\n"); error = FALSE; goto ERROR_END; } ERROR_END: if (audioHandle) { THPAudioQuit(); for (cnt = 0; cnt < num_WAVfiles; cnt++) { if (audioHandle[cnt] != NULL) { THPAudioFreeHandle(audioHandle[cnt]); } } free(audioHandle); } if (jpegFileList != NULL) { free(jpegFileList); } if (thpFp != NULL) { fclose(thpFp); } return error; } /*---------------------------------------------------------------------------* Name: changeAudioTrack Description: Replaces the existing audio tracks within THP file Arguments: fileHeader Pointer to the THP file header Returns: TRUE Ended successfully FALSE Ended in an error *---------------------------------------------------------------------------*/ static s32 changeAudioTrack(THPFileHeader* fileHeader) { s32 rtn; s32 error = TRUE; u32 cnt; //u32 adjustFrequency; char tmpFilename[_MAX_PATH]; char* fileName = NULL; FILE* outTHPFp = NULL; FILE* inTHPFp = NULL; THPAudioHandle** audioHandle = NULL; THPAudioInfo audioInfo; // Check whether the THP file has an audio track for (cnt = 0; cnt < fileHeader->frameCompInfo.numComponents; cnt++) { if (fileHeader->frameCompInfo.frameComp[cnt] == THP_AUDIO_COMP) { break; } } if (cnt >= fileHeader->frameCompInfo.numComponents) { printf("\aERROR : [%s] doesn't have a Sound Data!\n", inFile); error = FALSE; goto ERROR_END; } // Check the THP file's number of channels and playback frequency if (fileHeader->audioInfo.sndChannels == 0 || fileHeader->audioInfo.sndFrequency == 0) { printf("\aERROR : [%s] doesn't have a Sound Data!\n", inFile); error = FALSE; goto ERROR_END; } // Check the number of THP file tracks if (trackNo >= fileHeader->audioInfo.sndNumTracks) { printf("\aERROR : Must input [u8 track No.] < %d.\n", fileHeader->audioInfo.sndNumTracks); error = FALSE; goto ERROR_END; } if (Convert_Verbose) { printf("START: Audio Initializing.\n"); } // Set THPAudioHandle audioHandle = (THPAudioHandle**)malloc(sizeof(THPAudioHandle*) * fileHeader->audioInfo.sndNumTracks); if (audioHandle == NULL) { printf("\aERROR : Can't alloc memory.\n"); error = FALSE; goto ERROR_END; } for (cnt = 0; cnt < fileHeader->audioInfo.sndNumTracks; cnt++) { audioHandle[cnt] = NULL; } THPUtyConvertToUnixFmt(waveFilesPtr[0]); audioHandle[trackNo] = THPAudioCreateHandle(waveFilesPtr[0], fileHeader->header.frameRate); if (audioHandle[trackNo] == NULL) { printf("\aERROR : Fail to execute THPAudioCreateHandle().\n"); error = FALSE; goto ERROR_END; } // Get additional audio data information rtn = THPAudioGetInfo(audioHandle[trackNo], &audioInfo); if (rtn == FALSE) { printf("\aERROR : Fail to execute THPAudioGetInfo().\n"); error = FALSE; goto ERROR_END; } // Check the number of channels if (fileHeader->audioInfo.sndChannels != audioInfo.sndChannels ) { printf("\aERROR : Channels of [%s] is %d (!= %d).\n", waveFilesPtr[0], audioInfo.sndChannels, fileHeader->audioInfo.sndChannels); error = FALSE; goto ERROR_END; } // Check the playback frequency if (fileHeader->audioInfo.sndFrequency != audioInfo.sndFrequency) { printf("\aERROR : Frequency of [%s] is %d (!= %d).\n", waveFilesPtr[0], audioInfo.sndFrequency, fileHeader->audioInfo.sndFrequency); error = FALSE; goto ERROR_END; } if (Convert_Verbose) { printf(" [AudioData]:(%2ld Tracks)\n", fileHeader->audioInfo.sndNumTracks); printf(" >> Channels = %ld\n", fileHeader->audioInfo.sndChannels); printf(" >> Frequency = %ld\n", fileHeader->audioInfo.sndFrequency); printf(" <+> Track%02d <= [%s]\n", trackNo, waveFilesPtr[0]); printf("END : Audio Initializing.\n"); } // Open the input THP file inTHPFp = fopen(inFile, "rb"); if (inTHPFp == NULL) { printf("\aERROR : Can't open [%s].\n", inFile); error = FALSE; goto ERROR_END; } // Open the output THP file if (outFile != NULL) { fileName = outFile; } else { char drive[_MAX_DRIVE]; char dir[_MAX_DIR]; _splitpath(inFile, drive, dir, NULL, NULL); _makepath(tmpFilename, drive, dir, TMP_FILENAME, NULL); fileName = tmpFilename; } outTHPFp = fopen(fileName, "w+b"); if (outTHPFp == NULL) { printf("\aERROR : Can't open [%s].\n", fileName); error = FALSE; goto ERROR_END; } // Copy the input THP file to the output THP file rtn = THPUtyCopyTHPFile(inTHPFp, fileHeader, outTHPFp); if (rtn != THP_ERROR_NOERROR) { printf("\aERROR : Fail to execute THPUtyCopyTHPFile().\n"); error = FALSE; goto ERROR_END; } rtn = fseek(outTHPFp, 0, SEEK_SET); if (rtn != 0) { THPPrintError("\aERROR : Can't rewind output THP file.\n"); error = FALSE; goto ERROR_END; } // Exchange the audio track rtn = THPUtyChangeAudioTrack(outTHPFp, fileHeader, audioHandle); if (rtn != THP_ERROR_NOERROR) { printf("\aERROR : Fail to execute THPUtyChangeAudioTrack().\n"); error = FALSE; goto ERROR_END; } ERROR_END: if (audioHandle) { for (cnt = 0; cnt < fileHeader->audioInfo.sndNumTracks; cnt++) { if (audioHandle[cnt] != NULL) { THPAudioFreeHandle(audioHandle[cnt]); } } free(audioHandle); } if (inTHPFp != NULL) { fclose(inTHPFp); } if (outTHPFp != NULL) { fclose(outTHPFp); } // Rename the temporary file during an overwrite if (outFile == NULL) { if (error == TRUE) { if (remove(inFile) == -1) { printf("\aERROR : Can't remove old THP file[%s].\n", inFile); } if (rename(fileName, inFile)) { printf("\aERROR : Can't rename THP file[%s -> %s].\n", fileName, inFile); } } else { if (fileName) { if (remove(fileName) == -1) { printf("\aERROR : Can't remove temporary THP file[%s].\n", fileName); } } } } return error; } /*---------------------------------------------------------------------------* Name: appendAudioData Description: Adds audio data when none exists. If it does exist, replace it. (with the -s option) Arguments: fileHeader Pointer to the file header Returns: TRUE Ended successfully FALSE Ended in an error *---------------------------------------------------------------------------*/ static s32 appendAudioData(THPFileHeader* fileHeader) { s32 rtn; s32 error = TRUE; u32 cnt; char tmpFilename[_MAX_PATH]; char* fileName = NULL; FILE* outTHPFp = NULL; FILE* inTHPFp = NULL; THPAudioHandle** audioHandle = NULL; // Open the input THP file inTHPFp = fopen(inFile, "rb"); if (inTHPFp == NULL) { printf("\aERROR : Can't open [%s].\n", inFile); error = FALSE; goto ERROR_END; } // Open the output THP file if (outFile != NULL) { fileName = outFile; } else { char drive[_MAX_DRIVE]; char dir[_MAX_DIR]; _splitpath(inFile, drive, dir, NULL, NULL); _makepath(tmpFilename, drive, dir, TMP_FILENAME, NULL); fileName = tmpFilename; } outTHPFp = fopen(fileName, "wb"); if (outTHPFp == NULL) { printf("\aERROR : Can't open [%s].\n", fileName); error = FALSE; goto ERROR_END; } if (Convert_Verbose) { printf("START: Audio Initializing.\n"); } // Update THPAudioInfo (number of tracks: sndNumTracks) fileHeader->audioInfo.sndNumTracks = num_WAVfiles; audioHandle = (THPAudioHandle**)malloc(sizeof(THPAudioHandle*) * num_WAVfiles); if (audioHandle == NULL) { printf("\aERROR : Can't alloc memory.\n"); error = FALSE; goto ERROR_END; } // Create the WAV file list for (cnt = 0; cnt < num_WAVfiles; cnt++) { THPUtyConvertToUnixFmt(waveFilesPtr[cnt]); audioHandle[cnt] = THPAudioCreateHandle(waveFilesPtr[cnt], fileHeader->header.frameRate); if (audioHandle[cnt] == NULL) { printf("\aERROR : Fail to execute THPAudioCreateHandle().\n"); error = FALSE; goto ERROR_END; } } // Main Sound rtn = THPAudioGetInfo(audioHandle[0], &fileHeader->audioInfo); if (rtn == FALSE) { printf("\aERROR : Fail to execute THPAudioGetInfo().\n"); error = FALSE; goto ERROR_END; } if (Convert_Verbose) { printf(" [AudioData]:(%2ld Tracks)\n", num_WAVfiles); printf(" >> Channels = %ld\n", fileHeader->audioInfo.sndChannels); printf(" >> Frequency = %ld\n", fileHeader->audioInfo.sndFrequency); printf(" <+> Track%02d <= [%s]\n", 0, waveFilesPtr[0]); } // Sub Sound for (cnt = 1; cnt < num_WAVfiles; cnt++) { THPAudioInfo subAudioInfo; rtn = THPAudioGetInfo(audioHandle[cnt], &subAudioInfo); if (rtn == FALSE) { printf("\aERROR : Fail to execute THPAudioGetInfo().\n"); error = FALSE; goto ERROR_END; } // Check the number of channels in a WAV file if (audioHandle[0]->audioInfo.channel != subAudioInfo.sndChannels) { printf("\aERROR : Channels of [%s] is %d (!= %d).\n", waveFilesPtr[cnt], subAudioInfo.sndChannels, audioHandle[0]->audioInfo.channel); error = FALSE; goto ERROR_END; } // Check the playback frequency in a WAV file if (audioHandle[0]->audioInfo.frequency != subAudioInfo.sndFrequency) { printf("\aERROR : Frequency of [%s] is %d (!= %d).\n", waveFilesPtr[cnt], subAudioInfo.sndFrequency, audioHandle[0]->audioInfo.frequency); error = FALSE; goto ERROR_END; } if (Convert_Verbose) { printf(" <+> Track%02d <= [%s]\n", cnt, waveFilesPtr[cnt]); } } if (Convert_Verbose) { printf("END : Audio Initializing.\n"); } // Update THPHeader fileHeader->header.audioMaxSamples = THPAudioGetMaxFrameSamples(audioHandle[0], fileHeader->header.numFrames); // Update THPAudioInfo fileHeader->audioInfo.sndNumTracks = num_WAVfiles; fileHeader->audioInfo.sndNumSamples = THPAudioGetTotalSamples(audioHandle[0], fileHeader->header.numFrames); // Update THPFrameCompInfo if (fileHeader->frameCompInfo.frameComp[1] == THP_NOCOMP_COMP) { fileHeader->frameCompInfo.numComponents++; fileHeader->frameCompInfo.frameComp[1] = THP_AUDIO_COMP; } // Add/exchange audio data in THP data rtn = THPUtyCreateTHP(outTHPFp, THP_CREATETHP_FILEFLAG_THP, (void*)inTHPFp, fileHeader, audioHandle); if (rtn != THP_ERROR_NOERROR) { printf("\aERROR : Fail to execute THPUtyCreateTHP().\n"); error = FALSE; goto ERROR_END; } ERROR_END: if (audioHandle) { for (cnt = 0; cnt < num_WAVfiles; cnt++) { if (audioHandle[cnt] != NULL) { THPAudioFreeHandle(audioHandle[cnt]); } } free(audioHandle); } if (inTHPFp != NULL) { fclose(inTHPFp); } if (outTHPFp != NULL) { fclose(outTHPFp); } // Rename the temporary file during an overwrite if (outFile == NULL) { if (error == TRUE) { if (remove(inFile) == -1) { printf("\aERROR : Can't remove old THP file[%s].\n", inFile); } if (rename(fileName, inFile)) { printf("\aERROR : Can't rename THP file[%s -> %s].\n", fileName, inFile); } } else { if (fileName) { if (remove(fileName) == -1) { printf("\aERROR : Can't remove temporary THP file[%s].\n", fileName); } } } } return error; } /*---------------------------------------------------------------------------* Name: changeAudioData Description: Adds or replaces audio data Arguments: None Returns: TRUE Ended successfully FALSE Ended in an error *---------------------------------------------------------------------------*/ static s32 changeAudioData(void) { THPFileHeader fileHeader; FILE* inTHPFp = NULL; s32 rtn; s32 error = TRUE; if (THPAudioInit() == FALSE) { printf("\aERROR : Fail to execute THPAudioInit().\n"); error = FALSE; goto ERROR_END; } // Open the input THP file inTHPFp = fopen(inFile, "rb"); if (inTHPFp == NULL) { printf("\aERROR : Can't open [%s].\n", inFile); error = FALSE; goto ERROR_END; } // Read the input THP file's THPFileHeader rtn = THPUtyReadTHPFileHeader(inTHPFp, &fileHeader); if (rtn != THP_ERROR_NOERROR) { printf("\aERROR : Can't read THPHeader of [%s].\n", inFile); fclose(inTHPFp); error = FALSE; goto ERROR_END; } fclose(inTHPFp); // Add or replace audio data if (change_onetrack) { // When replacing audio data for a specific track (-trk) if (Convert_Verbose) { printf("Replace audio data of one track.\n"); } // -o/-r/-non/-odd/-even are ignored error = changeAudioTrack(&fileHeader); } else { // When replacing audio data for all tracks (-s) if (Convert_Verbose) { printf("Append/Replace audio data of all tracks.\n"); } // Frame rate update (-r) if (change_framerate) { fileHeader.header.frameRate = frame_rate; } // Update existence of offset table (-o) if (offset_mode) { fileHeader.header.offsetDataOffsets = offset_mode; } // Update the specification of the display start field (-non/odd/even) if (change_videotype) { fileHeader.videoInfo.videoType = videotype; } error = appendAudioData(&fileHeader); } ERROR_END: THPAudioQuit(); return error; } /*---------------------------------------------------------------------------* Name: logPrintFunc Description: Outputs messages, logs and errors Arguments: ... Returns: None *---------------------------------------------------------------------------*/ static void logPrintFunc(const char* format, va_list argptr) { vfprintf(stdout, format, argptr); } /*---------------------------------------------------------------------------* Name: Main Description: THPConv main Arguments: (references Usage()) Returns: 0 Ended normally 1 Ends in an error *---------------------------------------------------------------------------*/ int main(int argc, char *argv[]) { s32 error = 0; // Hook the function for output of messages by the THP Library THPPrintFunc = logPrintFunc; THPPrintLogFunc = logPrintFunc; THPPrintErrorFunc = logPrintFunc; // Argument checker error = checkArguments(argc, argv); // Set the verbose mode flag for the THP Library THPVerboseFlag = Convert_Verbose; if (Convert_Verbose) { printf("Dolphin Jpegs to THP converter. Copyright 2002-2003 Nintendo.\n"); printf("Built : %s %s\n\n", __DATE__, __TIME__); } if ((error == FALSE) || (argc == 1)) { exit(1); } printf("THP file Version is %X.%X\n", THP_VERSION >> 16, THP_VERSION & 0xFFFF); // Create THP if (pack_jpegs) { // Convert from JPEG to THP (-j) if (Convert_Verbose) { printf("Convert from JPEGs to THP...\n"); } error = convertJPEGtoTHP(); } else if (change_snd_data) { // Add/replace audio data in existing THP (-c) if (Convert_Verbose) { printf("Append/Replace audio data...\n"); } // Overwrite confirmation if (outFile == NULL) { if (verifyWriteOverInputData(inFile) == FALSE) { exit(0); } } error = changeAudioData(); } if (error == FALSE) { printf("WARNING : Errors occurred while converting to THP.\n"); printf(" Please check for error messages above!\n"); exit(1); } else { if (outFile) { printf("Successfully generated [%s].\n", outFile); } else { printf("Successfully overwrote [%s].\n", inFile); } } exit(0); }