mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-02-07 23:11:26 +01:00
Merge pull request #1272 from bnnm/txth-ogg
- Add NIS .bgm Ogg and cleanup [Yomawari: Midnight Shadows (PC)] - Add RIFF .xms [Ty the Tasmanian Tiger (Xbox)] - Fix Ogg .eno - Improve TXTH dynamic dechunking - Remove unused aif-loop extension
This commit is contained in:
commit
599326a362
45
doc/TXTH.md
45
doc/TXTH.md
@ -462,24 +462,29 @@ Optional settings (set before main):
|
|||||||
- If size is 0x1000 and data_size 0x800 last 0x200 is ignored padding.
|
- If size is 0x1000 and data_size 0x800 last 0x200 is ignored padding.
|
||||||
|
|
||||||
Dynamic settings (set before main, requires `chunk_header_size`):
|
Dynamic settings (set before main, requires `chunk_header_size`):
|
||||||
- `chunk_value`: ignores chunks that don't match this value at chunk offset 0x00 (32-bit, in `chunk_endianness`)
|
- `chunk_value`: ignores chunks that don't match this value at chunk offset 0x00 (32-bit, in `chunk_endianness`). Can be used to ignore video chunks in movie files, for example.
|
||||||
- `chunk_size_offset`: reads chunk size at this offset, in header (32-bit in `chunk_endianness`).
|
- `chunk_size_offset`: reads chunk size at this offset, in header (32-bit in `chunk_endianness`). For chunks of dynamic sizes (no need to set `chunk_size` as will be ignored). Includes header size.
|
||||||
- `chunk_endianness`: sets endianness of the above values
|
- `chunk_data_size_offset`: same, for data sizes (not including headers). Note that you can use header+data+size configs to handle padding between blocks.
|
||||||
|
- `chunk_endianness`: sets endianness of the above two settings.
|
||||||
|
|
||||||
For technical reasons, "dechunking" activates when setting all main settings, so set optional config first. Note that config is static (not per-chunk), so `chunk_size = @0x10` is read from the beginning of the file once, not every time a new chunk is found.
|
For technical reasons, "dechunking" activates when setting all main settings, so set optional config first. Note that `chunk_size` is static (read once from a fixed offset) while `chunk_size_offset` is dynamic (read on every chunk).
|
||||||
|
|
||||||
```
|
```
|
||||||
chunk_count = (value)
|
# optional - fixed
|
||||||
chunk_start = (value)
|
|
||||||
chunk_size = (value)
|
|
||||||
|
|
||||||
chunk_number = (value)
|
chunk_number = (value)
|
||||||
chunk_header_size = (value)
|
chunk_header_size = (value)
|
||||||
chunk_data_size = (value)
|
chunk_data_size = (value)
|
||||||
|
|
||||||
|
# optional - dynamic
|
||||||
chunk_value = (value)
|
chunk_value = (value)
|
||||||
chunk_size_offset = (value)
|
chunk_size_offset = (value)
|
||||||
chunk_endian = LE|BE
|
chunk_data_size_offset = (value)
|
||||||
|
chunk_endianness = LE|BE
|
||||||
|
|
||||||
|
# main
|
||||||
|
chunk_count = (value)
|
||||||
|
chunk_start = (value)
|
||||||
|
chunk_size = (value)
|
||||||
```
|
```
|
||||||
|
|
||||||
#### NAME TABLE
|
#### NAME TABLE
|
||||||
@ -1275,7 +1280,7 @@ loop_start = @0x28:BE
|
|||||||
loop_flag = @0x2c:BE
|
loop_flag = @0x2c:BE
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Grand Theft Auto: San Andreas .vgmstream.txth
|
#### Grand Theft Auto: San Andreas (PS2) .vgmstream.txth
|
||||||
```
|
```
|
||||||
# once extracted from bigfiles there are 2 types of files with hardcoded settings,
|
# once extracted from bigfiles there are 2 types of files with hardcoded settings,
|
||||||
# so we need 2 .txth
|
# so we need 2 .txth
|
||||||
@ -1345,3 +1350,23 @@ num_samples = data_size
|
|||||||
#1: 0x1F40, 0x800, 0x00, 0x1000
|
#1: 0x1F40, 0x800, 0x00, 0x1000
|
||||||
#2: 0x1F50, 0x10000, 0x1000, 0x20000
|
#2: 0x1F50, 0x10000, 0x1000, 0x20000
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### LEGO Batman 2 (Wii) .fmv.txth
|
||||||
|
```
|
||||||
|
# "dechunks" videos with dynamic chunks realtime and plays audio only
|
||||||
|
|
||||||
|
codec = IMA
|
||||||
|
channels = 2
|
||||||
|
sample_rate = @0x20
|
||||||
|
|
||||||
|
# each chunk is 0x00: ID + 0x04 data size (not including header)
|
||||||
|
chunk_number = 1
|
||||||
|
chunk_header_size = 0x08
|
||||||
|
chunk_value = 0x00414D46 #"FMA\0" LE
|
||||||
|
chunk_data_size_offset = 0x04
|
||||||
|
|
||||||
|
chunk_count = 1
|
||||||
|
chunk_start = 0x28 #first chunk after header
|
||||||
|
|
||||||
|
num_samples = data_size
|
||||||
|
```
|
||||||
|
@ -57,7 +57,6 @@ static const char* extension_list[] = {
|
|||||||
"ahv",
|
"ahv",
|
||||||
"ai",
|
"ai",
|
||||||
//"aif", //common
|
//"aif", //common
|
||||||
"aif-Loop",
|
|
||||||
"aifc", //common?
|
"aifc", //common?
|
||||||
//"aiff", //common
|
//"aiff", //common
|
||||||
"aix",
|
"aix",
|
||||||
@ -650,6 +649,7 @@ static const char* extension_list[] = {
|
|||||||
"xen",
|
"xen",
|
||||||
"xma",
|
"xma",
|
||||||
"xma2",
|
"xma2",
|
||||||
|
"xms",
|
||||||
"xmu",
|
"xmu",
|
||||||
"xmv",
|
"xmv",
|
||||||
"xnb",
|
"xnb",
|
||||||
|
@ -158,137 +158,164 @@ fail:
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int _init_vgmstream_ogg_vorbis_tests(STREAMFILE* sf, ogg_vorbis_io_config_data* cfg, ogg_vorbis_meta_info_t* ovmi) {
|
||||||
/* Ogg Vorbis - standard .ogg with (possibly) loop comments/metadata */
|
|
||||||
static VGMSTREAM* _init_vgmstream_ogg_vorbis_common(STREAMFILE* sf) {
|
|
||||||
ogg_vorbis_io_config_data cfg = {0};
|
|
||||||
ogg_vorbis_meta_info_t ovmi = {0};
|
|
||||||
|
|
||||||
int is_ogg = 0;
|
|
||||||
int is_um3 = 0;
|
|
||||||
int is_kovs = 0;
|
|
||||||
int is_sngw = 0;
|
|
||||||
int is_isd = 0;
|
|
||||||
int is_rpgmvo = 0;
|
|
||||||
int is_eno = 0;
|
|
||||||
int is_gwm = 0;
|
|
||||||
int is_mus = 0;
|
|
||||||
int is_lse = 0;
|
|
||||||
int is_bgm = 0;
|
|
||||||
|
|
||||||
|
|
||||||
/* check extension */
|
/* standard */
|
||||||
/* .ogg: standard/various, .logg: renamed for plugins
|
if (is_id32be(0x00,sf, "OggS")) {
|
||||||
|
|
||||||
|
/* .ogg: common, .logg: renamed for plugins
|
||||||
* .adx: KID games [Remember11 (PC)]
|
* .adx: KID games [Remember11 (PC)]
|
||||||
* .rof: The Rhythm of Fighters (Mobile)
|
* .rof: The Rhythm of Fighters (Mobile)
|
||||||
* .acm: Planescape Torment Enhanced Edition (PC)
|
* .acm: Planescape Torment Enhanced Edition (PC)
|
||||||
* .sod: Zone 4 (PC)
|
* .sod: Zone 4 (PC)
|
||||||
* .msa: Metal Slug Attack (Mobile)
|
* .msa: Metal Slug Attack (Mobile)
|
||||||
* .aif/laif/aif-Loop: Psychonauts (PC) raw extractions (named)
|
|
||||||
* .bin/lbin: Devil May Cry 3: Special Edition (PC) */
|
* .bin/lbin: Devil May Cry 3: Special Edition (PC) */
|
||||||
if (check_extensions(sf,"ogg,logg,adx,rof,acm,sod,msa,aif,laif,aif-Loop,bin,lbin")) {
|
if (check_extensions(sf,"ogg,logg,adx,rof,acm,sod,msa,bin,lbin"))
|
||||||
is_ogg = 1;
|
return 1;
|
||||||
} else if (check_extensions(sf,"um3")) {
|
/* ignore others to allow stuff like .sngw */
|
||||||
is_um3 = 1;
|
}
|
||||||
} else if (check_extensions(sf,"kvs,kovs")) {
|
|
||||||
/* .kvs: Atelier Sophie (PC), kovs: header id only? */
|
/* Koei Tecmo PC games */
|
||||||
is_kovs = 1;
|
if (is_id32be(0x00,sf, "KOVS")) {
|
||||||
} else if (check_extensions(sf,"sngw")) { /* .sngw: Capcom [Devil May Cry 4 SE (PC), Biohazard 6 (PC)] */
|
ovmi->loop_start = read_s32le(0x08,sf);
|
||||||
is_sngw = 1;
|
ovmi->loop_flag = (ovmi->loop_start != 0);
|
||||||
} else if (check_extensions(sf,"isd")) { /* .isd: Inti Creates PC games */
|
ovmi->decryption_callback = kovs_ogg_decryption_callback;
|
||||||
is_isd = 1;
|
ovmi->meta_type = meta_OGG_KOVS;
|
||||||
} else if (check_extensions(sf,"rpgmvo,ogg_")) {
|
|
||||||
|
cfg->start = 0x20;
|
||||||
|
|
||||||
|
/* .kvs: Atelier Sophie (PC)
|
||||||
|
* .kovs: header id only? */
|
||||||
|
if (!check_extensions(sf,"kvs,kovs"))
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* [RPG Maker MV (PC), RPG Maker MZ (PC)] */
|
||||||
|
if (is_id64be(0x00,sf, "RPGMV\0\0\0")) {
|
||||||
|
ovmi->decryption_callback = rpgmvo_ogg_decryption_callback;
|
||||||
|
|
||||||
|
cfg->start = 0x10;
|
||||||
|
|
||||||
/* .rpgmvo: RPG Maker MV games (PC), .ogg_: RPG Maker MZ games (PC) */
|
/* .rpgmvo: RPG Maker MV games (PC), .ogg_: RPG Maker MZ games (PC) */
|
||||||
is_rpgmvo = 1;
|
if (!check_extensions(sf,"rpgmvo,ogg_"))
|
||||||
} else if (check_extensions(sf,"eno")) { /* .eno: Metronomicon (PC) */
|
|
||||||
is_eno = 1;
|
|
||||||
} else if (check_extensions(sf,"gwm")) { /* .gwm: Adagio: Cloudburst (PC) */
|
|
||||||
is_gwm = 1;
|
|
||||||
} else if (check_extensions(sf,"mus")) { /* .mus: Redux - Dark Matters (PC) */
|
|
||||||
is_mus = 1;
|
|
||||||
} else if (check_extensions(sf,"lse")) { /* .lse: Labyrinth of Refrain: Coven of Dusk (PC) */
|
|
||||||
is_lse = 1;
|
|
||||||
} else if (check_extensions(sf,"bgm")) { /* .bgm: Fortissimo (PC) */
|
|
||||||
is_bgm = 1;
|
|
||||||
} else {
|
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_ogg) {
|
/* L2SD [Lineage II Chronicle 4 (PC)] */
|
||||||
if (read_u32be(0x00,sf) == 0x2c444430) { /* Psychic Software [Darkwind: War on Wheels (PC)] */
|
if (is_id32be(0x00,sf, "L2SD")) {
|
||||||
ovmi.decryption_callback = psychic_ogg_decryption_callback;
|
cfg->is_header_swap = 1;
|
||||||
}
|
cfg->is_encrypted = 1;
|
||||||
else if (read_u32be(0x00,sf) == 0x4C325344) { /* "L2SD" instead of "OggS" [Lineage II Chronicle 4 (PC)] */
|
|
||||||
cfg.is_header_swap = 1;
|
|
||||||
cfg.is_encrypted = 1;
|
|
||||||
}
|
|
||||||
else if (read_u32be(0x00,sf) == 0x048686C5) { /* "OggS" XOR'ed + bitswapped [Ys VIII (PC)] */
|
|
||||||
cfg.key[0] = 0xF0;
|
|
||||||
cfg.key_len = 1;
|
|
||||||
cfg.is_nibble_swap = 1;
|
|
||||||
cfg.is_encrypted = 1;
|
|
||||||
|
|
||||||
}
|
if (!check_extensions(sf,"ogg,logg"))
|
||||||
else if (read_u32be(0x00,sf) == 0x00000000 && /* null instead of "OggS" [Yuppie Psycho (PC)] */
|
|
||||||
read_u32be(0x3a,sf) == 0x4F676753) { /* "OggS" in next page */
|
|
||||||
cfg.is_header_swap = 1;
|
|
||||||
cfg.is_encrypted = 1;
|
|
||||||
}
|
|
||||||
else if (read_u32be(0x00,sf) != 0x4F676753 && /* random(?) swap instead of "OggS" [Tobi Tsukihime (PC)] */
|
|
||||||
read_u32be(0x3a,sf) == 0x4F676753) { /* "OggS" in next page */
|
|
||||||
cfg.is_header_swap = 1;
|
|
||||||
cfg.is_encrypted = 1;
|
|
||||||
}
|
|
||||||
else if (read_u32be(0x00,sf) == 0x4F756F71) { /* "OggS" encrypted [Adventure Field 4 (PC)]*/
|
|
||||||
ovmi.decryption_callback = at4_ogg_decryption_callback;
|
|
||||||
}
|
|
||||||
else if (read_u32be(0x00,sf) == 0x4F676753) { /* "OggS" (standard) */
|
|
||||||
;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
goto fail; /* unknown/not Ogg Vorbis (ex. Wwise) */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_um3) { /* ["Ultramarine3" (???)] */
|
|
||||||
if (read_u32be(0x00,sf) != 0x4f676753) { /* "OggS" (optionally encrypted) */
|
|
||||||
ovmi.decryption_callback = um3_ogg_decryption_callback;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_kovs) { /* Koei Tecmo PC games */
|
|
||||||
if (read_u32be(0x00,sf) != 0x4b4f5653) { /* "KOVS" */
|
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
|
||||||
ovmi.loop_start = read_32bitLE(0x08,sf);
|
|
||||||
ovmi.loop_flag = (ovmi.loop_start != 0);
|
|
||||||
ovmi.decryption_callback = kovs_ogg_decryption_callback;
|
|
||||||
ovmi.meta_type = meta_OGG_KOVS;
|
|
||||||
|
|
||||||
cfg.start = 0x20;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_sngw) { /* [Capcom's MT Framework PC games] */
|
/* NIS's "OggS" XOR'ed + bitswapped [Ys VIII (PC), Yomawari: Midnight Shadows (PC)] */
|
||||||
if (read_u32be(0x00,sf) != 0x4f676753) { /* "OggS" (optionally encrypted) */
|
if (read_u32be(0x00,sf) == 0x048686C5) {
|
||||||
cfg.key_len = read_streamfile(cfg.key, 0x00, 0x04, sf);
|
cfg->key[0] = 0xF0;
|
||||||
cfg.is_header_swap = 1;
|
cfg->key_len = 0x01;
|
||||||
cfg.is_nibble_swap = 1;
|
cfg->is_nibble_swap = 1;
|
||||||
cfg.is_encrypted = 1;
|
cfg->is_encrypted = 1;
|
||||||
|
|
||||||
|
/* .bgm: Yomawari */
|
||||||
|
if (!check_extensions(sf,"ogg,logg,bgm"))
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ovmi.disable_reordering = 1; /* must be an MT Framework thing */
|
/* Psychic Software [Darkwind: War on Wheels (PC)] */
|
||||||
|
if (read_u32be(0x00,sf) == 0x2c444430) {
|
||||||
|
ovmi->decryption_callback = psychic_ogg_decryption_callback;
|
||||||
|
|
||||||
|
if (!check_extensions(sf,"ogg,logg"))
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_isd) { /* Inti Creates PC games */
|
/* null id + check next page [Yuppie Psycho (PC)] */
|
||||||
const char *isl_name = NULL;
|
if (read_u32be(0x00,sf) == 0x00000000 && is_id32be(0x3a,sf, "OggS")) {
|
||||||
|
cfg->is_header_swap = 1;
|
||||||
|
cfg->is_encrypted = 1;
|
||||||
|
|
||||||
|
if (!check_extensions(sf,"ogg,logg"))
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* random(?) id + check next page [Tobi Tsukihime (PC)] */
|
||||||
|
if (!is_id32be(0x00,sf, "OggS") && is_id32be(0x3a,sf, "OggS")) {
|
||||||
|
cfg->is_header_swap = 1;
|
||||||
|
cfg->is_encrypted = 1;
|
||||||
|
|
||||||
|
if (!check_extensions(sf,"ogg,logg"))
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* encrypted [Adventure Field 4 (PC)] */
|
||||||
|
if (read_u32be(0x00,sf) == 0x4F756F71) {
|
||||||
|
ovmi->decryption_callback = at4_ogg_decryption_callback; //TODO replace with generic descryption?
|
||||||
|
|
||||||
|
if (!check_extensions(sf,"ogg,logg"))
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* .gwm: Adagio: Cloudburst (PC) */
|
||||||
|
if (read_u32be(0x00,sf) == 0x123A3A0E) {
|
||||||
|
cfg->key[0] = 0x5D;
|
||||||
|
cfg->key_len = 1;
|
||||||
|
cfg->is_encrypted = 1;
|
||||||
|
|
||||||
|
if (!check_extensions(sf,"gwm"))
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* .mus: Redux - Dark Matters (PC) */
|
||||||
|
if (read_u32be(0x00,sf) == 0x6C381C21) {
|
||||||
|
static const uint8_t mus_key[16] = {
|
||||||
|
0x21,0x4D,0x6F,0x01,0x20,0x4C,0x6E,0x02,0x1F,0x4B,0x6D,0x03,0x20,0x4C,0x6E,0x02
|
||||||
|
};
|
||||||
|
cfg->key_len = sizeof(mus_key);
|
||||||
|
memcpy(cfg->key, mus_key, cfg->key_len);
|
||||||
|
cfg->is_header_swap = 1; /* decrypted header gives "Mus " */
|
||||||
|
cfg->is_encrypted = 1;
|
||||||
|
|
||||||
|
if (!check_extensions(sf,"mus"))
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************/
|
||||||
|
/* harder to check (could be improved) */
|
||||||
|
|
||||||
|
/* .isd: Inti Creates PC games */
|
||||||
|
if (check_extensions(sf,"isd")) {
|
||||||
|
const char* isl_name = NULL;
|
||||||
|
|
||||||
/* check various encrypted "OggS" values */
|
/* check various encrypted "OggS" values */
|
||||||
if (read_u32be(0x00,sf) == 0xAF678753) { /* Azure Striker Gunvolt (PC) */
|
if (read_u32be(0x00,sf) == 0xAF678753) { /* Azure Striker Gunvolt (PC) */
|
||||||
static const uint8_t isd_gv_key[16] = {
|
static const uint8_t isd_gv_key[16] = {
|
||||||
0xe0,0x00,0xe0,0x00,0xa0,0x00,0x00,0x00,0xe0,0x00,0xe0,0x80,0x40,0x40,0x40,0x00
|
0xe0,0x00,0xe0,0x00,0xa0,0x00,0x00,0x00,0xe0,0x00,0xe0,0x80,0x40,0x40,0x40,0x00
|
||||||
};
|
};
|
||||||
cfg.key_len = sizeof(isd_gv_key);
|
cfg->key_len = sizeof(isd_gv_key);
|
||||||
memcpy(cfg.key, isd_gv_key, cfg.key_len);
|
memcpy(cfg->key, isd_gv_key, cfg->key_len);
|
||||||
isl_name = "GV_steam.isl";
|
isl_name = "GV_steam.isl";
|
||||||
}
|
}
|
||||||
else if (read_u32be(0x00,sf) == 0x0FE787D3) { /* Mighty Gunvolt (PC) */
|
else if (read_u32be(0x00,sf) == 0x0FE787D3) { /* Mighty Gunvolt (PC) */
|
||||||
@ -302,8 +329,8 @@ static VGMSTREAM* _init_vgmstream_ogg_vorbis_common(STREAMFILE* sf) {
|
|||||||
0x60,0x00,0xE0,0x80,0x00,0xC0,0x00,0x00,0x60,0x80,0x40,0x80,0x20,0x80,0x20,0x00,
|
0x60,0x00,0xE0,0x80,0x00,0xC0,0x00,0x00,0x60,0x80,0x40,0x80,0x20,0x80,0x20,0x00,
|
||||||
0x80,0x40,0xE0,0x00,0x20,0x00,0x20,0x00,
|
0x80,0x40,0xE0,0x00,0x20,0x00,0x20,0x00,
|
||||||
};
|
};
|
||||||
cfg.key_len = sizeof(isd_mgv_key);
|
cfg->key_len = sizeof(isd_mgv_key);
|
||||||
memcpy(cfg.key, isd_mgv_key, cfg.key_len);
|
memcpy(cfg->key, isd_mgv_key, cfg->key_len);
|
||||||
isl_name = "MGV_steam.isl";
|
isl_name = "MGV_steam.isl";
|
||||||
}
|
}
|
||||||
else if (read_u32be(0x00,sf) == 0x0FA74753) { /* Blaster Master Zero (PC) */
|
else if (read_u32be(0x00,sf) == 0x0FA74753) { /* Blaster Master Zero (PC) */
|
||||||
@ -317,15 +344,15 @@ static VGMSTREAM* _init_vgmstream_ogg_vorbis_common(STREAMFILE* sf) {
|
|||||||
0x40,0x40,0x00,0x00,0x20,0x40,0x80,0x00,0xE0,0x80,0x20,0x80,0x40,0x80,0xE0,0x00,
|
0x40,0x40,0x00,0x00,0x20,0x40,0x80,0x00,0xE0,0x80,0x20,0x80,0x40,0x80,0xE0,0x00,
|
||||||
0xA0,0x00,0xC0,0x80,0xE0,0x00,0x20,0x00
|
0xA0,0x00,0xC0,0x80,0xE0,0x00,0x20,0x00
|
||||||
};
|
};
|
||||||
cfg.key_len = sizeof(isd_bmz_key);
|
cfg->key_len = sizeof(isd_bmz_key);
|
||||||
memcpy(cfg.key, isd_bmz_key, cfg.key_len);
|
memcpy(cfg->key, isd_bmz_key, cfg->key_len);
|
||||||
isl_name = "output.isl";
|
isl_name = "output.isl";
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg.is_encrypted = 1;
|
cfg->is_encrypted = 1;
|
||||||
|
|
||||||
/* .isd have companion files in the prev folder:
|
/* .isd have companion files in the prev folder:
|
||||||
* - .ish: constant id/names (not always)
|
* - .ish: constant id/names (not always)
|
||||||
@ -348,7 +375,7 @@ static VGMSTREAM* _init_vgmstream_ogg_vorbis_common(STREAMFILE* sf) {
|
|||||||
if (sf_isl) {
|
if (sf_isl) {
|
||||||
STREAMFILE* dec_sf = NULL;
|
STREAMFILE* dec_sf = NULL;
|
||||||
|
|
||||||
dec_sf = setup_ogg_vorbis_streamfile(sf_isl, &cfg);
|
dec_sf = setup_ogg_vorbis_streamfile(sf_isl, cfg);
|
||||||
if (dec_sf) {
|
if (dec_sf) {
|
||||||
off_t loop_offset;
|
off_t loop_offset;
|
||||||
char basename[PATH_LIMIT];
|
char basename[PATH_LIMIT];
|
||||||
@ -362,10 +389,10 @@ static VGMSTREAM* _init_vgmstream_ogg_vorbis_common(STREAMFILE* sf) {
|
|||||||
|
|
||||||
read_string(testname, sizeof(testname), loop_offset+0x2c, dec_sf);
|
read_string(testname, sizeof(testname), loop_offset+0x2c, dec_sf);
|
||||||
if (strcmp(basename, testname) == 0) {
|
if (strcmp(basename, testname) == 0) {
|
||||||
ovmi.loop_start = read_32bitLE(loop_offset+0x1c, dec_sf);
|
ovmi->loop_start = read_32bitLE(loop_offset+0x1c, dec_sf);
|
||||||
ovmi.loop_end = read_32bitLE(loop_offset+0x20, dec_sf);
|
ovmi->loop_end = read_32bitLE(loop_offset+0x20, dec_sf);
|
||||||
ovmi.loop_end_found = 1;
|
ovmi->loop_end_found = 1;
|
||||||
ovmi.loop_flag = (ovmi.loop_end != 0);
|
ovmi->loop_flag = (ovmi->loop_end != 0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -378,62 +405,61 @@ static VGMSTREAM* _init_vgmstream_ogg_vorbis_common(STREAMFILE* sf) {
|
|||||||
close_streamfile(sf_isl);
|
close_streamfile(sf_isl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_rpgmvo) { /* [RPG Maker MV (PC), RPG Maker MZ (PC)] */
|
/* Capcom's MT Framework PC games [Devil May Cry 4 SE (PC), Biohazard 6 (PC), Mega Man X Legacy Collection (PC)] */
|
||||||
if (!is_id64be(0x00,sf, "RPGMV\0\0\0"))
|
if (check_extensions(sf,"sngw")) {
|
||||||
goto fail;
|
/* optionally(?) encrypted */
|
||||||
|
if (!is_id32be(0x00,sf, "OggS") && read_u32be(0x00,sf) == read_u32be(0x10,sf)) {
|
||||||
ovmi.decryption_callback = rpgmvo_ogg_decryption_callback;
|
cfg->key_len = read_streamfile(cfg->key, 0x00, 0x04, sf);
|
||||||
|
cfg->is_header_swap = 1;
|
||||||
cfg.start = 0x10;
|
cfg->is_nibble_swap = 1;
|
||||||
|
cfg->is_encrypted = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_eno) { /* [Metronomicon (PC)] */
|
ovmi->disable_reordering = 1; /* must be an MT Framework thing */
|
||||||
/* first byte probably derives into key, but this works too */
|
|
||||||
cfg.key[0] = read_u8(0x05,sf); /* regular ogg have a zero at this offset = easy key */
|
return 1;
|
||||||
cfg.key_len = 1;
|
|
||||||
cfg.is_encrypted = 1;
|
|
||||||
cfg.start = 0x01; /* "OggS" starts after key-thing */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_gwm) { /* [Adagio: Cloudburst (PC)] */
|
/* Nippon Ichi PC games */
|
||||||
cfg.key[0] = 0x5D;
|
if (check_extensions(sf,"lse")) {
|
||||||
cfg.key_len = 1;
|
|
||||||
cfg.is_encrypted = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_mus) { /* [Redux: Dark Matters (PC)] */
|
|
||||||
static const uint8_t mus_key[16] = {
|
|
||||||
0x21,0x4D,0x6F,0x01,0x20,0x4C,0x6E,0x02,0x1F,0x4B,0x6D,0x03,0x20,0x4C,0x6E,0x02
|
|
||||||
};
|
|
||||||
cfg.key_len = sizeof(mus_key);
|
|
||||||
memcpy(cfg.key, mus_key, cfg.key_len);
|
|
||||||
cfg.is_header_swap = 1; /* decrypted header gives "Mus " */
|
|
||||||
cfg.is_encrypted = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_lse) { /* [Nippon Ichi PC games] */
|
|
||||||
if (read_u32be(0x00,sf) == 0xFFFFFFFF) { /* [Operation Abyss: New Tokyo Legacy (PC)] */
|
if (read_u32be(0x00,sf) == 0xFFFFFFFF) { /* [Operation Abyss: New Tokyo Legacy (PC)] */
|
||||||
cfg.key[0] = 0xFF;
|
cfg->key[0] = 0xFF;
|
||||||
cfg.key_len = 1;
|
cfg->key_len = 1;
|
||||||
cfg.is_header_swap = 1;
|
cfg->is_header_swap = 1;
|
||||||
cfg.is_encrypted = 1;
|
cfg->is_encrypted = 1;
|
||||||
}
|
}
|
||||||
else { /* [Operation Babel: New Tokyo Legacy (PC), Labyrinth of Refrain: Coven of Dusk (PC)] */
|
else { /* [Operation Babel: New Tokyo Legacy (PC), Labyrinth of Refrain: Coven of Dusk (PC)] */
|
||||||
int i;
|
int i;
|
||||||
/* found at file_size-1 but this works too (same key for most files but can vary) */
|
/* found at file_size-1 but this works too (same key for most files but can vary) */
|
||||||
uint8_t base_key = read_u8(0x04,sf) - 0x04;
|
uint8_t base_key = read_u8(0x04,sf) - 0x04;
|
||||||
|
|
||||||
cfg.key_len = 256;
|
cfg->key_len = 256;
|
||||||
for (i = 0; i < cfg.key_len; i++) {
|
for (i = 0; i < cfg->key_len; i++) {
|
||||||
cfg.key[i] = (uint8_t)(base_key + i);
|
cfg->key[i] = (uint8_t)(base_key + i);
|
||||||
}
|
|
||||||
cfg.is_encrypted = 1;
|
|
||||||
}
|
}
|
||||||
|
cfg->is_encrypted = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_bgm) { /* [Fortissimo (PC)] */
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* .eno: Metronomicon (PC) */
|
||||||
|
if (check_extensions(sf,"eno")) {
|
||||||
|
/* 0x00: first byte probably derives into key, but this works too */
|
||||||
|
cfg->key[0] = read_u8(0x05,sf); /* regular ogg have a zero at this offset = easy key */
|
||||||
|
cfg->key_len = 0x01;
|
||||||
|
cfg->is_encrypted = 1;
|
||||||
|
cfg->start = 0x01; /* encrypted "OggS" starts after key-thing */
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* .bgm: Fortissimo (PC) */
|
||||||
|
if (check_extensions(sf,"bgm")) {
|
||||||
uint32_t file_size = get_streamfile_size(sf);
|
uint32_t file_size = get_streamfile_size(sf);
|
||||||
uint8_t key[0x04];
|
uint8_t key[0x04];
|
||||||
uint32_t xor_be;
|
uint32_t xor_be;
|
||||||
@ -442,14 +468,38 @@ static VGMSTREAM* _init_vgmstream_ogg_vorbis_common(STREAMFILE* sf) {
|
|||||||
xor_be = get_u32be(key);
|
xor_be = get_u32be(key);
|
||||||
if ((read_u32be(0x00,sf) ^ xor_be) == get_id32be("OggS")) {
|
if ((read_u32be(0x00,sf) ^ xor_be) == get_id32be("OggS")) {
|
||||||
int i;
|
int i;
|
||||||
cfg.key_len = 4;
|
cfg->key_len = 4;
|
||||||
for (i = 0; i < cfg.key_len; i++) {
|
for (i = 0; i < cfg->key_len; i++) {
|
||||||
cfg.key[i] = key[i];
|
cfg->key[i] = key[i];
|
||||||
}
|
}
|
||||||
cfg.is_encrypted = 1;
|
cfg->is_encrypted = 1;
|
||||||
|
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* "Ultramarine3" (???) */
|
||||||
|
if (check_extensions(sf,"um3")) {
|
||||||
|
if (!is_id32be(0x00,sf, "OggS")) {
|
||||||
|
ovmi->decryption_callback = um3_ogg_decryption_callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fail:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ogg Vorbis - standard .ogg with (possibly) loop comments/metadata */
|
||||||
|
static VGMSTREAM* _init_vgmstream_ogg_vorbis_common(STREAMFILE* sf) {
|
||||||
|
ogg_vorbis_io_config_data cfg = {0};
|
||||||
|
ogg_vorbis_meta_info_t ovmi = {0};
|
||||||
|
|
||||||
|
/* checks */
|
||||||
|
if (!_init_vgmstream_ogg_vorbis_tests(sf, &cfg, &ovmi))
|
||||||
|
goto fail;
|
||||||
|
|
||||||
return _init_vgmstream_ogg_vorbis_cfg_ovmi(sf, &cfg, &ovmi);
|
return _init_vgmstream_ogg_vorbis_cfg_ovmi(sf, &cfg, &ovmi);
|
||||||
fail:
|
fail:
|
||||||
|
@ -26,30 +26,30 @@ static size_t ogg_vorbis_io_read(STREAMFILE *sf, uint8_t *dest, off_t offset, si
|
|||||||
|
|
||||||
if (data->cfg.is_encrypted) {
|
if (data->cfg.is_encrypted) {
|
||||||
int max = bytes;
|
int max = bytes;
|
||||||
int header_end = 0x04 + data->cfg.start;
|
int header_end = 0x04;
|
||||||
|
|
||||||
if (data->cfg.max_offset + data->cfg.start) {
|
//TODO handle data->cfg.start;
|
||||||
if (offset > data->cfg.max_offset + data->cfg.start) {
|
|
||||||
|
if (data->cfg.max_offset) {
|
||||||
|
if (offset > data->cfg.max_offset) {
|
||||||
max = 0;
|
max = 0;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
max = data->cfg.max_offset + data->cfg.start - offset;
|
max = data->cfg.max_offset - offset;
|
||||||
if (max > bytes)
|
if (max > bytes)
|
||||||
max = bytes;
|
max = bytes;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < max; i++) {
|
for (i = 0; i < max; i++) {
|
||||||
if (data->cfg.is_header_swap &&
|
if (data->cfg.is_header_swap && (offset + i) < header_end) {
|
||||||
(offset + i) >= data->cfg.start &&
|
|
||||||
(offset + i) < header_end) {
|
|
||||||
dest[i] = header_swap[(offset + i) % 0x04];
|
dest[i] = header_swap[(offset + i) % 0x04];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (!data->cfg.key_len && !data->cfg.is_nibble_swap)
|
if (!data->cfg.key_len && !data->cfg.is_nibble_swap)
|
||||||
break;
|
break;
|
||||||
if (data->cfg.key_len && (offset + i) >= data->cfg.start)
|
if (data->cfg.key_len)
|
||||||
dest[i] ^= data->cfg.key[(offset + i - data->cfg.start) % data->cfg.key_len];
|
dest[i] ^= data->cfg.key[(offset + i) % data->cfg.key_len];
|
||||||
if (data->cfg.is_nibble_swap)
|
if (data->cfg.is_nibble_swap)
|
||||||
dest[i] = ((dest[i] << 4) & 0xf0) | ((dest[i] >> 4) & 0x0f);
|
dest[i] = ((dest[i] << 4) & 0xf0) | ((dest[i] >> 4) & 0x0f);
|
||||||
}
|
}
|
||||||
|
@ -378,8 +378,9 @@ VGMSTREAM* init_vgmstream_riff(STREAMFILE* sf) {
|
|||||||
* .xvag: Uncharted Golden Abyss (Vita)[ATRAC9]
|
* .xvag: Uncharted Golden Abyss (Vita)[ATRAC9]
|
||||||
* .ogg/logg: Luftrausers (Vita)[ATRAC9]
|
* .ogg/logg: Luftrausers (Vita)[ATRAC9]
|
||||||
* .p1d: Farming Simulator 15 (Vita)[ATRAC9]
|
* .p1d: Farming Simulator 15 (Vita)[ATRAC9]
|
||||||
|
* .xms: Ty the Tasmanian Tiger (Xbox)
|
||||||
*/
|
*/
|
||||||
if ( check_extensions(sf, "wav,lwav,xwav,da,dax,cd,med,snd,adx,adp,xss,xsew,adpcm,adw,wd,,sbv,wvx,str,at3,rws,aud,at9,ckd,saf,ima,nsa,pcm,xvag,ogg,logg,p1d") ) {
|
if ( check_extensions(sf, "wav,lwav,xwav,da,dax,cd,med,snd,adx,adp,xss,xsew,adpcm,adw,wd,,sbv,wvx,str,at3,rws,aud,at9,ckd,saf,ima,nsa,pcm,xvag,ogg,logg,p1d,xms") ) {
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
else if ( check_extensions(sf, "mwv") ) {
|
else if ( check_extensions(sf, "mwv") ) {
|
||||||
|
@ -121,7 +121,7 @@ typedef struct {
|
|||||||
int subfile_set;
|
int subfile_set;
|
||||||
uint32_t subfile_offset;
|
uint32_t subfile_offset;
|
||||||
uint32_t subfile_size;
|
uint32_t subfile_size;
|
||||||
char subfile_extension[32];
|
char subfile_extension[16];
|
||||||
|
|
||||||
uint32_t chunk_number;
|
uint32_t chunk_number;
|
||||||
uint32_t chunk_start;
|
uint32_t chunk_start;
|
||||||
@ -130,8 +130,9 @@ typedef struct {
|
|||||||
uint32_t chunk_header_size;
|
uint32_t chunk_header_size;
|
||||||
uint32_t chunk_data_size;
|
uint32_t chunk_data_size;
|
||||||
uint32_t chunk_value;
|
uint32_t chunk_value;
|
||||||
uint32_t chunk_size_offset;
|
uint32_t chunk_bsize_offset;
|
||||||
uint32_t chunk_be;
|
uint32_t chunk_dsize_offset;
|
||||||
|
uint32_t chunk_big_endian;
|
||||||
int chunk_start_set;
|
int chunk_start_set;
|
||||||
int chunk_size_set;
|
int chunk_size_set;
|
||||||
int chunk_count_set;
|
int chunk_count_set;
|
||||||
@ -820,9 +821,9 @@ static void set_body_chunk(txth_header* txth) {
|
|||||||
//todo maybe should only be done once, or have some count to retrigger to simplify?
|
//todo maybe should only be done once, or have some count to retrigger to simplify?
|
||||||
if (!txth->chunk_start_set || !txth->chunk_size_set || !txth->chunk_count_set)
|
if (!txth->chunk_start_set || !txth->chunk_size_set || !txth->chunk_count_set)
|
||||||
return;
|
return;
|
||||||
if ((txth->chunk_size == 0 && ! txth->chunk_size_offset) ||
|
if (txth->chunk_size == 0 && !(txth->chunk_bsize_offset || txth->chunk_dsize_offset))
|
||||||
txth->chunk_start > txth->data_size ||
|
return;
|
||||||
txth->chunk_count == 0)
|
if (txth->chunk_start > txth->data_size || txth->chunk_count == 0)
|
||||||
return;
|
return;
|
||||||
if (!txth->sf_body)
|
if (!txth->sf_body)
|
||||||
return;
|
return;
|
||||||
@ -843,8 +844,9 @@ static void set_body_chunk(txth_header* txth) {
|
|||||||
cfg.chunk_data_size = txth->chunk_data_size;
|
cfg.chunk_data_size = txth->chunk_data_size;
|
||||||
|
|
||||||
cfg.chunk_value = txth->chunk_value;
|
cfg.chunk_value = txth->chunk_value;
|
||||||
cfg.chunk_size_offset = txth->chunk_size_offset;
|
cfg.chunk_bsize_offset = txth->chunk_bsize_offset;
|
||||||
cfg.chunk_be = txth->chunk_be;
|
cfg.chunk_dsize_offset = txth->chunk_dsize_offset;
|
||||||
|
cfg.chunk_be = txth->chunk_big_endian;
|
||||||
|
|
||||||
cfg.chunk_start = txth->chunk_start;
|
cfg.chunk_start = txth->chunk_start;
|
||||||
cfg.chunk_size = txth->chunk_size;
|
cfg.chunk_size = txth->chunk_size;
|
||||||
@ -877,7 +879,7 @@ static void set_body_chunk(txth_header* txth) {
|
|||||||
|
|
||||||
static int parse_keyval(STREAMFILE* sf, txth_header* txth, const char* key, char* val);
|
static int parse_keyval(STREAMFILE* sf, txth_header* txth, const char* key, char* val);
|
||||||
static int parse_num(STREAMFILE* sf, txth_header* txth, const char* val, uint32_t* out_value);
|
static int parse_num(STREAMFILE* sf, txth_header* txth, const char* val, uint32_t* out_value);
|
||||||
static int parse_string(STREAMFILE* sf, txth_header* txth, const char* val, char* str);
|
static int parse_string(STREAMFILE* sf, txth_header* txth, const char* val, char* str, int str_len);
|
||||||
static int parse_coef_table(STREAMFILE* sf, txth_header* txth, const char* val, uint8_t* out_value, size_t out_size);
|
static int parse_coef_table(STREAMFILE* sf, txth_header* txth, const char* val, uint8_t* out_value, size_t out_size);
|
||||||
static int parse_name_table(txth_header* txth, char* val);
|
static int parse_name_table(txth_header* txth, char* val);
|
||||||
static int parse_multi_txth(txth_header* txth, char* val);
|
static int parse_multi_txth(txth_header* txth, char* val);
|
||||||
@ -1328,7 +1330,7 @@ static int parse_keyval(STREAMFILE* sf_, txth_header* txth, const char* key, cha
|
|||||||
txth->subfile_set = 1;
|
txth->subfile_set = 1;
|
||||||
}
|
}
|
||||||
else if (is_string(key,"subfile_extension")) {
|
else if (is_string(key,"subfile_extension")) {
|
||||||
if (!parse_string(txth->sf_head,txth,val, txth->subfile_extension)) goto fail;
|
if (!parse_string(txth->sf_head,txth,val, txth->subfile_extension, sizeof(txth->subfile_extension))) goto fail;
|
||||||
txth->subfile_set = 1;
|
txth->subfile_set = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1442,10 +1444,15 @@ static int parse_keyval(STREAMFILE* sf_, txth_header* txth, const char* key, cha
|
|||||||
if (!parse_num(txth->sf_head,txth,val, &txth->chunk_value)) goto fail;
|
if (!parse_num(txth->sf_head,txth,val, &txth->chunk_value)) goto fail;
|
||||||
}
|
}
|
||||||
else if (is_string(key,"chunk_size_offset")) {
|
else if (is_string(key,"chunk_size_offset")) {
|
||||||
if (!parse_num(txth->sf_head,txth,val, &txth->chunk_size_offset)) goto fail;
|
if (!parse_num(txth->sf_head,txth,val, &txth->chunk_bsize_offset)) goto fail;
|
||||||
|
txth->chunk_size_set = 1;
|
||||||
|
}
|
||||||
|
else if (is_string(key,"chunk_data_size_offset")) {
|
||||||
|
if (!parse_num(txth->sf_head,txth,val, &txth->chunk_dsize_offset)) goto fail;
|
||||||
|
txth->chunk_size_set = 1;
|
||||||
}
|
}
|
||||||
else if (is_string(key,"chunk_endianness")) {
|
else if (is_string(key,"chunk_endianness")) {
|
||||||
if (!parse_endianness(txth, val, &txth->chunk_be, NULL)) goto fail;
|
if (!parse_endianness(txth, val, &txth->chunk_big_endian, NULL)) goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1615,9 +1622,13 @@ static int is_string_match(const char* text, const char* pattern) {
|
|||||||
/* either all chars consumed/matched and both pos point to null, or one didn't so string didn't match */
|
/* either all chars consumed/matched and both pos point to null, or one didn't so string didn't match */
|
||||||
return text[t_pos] == '\0' && pattern[p_pos] == '\0';
|
return text[t_pos] == '\0' && pattern[p_pos] == '\0';
|
||||||
}
|
}
|
||||||
static int parse_string(STREAMFILE* sf, txth_header* txth, const char* val, char* str) {
|
|
||||||
|
static int parse_string(STREAMFILE* sf, txth_header* txth, const char* val, char* str, int str_len) {
|
||||||
int n = 0;
|
int n = 0;
|
||||||
|
|
||||||
|
if (strlen(val) >= str_len)
|
||||||
|
return 0;
|
||||||
|
|
||||||
/* read string without trailing spaces */
|
/* read string without trailing spaces */
|
||||||
if (sscanf(val, " %s%n[^ ]%n", str, &n, &n) != 1)
|
if (sscanf(val, " %s%n[^ ]%n", str, &n, &n) != 1)
|
||||||
return 0;
|
return 0;
|
||||||
@ -2001,7 +2012,8 @@ static int parse_num(STREAMFILE* sf, txth_header* txth, const char* val, uint32_
|
|||||||
else if ((n = is_string_field(val,"chunk_count"))) value = txth->chunk_count;
|
else if ((n = is_string_field(val,"chunk_count"))) value = txth->chunk_count;
|
||||||
else if ((n = is_string_field(val,"chunk_start"))) value = txth->chunk_start;
|
else if ((n = is_string_field(val,"chunk_start"))) value = txth->chunk_start;
|
||||||
else if ((n = is_string_field(val,"chunk_size"))) value = txth->chunk_size;
|
else if ((n = is_string_field(val,"chunk_size"))) value = txth->chunk_size;
|
||||||
else if ((n = is_string_field(val,"chunk_size_offset"))) value = txth->chunk_size_offset;
|
else if ((n = is_string_field(val,"chunk_size_offset"))) value = txth->chunk_bsize_offset;
|
||||||
|
else if ((n = is_string_field(val,"chunk_data_size_offset")))value = txth->chunk_dsize_offset;
|
||||||
else if ((n = is_string_field(val,"chunk_number"))) value = txth->chunk_number;
|
else if ((n = is_string_field(val,"chunk_number"))) value = txth->chunk_number;
|
||||||
else if ((n = is_string_field(val,"chunk_data_size"))) value = txth->chunk_data_size;
|
else if ((n = is_string_field(val,"chunk_data_size"))) value = txth->chunk_data_size;
|
||||||
else if ((n = is_string_field(val,"chunk_header_size"))) value = txth->chunk_header_size;
|
else if ((n = is_string_field(val,"chunk_header_size"))) value = txth->chunk_header_size;
|
||||||
|
@ -14,7 +14,8 @@ typedef struct {
|
|||||||
int chunk_number;
|
int chunk_number;
|
||||||
|
|
||||||
uint32_t chunk_value;
|
uint32_t chunk_value;
|
||||||
uint32_t chunk_size_offset;
|
uint32_t chunk_bsize_offset;
|
||||||
|
uint32_t chunk_dsize_offset;
|
||||||
int chunk_be;
|
int chunk_be;
|
||||||
} txth_io_config_data;
|
} txth_io_config_data;
|
||||||
|
|
||||||
@ -71,11 +72,26 @@ static size_t txth_io_read(STREAMFILE* sf, uint8_t* dest, off_t offset, size_t l
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* chunk size reader (overwrites the above) */
|
/* chunk size reader (overwrites the above) */
|
||||||
if (data->cfg.chunk_header_size && data->cfg.chunk_size_offset) {
|
if (data->cfg.chunk_header_size && (data->cfg.chunk_bsize_offset || data->cfg.chunk_dsize_offset)) {
|
||||||
read_u32_t read_u32 = data->cfg.chunk_be ? read_u32be : read_u32le;
|
read_u32_t read_u32 = data->cfg.chunk_be ? read_u32be : read_u32le;
|
||||||
|
|
||||||
data->block_size = read_u32(data->physical_offset + data->cfg.chunk_size_offset, sf);
|
data->block_size = 0;
|
||||||
data->data_size = data->block_size - data->cfg.chunk_header_size;
|
data->data_size = 0;
|
||||||
|
|
||||||
|
if (data->cfg.chunk_bsize_offset)
|
||||||
|
data->block_size = read_u32(data->physical_offset + data->cfg.chunk_bsize_offset, sf);
|
||||||
|
if (data->cfg.chunk_dsize_offset)
|
||||||
|
data->data_size = read_u32(data->physical_offset + data->cfg.chunk_dsize_offset, sf);
|
||||||
|
|
||||||
|
if (!data->block_size && !data->data_size) { /* bad read? */
|
||||||
|
data->block_size = data->cfg.chunk_header_size;
|
||||||
|
data->data_size = data->cfg.chunk_header_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data->block_size)
|
||||||
|
data->block_size = data->data_size + data->cfg.chunk_header_size;
|
||||||
|
if (!data->data_size)
|
||||||
|
data->data_size = data->block_size - data->cfg.chunk_header_size; /* may be 0 */
|
||||||
|
|
||||||
/* skip chunk if doesn't match expected header value */
|
/* skip chunk if doesn't match expected header value */
|
||||||
if (data->cfg.chunk_value) {
|
if (data->cfg.chunk_value) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user