mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-11-14 18:47:39 +01:00
Add .awb+acb using CPK [MGS3 (3DS), SF vs TK (X360)]
This commit is contained in:
parent
9e971af292
commit
8da533e815
@ -620,6 +620,10 @@
|
||||
RelativePath=".\meta\ck.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\cpk.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\cri_utf.c"
|
||||
>
|
||||
|
@ -306,6 +306,7 @@
|
||||
<ClCompile Include="meta\bsf.c" />
|
||||
<ClCompile Include="meta\capdsp.c" />
|
||||
<ClCompile Include="meta\ck.c" />
|
||||
<ClCompile Include="meta\cpk.c" />
|
||||
<ClCompile Include="meta\cri_utf.c" />
|
||||
<ClCompile Include="meta\csb.c" />
|
||||
<ClCompile Include="meta\csmp.c" />
|
||||
|
@ -427,6 +427,9 @@
|
||||
<ClCompile Include="meta\ck.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\cpk.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\cri_utf.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
|
@ -15,11 +15,11 @@ VGMSTREAM* init_vgmstream_acb(STREAMFILE* sf) {
|
||||
/* checks */
|
||||
if (!check_extensions(sf, "acb"))
|
||||
goto fail;
|
||||
if (read_32bitBE(0x00,sf) != 0x40555446) /* "@UTF" */
|
||||
if (read_u32be(0x00,sf) != 0x40555446) /* "@UTF" */
|
||||
goto fail;
|
||||
|
||||
/* .acb is a cue sheet that uses @UTF (CRI's generic table format) to store row/columns
|
||||
* with complex info (cues, sequences, spatial info, etc). it can store a memory .awb
|
||||
* with complex info (cues, sequences, spatial info, etc). It can store a memory .awb
|
||||
* (our target here), or reference external/streamed .awb (loaded elsewhere)
|
||||
* we only want .awb with actual waves but may use .acb to get names */
|
||||
{
|
||||
@ -34,8 +34,6 @@ VGMSTREAM* init_vgmstream_acb(STREAMFILE* sf) {
|
||||
if (rows != 1 || strcmp(name, "Header") != 0)
|
||||
goto fail;
|
||||
|
||||
//todo acb+cpk is also possible
|
||||
|
||||
if (!utf_query_data(utf, 0, "AwbFile", &offset, &size))
|
||||
goto fail;
|
||||
|
||||
@ -52,10 +50,16 @@ VGMSTREAM* init_vgmstream_acb(STREAMFILE* sf) {
|
||||
temp_sf = setup_subfile_streamfile(sf, subfile_offset,subfile_size, "awb");
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
vgmstream = init_vgmstream_awb_memory(temp_sf, sf);
|
||||
if (read_u32be(0x00, temp_sf) == 0x43504B20) { /* "CPK " */
|
||||
vgmstream = init_vgmstream_cpk_memory(temp_sf, sf); /* older */
|
||||
if (!vgmstream) goto fail;
|
||||
}
|
||||
else {
|
||||
vgmstream = init_vgmstream_awb_memory(temp_sf, sf); /* newer */
|
||||
if (!vgmstream) goto fail;
|
||||
}
|
||||
|
||||
/* name-loading for this for memory .awb will be called from init_vgmstream_awb_memory */
|
||||
/* name-loading for this for memory .awb will be called from init_vgmstream_awb/cpk_memory */
|
||||
|
||||
utf_close(utf);
|
||||
close_streamfile(temp_sf);
|
||||
|
248
src/meta/cpk.c
Normal file
248
src/meta/cpk.c
Normal file
@ -0,0 +1,248 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
#include "cri_utf.h"
|
||||
|
||||
|
||||
typedef enum { HCA, CWAV, } cpk_type_t;
|
||||
|
||||
static void load_cpk_name(STREAMFILE* sf, STREAMFILE* sf_acb, VGMSTREAM* vgmstream, int waveid);
|
||||
|
||||
/* CPK - CRI container, audio part only [Metal Gear Solid: Snake Eater 3D (3DS), Street Fighter X Tekken Rip (X360)] */
|
||||
VGMSTREAM* init_vgmstream_cpk(STREAMFILE* sf) {
|
||||
return init_vgmstream_cpk_memory(sf, NULL);
|
||||
}
|
||||
|
||||
VGMSTREAM* init_vgmstream_cpk_memory(STREAMFILE* sf, STREAMFILE* sf_acb) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
STREAMFILE* temp_sf = NULL;
|
||||
off_t subfile_offset = 0;
|
||||
size_t subfile_size = 0;
|
||||
utf_context* utf = NULL;
|
||||
int total_subsongs, target_subsong = sf->stream_index;
|
||||
int subfile_id = 0;
|
||||
cpk_type_t type;
|
||||
const char* extension = NULL;
|
||||
uint32_t* sizes = NULL;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(sf, "awb"))
|
||||
goto fail;
|
||||
if (read_u32be(0x00,sf) != 0x43504B20) /* "CPK " */
|
||||
goto fail;
|
||||
if (read_u32be(0x10,sf) != 0x40555446) /* "@UTF" */
|
||||
goto fail;
|
||||
/* 04: 0xFF? */
|
||||
/* 08: 0x02A0? */
|
||||
/* 0c: null? */
|
||||
|
||||
/* .cpk is CRI's generic file container, but here we only support CPK .awb used as
|
||||
* early audio bank, that like standard AFS2 .awb comes with .acb */
|
||||
{
|
||||
int rows, i;
|
||||
const char* name;
|
||||
const char* Tvers;
|
||||
uint32_t table_offset = 0, offset;
|
||||
uint32_t Files = 0, FilesL = 0, FilesH = 0;
|
||||
uint64_t ContentOffset = 0, ItocOffset = 0;
|
||||
uint16_t Align = 0;
|
||||
uint32_t DataL_offset = 0, DataL_size = 0, DataH_offset = 0, DataH_size = 0;
|
||||
|
||||
/* base header */
|
||||
table_offset = 0x10;
|
||||
utf = utf_open(sf, table_offset, &rows, &name);
|
||||
if (!utf || strcmp(name, "CpkHeader") != 0 || rows != 1)
|
||||
goto fail;
|
||||
|
||||
if (!utf_query_string(utf, 0, "Tvers", &Tvers) ||
|
||||
!utf_query_u32(utf, 0, "Files", &Files) ||
|
||||
!utf_query_u64(utf, 0, "ContentOffset", &ContentOffset) || /* absolute */
|
||||
!utf_query_u64(utf, 0, "ItocOffset", &ItocOffset) || /* Toc seems used for regular files */
|
||||
!utf_query_u16(utf, 0, "Align", &Align))
|
||||
goto fail;
|
||||
|
||||
utf_close(utf);
|
||||
utf = NULL;
|
||||
|
||||
if (strncmp(Tvers, "awb.", 4) != 0) /* starts with "awb." + version */
|
||||
goto fail;
|
||||
if (Files <= 0)
|
||||
goto fail;
|
||||
|
||||
|
||||
/* Itoc header */
|
||||
table_offset = 0x10 + ItocOffset;
|
||||
utf = utf_open(sf, table_offset, &rows, &name);
|
||||
if (!utf) goto fail;
|
||||
|
||||
if (rows != 1 || strcmp(name, "CpkItocInfo") != 0)
|
||||
goto fail;
|
||||
|
||||
if (!utf_query_u32(utf, 0, "FilesL", &FilesL) ||
|
||||
!utf_query_u32(utf, 0, "FilesH", &FilesH) ||
|
||||
!utf_query_data(utf, 0, "DataL", &DataL_offset, &DataL_size) || /* absolute */
|
||||
!utf_query_data(utf, 0, "DataH", &DataH_offset, &DataH_size)) /* absolute */
|
||||
goto fail;
|
||||
|
||||
utf_close(utf);
|
||||
utf = NULL;
|
||||
|
||||
|
||||
/* For maximum annoyance there are 2 tables (small+big files) that only list sizes,
|
||||
* and files can be mixed (small small big small big).
|
||||
* Must pre-read all entries to find actual offset plus subsongs number. */
|
||||
if (FilesL + FilesH != Files)
|
||||
goto fail;
|
||||
|
||||
total_subsongs = Files;
|
||||
if (target_subsong == 0) target_subsong = 1;
|
||||
if (target_subsong > total_subsongs || total_subsongs <= 0) goto fail;
|
||||
|
||||
sizes = calloc(Files, sizeof(uint32_t));
|
||||
if (!sizes) goto fail;
|
||||
|
||||
/* DataL header */
|
||||
table_offset = DataL_offset;
|
||||
utf = utf_open(sf, table_offset, &rows, &name);
|
||||
if (!utf || strcmp(name, "CpkItocL") != 0 || rows != FilesL)
|
||||
goto fail;
|
||||
|
||||
for (i = 0; i < rows; i++) {
|
||||
uint16_t ID = 0;
|
||||
uint16_t FileSize, ExtractSize;
|
||||
|
||||
if (!utf_query_u16(utf, i, "ID", &ID) ||
|
||||
!utf_query_u16(utf, i, "FileSize", &FileSize) ||
|
||||
!utf_query_u16(utf, i, "ExtractSize", &ExtractSize))
|
||||
goto fail;
|
||||
|
||||
if (ID >= Files || FileSize != ExtractSize || sizes[ID])
|
||||
goto fail;
|
||||
|
||||
sizes[ID] = FileSize;
|
||||
}
|
||||
|
||||
utf_close(utf);
|
||||
utf = NULL;
|
||||
|
||||
/* DataR header */
|
||||
table_offset = DataH_offset;
|
||||
utf = utf_open(sf, table_offset, &rows, &name);
|
||||
if (!utf || strcmp(name, "CpkItocH") != 0 || rows != FilesH)
|
||||
goto fail;
|
||||
|
||||
for (i = 0; i < rows; i++) {
|
||||
uint16_t ID = 0;
|
||||
uint32_t FileSize, ExtractSize;
|
||||
|
||||
if (!utf_query_u16(utf, i, "ID", &ID) ||
|
||||
!utf_query_u32(utf, i, "FileSize", &FileSize) ||
|
||||
!utf_query_u32(utf, i, "ExtractSize", &ExtractSize))
|
||||
goto fail;
|
||||
|
||||
if (ID >= Files || FileSize != ExtractSize || sizes[ID])
|
||||
goto fail;
|
||||
|
||||
sizes[ID] = FileSize;
|
||||
}
|
||||
|
||||
utf_close(utf);
|
||||
utf = NULL;
|
||||
|
||||
|
||||
/* find actual offset */
|
||||
offset = ContentOffset;
|
||||
for (i = 0; i < Files; i++) {
|
||||
uint32_t size = sizes[i];
|
||||
if (i + 1 == target_subsong) {
|
||||
subfile_id = i;
|
||||
subfile_offset = offset;
|
||||
subfile_size = size;
|
||||
break;
|
||||
}
|
||||
|
||||
offset += size;
|
||||
if (Align && (offset % Align))
|
||||
offset += Align - (offset % Align);
|
||||
}
|
||||
|
||||
free(sizes);
|
||||
sizes = NULL;
|
||||
}
|
||||
|
||||
if (!subfile_offset)
|
||||
goto fail;
|
||||
|
||||
//;VGM_LOG("CPK: subfile offset=%lx + %x, id=%i\n", subfile_offset, subfile_size, subfile_id);
|
||||
|
||||
|
||||
if ((read_u32be(subfile_offset,sf) & 0x7f7f7f7f) == 0x48434100) { /* "HCA\0" */
|
||||
type = HCA;
|
||||
extension = "hca";
|
||||
}
|
||||
else if (read_u32be(subfile_offset,sf) == 0x43574156) { /* "CWAV" */
|
||||
type = CWAV;
|
||||
extension = "bcwav";
|
||||
}
|
||||
else {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, extension);
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
switch(type) {
|
||||
case HCA:
|
||||
vgmstream = init_vgmstream_hca(temp_sf);
|
||||
if (!vgmstream) goto fail;
|
||||
break;
|
||||
case CWAV: /* Metal Gear Solid: Snake Eater 3D (3DS) */
|
||||
vgmstream = init_vgmstream_rwsd(temp_sf);
|
||||
if (!vgmstream) goto fail;
|
||||
break;
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
vgmstream->num_streams = total_subsongs;
|
||||
|
||||
/* try to load cue names */
|
||||
load_cpk_name(sf, sf_acb, vgmstream, subfile_id);
|
||||
|
||||
close_streamfile(temp_sf);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
free(sizes);
|
||||
utf_close(utf);
|
||||
close_streamfile(temp_sf);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void load_cpk_name(STREAMFILE* sf, STREAMFILE* sf_acb, VGMSTREAM* vgmstream, int waveid) {
|
||||
int is_memory = (sf_acb != NULL);
|
||||
|
||||
/* .acb is passed when loading memory .awb inside .acb */
|
||||
if (!is_memory) {
|
||||
/* try parsing TXTM if present */
|
||||
sf_acb = read_filemap_file(sf, 0);
|
||||
|
||||
/* try (name).awb + (name).awb */
|
||||
if (!sf_acb)
|
||||
sf_acb = open_streamfile_by_ext(sf, "acb");
|
||||
|
||||
/* (name)_streamfiles.awb + (name).acb also exist */
|
||||
|
||||
if (!sf_acb)
|
||||
return;
|
||||
|
||||
/* companion .acb probably loaded */
|
||||
load_acb_wave_name(sf_acb, vgmstream, waveid, is_memory);
|
||||
|
||||
close_streamfile(sf_acb);
|
||||
}
|
||||
else {
|
||||
load_acb_wave_name(sf_acb, vgmstream, waveid, is_memory);
|
||||
}
|
||||
}
|
@ -369,15 +369,30 @@ static int utf_query_value(utf_context* utf, int row, const char* column, void*
|
||||
return 1;
|
||||
}
|
||||
|
||||
int utf_query_s8(utf_context* utf, int row, const char* column, int8_t* value) {
|
||||
return utf_query_value(utf, row, column, (void*)value, COLUMN_TYPE_SINT8);
|
||||
}
|
||||
int utf_query_u8(utf_context* utf, int row, const char* column, uint8_t* value) {
|
||||
return utf_query_value(utf, row, column, (void*)value, COLUMN_TYPE_UINT8);
|
||||
}
|
||||
int utf_query_s16(utf_context* utf, int row, const char* column, int16_t* value) {
|
||||
return utf_query_value(utf, row, column, (void*)value, COLUMN_TYPE_SINT16);
|
||||
}
|
||||
int utf_query_u16(utf_context* utf, int row, const char* column, uint16_t* value) {
|
||||
return utf_query_value(utf, row, column, (void*)value, COLUMN_TYPE_UINT16);
|
||||
}
|
||||
int utf_query_s32(utf_context* utf, int row, const char* column, int32_t* value) {
|
||||
return utf_query_value(utf, row, column, (void*)value, COLUMN_TYPE_SINT32);
|
||||
}
|
||||
int utf_query_u32(utf_context* utf, int row, const char* column, uint32_t* value) {
|
||||
return utf_query_value(utf, row, column, (void*)value, COLUMN_TYPE_UINT32);
|
||||
}
|
||||
int utf_query_s64(utf_context* utf, int row, const char* column, int64_t* value) {
|
||||
return utf_query_value(utf, row, column, (void*)value, COLUMN_TYPE_SINT64);
|
||||
}
|
||||
int utf_query_u64(utf_context* utf, int row, const char* column, uint64_t* value) {
|
||||
return utf_query_value(utf, row, column, (void*)value, COLUMN_TYPE_UINT64);
|
||||
}
|
||||
int utf_query_string(utf_context* utf, int row, const char* column, const char** value) {
|
||||
return utf_query_value(utf, row, column, (void*)value, COLUMN_TYPE_STRING);
|
||||
}
|
||||
|
@ -24,9 +24,14 @@ typedef struct utf_context utf_context;
|
||||
utf_context* utf_open(STREAMFILE* sf, uint32_t table_offset, int* p_rows, const char** p_row_name);
|
||||
void utf_close(utf_context* utf);
|
||||
/* query calls */
|
||||
int utf_query_s8(utf_context* utf, int row, const char* column, int8_t* value);
|
||||
int utf_query_u8(utf_context* utf, int row, const char* column, uint8_t* value);
|
||||
int utf_query_s16(utf_context* utf, int row, const char* column, int16_t* value);
|
||||
int utf_query_u16(utf_context* utf, int row, const char* column, uint16_t* value);
|
||||
int utf_query_s32(utf_context* utf, int row, const char* column, int32_t* value);
|
||||
int utf_query_u32(utf_context* utf, int row, const char* column, uint32_t* value);
|
||||
int utf_query_s64(utf_context* utf, int row, const char* column, int64_t* value);
|
||||
int utf_query_u64(utf_context* utf, int row, const char* column, uint64_t* value);
|
||||
int utf_query_string(utf_context* utf, int row, const char* column, const char** value);
|
||||
int utf_query_data(utf_context* utf, int row, const char* column, uint32_t* offset, uint32_t* size);
|
||||
|
||||
|
@ -924,4 +924,7 @@ VGMSTREAM* init_vgmstream_xse_old(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM* init_vgmstream_wady(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM* init_vgmstream_cpk(STREAMFILE* sf);
|
||||
VGMSTREAM* init_vgmstream_cpk_memory(STREAMFILE* sf, STREAMFILE* sf_acb);
|
||||
|
||||
#endif /*_META_H*/
|
||||
|
@ -510,6 +510,7 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
|
||||
init_vgmstream_dsp_sqex,
|
||||
init_vgmstream_dsp_wiivoice,
|
||||
init_vgmstream_xws,
|
||||
init_vgmstream_cpk,
|
||||
|
||||
/* lowest priority metas (should go after all metas, and TXTH should go before raw formats) */
|
||||
init_vgmstream_txth, /* proper parsers should supersede TXTH, once added */
|
||||
|
Loading…
Reference in New Issue
Block a user