static struct utf_query_result query_utf_key(STREAMFILE *infile, const long offset, int index, const char *name, int *error) { struct utf_query query; query.index = index; query.name = name; return query_utf_nofail(infile, offset, &query, error); }
struct utf_query_result query_utf_key(FILE *infile, const long offset, int index, const char *name) { struct utf_query query; query.index = index; query.name = name; return query_utf_nofail(infile, offset, &query); }
char *load_utf_string_table(FILE *infile, const long offset) { const struct utf_query_result result = query_utf_nofail(infile, offset, NULL); const size_t string_table_size = result.data_offset - result.string_table_offset; const long string_table_offset = offset + 8 + result.string_table_offset; char *string_table = malloc(string_table_size + 1); CHECK_ERRNO (!string_table, "malloc"); memset(string_table, 0, string_table_size+1); get_bytes_seek(string_table_offset, infile, (unsigned char *)string_table, string_table_size); return string_table; }
/* CRI's UTF wrapper around DSP */ VGMSTREAM * init_vgmstream_utf_dsp(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; char filename[1024]; int table_error = 0; int loop_flag = 0; const long top_offset = 0; int channel_count; int sample_rate; long sample_count; long top_data_offset, segment_count; long body_offset, body_size; long header_offset, header_size; /* check extension, case insensitive */ streamFile->get_name(streamFile,filename,sizeof(filename)); //if (strcasecmp("aax",filename_extension(filename))) goto fail; /* get entry count, data offset */ { struct utf_query_result result; long top_string_table_offset; long top_string_table_size; long name_offset; result = query_utf_nofail(streamFile, top_offset, NULL, &table_error); if (table_error) goto fail; segment_count = result.rows; if (segment_count != 1) goto fail; // only simple stuff for now top_string_table_offset = top_offset + 8 + result.string_table_offset; top_data_offset = top_offset + 8 + result.data_offset; top_string_table_size = top_data_offset - top_string_table_offset; if (result.name_offset+10 > top_string_table_size) goto fail; name_offset = top_string_table_offset + result.name_offset; if (read_32bitBE(name_offset, streamFile) != 0x41445043 ||// "ADPC" read_32bitBE(name_offset+4, streamFile) != 0x4D5F5749 ||// "M_WI" read_16bitBE(name_offset+8, streamFile) != 0x4900) // "I\0" goto fail; } { struct offset_size_pair offset_size; offset_size = query_utf_data(streamFile, top_offset, 0, "data", &table_error); if (table_error) goto fail; body_offset = top_data_offset + offset_size.offset; body_size = offset_size.size; offset_size = query_utf_data(streamFile, top_offset, 0, "header", &table_error); if (table_error) goto fail; header_offset = top_data_offset + offset_size.offset; header_size = offset_size.size; } channel_count = query_utf_1byte(streamFile, top_offset, 0, "nch", &table_error); sample_count = query_utf_4byte(streamFile, top_offset, 0, "nsmpl", &table_error); sample_rate = query_utf_4byte(streamFile, top_offset, 0, "sfreq", &table_error); if (table_error) goto fail; if (channel_count != 1 && channel_count != 2) goto fail; if (header_size != channel_count * 0x60) goto fail; vgmstream = allocate_vgmstream(channel_count,loop_flag); vgmstream->num_samples = sample_count; vgmstream->sample_rate = sample_rate; vgmstream->coding_type = coding_NGC_DSP; vgmstream->layout_type = layout_none; vgmstream->meta_type = meta_UTF_DSP; { int i,j; long channel_size = (body_size+7)/8*8/channel_count; for (i = 0; i < channel_count; i++) { vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); if (!vgmstream->ch[i].streamfile) goto fail; vgmstream->ch[i].channel_start_offset = vgmstream->ch[i].offset = body_offset + i * channel_size; for (j=0;j<16;j++) { vgmstream->ch[i].adpcm_coef[j] = read_16bitBE(header_offset + 0x60*i + 0x1c + j*2, streamFile); } } } return vgmstream; /* clean up anything we may have opened */ fail: if (vgmstream) close_vgmstream(vgmstream); return NULL; }
/* Actual AAX init fcn */ VGMSTREAM * init_vgmstream_aax(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; STREAMFILE * streamFileAAX = NULL; STREAMFILE * streamFileADX = NULL; char filename[1024]; off_t *segment_offset = NULL; off_t *segment_size = NULL; int32_t sample_count; int table_error = 0; int loop_flag = 0; int32_t loop_start_sample=0; int32_t loop_end_sample=0; int loop_segment = 0; aax_codec_data *data = NULL; const long AAX_offset = 0; int channel_count = 0, segment_count; int sample_rate = 0; int i; long aax_data_offset; /* check extension, case insensitive */ streamFile->get_name(streamFile,filename,sizeof(filename)); if (strcasecmp("aax",filename_extension(filename))) goto fail; /* get AAX entry count, data offset */ { struct utf_query_result result; long aax_string_table_offset; long aax_string_table_size; result = query_utf_nofail(streamFile, AAX_offset, NULL, &table_error); if (table_error) goto fail; segment_count = result.rows; aax_string_table_offset = AAX_offset + 8 + result.string_table_offset; aax_data_offset = AAX_offset + 8 + result.data_offset; aax_string_table_size = aax_data_offset - aax_string_table_offset; if (result.name_offset+4 > aax_string_table_size) goto fail; if (read_32bitBE(aax_string_table_offset + result.name_offset, streamFile) != 0x41415800) /* "AAX\0" */ goto fail; } segment_offset = calloc(segment_count,sizeof(off_t)); if (!segment_offset) goto fail; segment_size = calloc(segment_count,sizeof(off_t)); if (!segment_size) goto fail; /* get offsets of constituent ADXs */ for (i = 0; i < segment_count; i++) { struct offset_size_pair offset_size; offset_size = query_utf_data(streamFile, AAX_offset, i, "data", &table_error); if (table_error) goto fail; segment_offset[i] = aax_data_offset + offset_size.offset; segment_size[i] = offset_size.size; } streamFileAAX = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); if (!streamFileAAX) goto fail; data = malloc(sizeof(aax_codec_data)); if (!data) goto fail; data->segment_count = segment_count; data->adxs = malloc(sizeof(STREAMFILE *)*segment_count); if (!data->adxs) goto fail; for (i=0;i<segment_count;i++) { data->adxs[i] = NULL; } data->sample_counts = calloc(segment_count,sizeof(int32_t)); if (!data->sample_counts) goto fail; /* for each segment */ for (i = 0; i < segment_count; i++) { VGMSTREAM *adx; /*printf("try opening segment %d/%d %x\n",i,segment_count,segment_offset[i]);*/ streamFileADX = open_aax_with_STREAMFILE(streamFileAAX,segment_offset[i],segment_size[i]); if (!streamFileADX) goto fail; adx = data->adxs[i] = init_vgmstream_adx(streamFileADX); if (!adx) goto fail; data->sample_counts[i] = adx->num_samples; close_streamfile(streamFileADX); streamFileADX = NULL; if (i == 0) { channel_count = adx->channels; sample_rate = adx->sample_rate; } else { if (channel_count != adx->channels) goto fail; if (sample_rate != adx->sample_rate) goto fail; } if (adx->loop_flag != 0) goto fail; /* save start things so we can restart for seeking/looping */ /* copy the channels */ memcpy(adx->start_ch,adx->ch,sizeof(VGMSTREAMCHANNEL)*adx->channels); /* copy the whole VGMSTREAM */ memcpy(adx->start_vgmstream,adx,sizeof(VGMSTREAM)); } sample_count = 0; loop_flag = 0; for (i = 0; i < segment_count; i++) { int segment_loop_flag = query_utf_1byte(streamFile, AAX_offset, i, "lpflg", &table_error); if (table_error) segment_loop_flag = 0; if (!loop_flag && segment_loop_flag) { loop_start_sample = sample_count; loop_segment = i; } sample_count += data->sample_counts[i]; if (!loop_flag && segment_loop_flag) { loop_end_sample = sample_count; loop_flag = 1; } } vgmstream = allocate_vgmstream(channel_count,loop_flag); vgmstream->num_samples = sample_count; vgmstream->sample_rate = sample_rate; vgmstream->loop_start_sample = loop_start_sample; vgmstream->loop_end_sample = loop_end_sample; vgmstream->coding_type = data->adxs[0]->coding_type; vgmstream->layout_type = layout_aax; vgmstream->meta_type = meta_AAX; vgmstream->ch[0].streamfile = streamFileAAX; data->current_segment = 0; data->loop_segment = loop_segment; vgmstream->codec_data = data; free(segment_offset); free(segment_size); return vgmstream; /* clean up anything we may have opened */ fail: if (streamFileAAX) close_streamfile(streamFileAAX); if (streamFileADX) close_streamfile(streamFileADX); if (vgmstream) close_vgmstream(vgmstream); if (segment_offset) free(segment_offset); if (segment_size) free(segment_size); if (data) { if (data->adxs) { int i; for (i=0;i<data->segment_count;i++) if (data->adxs) close_vgmstream(data->adxs[i]); free(data->adxs); } if (data->sample_counts) { free(data->sample_counts); } free(data); } return NULL; }
void analyze_CPK(FILE *infile, long file_length, struct cpk_file *file, int new_file_count) { const long CpkHeader_offset = 0x0; char *toc_string_table = NULL; /* check header */ { unsigned char buf[4]; static const char CPK_signature[4] = "CPK "; /* intentionally unterminated */ get_bytes_seek(CpkHeader_offset, infile, buf, 4); CHECK_ERROR (memcmp(buf, CPK_signature, sizeof(CPK_signature)), "CPK signature not found"); } /* check CpkHeader */ { struct utf_query_result result = query_utf_nofail(infile, CpkHeader_offset+0x10, NULL); CHECK_ERROR (result.rows != 1, "wrong number of rows in CpkHeader"); } /* get TOC offset */ long toc_offset = query_utf_8byte(infile, CpkHeader_offset+0x10, 0, "TocOffset"); /* get content offset */ long content_offset = query_utf_8byte(infile, CpkHeader_offset+0x10, 0, "ContentOffset"); /* get file count from CpkHeader */ long CpkHeader_count = query_utf_4byte(infile, CpkHeader_offset+0x10, 0, "Files"); #ifdef DEBUG printf("(Debug)TocOffset: %d\n(Debug)ContentOffset: %d\n(Debug)Files(Count): %d\n",toc_offset,content_offset,CpkHeader_count); #endif /* check TOC header */ { unsigned char buf[4]; static const char TOC_signature[4] = "TOC "; /* intentionally unterminated */ get_bytes_seek(toc_offset, infile, buf, 4); CHECK_ERROR (memcmp(buf, TOC_signature, sizeof(TOC_signature)), "TOC signature not found"); } /* get TOC entry count, string table offset */ long toc_entries; long toc_string_table_offset; long toc_string_table_size; { struct utf_query_result result = query_utf_nofail(infile, toc_offset+0x10, NULL); toc_entries = result.rows; toc_string_table_offset = toc_offset + 0x10 + 8 + result.string_table_offset; toc_string_table_size = result.data_offset - result.string_table_offset; } #ifdef DEBUG printf("(Debug)TocEntries: %d\n(Debug)TocStringTableOffset: %d\n(Debug)TocStringTableSize:%d\n",toc_entries,toc_string_table_offset,toc_string_table_size); #endif /* check that counts match */ CHECK_ERROR( toc_entries != CpkHeader_count, "CpkHeader file count and TOC entry count do not match" ); /* load string table */ toc_string_table = malloc(toc_string_table_size + 1); { CHECK_ERRNO(!toc_string_table, "malloc"); memset(toc_string_table, 0, toc_string_table_size+1); get_bytes_seek(toc_string_table_offset, infile, (unsigned char *)toc_string_table, toc_string_table_size); } /* find files in cpk */ for (int i = 0; i < toc_entries; i++) { /* get file name */ const char *file_name = query_utf_string(infile, toc_offset+0x10, i, "FileName", toc_string_table); const char *dir_name=query_utf_string(infile,toc_offset+0x10,i,"DirName",toc_string_table); /* to query a file with relative path, remember to free it */ char* query_file=malloc(strlen(dir_name)+strlen(file_name)+2); strcpy(query_file,dir_name); char sep[]="/"; strcat(query_file,sep); strcat(query_file,file_name); int entry_num = contains_file_name(query_file, file, new_file_count); if (entry_num >= 0) { #ifdef DEBUG printf("(Debug)FileName length:%d\n(Debug)DirName length:%d\n",strlen(file_name),strlen(dir_name)); printf("(Debug)query file location:%s\n",query_file); #endif printf("Found %s/%s in cpk\n",dir_name, file_name); /* get file offset */ file[entry_num].orig_offset = content_offset + query_utf_8byte(infile, toc_offset+0x10, i, "FileOffset"); /* get file size */ file[entry_num].orig_size = query_utf_4byte(infile, toc_offset+0x10, i, "FileSize"); /* save index */ file[entry_num].index = i; file[entry_num].found = 1; #ifdef DEBUG printf("(Debug)EntryNum:%d\n",entry_num); #endif } /* free the query_file to avoid leak */ if(query_file) free(query_file); } sort_by_offset(file, new_file_count); FILE *outfile = fopen("out.cpk", "w+b"); printf("Starting file copy\n"); long current = 0; for (int i = 0; i < new_file_count; i++) { if (file[i].found) { /* copy from infile to start of current new file */ dump(infile, outfile, current, file[i].orig_offset-current); current = file[i].orig_offset; /* open new file and copy data */ FILE *newfile = fopen(file[i].location, "rb"); CHECK_ERRNO(!newfile, "fopen"); CHECK_ERRNO(fseek(newfile, 0 , SEEK_END) != 0, "fseek"); file[i].new_size = ftell(newfile); CHECK_ERRNO(file_length == -1, "ftell"); rewind(infile); printf("Copying %s(file name:%s)\n", file[i].location,file[i].filename); dump(newfile, outfile, 0, file[i].new_size); printf("Finished copying %s(file name:%s)\n", file[i].location,file[i].filename); fclose(newfile); /* update information */ current += file[i].orig_size; file[i].copied = 1; file[i].offset_diff = file[i].new_size -file[i].orig_size; } } /* copy remaining data */ dump(infile, outfile, current, file_length-current); printf("Finished file copy\n"); /* fix toc_offset */ toc_offset = fix_offset(outfile, CpkHeader_offset+0x10, 0, 0, "TocOffset", file, new_file_count); printf("Fixed toc offset\n"); /* fix file offsets */ for (int i = 0; i < toc_entries; i++) fix_offset(outfile, toc_offset+0x10, content_offset, i, "FileOffset", file, new_file_count); printf("Fixed file offsets\n"); /* fix file sizes */ for (int i = 0; i < new_file_count; i++) fix_file_sizes(outfile, toc_offset+0x10, file, i); printf("Finished replacing files and fixing data\n"); CHECK_ERRNO(fclose(outfile) != 0, "fclose"); if (toc_string_table) { free(toc_string_table); toc_string_table = NULL; } }