Merge pull request #942 from bnnm/fixes

- Fix some .p3d [Spider-Man 4 beta (X360)]
- Fix some .ktsl2asbin/atsl [Nioh (PC)]
- build: fix some emscripten/wasm crashes
This commit is contained in:
bnnm 2021-09-12 21:34:47 +02:00 committed by GitHub
commit 95aada74c6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 396 additions and 326 deletions

View File

@ -103,10 +103,11 @@ endif()
if(BUILD_STATIC)
set(BUILD_SHARED_LIBS OFF)
set(CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++ -static")
find_package(PkgConfig REQUIRED)
pkg_check_modules(PC_OPUS REQUIRED opus>=1.1)
# it is already kind of obvious when you do not have it, and just prevents you from building without ffmpeg
#find_package(PkgConfig REQUIRED)
#pkg_check_modules(PC_OPUS REQUIRED opus>=1.1)
# Incompatible with static builds
set(BUILD_AUDACIOUS OFF)
endif()

View File

@ -58,7 +58,7 @@ You may compile them from source as well (see *build guide*).
### Linux
Generally you need to build vgmstream's components manually (see *build guide*). For
a quick build call `/make-build-cmake.sh` (for Debian/Ubuntu-style distros, installs
a quick build call `./make-build-cmake.sh` (for Debian/Ubuntu-style distros, installs
various deps first so you may prefer to call commands manually).
Releases also distribute a static version of the CLI tool (kernel v3.2+).

View File

@ -362,6 +362,31 @@ For integration and "API" usage, easiest would be checking how `vgmstream_cli.c`
A cleaner API/.h and build methods is planned for the future (low priority though).
# emscripten / wasm
It's possible to build vgmstream components with emscripten (in-browser support).
Follow emscripten's installation instructions:
- https://emscripten.org/docs/getting_started/downloads.html
- https://emscripten.org/docs/compiling/Building-Projects.html#building-projects
Then should be buildable on Linux (Windows should be possible too but has some issues at the moment), for example:
```
git clone https://github.com/vgmstream/vgmstream
cd vgmstream
mkdir -p build && cd build
# quickest example, some can be enabled
emcmake cmake -DBUILD_STATIC=ON -DUSE_JANSSON=OFF -DUSE_FFMPEG=OFF -DUSE_VORBIS=OFF -DUSE_MPEG=OFF -DUSE_G7221=OFF -DUSE_G719=OFF -DUSE_ATRAC9=OFF -DUSE_SPEEX=OFF -DUSE_MPEG=OFF -S .. -B .
emmake make
```
Or with the base makefiles (may need to rename output to .js ATM):
```
git clone https://github.com/vgmstream/vgmstream
cd vgmstream
make vgmstream-cli CC=emcc AR=emar strip=echo
```
## External libraries
Support for some codecs is done with external libs, instead of copying their code in vgmstream. There are various reasons for this:
- each lib may have complex or conflicting ways to compile that aren't simple to replicate

View File

@ -7,15 +7,15 @@
VGMSTREAM* init_vgmstream_acb(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
STREAMFILE* temp_sf = NULL;
off_t subfile_offset;
size_t subfile_size;
utf_context *utf = NULL;
uint32_t subfile_offset;
uint32_t subfile_size;
utf_context* utf = NULL;
/* checks */
if (!check_extensions(sf, "acb"))
if (!is_id32be(0x00,sf, "@UTF"))
goto fail;
if (read_u32be(0x00,sf) != 0x40555446) /* "@UTF" */
if (!check_extensions(sf, "acb"))
goto fail;
/* .acb is a cue sheet that uses @UTF (CRI's generic table format) to store row/columns
@ -41,8 +41,10 @@ VGMSTREAM* init_vgmstream_acb(STREAMFILE* sf) {
subfile_size = size;
/* column exists but can be empty */
if (subfile_size == 0)
if (subfile_size == 0) {
vgm_logi("ACB: bank has no subsongs (ignore)\n");
goto fail;
}
}
//;VGM_LOG("ACB: subfile offset=%lx + %x\n", subfile_offset, subfile_size);
@ -50,7 +52,7 @@ VGMSTREAM* init_vgmstream_acb(STREAMFILE* sf) {
temp_sf = setup_subfile_streamfile(sf, subfile_offset,subfile_size, "awb");
if (!temp_sf) goto fail;
if (read_u32be(0x00, temp_sf) == 0x43504B20) { /* "CPK " */
if (is_id32be(0x00, temp_sf, "CPK ")) {
vgmstream = init_vgmstream_cpk_memory(temp_sf, sf); /* older */
if (!vgmstream) goto fail;
}
@ -107,17 +109,17 @@ typedef struct {
STREAMFILE* acbFile; /* original reference, don't close */
/* keep track of these tables so they can be closed when done */
utf_context *Header;
utf_context* Header;
utf_context *CueNameTable;
utf_context *CueTable;
utf_context *BlockSequenceTable;
utf_context *BlockTable;
utf_context *SequenceTable;
utf_context *TrackTable;
utf_context *TrackCommandTable;
utf_context *SynthTable;
utf_context *WaveformTable;
utf_context* CueNameTable;
utf_context* CueTable;
utf_context* BlockSequenceTable;
utf_context* BlockTable;
utf_context* SequenceTable;
utf_context* TrackTable;
utf_context* TrackCommandTable;
utf_context* SynthTable;
utf_context* WaveformTable;
STREAMFILE* CueNameSf;
STREAMFILE* CueSf;
@ -142,7 +144,7 @@ typedef struct {
/* name stuff */
int16_t cuename_index;
const char * cuename_name;
const char* cuename_name;
int awbname_count;
int16_t awbname_list[ACB_MAX_NAMELIST];
char name[ACB_MAX_NAME];

View File

@ -1,157 +1,147 @@
#include "meta.h"
#include "../coding/coding.h"
typedef enum { ATRAC3, ATRAC9, KOVS, KTSS, KTAC } atsl_codec;
/* .ATSL - Koei Tecmo audio container [One Piece Pirate Warriors (PS3), Warriors All-Stars (PC)] */
VGMSTREAM * init_vgmstream_atsl(STREAMFILE *streamFile) {
VGMSTREAM *vgmstream = NULL;
STREAMFILE *temp_streamFile = NULL;
int total_subsongs, target_subsong = streamFile->stream_index;
int type, big_endian = 0, entries;
atsl_codec codec;
const char* fake_ext;
off_t subfile_offset = 0;
size_t subfile_size = 0, header_size, entry_size;
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
/* checks */
/* .atsl: header id (for G1L extractions), .atsl3: PS3 games, .atsl4: PS4 games */
if ( !check_extensions(streamFile,"atsl,atsl3,atsl4"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x4154534C) /* "ATSL" */
goto fail;
/* main header (LE) */
header_size = read_32bitLE(0x04,streamFile);
/* 0x08/0c: flags?, 0x10: fixed? (0x03E8) */
entries = read_32bitLE(0x14,streamFile);
/* 0x18: 0x28, or 0x30 (rarer) */
/* 0x1c: null, 0x20: subheader size, 0x24/28: null */
/* Type byte may be wrong (could need header id tests instead). Example flags at 0x08/0x0c:
* - 00010101 00020001 .atsl3 from One Piece Pirate Warriors (PS3)[ATRAC3]
* - 00000201 00020001 .atsl3 from Fist of North Star: Ken's Rage 2 (PS3)[ATRAC3]
* 00000301 00020101 (same)
* - 01040301 00060301 .atsl4 from Nobunaga's Ambition: Sphere of Influence (PS4)[ATRAC9]
* - 00060301 00040301 atsl in G1L from One Piece Pirate Warriors 3 (Vita)[ATRAC9]
* - 00060301 00010301 atsl in G1L from One Piece Pirate Warriors 3 (PC)[KOVS]
* - 000A0301 00010501 atsl in G1L from Warriors All-Stars (PC)[KOVS]
* - 000B0301 00080601 atsl in G1l from Sengoku Musou Sanada Maru (Switch)[KTSS]
* - 010C0301 01060601 .atsl from Dynasty Warriors 9 (PS4)[KTAC]
*/
entry_size = 0x28;
type = read_16bitLE(0x0c, streamFile);
switch(type) {
case 0x0100:
codec = KOVS;
fake_ext = "kvs";
break;
case 0x0200:
codec = ATRAC3;
fake_ext = "at3";
big_endian = 1;
break;
case 0x0400:
case 0x0600:
codec = ATRAC9;
fake_ext = "at9";
break;
case 0x0601:
codec = KTAC;
fake_ext = "ktac";
entry_size = 0x3c;
break;
case 0x0800:
codec = KTSS;
fake_ext = "ktss";
break;
default:
VGM_LOG("ATSL: unknown type %x\n", type);
goto fail;
}
read_32bit = big_endian ? read_32bitBE : read_32bitLE;
/* entries can point to the same file, count unique only */
{
int i,j;
total_subsongs = 0;
if (target_subsong == 0) target_subsong = 1;
/* parse entry header (in machine endianness) */
for (i = 0; i < entries; i++) {
int is_unique = 1;
/* 0x00: id */
off_t entry_subfile_offset = read_32bit(header_size + i*entry_size + 0x04,streamFile);
size_t entry_subfile_size = read_32bit(header_size + i*entry_size + 0x08,streamFile);
/* 0x08+: channels/sample rate/num_samples/loop_start/etc (match subfile header) */
/* check if current entry was repeated in a prev entry */
for (j = 0; j < i; j++) {
off_t prev_offset = read_32bit(header_size + j*entry_size + 0x04,streamFile);
if (prev_offset == entry_subfile_offset) {
is_unique = 0;
break;
}
}
if (!is_unique)
continue;
total_subsongs++;
/* target GET, but keep going to count subsongs */
if (!subfile_offset && target_subsong == total_subsongs) {
subfile_offset = entry_subfile_offset;
subfile_size = entry_subfile_size;
}
}
}
if (target_subsong > total_subsongs || total_subsongs <= 0) goto fail;
if (!subfile_offset || !subfile_size) goto fail;
/* some kind of seek/switch table may follow (optional, found in .atsl3) */
temp_streamFile = setup_subfile_streamfile(streamFile, subfile_offset,subfile_size, fake_ext);
if (!temp_streamFile) goto fail;
/* init the VGMSTREAM */
switch(codec) {
case ATRAC3:
case ATRAC9:
vgmstream = init_vgmstream_riff(temp_streamFile);
if (!vgmstream) goto fail;
break;
#ifdef VGM_USE_VORBIS
case KOVS:
vgmstream = init_vgmstream_ogg_vorbis(temp_streamFile);
if (!vgmstream) goto fail;
break;
#endif
case KTSS:
vgmstream = init_vgmstream_ktss(temp_streamFile);
if (!vgmstream) goto fail;
break;
case KTAC:
//vgmstream = init_vgmstream_ktac(temp_streamFile); //Koei Tecto VBR-like ATRAC9
//if (!vgmstream) goto fail;
//break;
default:
goto fail;
}
vgmstream->num_streams = total_subsongs;
close_streamfile(temp_streamFile);
return vgmstream;
fail:
close_streamfile(temp_streamFile);
close_vgmstream(vgmstream);
return NULL;
}
#include "meta.h"
#include "../coding/coding.h"
/* .ATSL - Koei Tecmo audio container [One Piece Pirate Warriors (PS3), Warriors All-Stars (PC)] */
VGMSTREAM* init_vgmstream_atsl(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
STREAMFILE* temp_sf = NULL;
int total_subsongs, target_subsong = sf->stream_index;
int type, big_endian = 0, entries;
uint32_t subfile_offset = 0, subfile_size = 0, header_size, entry_size;
VGMSTREAM* (*init_vgmstream)(STREAMFILE* sf) = NULL;
const char* fake_ext;
/* checks */
if (!is_id32be(0x00,sf, "ATSL"))
goto fail;
/* .atsl: header id (for G1L extractions), .atsl3: PS3 games, .atsl4: PS4 games */
if (!check_extensions(sf,"atsl,atsl3,atsl4"))
goto fail;
/* main header (LE) */
header_size = read_u32le(0x04,sf);
/* 0x08/0c: flags? */
/* 0x10: volume? (always 1000) */
entries = read_u32le(0x14,sf);
/* 0x18: 0x28, or 0x30 (rarer) */
/* 0x1c: null */
/* 0x20: subheader size */
/* 0x24/28: null */
/* Type byte may be wrong (could need header id tests instead). Example flags at 0x08/0x0c:
* - 00010101 00020001 .atsl3 from One Piece Pirate Warriors (PS3)[ATRAC3]
* - 00000201 00020001 .atsl3 from Fist of North Star: Ken's Rage 2 (PS3)[ATRAC3]
* 00000301 00020101 (same)
* - 01040301 00060301 .atsl4 from Nobunaga's Ambition: Sphere of Influence (PS4)[ATRAC9]
* - 00060301 00040301 atsl in G1L from One Piece Pirate Warriors 3 (Vita)[ATRAC9]
* - 00060301 00010301 atsl in G1L from One Piece Pirate Warriors 3 (PC)[KOVS]
* - 000A0301 00010501 atsl in G1L from Warriors All-Stars (PC)[KOVS]
* - 000B0301 00080601 atsl in G1l from Sengoku Musou Sanada Maru (Switch)[KTSS]
* - 010C0301 01060601 .atsl from Dynasty Warriors 9 (PS4)[KTAC]
* - 01000000 01010501 .atsl from Nioh (PC)[KOVS]
* - 01000000 00010501 .atsl from Nioh (PC)[KOVS]
*/
type = read_u16le(0x0c, sf);
switch(type) {
#ifdef VGM_USE_VORBIS
case 0x0100: /* KOVS */
init_vgmstream = init_vgmstream_ogg_vorbis;
fake_ext = "kvs";
entry_size = 0x28;
break;
case 0x0101:
init_vgmstream = init_vgmstream_ogg_vorbis;
fake_ext = "kvs";
entry_size = 0x3c;
break;
#endif
case 0x0200: /* ATRAC3 */
init_vgmstream = init_vgmstream_riff;
fake_ext = "at3";
entry_size = 0x28;
big_endian = 1;
break;
case 0x0400:
case 0x0600: /* ATRAC9 */
init_vgmstream = init_vgmstream_riff;
fake_ext = "at9";
entry_size = 0x28;
break;
case 0x0601: /* KTAC */
init_vgmstream = init_vgmstream_ktac;
fake_ext = "ktac";
entry_size = 0x3c;
break;
case 0x0800: /* KTSS */
init_vgmstream = init_vgmstream_ktss;
fake_ext = "ktss";
entry_size = 0x28;
break;
default:
vgm_logi("ATSL: unknown type %x (report)\n", type);
goto fail;
}
/* entries can point to the same file, count unique only */
{
int i, j;
uint32_t (*read_u32)(off_t,STREAMFILE*) = big_endian ? read_u32be : read_u32le;
total_subsongs = 0;
if (target_subsong == 0) target_subsong = 1;
/* parse entry header (in machine endianness) */
for (i = 0; i < entries; i++) {
int is_unique = 1;
/* 0x00: id */
uint32_t entry_subfile_offset = read_u32(header_size + i*entry_size + 0x04,sf);
uint32_t entry_subfile_size = read_u32(header_size + i*entry_size + 0x08,sf);
/* 0x08+: channels/sample rate/num_samples/loop_start/etc (match subfile header) */
/* check if current entry was repeated in a prev entry */
for (j = 0; j < i; j++) {
off_t prev_offset = read_u32(header_size + j*entry_size + 0x04,sf);
if (prev_offset == entry_subfile_offset) {
is_unique = 0;
break;
}
}
if (!is_unique)
continue;
total_subsongs++;
/* target GET, but keep going to count subsongs */
if (!subfile_offset && target_subsong == total_subsongs) {
subfile_offset = entry_subfile_offset;
subfile_size = entry_subfile_size;
}
}
}
if (target_subsong > total_subsongs || total_subsongs <= 0) goto fail;
if (!subfile_offset || !subfile_size) goto fail;
/* some kind of seek/switch table may follow (optional, found in .atsl3) */
temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, fake_ext);
if (!temp_sf) goto fail;
/* init the VGMSTREAM */
vgmstream = init_vgmstream(temp_sf);
if (!vgmstream) goto fail;
vgmstream->num_streams = total_subsongs;
close_streamfile(temp_sf);
return vgmstream;
fail:
close_streamfile(temp_sf);
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -38,26 +38,25 @@ static layered_layout_data* build_layered_atrac9(ktsr_header* ktsr, STREAMFILE *
/* KTSR - Koei Tecmo sound resource countainer */
VGMSTREAM* init_vgmstream_ktsr(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
STREAMFILE *sf_b = NULL;
STREAMFILE* sf_b = NULL;
ktsr_header ktsr = {0};
int target_subsong = sf->stream_index;
int separate_offsets = 0;
/* checks */
if (!is_id32be(0x00, sf, "KTSR"))
goto fail;
if (read_u32be(0x04, sf) != 0x777B481A) /* hash(?) id: 0x777B481A=as, 0x0294DDFC=st, 0xC638E69E=gc */
goto fail;
/* .ktsl2asbin: common [Atelier Ryza (PC/Switch), Nioh (PC)] */
if (!check_extensions(sf, "ktsl2asbin"))
goto fail;
/* KTSR can be a memory file (ktsl2asbin), streams (ktsl2stbin) and global config (ktsl2gcbin)
* This accepts ktsl2asbin with internal data, or opening external streams as subsongs.
* This accepts .ktsl2asbin with internal data or external streams as subsongs.
* Some info from KTSR.bt */
if (!is_id32be(0x00, sf, "KTSR"))
goto fail;
if (read_u32be(0x04, sf) != 0x777B481A) /* hash(?) id: 0x777B481A=as, 0x0294DDFC=st, 0xC638E69E=gc */
goto fail;
if (target_subsong == 0) target_subsong = 1;
ktsr.target_subsong = target_subsong;
@ -68,7 +67,7 @@ VGMSTREAM* init_vgmstream_ktsr(STREAMFILE* sf) {
if (ktsr.is_external) {
sf_b = open_streamfile_by_ext(sf, "ktsl2stbin");
if (!sf_b) {
VGM_LOG("KTSR: companion file not found\n");
vgm_logi("KTSR: companion file '*.ktsl2stbin' not found\n");
goto fail;
}
}
@ -129,28 +128,23 @@ VGMSTREAM* init_vgmstream_ktsr(STREAMFILE* sf) {
#ifdef VGM_USE_VORBIS
case KVS: {
VGMSTREAM *ogg_vgmstream = NULL; //TODO: meh
STREAMFILE *sf_kvs = setup_subfile_streamfile(sf_b, ktsr.stream_offsets[0], ktsr.stream_sizes[0], "kvs");
if (!sf_kvs) goto fail;
VGMSTREAM* ogg_vgmstream = NULL; //TODO: meh
STREAMFILE* temp_sf = setup_subfile_streamfile(sf_b, ktsr.stream_offsets[0], ktsr.stream_sizes[0], "kvs");
if (!temp_sf) goto fail;
ogg_vgmstream = init_vgmstream_ogg_vorbis(sf_kvs);
close_streamfile(sf_kvs);
if (ogg_vgmstream) {
ogg_vgmstream->stream_size = vgmstream->stream_size;
ogg_vgmstream->num_streams = vgmstream->num_streams;
ogg_vgmstream->channel_layout = vgmstream->channel_layout;
/* loops look shared */
strcpy(ogg_vgmstream->stream_name, vgmstream->stream_name);
ogg_vgmstream = init_vgmstream_ogg_vorbis(temp_sf);
close_streamfile(temp_sf);
if (!ogg_vgmstream) goto fail;
close_vgmstream(vgmstream);
if (sf_b != sf) close_streamfile(sf_b);
return ogg_vgmstream;
}
else {
goto fail;
}
ogg_vgmstream->stream_size = vgmstream->stream_size;
ogg_vgmstream->num_streams = vgmstream->num_streams;
ogg_vgmstream->channel_layout = vgmstream->channel_layout;
/* loops look shared */
strcpy(ogg_vgmstream->stream_name, vgmstream->stream_name);
break;
close_vgmstream(vgmstream);
if (sf_b != sf) close_streamfile(sf_b);
return ogg_vgmstream;
}
#endif
@ -158,7 +152,6 @@ VGMSTREAM* init_vgmstream_ktsr(STREAMFILE* sf) {
goto fail;
}
if (!vgmstream_open_stream_bf(vgmstream, sf_b, ktsr.stream_offsets[0], 1))
goto fail;
@ -282,13 +275,14 @@ static int parse_ktsr_subfile(ktsr_header* ktsr, STREAMFILE* sf, off_t offset) {
int i;
uint32_t type;
type = read_u32be(offset + 0x00, sf);
type = read_u32be(offset + 0x00, sf); /* hash-id? */
//size = read_u32le(offset + 0x04, sf);
/* probably could check the flag in sound header, but the format is kinda messy */
switch(type) { /* hash-id? */
switch(type) {
case 0x38D0437D: /* external [Nioh (PC), Atelier Ryza (PC)] */
case 0x3DEA478D: /* external [Nioh (PC)] */
case 0xDF92529F: /* external [Atelier Ryza (PC)] */
case 0x6422007C: /* external [Atelier Ryza (PC)] */
/* 08 subtype? (ex. 0x522B86B9)
@ -311,10 +305,16 @@ static int parse_ktsr_subfile(ktsr_header* ktsr, STREAMFILE* sf, off_t offset) {
ktsr->format = read_u32le(offset + 0x14, sf);
/* other fields will be read in the external stream */
ktsr->channel_layout= read_u32le(offset + 0x28, sf);
ktsr->channel_layout = read_u32le(offset + 0x28, sf);
ktsr->stream_offsets[0] = read_u32le(offset + 0x34, sf);
ktsr->stream_sizes[0] = read_u32le(offset + 0x38, sf);
if (type == 0x3DEA478D) { /* Nioh (PC) has one less field, some files only [ABS.ktsl2asbin] */
ktsr->stream_offsets[0] = read_u32le(offset + 0x30, sf);
ktsr->stream_sizes[0] = read_u32le(offset + 0x34, sf);
}
else {
ktsr->stream_offsets[0] = read_u32le(offset + 0x34, sf);
ktsr->stream_sizes[0] = read_u32le(offset + 0x38, sf);
}
ktsr->is_external = 1;
if (ktsr->format != 0x05) {

View File

@ -1,139 +1,152 @@
#include "meta.h"
#include "../coding/coding.h"
/* P3D - from Radical's Prototype 1/2 (PC/PS3/X360) */
VGMSTREAM * init_vgmstream_p3d(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset, parse_offset, name_offset = 0;
/* P3D - from Radical's Prototype 1/2 (PC/PS3/X360), Spider-Man 4 Beta (X360) */
VGMSTREAM* init_vgmstream_p3d(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
off_t start_offset, offset, name_offset = 0;
size_t header_size, file_size, data_size;
int loop_flag = 0, channel_count, sample_rate, codec;
int i, name_count, text_len, block_size = 0, block_count = 0, num_samples;
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
uint32_t xma2_offset = 0, xma2_size = 0;
int loop_flag, channels, sample_rate, codec;
int i, name_count, text_len, block_size = 0, num_samples;
uint32_t (*read_u32)(off_t,STREAMFILE*) = NULL;
/* checks */
if (!check_extensions(streamFile,"p3d"))
if (!is_id32be(0x00,sf, "P3D\xFF") && /* LE: PC */
!is_id32le(0x00,sf, "P3D\xFF")) /* BE: PS3, X360 */
goto fail;
if (read_32bitBE(0x0,streamFile) != 0x503344FF && /* "P3D"\FF (LE: PC) */
read_32bitBE(0x0,streamFile) != 0xFF443350) /* \FF"D3P" (BE: PS3, X360) */
if (!check_extensions(sf,"p3d"))
goto fail;
read_32bit = read_32bitBE(0x0,streamFile) == 0xFF443350 ? read_32bitBE : read_32bitLE;
file_size = get_streamfile_size(streamFile);
read_u32 = guess_endianness32bit(0x04,sf) ? read_u32be : read_u32le;
file_size = get_streamfile_size(sf);
/* base header */
header_size = read_32bit(0x4,streamFile);
if (0x0C != header_size) goto fail;
if (read_32bit(0x08,streamFile) != file_size) goto fail;
if (read_32bit(0x0C,streamFile) != 0xFE000000) goto fail; /* fixed */
if (read_32bit(0x10,streamFile) + header_size != file_size) goto fail;
if (read_32bit(0x14,streamFile) + header_size != file_size) goto fail; /* body size again */
if (read_32bit(0x18,streamFile) != 0x0000000A) goto fail; /* fixed */
header_size = read_u32(0x04,sf);
if (header_size != 0x0C) goto fail;
if (read_u32(0x08,sf) != file_size) goto fail;
if (read_u32(0x0C,sf) != 0xFE000000) goto fail; /* fixed */
if (read_u32(0x10,sf) + header_size != file_size) goto fail;
if (read_u32(0x14,sf) + header_size != file_size) goto fail; /* body size again */
if (read_u32(0x18,sf) != 0x0000000A) goto fail; /* fixed */
/* header text */
parse_offset = 0x1C;
text_len = read_32bit(parse_offset,streamFile);
if (9 != text_len) goto fail;
parse_offset += 4;
offset = 0x1C;
text_len = read_u32(offset,sf);
if (text_len != 9) goto fail;
offset += 0x04;
/* check the type as P3D is just a generic container used in Radical's games */
if (read_32bitBE(parse_offset+0x00,streamFile) != 0x41756469 ||
read_32bitBE(parse_offset+0x04,streamFile) != 0x6F46696C ||
read_16bitBE(parse_offset+0x08,streamFile) != 0x6500) goto fail; /* "AudioFile\0" */
parse_offset += text_len + 1;
if (!is_id64be(offset+0x00,sf, "AudioFil") || read_u16be(offset+0x08,sf) != 0x6500) /* "AudioFile\0" */
goto fail;
offset += text_len + 0x01;
/* file names: always 2 (repeated); but if it's 3 there is an extra string later */
name_count = read_32bit(parse_offset,streamFile);
name_count = read_u32(offset,sf);
if (name_count != 2 && name_count != 3) goto fail; /* 2: Prototype1, 3: Prototype2 */
parse_offset += 4;
offset += 0x04;
/* skip names */
for (i = 0; i < 2; i++) {
if (!name_offset)
name_offset = parse_offset + 4;
text_len = read_32bit(parse_offset,streamFile) + 1; /* null-terminated */
parse_offset += 4 + text_len;
name_offset = offset + 0x04;
text_len = read_u32(offset,sf) + 1; /* null-terminated */
offset += 0x04 + text_len;
}
/* info count? */
if (0x01 != read_32bit(parse_offset,streamFile)) goto fail;
parse_offset += 4;
if (0x01 != read_u32(offset,sf)) goto fail;
offset += 0x04;
/* next string can be used as a codec id */
text_len = read_32bit(parse_offset,streamFile);
codec = read_32bitBE(parse_offset+4,streamFile);
parse_offset += 4 + text_len + 1;
text_len = read_u32(offset,sf);
codec = read_u32be(offset+0x04,sf);
offset += 0x04 + text_len + 0x01;
/* extra "Music" string in Prototype 2 */
if (name_count == 3) {
text_len = read_32bit(parse_offset,streamFile) + 1; /* null-terminated */
parse_offset += 4 + text_len;
text_len = read_u32(offset,sf) + 1; /* null-terminated */
offset += 0x04 + text_len;
}
loop_flag = 0;
/* sub-header per codec */
switch(codec) {
case 0x72616470: /* "radp" (PC) */
if (read_32bitBE(parse_offset,streamFile) != 0x52414450) goto fail; /* "RADP" */
parse_offset += 0x04;
channel_count = read_32bit(parse_offset+0x00,streamFile);
sample_rate = read_32bit(parse_offset+0x04,streamFile);
if (!is_id32be(offset,sf, "RADP"))
goto fail;
offset += 0x04;
channels = read_u32(offset+0x00,sf);
sample_rate = read_u32(offset+0x04,sf);
/* 0x08: ? (0x0F) */
data_size = read_32bit(parse_offset+0x0c,streamFile);
block_size = 0x14;
num_samples = data_size / block_size / channel_count * 32;
start_offset = parse_offset+0x10;
data_size = read_u32(offset+0x0c,sf);
block_size = 0x14;
num_samples = data_size / block_size / channels * 32;
start_offset = offset + 0x10;
break;
case 0x6D703300: /* "mp3\0" (PS3) */
if ((read_32bitBE(parse_offset,streamFile) & 0xFFFFFF00) != 0x6D703300) goto fail; /* "mp3" */
parse_offset += 0x03;
if ((read_u32be(offset,sf) & 0xFFFFFF00) != get_id32be("mp3\0"))
goto fail;
offset += 0x03;
/* all fields LE even though the prev parts were BE */
sample_rate = read_32bitLE(parse_offset+0x00,streamFile);
sample_rate = read_s32le(offset+0x00,sf);
/* 0x04: mp3 sample rate (ex. @0x00 is 47999 and @0x04 is 48000) */
num_samples = read_32bitLE(parse_offset+0x08,streamFile);
data_size = read_32bitLE(parse_offset+0x0c,streamFile);
channel_count = read_32bitLE(parse_offset+0x10,streamFile);
block_size = read_32bitLE(parse_offset+0x14,streamFile);
num_samples = num_samples / channel_count; /* total samples */
start_offset = parse_offset+0x18;
num_samples = read_s32le(offset+0x08,sf);
data_size = read_u32le(offset+0x0c,sf);
channels = read_s32le(offset+0x10,sf);
block_size = read_u32le(offset+0x14,sf);
num_samples = num_samples / channels; /* total samples */
start_offset = offset + 0x18;
break;
case 0x786D6100: /* "xma\0" (X360) */
if (read_32bitBE(parse_offset,streamFile) != 0x584D4132) goto fail; /* "XMA2" */
parse_offset += 0x04;
/* 0x00: subheader size? (0x2c), 0x04: seek table size */
data_size = read_32bitBE(parse_offset+0x08,streamFile);
/* 0x0c: ?, 0x10: ?, 0x14/18: 0x0 */
sample_rate = read_32bitBE(parse_offset+0x1c,streamFile);
/* 0x20: XMA decoder params, 0x24: abr */
block_size = read_32bitBE(parse_offset+0x28,streamFile);
num_samples = read_32bitBE(parse_offset+0x2c,streamFile);
/* 0x30: original file's samples */
block_count = read_32bitBE(parse_offset+0x34,streamFile);
channel_count = read_8bit(parse_offset+0x38,streamFile);
/* 0x39: channel related? (stream config? channel layout?) */
start_offset = parse_offset + 0x3c + read_32bitBE(parse_offset+0x04,streamFile);
case 0x786D6100: { /* "xma\0" (X360) */
uint32_t seek_size;
if (!is_id32be(offset,sf, "XMA2"))
goto fail;
offset += 0x04;
xma2_size = read_u32be(offset+0x00,sf);
seek_size = read_u32be(offset+0x04,sf);
data_size = read_u32be(offset+0x08,sf);
/* 0x0c: ? */
xma2_offset = offset+0x10;
if (!read_u8(xma2_offset+0x00, sf)) /* needs "xma2" chunk (Spider-Man 4 beta has multi-streams) */
goto fail;
/* loops never set */
xma2_parse_xma2_chunk(sf, xma2_offset, &channels, &sample_rate, &loop_flag, &num_samples, NULL, NULL);
start_offset = offset + 0x10 + xma2_size + seek_size;
break;
}
default:
VGM_LOG("P3D: unknown codec 0x%04x\n", codec);
vgm_logi("P3D: unknown codec 0x%04x\n", codec);
goto fail;
}
if (start_offset + data_size != file_size) goto fail;
if (start_offset + data_size != file_size)
goto fail;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_P3D;
vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = num_samples;
vgmstream->meta_type = meta_P3D;
if (name_offset)
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamFile);
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,sf);
/* codec init */
switch(codec) {
case 0x72616470: /* "radp" (PC) */
vgmstream->coding_type = coding_RAD_IMA_mono;
@ -149,7 +162,7 @@ VGMSTREAM * init_vgmstream_p3d(STREAMFILE *streamFile) {
cfg.data_size = data_size;
/* block_size * 3 = frame size (0x60*3=0x120 or 0x40*3=0xC0) but doesn't seem to have any significance) */
vgmstream->codec_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_P3D, &cfg);
vgmstream->codec_data = init_mpeg_custom(sf, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_P3D, &cfg);
if (!vgmstream->codec_data) goto fail;
vgmstream->layout_type = layout_none;
break;
@ -161,23 +174,24 @@ VGMSTREAM * init_vgmstream_p3d(STREAMFILE *streamFile) {
uint8_t buf[0x100];
size_t bytes;
bytes = ffmpeg_make_riff_xma2(buf,0x100, vgmstream->num_samples, data_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size);
vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,data_size);
if ( !vgmstream->codec_data ) goto fail;
//TODO: some in Spider-Man 4 beta use 18ch but ffmpeg supports max 16ch XMA2
bytes = ffmpeg_make_riff_xma2_from_xma2_chunk(buf, sizeof(buf), xma2_offset, xma2_size, data_size, sf);
vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf,bytes, start_offset,data_size);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
xma_fix_raw_samples(vgmstream, streamFile, start_offset, data_size, 0, 1,1); /* samples needs adjustment */
xma_fix_raw_samples(vgmstream, sf, start_offset, data_size, 0, 1,1); /* samples needs adjustment */
break;
}
#endif
default:
VGM_LOG("P3D: unknown codec 0x%04x\n", codec);
vgm_logi("P3D: unknown codec 0x%04x\n", codec);
goto fail;
}
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
goto fail;
return vgmstream;

View File

@ -6,7 +6,7 @@
static STREAMFILE* setup_ubi_lyn_streamfile(STREAMFILE* sf, off_t stream_offset, size_t interleave_size, int stream_number, int stream_count, size_t logical_size) {
STREAMFILE *new_sf = NULL;
deblock_config_t cfg = {0};
VGM_LOG("so=%lx, chu=%x, n=%i, c=%i, lo=%x\n", stream_offset, interleave_size, stream_number, stream_count, logical_size);
cfg.stream_start = stream_offset;
cfg.chunk_size = interleave_size;
cfg.step_start = stream_number;

View File

@ -2,6 +2,22 @@
#include "util.h"
#include "vgmstream.h"
/* for dup/fdopen in some systems */
#ifndef _MSC_VER
#include <unistd.h>
#endif
/* For (rarely needed) +2GB file support we use fseek64/ftell64. Those are usually available
* but may depend on compiler.
* - MSVC: +VS2008 should work
* - GCC/MingW: should be available
* - GCC/Linux: should be available but some systems may need __USE_FILE_OFFSET64,
* that we (probably) don't want since that turns off_t to off64_t
* - Clang: seems only defined on Linux/GNU environments, somehow emscripten is out
* (unsure about Clang Win since apparently they define _MSC_VER)
* - Android: API +24 if not using __USE_FILE_OFFSET64
* Not sure if fopen64 is needed in some cases. May be work adding some compiler flag to control this manually.
*/
/* MSVC fixes (though mingw uses MSVCRT but not MSC_VER, maybe use AND?) */
#if defined(__MSVCRT__) || defined(_MSC_VER)
@ -15,8 +31,15 @@
#define ftello ftell
#endif
*/
#define fseek_v _fseeki64 //fseek/fseeko
#define ftell_v _ftelli64 //ftell/ftello
#define fopen_v fopen
#if (_MSC_VER >= 1400)
#define fseek_v _fseeki64
#define ftell_v _ftelli64
#else
#define fseek_v fseek
#define ftell_v ftell
#endif
#ifdef fileno
#undef fileno
@ -29,12 +52,15 @@
#define off_t __int64
#endif
#elif defined(XBMC)
#elif defined(XBMC) || defined(__EMSCRIPTEN__) || defined (__ANDROID__)
#define fopen_v fopen
#define fseek_v fseek
#define ftell_v ftell
#else
#define fopen_v fopen
#define fseek_v fseeko64 //fseeko
#define ftell_v ftello64 //ftelloo
#define ftell_v ftello64 //ftello
#endif
@ -48,7 +74,7 @@ typedef struct {
offv_t buf_offset; /* current buffer data start */
uint8_t* buf; /* data buffer */
size_t buf_size; /* max buffer size */
size_t valid_size; /* current buffer size */
size_t valid_size; /* current buffer size */
size_t file_size; /* buffered file size */
} STDIO_STREAMFILE;
@ -154,12 +180,6 @@ static void stdio_get_name(STDIO_STREAMFILE* sf, char* name, size_t name_size) {
strncpy(name, sf->name, name_size);
name[name_size - 1] = '\0';
}
static void stdio_close(STDIO_STREAMFILE* sf) {
if (sf->infile)
fclose(sf->infile);
free(sf->buf);
free(sf);
}
static STREAMFILE* stdio_open(STDIO_STREAMFILE* sf, const char* const filename, size_t buf_size) {
if (!filename)
@ -187,11 +207,19 @@ static STREAMFILE* stdio_open(STDIO_STREAMFILE* sf, const char* const filename,
/* on failure just close and try the default path (which will probably fail a second time) */
}
#endif
#endif
// a normal open, open a new file
return open_stdio_streamfile_buffer(filename, buf_size);
}
static void stdio_close(STDIO_STREAMFILE* sf) {
if (sf->infile)
fclose(sf->infile);
free(sf->buf);
free(sf);
}
static STREAMFILE* open_stdio_streamfile_buffer_by_file(FILE* infile, const char* const filename, size_t buf_size) {
uint8_t* buf = NULL;
STDIO_STREAMFILE* this_sf = NULL;
@ -245,7 +273,7 @@ static STREAMFILE* open_stdio_streamfile_buffer(const char* const filename, size
FILE* infile = NULL;
STREAMFILE* sf = NULL;
infile = fopen(filename,"rb");
infile = fopen_v(filename,"rb");
if (!infile) {
/* allow non-existing files in some cases */
if (!vgmstream_is_virtual_filename(filename))
@ -360,10 +388,12 @@ static offv_t buffer_get_offset(BUFFER_STREAMFILE* sf) {
static void buffer_get_name(BUFFER_STREAMFILE* sf, char* name, size_t name_size) {
sf->inner_sf->get_name(sf->inner_sf, name, name_size); /* default */
}
static STREAMFILE* buffer_open(BUFFER_STREAMFILE* sf, const char* const filename, size_t buf_size) {
STREAMFILE* new_inner_sf = sf->inner_sf->open(sf->inner_sf,filename,buf_size);
return open_buffer_streamfile(new_inner_sf, buf_size); /* original buffer size is preferable? */
}
static void buffer_close(BUFFER_STREAMFILE* sf) {
sf->inner_sf->close(sf->inner_sf);
free(sf->buf);
@ -435,12 +465,14 @@ static size_t wrap_get_size(WRAP_STREAMFILE* sf) {
static offv_t wrap_get_offset(WRAP_STREAMFILE* sf) {
return sf->inner_sf->get_offset(sf->inner_sf); /* default */
}
static void wrap_get_name(WRAP_STREAMFILE* sf, char* name, size_t name_len) {
sf->inner_sf->get_name(sf->inner_sf, name, name_len); /* default */
static void wrap_get_name(WRAP_STREAMFILE* sf, char* name, size_t name_size) {
sf->inner_sf->get_name(sf->inner_sf, name, name_size); /* default */
}
static void wrap_open(WRAP_STREAMFILE* sf, const char* const filename, size_t buf_size) {
sf->inner_sf->open(sf->inner_sf, filename, buf_size); /* default (don't wrap) */
static STREAMFILE* wrap_open(WRAP_STREAMFILE* sf, const char* const filename, size_t buf_size) {
return sf->inner_sf->open(sf->inner_sf, filename, buf_size); /* default (don't call open_wrap_streamfile) */
}
static void wrap_close(WRAP_STREAMFILE* sf) {
//sf->inner_sf->close(sf->inner_sf); /* don't close */
free(sf);
@ -503,9 +535,10 @@ static size_t clamp_get_size(CLAMP_STREAMFILE* sf) {
static offv_t clamp_get_offset(CLAMP_STREAMFILE* sf) {
return sf->inner_sf->get_offset(sf->inner_sf) - sf->start;
}
static void clamp_get_name(CLAMP_STREAMFILE* sf, char* name, size_t name_len) {
sf->inner_sf->get_name(sf->inner_sf, name, name_len); /* default */
static void clamp_get_name(CLAMP_STREAMFILE* sf, char* name, size_t name_size) {
sf->inner_sf->get_name(sf->inner_sf, name, name_size); /* default */
}
static STREAMFILE* clamp_open(CLAMP_STREAMFILE* sf, const char* const filename, size_t buf_size) {
char original_filename[PATH_LIMIT];
STREAMFILE* new_inner_sf = NULL;
@ -520,6 +553,7 @@ static STREAMFILE* clamp_open(CLAMP_STREAMFILE* sf, const char* const filename,
return new_inner_sf;
}
}
static void clamp_close(CLAMP_STREAMFILE* sf) {
sf->inner_sf->close(sf->inner_sf);
free(sf);
@ -582,13 +616,15 @@ static size_t io_get_size(IO_STREAMFILE* sf) {
static offv_t io_get_offset(IO_STREAMFILE* sf) {
return sf->inner_sf->get_offset(sf->inner_sf); /* default */
}
static void io_get_name(IO_STREAMFILE* sf, char* name, size_t name_len) {
sf->inner_sf->get_name(sf->inner_sf, name, name_len); /* default */
static void io_get_name(IO_STREAMFILE* sf, char* name, size_t name_size) {
sf->inner_sf->get_name(sf->inner_sf, name, name_size); /* default */
}
static STREAMFILE* io_open(IO_STREAMFILE* sf, const char* const filename, size_t buf_size) {
STREAMFILE* new_inner_sf = sf->inner_sf->open(sf->inner_sf,filename,buf_size);
return open_io_streamfile_ex(new_inner_sf, sf->data, sf->data_size, sf->read_callback, sf->size_callback, sf->init_callback, sf->close_callback);
}
static void io_close(IO_STREAMFILE* sf) {
if (sf->close_callback)
sf->close_callback(sf->inner_sf, sf->data);
@ -633,7 +669,7 @@ STREAMFILE* open_io_streamfile_ex(STREAMFILE* sf, void* data, size_t data_size,
}
return &this_sf->vt;
fail:
if (this_sf) free(this_sf->data);
free(this_sf);
@ -676,6 +712,7 @@ static void fakename_get_name(FAKENAME_STREAMFILE* sf, char* name, size_t name_s
strncpy(name,sf->fakename, name_size);
name[name_size - 1] = '\0';
}
static STREAMFILE* fakename_open(FAKENAME_STREAMFILE* sf, const char* const filename, size_t buf_size) {
/* detect re-opening the file */
if (strcmp(filename, sf->fakename) == 0) {
@ -797,6 +834,7 @@ static offv_t multifile_get_offset(MULTIFILE_STREAMFILE* sf) {
static void multifile_get_name(MULTIFILE_STREAMFILE* sf, char* name, size_t name_size) {
sf->inner_sfs[0]->get_name(sf->inner_sfs[0], name, name_size);
}
static STREAMFILE* multifile_open(MULTIFILE_STREAMFILE* sf, const char* const filename, size_t buf_size) {
char original_filename[PATH_LIMIT];
STREAMFILE* new_sf = NULL;
@ -1219,7 +1257,7 @@ STREAMFILE* read_filemap_file_pos(STREAMFILE* sf, int file_num, int* p_pos) {
/* get key/val (ignores lead/trailing spaces, stops at comment/separator) */
ok = sscanf(line, " %[^\t#:] : %[^\t#\r\n] ", key, val);
if (ok != 2) { /* ignore line if no key=val (comment or garbage) */
continue;
continue;
}
if (strcmp(key, filename) == 0) {
@ -1454,7 +1492,7 @@ void dump_streamfile(STREAMFILE* sf, int num) {
get_streamfile_filename(sf, filename, sizeof(filename));
snprintf(dumpname, sizeof(dumpname), "%s_%02i.dump", filename, num);
f = fopen(dumpname,"wb");
f = fopen_v(dumpname,"wb");
if (!f) return;
}

View File

@ -66,16 +66,16 @@ typedef struct _STREAMFILE {
size_t (*get_size)(struct _STREAMFILE* sf);
//todo: DO NOT USE, NOT RESET PROPERLY (remove?)
offv_t (*get_offset)(struct _STREAMFILE*);
offv_t (*get_offset)(struct _STREAMFILE* sf);
/* copy current filename to name buf */
void (*get_name)(struct _STREAMFILE* sf, char* name, size_t name_size);
/* open another streamfile from filename */
struct _STREAMFILE* (*open)(struct _STREAMFILE* sf, const char* const filename, size_t buffer_size);
struct _STREAMFILE* (*open)(struct _STREAMFILE* sf, const char* const filename, size_t buf_size);
/* free current STREAMFILE */
void (*close)(struct _STREAMFILE*);
void (*close)(struct _STREAMFILE* sf);
/* Substream selection for formats with subsongs.
* Not ideal here, but it was the simplest way to pass to all init_vgmstream_x functions. */

View File

@ -140,7 +140,7 @@ static inline int clamp16(int32_t val) {
/* transforms a string to uint32 (for comparison), but if this is static + all goes well
* compiler should pre-calculate and use uint32 directly */
static inline /*const*/ uint32_t get_id32be(const char* s) {
return (uint32_t)(s[0] << 24) | (s[1] << 16) | (s[2] << 8) | (s[3] << 0);
return (uint32_t)((uint8_t)s[0] << 24) | ((uint8_t)s[1] << 16) | ((uint8_t)s[2] << 8) | ((uint8_t)s[3] << 0);
}
//static inline /*const*/ uint32_t get_id32le(const char* s) {