mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-11-28 00:20:47 +01:00
Merge pull request #1468 from bnnm/ghs-xa-etc
- Fix some .gtd/ged [Gunslinger Stratos (AC)] - xa: fix crash - bik: cleanup - Add HCA key - Fix some .sm1 [Splinter Cell: Pandora Tomorrow (PS2)] - Add .ogg NXOpus [Trouble Witches Origin (Switch)] - cleanup
This commit is contained in:
commit
5de0d05537
@ -59,9 +59,13 @@ class App(object):
|
||||
|
||||
def start(self):
|
||||
print("TXTP dumper start")
|
||||
|
||||
filenames = []
|
||||
for filename in self.args.files:
|
||||
filenames += glob.glob(filename)
|
||||
if os.path.isfile(filename): #for files in paths with regex-like format
|
||||
filenames += [filename]
|
||||
else:
|
||||
filenames += glob.glob(filename)
|
||||
|
||||
for filename in filenames:
|
||||
path = self.args.output or '.'
|
||||
|
@ -24,7 +24,7 @@ class Cli(object):
|
||||
" %(prog)s * -r -fss 1\n"
|
||||
" - make .txtp for all files in any subdirs with at least 1 subsong\n"
|
||||
" (ignores formats without subsongs)\n\n"
|
||||
" %(prog)s bgm.fsb -in -fcm 2 -fms 5.0\n"
|
||||
" %(prog)s bgm.fsb -in -fcm 2 -fsm 5.0\n"
|
||||
" - make .txtp for subsongs with at least 2 channels and 5 seconds\n\n"
|
||||
" %(prog)s *.scd -r -fd -l 2\n"
|
||||
" - make .txtp for all .scd in subdirs, ignoring dupes, one .txtp per 2ch\n\n"
|
||||
@ -53,6 +53,7 @@ class Cli(object):
|
||||
"* may be inside <...> for conditional text\n"))
|
||||
p.add_argument('-z', dest='zero_fill', help="Zero-fill subsong number (default: auto per subsongs)", type=int)
|
||||
p.add_argument('-ie', dest='no_internal_ext', help="Remove internal name's extension if any", action='store_true')
|
||||
p.add_argument('-ip', dest='allow_internal_paths', help="Replace internal name's / paths with ~ (otherwise removed)", action='store_true')
|
||||
p.add_argument('-m', dest='mini_txtp', help="Create mini-txtp", action='store_true')
|
||||
p.add_argument('-s', dest='subsong_start', help="Start subsong", type=int)
|
||||
p.add_argument('-S', dest='subsong_end', help="End subsong", type=int)
|
||||
@ -270,13 +271,17 @@ class TxtpMaker(object):
|
||||
return None
|
||||
|
||||
txt = self.info.stream_name
|
||||
# remove paths #todo maybe config/replace?
|
||||
pos = txt.rfind('\\')
|
||||
if pos >= 0:
|
||||
txt = txt[pos+1:]
|
||||
pos = txt.rfind('/')
|
||||
if pos >= 0:
|
||||
txt = txt[pos+1:]
|
||||
# remove paths
|
||||
if self.cfg.allow_internal_paths:
|
||||
txt = txt.replace("\\", "~")
|
||||
txt = txt.replace("/", "~")
|
||||
else:
|
||||
pos = txt.rfind('\\')
|
||||
if pos >= 0:
|
||||
txt = txt[pos+1:]
|
||||
pos = txt.rfind('/')
|
||||
if pos >= 0:
|
||||
txt = txt[pos+1:]
|
||||
|
||||
# remove bad chars
|
||||
badchars = ['%', '*', '?', ':', '\"', '|', '<', '>']
|
||||
@ -613,6 +618,7 @@ class App(object):
|
||||
# subsongs should treat repeat names separately? pass flag?
|
||||
#maker.reset(rename_map)
|
||||
|
||||
processing = False
|
||||
while True:
|
||||
try:
|
||||
# main call to vgmstream
|
||||
@ -623,10 +629,12 @@ class App(object):
|
||||
# basic parse of vgmstream info
|
||||
maker.parse(output_b)
|
||||
|
||||
processing = True
|
||||
except (subprocess.CalledProcessError, ValueError) as e:
|
||||
log.debug("ignoring CLI error in %s #%s: %s", filename_in, target_subsong, str(e))
|
||||
errors += 1
|
||||
break
|
||||
if not processing: #stop but only if first subsong fails
|
||||
break
|
||||
|
||||
if target_subsong == subsong_start:
|
||||
log.debug("processing %s...", filename_in_clean)
|
||||
@ -647,7 +655,7 @@ class App(object):
|
||||
target_subsong += 1
|
||||
|
||||
if target_subsong % 200 == 0:
|
||||
log.info("%s/%s subsongs... (%s dupes, %s errors)", target_subsong, maker.info.stream_count, dupes, errors)
|
||||
log.info("%s/%s subsongs... (%s done, %s dupes, %s errors)", target_subsong, maker.info.stream_count, created, dupes, errors)
|
||||
|
||||
if os.path.exists(filename_out):
|
||||
os.remove(filename_out)
|
||||
|
@ -500,10 +500,6 @@ different internally (encrypted, different versions, etc) and not always can be
|
||||
- Krome .MUS header [*MUS_KROME*]
|
||||
- *mus_krome*: `.mus`
|
||||
- Codecs: NGC_DSP
|
||||
- **dc_asd.c**
|
||||
- ASD Header [*DC_ASD*]
|
||||
- *dc_asd*: `.asd`
|
||||
- Codecs: PCM16LE
|
||||
- **spsd.c**
|
||||
- Sega Naomi SPSD header [*SPSD*]
|
||||
- *spsd*: `.str .spsd`
|
||||
@ -1008,7 +1004,7 @@ different internally (encrypted, different versions, etc) and not always can be
|
||||
- Codecs: FFmpeg(various)
|
||||
- **bik.c**
|
||||
- RAD Game Tools Bink header [*BINK*]
|
||||
- *bik*: `.bik .bik2 .bk2 .ps3 .xmv .xen .vid .bika`
|
||||
- *bik*: `.bik .bk2 .bik2 .ps3 .xmv .xen .vid .bika`
|
||||
- Codecs: FFmpeg(various)
|
||||
- **astb.c**
|
||||
- Capcom ASTB header [*ASTB*]
|
||||
@ -1049,7 +1045,9 @@ different internally (encrypted, different versions, etc) and not always can be
|
||||
- *ghs*: `.gtd`
|
||||
- *s_p_sth*: `.gtd`
|
||||
- Subfiles: *msf*
|
||||
- Codecs: XMA ATRAC9
|
||||
- *s_pack*: `.ged`
|
||||
- Subfiles: *ghs*
|
||||
- Codecs: PCM16LE MSADPCM XMA ATRAC9
|
||||
- **aac_triace.c**
|
||||
- tri-Ace AAC header [*AAC_TRIACE*]
|
||||
- *aac_triace*: `.aac .laac`
|
||||
@ -1098,7 +1096,7 @@ different internally (encrypted, different versions, etc) and not always can be
|
||||
- Codecs: PCM16BE PCM16LE AWC_IMA XMA2 MPEG VORBIS_custom ATRAC9 NGC_DSP
|
||||
- **opus.c**
|
||||
- Nintendo Switch OPUS header [*OPUS*]
|
||||
- *opus_std*: `.opus .lopus .bgm .opu + .psi`
|
||||
- *opus_std*: `.opus .lopus .bgm .opu .ogg .logg + .psi`
|
||||
- *opus_n1*: `.opus .lopus`
|
||||
- *opus_capcom*: `.opus .lopus`
|
||||
- *opus_nop*: `.nop`
|
||||
@ -1852,6 +1850,10 @@ different internally (encrypted, different versions, etc) and not always can be
|
||||
- Rockstar .ivaud header [*IVAUD*]
|
||||
- *ivaud*: `.ivaud .(extensionless)`
|
||||
- Codecs: PCM16LE XMA1 MPEG IMA_int
|
||||
- **asd_naxat.c**
|
||||
- Naxat .ASD header [*ASD_NAXAT*]
|
||||
- *asd_naxat*: `.asd`
|
||||
- Codecs: PCM16LE
|
||||
- **pos.c**
|
||||
- RIFF WAVE header (.pos looping) [*RIFF_WAVE_POS*]
|
||||
- *pos*: `.pos + .wav`
|
||||
@ -1932,7 +1934,7 @@ different internally (encrypted, different versions, etc) and not always can be
|
||||
- Codecs: FFmpeg(various)
|
||||
- **ea_eaac.c**
|
||||
- Electronic Arts SPS header [*EA_SPS*]
|
||||
- *eaaudiocore_header*: `(base) + .sns`
|
||||
- *eaaudiocore_main*: `(base) + .sns`
|
||||
- Codecs: PCM16_int EA_XAS_V1 MPEG NGC_DSP SPEEX ATRAC9 Opus XMA1 XMA2
|
||||
|
||||
## Supported extras
|
||||
|
@ -204,6 +204,7 @@ static const char* extension_list[] = {
|
||||
"gcm",
|
||||
"gcub",
|
||||
"gcw",
|
||||
"ged",
|
||||
"genh",
|
||||
"gin",
|
||||
"gmd", //txth/semi [High Voltage games: Charlie and the Chocolate Factory (GC), Zathura (GC)]
|
||||
@ -366,6 +367,7 @@ static const char* extension_list[] = {
|
||||
"msvp", //fake extension/header id for .msv
|
||||
"mta2",
|
||||
"mtaf",
|
||||
"mtt", //txth/reserved [Splinter Cell: Pandora Tomorrow (PS2)]
|
||||
"mul",
|
||||
"mups",
|
||||
"mus",
|
||||
@ -1101,7 +1103,7 @@ static const meta_info meta_info_list[] = {
|
||||
{meta_MUS_KROME, "Krome .MUS header"},
|
||||
{meta_WII_SNG, "SNG DSP Header"},
|
||||
{meta_RSD, "Radical RSD header"},
|
||||
{meta_DC_ASD, "ASD Header"},
|
||||
{meta_ASD_NAXAT, "Naxat .ASD header"},
|
||||
{meta_SPSD, "Sega Naomi SPSD header"},
|
||||
{meta_FFXI_BGW, "Square Enix .BGW header"},
|
||||
{meta_FFXI_SPW, "Square Enix .SPW header"},
|
||||
|
@ -361,6 +361,7 @@
|
||||
<ClCompile Include="meta\ao.c" />
|
||||
<ClCompile Include="meta\apc.c" />
|
||||
<ClCompile Include="meta\apple_caff.c" />
|
||||
<ClCompile Include="meta\asd_naxat.c" />
|
||||
<ClCompile Include="meta\asf.c" />
|
||||
<ClCompile Include="meta\ast.c" />
|
||||
<ClCompile Include="meta\astb.c" />
|
||||
@ -402,7 +403,6 @@
|
||||
<ClCompile Include="meta\cstr.c" />
|
||||
<ClCompile Include="meta\cxs.c" />
|
||||
<ClCompile Include="meta\dcs_wav.c" />
|
||||
<ClCompile Include="meta\dc_asd.c" />
|
||||
<ClCompile Include="meta\dc_idvi.c" />
|
||||
<ClCompile Include="meta\dc_kcey.c" />
|
||||
<ClCompile Include="meta\deblock_streamfile.c" />
|
||||
|
@ -904,6 +904,9 @@
|
||||
<ClCompile Include="meta\apple_caff.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\asd_naxat.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\asf.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
@ -1027,9 +1030,6 @@
|
||||
<ClCompile Include="meta\dcs_wav.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\dc_asd.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\dc_idvi.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
|
61
src/meta/asd_naxat.c
Normal file
61
src/meta/asd_naxat.c
Normal file
@ -0,0 +1,61 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* ASD - found Naxat (Spiel/Mesa) games [Miss Moonlight (DC), Yoshia no Oka de Nekoronde... (DC)] */
|
||||
VGMSTREAM* init_vgmstream_asd_naxat(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
uint32_t start_offset;
|
||||
int loop_flag, channels, sample_rate;
|
||||
|
||||
|
||||
/* checks */
|
||||
uint32_t data_size = read_u32le(0x00, sf); /* padded and slightly less than file size */
|
||||
if (data_size == 0 || data_size >= get_streamfile_size(sf) || data_size + 0x20 + 0x10 < get_streamfile_size(sf))
|
||||
return NULL;
|
||||
if (data_size != read_u32le(0x04,sf)) /* repeated size */
|
||||
return NULL;
|
||||
/* extension of the audio bigfiles (there are N offsets to these subfiles) */
|
||||
if (!check_extensions(sf, "asd"))
|
||||
return NULL;
|
||||
|
||||
/* fmt chunk, extra checks since format is simple */
|
||||
if (read_u16le(0x08,sf) != 0x01) /* format*/
|
||||
return NULL;
|
||||
channels = read_u16le(0x0a,sf);
|
||||
sample_rate = read_s32le(0x0c,sf);
|
||||
|
||||
if (channels < 1 || channels > 2)
|
||||
return NULL;
|
||||
if (sample_rate != 22050)
|
||||
return NULL;
|
||||
if (sample_rate * channels * sizeof(int16_t) != read_u32le(0x10,sf)) /* bitrate */
|
||||
return NULL;
|
||||
/* 04: block size, bps */
|
||||
if (read_u32le(0x18,sf) != 0x00 )
|
||||
return NULL;
|
||||
if (read_u32le(0x1c,sf) != 0x00 )
|
||||
return NULL;
|
||||
|
||||
start_offset = 0x20;
|
||||
loop_flag = 0;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_ASD_NAXAT;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = pcm16_bytes_to_samples(data_size, channels);
|
||||
|
||||
vgmstream->coding_type = coding_PCM16LE;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x02;
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
130
src/meta/bik.c
130
src/meta/bik.c
@ -2,7 +2,7 @@
|
||||
#include "../coding/coding.h"
|
||||
#include "../util.h"
|
||||
|
||||
static int bink_get_info(STREAMFILE* sf, int target_subsong, int* p_total_subsongs, size_t* p_stream_size, int* p_channels, int* p_sample_rate, int* p_num_samples);
|
||||
static bool bink_get_info(STREAMFILE* sf, int target_subsong, int* p_total_subsongs, size_t* p_stream_size, int* p_channels, int* p_sample_rate, int* p_num_samples);
|
||||
|
||||
/* BINK 1/2 - RAD Game Tools movies (audio/video format) */
|
||||
VGMSTREAM* init_vgmstream_bik(STREAMFILE* sf) {
|
||||
@ -17,13 +17,14 @@ VGMSTREAM* init_vgmstream_bik(STREAMFILE* sf) {
|
||||
(read_u32be(0x00,sf) & 0xffffff00) != get_id32be("KB2\0"))
|
||||
goto fail;
|
||||
|
||||
/* .bik/bik2/bk2: standard
|
||||
/* .bik/bk2: standard
|
||||
* .bik2: older?
|
||||
* .xmv: Reflections games [Driver: Parallel Lines (Wii), Emergency Heroes (Wii)]
|
||||
* .bik.ps3: Neversoft games [Guitar Hero: Warriors of Rock (PS3)]
|
||||
* .bik.xen: Neversoft games [various Guitar Hero (PC/PS3/X360)]
|
||||
* .vid: Etrange Libellules games [Alice in Wonderland (PC)]
|
||||
* .bika: fake extension for demuxed audio */
|
||||
if (!check_extensions(sf,"bik,bik2,bk2,ps3,xmv,xen,vid,bika"))
|
||||
if (!check_extensions(sf,"bik,bk2,bik2,ps3,xmv,xen,vid,bika"))
|
||||
goto fail;
|
||||
|
||||
/* find target stream info and samples */
|
||||
@ -59,79 +60,126 @@ fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* official values */
|
||||
#define BINK_MAX_FRAMES 1000000
|
||||
#define BINK_MAX_TRACKS 256
|
||||
|
||||
/**
|
||||
* Gets stream info, and number of samples in a BINK file by reading all frames' headers (as it's VBR),
|
||||
* as they are not in the main header. The header for BINK1 and 2 is the same.
|
||||
* (a ~3 min movie needs ~6000-7000 frames = fseeks, should be fast enough)
|
||||
*/
|
||||
static int bink_get_info(STREAMFILE* sf, int target_subsong, int* p_total_subsongs, size_t* p_stream_size, int* p_channels, int* p_sample_rate, int* p_num_samples) {
|
||||
uint32_t* offsets = NULL;
|
||||
uint32_t num_frames, num_samples_b = 0;
|
||||
* see: https://wiki.multimedia.cx/index.php?title=Bink_Container */
|
||||
static bool bink_get_info(STREAMFILE* sf, int target_subsong, int* p_total_subsongs, size_t* p_stream_size, int* p_channels, int* p_sample_rate, int* p_num_samples) {
|
||||
uint32_t* offsets = NULL; //TODO rename
|
||||
uint32_t num_samples_b = 0;
|
||||
off_t cur_offset;
|
||||
int i, j, sample_rate, channels;
|
||||
int total_subsongs;
|
||||
size_t stream_size = 0;
|
||||
|
||||
size_t filesize = get_streamfile_size(sf);
|
||||
uint32_t signature = (read_32bitBE(0x00,sf) & 0xffffff00);
|
||||
uint8_t revision = (read_32bitBE(0x00,sf) & 0xFF);
|
||||
/* known revisions:
|
||||
* bik1: b,d,f,g,h,i,k [no "j"]
|
||||
* bik2: a,d,f,g,h,i,j,k,m,n [no "l"]
|
||||
* (current public binkplay.exe allows 1=f~k and 2=f~n) */
|
||||
uint32_t head_id = read_u32be(0x00,sf);
|
||||
uint32_t file_size = read_u32le(0x04,sf) + 0x08;
|
||||
uint32_t num_frames = read_u32le(0x08,sf);
|
||||
/* 0x0c: largest frame */
|
||||
/* 0x10: num_frames again (found even for files without audio) */
|
||||
/* 0x14: video width (max 32767, apparently can be negative but no different meaning) */
|
||||
/* 0x18: video height (max 32767) */
|
||||
/* 0x1c: fps dividend (must be set) */
|
||||
/* 0x20: fps divider (must be set)
|
||||
* - ex. 2997/100 = 29.97 fps, 30/1 = 30 fps (common values) */
|
||||
//uint32_t video_flags = read_u32le(0x24,sf); /* (scale, alpha, color modes, etc) */
|
||||
/* 0x28: audio tracks */
|
||||
/* video flags:
|
||||
- F0000000 (bits 28-31): width and height scaling bits (doubled, interlaced, etc)
|
||||
- 00100000 (bit 20): has alpha plane
|
||||
- 00040000 (bit 18): unknown, related to packet format? (seen in some bik2 n+, with and w/o audio)
|
||||
- 00020000 (bit 17): grayscale
|
||||
- 00010000 (bit 16): 12 16b numbers? (seen in binkplay)
|
||||
- 00000010 (bit 4): 1 big number? (seems always set in bik1 k+ bik2 i+, but also set in bik2 g wihtout the number)
|
||||
- 00000004 (bit 2): 6 16b (seen in some bik2 n+)
|
||||
- 00000002 (bit 1): unknown (seen in some bik2 n+, with and w/o audio)
|
||||
(from binkplay, flags 0x04 and 0x10000 can't coexist)
|
||||
*/
|
||||
|
||||
if (file_size != get_streamfile_size(sf))
|
||||
return false;
|
||||
if (num_frames <= 0 || num_frames > BINK_MAX_FRAMES)
|
||||
return false; /* (avoids big allocs below) */
|
||||
|
||||
if (read_32bitLE(0x04,sf) + 0x08 != filesize)
|
||||
goto fail;
|
||||
|
||||
num_frames = (uint32_t)read_32bitLE(0x08,sf);
|
||||
if (num_frames == 0 || num_frames > 0x100000) goto fail; /* something must be off (avoids big allocs below) */
|
||||
uint32_t signature = head_id & 0xffffff00;
|
||||
uint8_t revision = head_id & 0xFF;
|
||||
int sample_rate, channels;
|
||||
uint16_t audio_flags;
|
||||
|
||||
/* multichannel/multilanguage audio is usually N streams of stereo/mono, no way to know channel layout */
|
||||
total_subsongs = read_32bitLE(0x28,sf);
|
||||
int total_subsongs = read_s32le(0x28,sf);
|
||||
if (total_subsongs < 1) {
|
||||
vgm_logi("BIK: no audio in movie (ignore)\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (target_subsong == 0) target_subsong = 1;
|
||||
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs > 255) goto fail;
|
||||
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs > BINK_MAX_TRACKS) goto fail;
|
||||
|
||||
|
||||
/* find stream info and position in offset table */
|
||||
cur_offset = 0x2c;
|
||||
if ((signature == 0x42494B00 && (revision == 0x6b)) || /* k */
|
||||
(signature == 0x4B423200 && (revision == 0x69 || revision == 0x6a || revision == 0x6b))) /* i,j,k */
|
||||
cur_offset += 0x04; /* unknown v2 header field */
|
||||
cur_offset += 0x04*total_subsongs; /* skip streams max packet bytes */
|
||||
sample_rate = (uint16_t)read_16bitLE(cur_offset+0x04*(target_subsong-1)+0x00,sf);
|
||||
channels = (uint16_t)read_16bitLE(cur_offset+0x04*(target_subsong-1)+0x02,sf) & 0x2000 ? 2 : 1; /* stereo flag */
|
||||
cur_offset += 0x04*total_subsongs; /* skip streams info */
|
||||
cur_offset += 0x04*total_subsongs; /* skip streams ids */
|
||||
|
||||
if ((signature == get_id32be("BIK\0") && revision >= 'k') || (signature == get_id32be("KB2\0") && revision >= 'i'))
|
||||
cur_offset += 0x04;
|
||||
|
||||
//if (video_flags & 0x000004) /* only n+? */
|
||||
// cur_offset += 0x0c; /* 6 number: s16 * 0.00003051850944757462 (video stuff?) */
|
||||
//if (video_flags & 0x010000)
|
||||
// cur_offset += 0x18;
|
||||
//if (video_flags & 0x000010) /* only i+? */
|
||||
// cur_offset += 0x04;
|
||||
|
||||
cur_offset += 0x04 * total_subsongs; /* skip streams max packet bytes */
|
||||
sample_rate = read_u16le(cur_offset + 0x04 * (target_subsong - 1) + 0x00, sf);
|
||||
audio_flags = read_u16le(cur_offset + 0x04 * (target_subsong - 1) + 0x02, sf);
|
||||
cur_offset += 0x04 * total_subsongs; /* skip streams info */
|
||||
cur_offset += 0x04 * total_subsongs; /* skip streams ids */
|
||||
|
||||
/* audio flags:
|
||||
- 8000 (bit 15): unknown (observed in some samples)
|
||||
- 4000 (bit 14): unknown (same file may have it set for none/some/all)
|
||||
- 2000 (bit 13): stereo flag
|
||||
- 1000 (bit 12): audio type (1=DCT, 0=FFT)
|
||||
*/
|
||||
channels = audio_flags & 0x2000 ? 2 : 1;
|
||||
|
||||
|
||||
/* read frame offsets in a buffer, to avoid fseeking to the table back and forth */
|
||||
offsets = malloc(sizeof(uint32_t) * num_frames);
|
||||
offsets = malloc(num_frames * sizeof(uint32_t));
|
||||
if (!offsets) goto fail;
|
||||
|
||||
for (i=0; i < num_frames; i++) {
|
||||
offsets[i] = read_32bitLE(cur_offset,sf) & 0xFFFFFFFE; /* mask first bit (= keyframe) */
|
||||
for (int i = 0; i < num_frames; i++) {
|
||||
offsets[i] = read_u32le(cur_offset, sf) & 0xFFFFFFFE; /* mask first bit (= keyframe) */
|
||||
cur_offset += 0x4;
|
||||
|
||||
if (offsets[i] > filesize) goto fail;
|
||||
if (offsets[i] > file_size)
|
||||
goto fail;
|
||||
}
|
||||
/* after the last index is the file size, validate just in case */
|
||||
if (read_32bitLE(cur_offset,sf) != filesize) goto fail;
|
||||
if (read_u32le(cur_offset,sf) != file_size)
|
||||
goto fail;
|
||||
|
||||
/* read each frame header and sum all samples
|
||||
* a frame has N audio packets with a header (one per stream) + video packet */
|
||||
for (i=0; i < num_frames; i++) {
|
||||
for (int i = 0; i < num_frames; i++) {
|
||||
cur_offset = offsets[i];
|
||||
|
||||
/* read audio packet headers per stream */
|
||||
for (j=0; j < total_subsongs; j++) {
|
||||
uint32_t ap_size = read_32bitLE(cur_offset+0x00,sf); /* not counting this int */
|
||||
for (int j = 0; j < total_subsongs; j++) {
|
||||
uint32_t ap_size = read_u32le(cur_offset + 0x00,sf); /* not counting this int */
|
||||
|
||||
if (j == target_subsong-1) {
|
||||
if (j + 1 == target_subsong) {
|
||||
stream_size += 0x04 + ap_size;
|
||||
if (ap_size > 0)
|
||||
num_samples_b += read_32bitLE(cur_offset+0x04,sf); /* decoded samples in bytes */
|
||||
num_samples_b += read_u32le(cur_offset + 0x04,sf); /* decoded samples in bytes */
|
||||
break; /* next frame */
|
||||
}
|
||||
else { /* next stream packet or frame */
|
||||
@ -140,8 +188,6 @@ static int bink_get_info(STREAMFILE* sf, int target_subsong, int* p_total_subson
|
||||
}
|
||||
}
|
||||
|
||||
free(offsets);
|
||||
|
||||
|
||||
if (p_total_subsongs) *p_total_subsongs = total_subsongs;
|
||||
if (p_stream_size) *p_stream_size = stream_size;
|
||||
@ -150,9 +196,9 @@ static int bink_get_info(STREAMFILE* sf, int target_subsong, int* p_total_subson
|
||||
//todo returns a few more samples (~48) than binkconv.exe?
|
||||
if (p_num_samples) *p_num_samples = num_samples_b / (2 * channels);
|
||||
|
||||
return 1;
|
||||
|
||||
free(offsets);
|
||||
return true;
|
||||
fail:
|
||||
free(offsets);
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
@ -1,84 +0,0 @@
|
||||
#include "meta.h"
|
||||
#include "../util.h"
|
||||
|
||||
/* ASD - found in Miss Moonlight (DC) */
|
||||
VGMSTREAM * init_vgmstream_dc_asd(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
char filename[PATH_LIMIT];
|
||||
off_t start_offset;
|
||||
int loop_flag;
|
||||
int channel_count;
|
||||
int sample_rate;
|
||||
|
||||
/* check extension, case insensitive */
|
||||
streamFile->get_name(streamFile,filename,sizeof(filename));
|
||||
if (strcasecmp("asd",filename_extension(filename))) goto fail;
|
||||
|
||||
/* We have no "Magic" words in this header format, so we have to do some,
|
||||
other checks, it seems the samplecount is stored twice in the header,
|
||||
we'll compare it... */
|
||||
if (read_32bitLE(0x0,streamFile) != read_32bitLE(0x4,streamFile))
|
||||
goto fail;
|
||||
|
||||
/* ableton related files can be incorrectly sent to this which don't have
|
||||
the same style of header and unless we null check we get a divide by zero
|
||||
crash... */
|
||||
sample_rate = read_32bitLE(0xC,streamFile);
|
||||
if (!sample_rate)
|
||||
goto fail;
|
||||
|
||||
/* compare the frequency with the bitrate, if it doesn't match we'll close
|
||||
the vgmstream... */
|
||||
if (read_32bitLE(0x10,streamFile)/sample_rate != (uint16_t)read_16bitLE(0xA,streamFile)*2)
|
||||
goto fail;
|
||||
|
||||
loop_flag = 0;
|
||||
channel_count = read_16bitLE(0x0A,streamFile);
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
/* fill in the vital statistics */
|
||||
start_offset = get_streamfile_size(streamFile) - read_32bitLE(0x0,streamFile);
|
||||
vgmstream->channels = channel_count;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->coding_type = coding_PCM16LE;
|
||||
vgmstream->num_samples = read_32bitLE(0x0,streamFile)/2/channel_count;
|
||||
if (loop_flag) {
|
||||
vgmstream->loop_start_sample = 0;
|
||||
vgmstream->loop_end_sample = read_32bitLE(0x0,streamFile)/2/channel_count;
|
||||
}
|
||||
|
||||
vgmstream->meta_type = meta_DC_ASD;
|
||||
|
||||
if (vgmstream->channels == 1) {
|
||||
vgmstream->layout_type = layout_none;
|
||||
} else if (vgmstream->channels == 2) {
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x2;
|
||||
}
|
||||
|
||||
/* open the file for reading */
|
||||
{
|
||||
int i;
|
||||
STREAMFILE * file;
|
||||
file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
|
||||
if (!file) goto fail;
|
||||
for (i=0;i<channel_count;i++) {
|
||||
vgmstream->ch[i].streamfile = file;
|
||||
|
||||
vgmstream->ch[i].channel_start_offset=
|
||||
vgmstream->ch[i].offset=start_offset+
|
||||
vgmstream->interleave_block_size*i;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return vgmstream;
|
||||
|
||||
/* clean up anything we may have opened */
|
||||
fail:
|
||||
if (vgmstream) close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
260
src/meta/ghs.c
260
src/meta/ghs.c
@ -1,96 +1,145 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
#include "../util/endianness.h"
|
||||
|
||||
typedef enum { XMA2, ATRAC9 } gtd_codec;
|
||||
//TODO rename gtd to ghs
|
||||
/* GHS - Hexadrive's HexaEngine games [Knights Contract (X360), Valhalla Knights 3 (Vita)] */
|
||||
typedef enum { PCM16LE, MSADPCM, XMA2, ATRAC9 } gtd_codec_t;
|
||||
|
||||
static void read_name(VGMSTREAM* vgmstream, STREAMFILE* sf, uint32_t offset);
|
||||
|
||||
|
||||
/* GHS - Hexadrive's HexaEngine games [Gunslinger Stratos (AC), Knights Contract (X360), Valhalla Knights 3 (Vita)] */
|
||||
VGMSTREAM* init_vgmstream_ghs(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t start_offset, chunk_offset, stpr_offset, name_offset = 0, loop_start_offset, loop_end_offset;
|
||||
size_t data_size, chunk_size;
|
||||
uint32_t stream_offset, stream_size, stpr_offset = 0, loop_start_offset = 0, loop_end_offset = 0;
|
||||
uint32_t chunk_offset, chunk_size = 0, at9_config_data = 0, block_size = 0;
|
||||
int loop_flag, channels, sample_rate;
|
||||
int num_samples, loop_start_sample, loop_end_sample;
|
||||
uint32_t at9_config_data;
|
||||
gtd_codec codec;
|
||||
int32_t num_samples, loop_start_sample, loop_end_sample;
|
||||
gtd_codec_t codec;
|
||||
int total_subsongs = 0, target_subsong = sf->stream_index;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!is_id32be(0x00,sf, "GHS "))
|
||||
goto fail;
|
||||
if ( !check_extensions(sf,"gtd"))
|
||||
return NULL;
|
||||
if (!check_extensions(sf,"gtd"))
|
||||
return NULL;
|
||||
|
||||
int big_endian = guess_endian32(0x04,sf);
|
||||
read_u32_t read_u32 = big_endian ? read_u32be : read_u32le;
|
||||
read_u16_t read_u16 = big_endian ? read_u16be : read_u16le;
|
||||
|
||||
int is_old = 0x34 + read_u32le(0x30,sf) + read_u32le(0x0c,sf) == get_streamfile_size(sf);
|
||||
|
||||
total_subsongs = read_u32(0x04, sf); /* seen in sfx packs inside .ged */
|
||||
if (!check_subsongs(&target_subsong, total_subsongs))
|
||||
return NULL;
|
||||
|
||||
/* not seen */
|
||||
if (target_subsong > 1 && is_old)
|
||||
goto fail;
|
||||
|
||||
|
||||
/* header type, not formally specified */
|
||||
if (read_32bitBE(0x04,sf) == 1 && read_16bitBE(0x0C,sf) == 0x0166) { /* XMA2 */
|
||||
/* 0x08(4): seek table size */
|
||||
chunk_offset = 0x0c; /* custom header with a "fmt " data chunk inside */
|
||||
chunk_size = 0x34;
|
||||
|
||||
channels = read_16bitBE(chunk_offset+0x02,sf);
|
||||
sample_rate = read_32bitBE(chunk_offset+0x04,sf);
|
||||
xma2_parse_fmt_chunk_extra(sf, chunk_offset, &loop_flag, &num_samples, &loop_start_sample, &loop_end_sample, 1);
|
||||
|
||||
start_offset = read_32bitBE(0x58,sf); /* always 0x800 */
|
||||
data_size = read_32bitBE(0x5c,sf);
|
||||
/* 0x34(18): null, 0x54(4): seek table offset, 0x58(4): seek table size, 0x5c(8): null, 0x64: seek table */
|
||||
|
||||
stpr_offset = read_32bitBE(chunk_offset+0x54,sf) + read_32bitBE(chunk_offset+0x58,sf);
|
||||
if (is_id32be(stpr_offset,sf, "STPR")) {
|
||||
/* SRPR encases the original "S_P_STH" header (no data) */
|
||||
name_offset = stpr_offset + 0xB8; /* there are offsets fields but seems to work */
|
||||
/* header version, not formally specified */
|
||||
if (!is_old) {
|
||||
/* 0x08: size of all seek tables (XMA2, all tables go together after headers) / null */
|
||||
uint32_t offset = 0x0c + (target_subsong - 1) * 0x64;
|
||||
|
||||
int format = read_u16(offset + 0x00,sf);
|
||||
if (format == 0x0001)
|
||||
codec = PCM16LE; /* GS bgm */
|
||||
else if (format == 0x0002)
|
||||
codec = MSADPCM; /* GS sfx */
|
||||
else if (format == 0x0166) {
|
||||
codec = XMA2;
|
||||
chunk_offset = offset; /* "fmt " */
|
||||
chunk_size = 0x34;
|
||||
}
|
||||
else {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
codec = XMA2;
|
||||
}
|
||||
else if (0x34 + read_32bitLE(0x30,sf) + read_32bitLE(0x0c,sf) == get_streamfile_size(sf)) { /* ATRAC9 */
|
||||
/* 0x0c: standard fmt chunk (depending on format, reserved with padding up to 0x48 if needed) */
|
||||
channels = read_u16(offset + 0x02,sf);
|
||||
sample_rate = read_u32(offset + 0x04,sf);
|
||||
block_size = read_u16(offset + 0x0c,sf);
|
||||
/* loops can be found at 0x28/2c in PCM16 (also later) */
|
||||
stream_offset = read_u32(offset + 0x4c,sf); /* always 0x800 */
|
||||
stream_size = read_u32(offset + 0x50,sf);
|
||||
/* 0x54: seek table offset (XMA2) / data start */
|
||||
/* 0x58: seek table size (XMA2) / null */
|
||||
loop_start_sample = read_u32(offset + 0x5c,sf); /* null in XMA2 */
|
||||
loop_end_sample = read_u32(offset + 0x60,sf) + loop_start_sample; /* +1? */
|
||||
|
||||
data_size = read_32bitLE(0x0c,sf);
|
||||
start_offset = 0x34 + read_32bitLE(0x30,sf);
|
||||
channels = read_32bitLE(0x10,sf);
|
||||
sample_rate = read_32bitLE(0x14,sf);
|
||||
loop_start_offset = read_32bitLE(0x1c, sf);
|
||||
loop_end_offset = read_32bitLE(0x20, sf);
|
||||
loop_flag = loop_end_offset > loop_start_offset;
|
||||
at9_config_data = read_32bitBE(0x28,sf);
|
||||
/* 0x18-0x28: fixed/unknown values */
|
||||
|
||||
stpr_offset = 0x2c;
|
||||
if (is_id32be(stpr_offset,sf, "STPR")) {
|
||||
/* STPR encases the original "S_P_STH" header (no data) */
|
||||
name_offset = stpr_offset + 0xE8; /* there are offsets fields but seems to work */
|
||||
if (codec == XMA2) {
|
||||
xma2_parse_fmt_chunk_extra(sf, chunk_offset, &loop_flag, &num_samples, &loop_start_sample, &loop_end_sample, 1);
|
||||
}
|
||||
else {
|
||||
loop_flag = loop_end_sample != 0;
|
||||
}
|
||||
|
||||
codec = ATRAC9;
|
||||
stpr_offset = read_u32(offset + 0x54,sf) + read_u32(offset + 0x58,sf);
|
||||
}
|
||||
else {
|
||||
goto fail;
|
||||
}
|
||||
codec = ATRAC9;
|
||||
|
||||
/* 08: always 02? */
|
||||
stream_size = read_u32(0x0c,sf);
|
||||
channels = read_u32(0x10,sf);
|
||||
sample_rate = read_u32(0x14,sf);
|
||||
/* 18: null? */
|
||||
loop_start_offset = read_u32(0x1c,sf);
|
||||
loop_end_offset = read_u32(0x20,sf);
|
||||
/* 24: channel layout? */
|
||||
at9_config_data = read_u32be(0x28,sf);
|
||||
/* 2c: STPR */
|
||||
stream_offset = read_u32(0x30,sf) + 0x34;
|
||||
loop_flag = loop_end_offset > loop_start_offset;
|
||||
|
||||
stpr_offset = 0x2c;
|
||||
}
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_GHS;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->loop_start_sample = loop_start_sample;
|
||||
vgmstream->loop_end_sample = loop_end_sample;
|
||||
vgmstream->meta_type = meta_GHS;
|
||||
if (name_offset) //encoding is Shift-Jis in some PSV files
|
||||
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,sf);
|
||||
vgmstream->loop_end_sample = loop_end_sample;
|
||||
|
||||
vgmstream->num_streams = total_subsongs;
|
||||
vgmstream->stream_size = stream_size;
|
||||
read_name(vgmstream, sf, stpr_offset);
|
||||
|
||||
switch(codec) {
|
||||
case PCM16LE:
|
||||
vgmstream->coding_type = coding_PCM16LE;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = block_size / channels;
|
||||
|
||||
vgmstream->num_samples = pcm16_bytes_to_samples(stream_size, channels);
|
||||
|
||||
break;
|
||||
|
||||
case MSADPCM:
|
||||
vgmstream->coding_type = coding_MSADPCM;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->frame_size = block_size;
|
||||
|
||||
vgmstream->num_samples = msadpcm_bytes_to_samples(stream_size, block_size, channels);
|
||||
|
||||
break;
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
case XMA2:
|
||||
vgmstream->codec_data = init_ffmpeg_xma_chunk(sf, start_offset, data_size, chunk_offset, chunk_size);
|
||||
vgmstream->codec_data = init_ffmpeg_xma_chunk(sf, stream_offset, stream_size, chunk_offset, chunk_size);
|
||||
if ( !vgmstream->codec_data ) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
vgmstream->num_samples = num_samples;
|
||||
|
||||
xma_fix_raw_samples(vgmstream, sf, start_offset, data_size, chunk_offset, 1,1);
|
||||
xma_fix_raw_samples(vgmstream, sf, stream_offset, stream_size, chunk_offset, 1,1);
|
||||
break;
|
||||
#endif
|
||||
#ifdef VGM_USE_ATRAC9
|
||||
@ -105,13 +154,12 @@ VGMSTREAM* init_vgmstream_ghs(STREAMFILE* sf) {
|
||||
vgmstream->coding_type = coding_ATRAC9;
|
||||
vgmstream->layout_type = layout_none;
|
||||
if (loop_flag) {
|
||||
vgmstream->loop_start_sample = atrac9_bytes_to_samples(loop_start_offset - start_offset, vgmstream->codec_data);
|
||||
vgmstream->loop_end_sample = atrac9_bytes_to_samples(loop_end_offset - start_offset, vgmstream->codec_data);
|
||||
vgmstream->loop_start_sample = atrac9_bytes_to_samples(loop_start_offset - stream_offset, vgmstream->codec_data);
|
||||
vgmstream->loop_end_sample = atrac9_bytes_to_samples(loop_end_offset - stream_offset, vgmstream->codec_data);
|
||||
}
|
||||
vgmstream->num_samples = atrac9_bytes_to_samples(data_size, vgmstream->codec_data);
|
||||
vgmstream->num_samples = atrac9_bytes_to_samples(stream_size, vgmstream->codec_data);
|
||||
break;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
default:
|
||||
@ -119,7 +167,7 @@ VGMSTREAM* init_vgmstream_ghs(STREAMFILE* sf) {
|
||||
}
|
||||
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
if (!vgmstream_open_stream(vgmstream, sf, stream_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
fail:
|
||||
@ -131,14 +179,14 @@ fail:
|
||||
VGMSTREAM* init_vgmstream_s_p_sth(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
STREAMFILE* temp_sf = NULL;
|
||||
uint32_t subfile_offset, subfile_size, name_offset;
|
||||
uint32_t subfile_offset, subfile_size, stpr_offset;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!is_id64be(0x00,sf,"S_P_STH\x01"))
|
||||
goto fail;
|
||||
return NULL;
|
||||
if (!check_extensions(sf,"gtd"))
|
||||
goto fail;
|
||||
return NULL;
|
||||
|
||||
subfile_offset = read_u32be(0x08, sf);
|
||||
subfile_size = get_streamfile_size(sf) - subfile_offset;
|
||||
@ -150,8 +198,52 @@ VGMSTREAM* init_vgmstream_s_p_sth(STREAMFILE* sf) {
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_GHS;
|
||||
name_offset = 0xB0; /* there are offsets fields but seems to work */
|
||||
read_string(vgmstream->stream_name, STREAM_NAME_SIZE, name_offset, sf);
|
||||
|
||||
stpr_offset = 0x00;
|
||||
read_name(vgmstream, sf, stpr_offset);
|
||||
|
||||
close_streamfile(temp_sf);
|
||||
return vgmstream;
|
||||
fail:
|
||||
close_streamfile(temp_sf);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* S_PACK - Hexadrive's HexaEngine games [Gunslinger Stratos (PC), Knights Contract (X360)] */
|
||||
VGMSTREAM* init_vgmstream_s_pack(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
STREAMFILE* temp_sf = NULL;
|
||||
|
||||
/* checks */
|
||||
if (!is_id64be(0x00,sf,"S_PACK\x00\x00") && !is_id64be(0x00,sf,"S_PACK\x00\x01")) /* v1: KC */
|
||||
return NULL;
|
||||
if (!check_extensions(sf,"ged"))
|
||||
return NULL;
|
||||
/* 0x08: file size */
|
||||
/* 0x0c-0x20: null */
|
||||
|
||||
int big_endian = guess_endian32(0x20,sf);
|
||||
read_u32_t read_u32 = big_endian ? read_u32be : read_u32le;
|
||||
|
||||
uint32_t offset = read_u32(0x20, sf); /* offset to minitable */
|
||||
/* 0x24: number of chunks in S_P_H? */
|
||||
/* 0x28: number of entries in minitable */
|
||||
|
||||
/* minitable */
|
||||
/* 0x00: offset to "S_P_H", that seems to have cuenames (may have more cues than waves though) */
|
||||
uint32_t subfile_offset = read_u32(offset + 0x04, sf); /* may be null or S_CHR_M (no GHS in file) */
|
||||
uint32_t schar_offset = read_u32(offset + 0x08, sf); /* S_CHR_M seen in KC, some kind of cues */
|
||||
|
||||
if (schar_offset == 0)
|
||||
schar_offset = get_streamfile_size(sf);
|
||||
uint32_t subfile_size = schar_offset - subfile_offset;
|
||||
|
||||
temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, "gtd");
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
vgmstream = init_vgmstream_ghs(temp_sf);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
close_streamfile(temp_sf);
|
||||
return vgmstream;
|
||||
@ -161,3 +253,41 @@ fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void read_name(VGMSTREAM* vgmstream, STREAMFILE* sf, uint32_t offset) {
|
||||
uint32_t name_offset = 0;
|
||||
|
||||
//if (!offset) //may be 0 in PS3
|
||||
// return;
|
||||
|
||||
if (is_id32be(offset,sf, "STPR"))
|
||||
offset += 0x08;
|
||||
|
||||
if (is_id64be(offset + 0x00,sf, "S_P_STH\0")) { /* stream header v0: GS, VK3 */
|
||||
/* 08 subheader size */
|
||||
/* 0c version/count? */
|
||||
/* 10 version/count? */
|
||||
/* 20 offset to header configs */
|
||||
/* 24 hash? */
|
||||
/* 2c -1? */
|
||||
/* 30 1? */
|
||||
if (!is_id64be(offset + 0x40,sf, "stream\0\0"))
|
||||
return;
|
||||
/* 50 bank name */
|
||||
/* 70+ header configs (some are repeats from GHS) */
|
||||
/* E0 file name .gtd */
|
||||
|
||||
name_offset = offset + 0xE0; /* show file name though actual files are already (bankname)_(filename).gtd */
|
||||
}
|
||||
else if (is_id64be(offset + 0x00,sf, "S_P_STH\1")) { /* stream header v1: KC */
|
||||
/* same as above, except no stream+bank name, so at 0x40 are header configs */
|
||||
name_offset = offset + 0xB0;
|
||||
}
|
||||
|
||||
/* optional (only found in streams, sfx packs that point to GHS have cue names) */
|
||||
if (!name_offset)
|
||||
return;
|
||||
|
||||
//TO-DO: Shift-Jis in some Vita files
|
||||
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,sf);
|
||||
}
|
||||
|
@ -1275,6 +1275,9 @@ static const hcakey_info hcakey_list[] = {
|
||||
// Girls' Frontline 2: Exilium (PC)
|
||||
{7152097263845921}, // 001968CB68CF8221
|
||||
|
||||
// Girls' Frontline 2: Exilium (PC)
|
||||
{4079616028775461768}, // 389DB529D726B388
|
||||
|
||||
};
|
||||
|
||||
#endif/*_HCA_KEYS_H_*/
|
||||
|
@ -300,7 +300,7 @@ VGMSTREAM* init_vgmstream_mus_krome(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM * init_vgmstream_rsd(STREAMFILE * streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_dc_asd(STREAMFILE * streamFile);
|
||||
VGMSTREAM* init_vgmstream_asd_naxat(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM* init_vgmstream_spsd(STREAMFILE* sf);
|
||||
|
||||
@ -580,6 +580,7 @@ VGMSTREAM * init_vgmstream_mc3(STREAMFILE *streamFile);
|
||||
|
||||
VGMSTREAM* init_vgmstream_ghs(STREAMFILE* sf);
|
||||
VGMSTREAM* init_vgmstream_s_p_sth(STREAMFILE* sf);
|
||||
VGMSTREAM* init_vgmstream_s_pack(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM* init_vgmstream_aac_triace(STREAMFILE* sf);
|
||||
|
||||
|
@ -48,6 +48,9 @@ static int read_dsp_header_endian(struct dsp_header *header, off_t offset, STREA
|
||||
header->sample_count = get_u32(buf+0x00);
|
||||
if (header->sample_count > 0x10000000)
|
||||
goto fail; /* unlikely to be that big, should catch fourccs */
|
||||
|
||||
/* usually nibbles = size*2 in mono, but interleaved stereo or L+R may use nibbles =~ size (or not), so can't
|
||||
* easily reject files with more nibbles than data (nibbles may be part of the -R file) without redoing L+R handling */
|
||||
header->nibble_count = get_u32(buf+0x04);
|
||||
if (header->nibble_count > 0x10000000)
|
||||
goto fail;
|
||||
|
@ -135,8 +135,9 @@ VGMSTREAM* init_vgmstream_opus_std(STREAMFILE* sf) {
|
||||
|
||||
/* .opus: standard / .lopus: for plugins
|
||||
* .bgm: Cotton Reboot (Switch)
|
||||
* .opu: Ys Memoire: The Oath in Felghana (Switch) */
|
||||
if (!check_extensions(sf,"opus,lopus,bgm,opu"))
|
||||
* .opu: Ys Memoire: The Oath in Felghana (Switch)
|
||||
* .ogg: Trouble Witches Origin (Switch) */
|
||||
if (!check_extensions(sf,"opus,lopus,bgm,opu,ogg,logg"))
|
||||
goto fail;
|
||||
|
||||
offset = 0x00;
|
||||
|
@ -1818,6 +1818,7 @@ static int parse_type_audio(ubi_sb_header* sb, off_t offset, STREAMFILE* sf) {
|
||||
sb->sample_rate = read_32bit(offset + sb->cfg.audio_sample_rate, sf);
|
||||
sb->stream_type = read_32bit(offset + sb->cfg.audio_stream_type, sf);
|
||||
|
||||
//TO-DO a handful of SC:PT PS2 streams have 0 stream offset+size, maybe should set config + allow as dummies (ex. MAPS.SM1 #14191 #14255)
|
||||
if (sb->stream_size == 0) {
|
||||
VGM_LOG("UBI SB: bad stream size\n");
|
||||
goto fail;
|
||||
@ -2414,7 +2415,8 @@ static int parse_offsets(ubi_sb_header* sb, STREAMFILE* sf) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (sb->stream_offset == 0) {
|
||||
/* valid in rare cases with ram-streamed but also external file (SC:PT PS2 > MAPS.RS1)*/
|
||||
if (sb->stream_offset == 0 && !sb->is_external) {
|
||||
VGM_LOG("UBI SM: Failed to find offset for resource %d in subblock %d in map %s\n", sb->header_index, sb->subblock_id, sb->map_name);
|
||||
goto fail;
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ VGMSTREAM* init_vgmstream_xa(STREAMFILE* sf) {
|
||||
}
|
||||
else {
|
||||
/* non-blocked (ISO 2048 mode1/data) or incorrectly ripped: use TXTH */
|
||||
goto fail;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* .xa: common
|
||||
@ -41,7 +41,7 @@ VGMSTREAM* init_vgmstream_xa(STREAMFILE* sf) {
|
||||
* .xai: Quake II (PS1)
|
||||
* (extensionless): bigfiles [Castlevania: Symphony of the Night (PS1)] */
|
||||
if (!check_extensions(sf,"xa,str,pxa,grn,an2,,xai"))
|
||||
goto fail;
|
||||
return NULL;
|
||||
|
||||
/* Proper XA comes in raw (BIN 2352 mode2/form2) CD sectors, that contain XA subheaders.
|
||||
* For headerless XA (ISO 2048 mode1/data) mode use TXTH. */
|
||||
@ -138,15 +138,16 @@ fail:
|
||||
|
||||
|
||||
static int xa_check_format(STREAMFILE *sf, off_t offset, int is_blocked) {
|
||||
uint8_t frame_hdr[0x10];
|
||||
int i, j, sector = 0, skip = 0;
|
||||
off_t test_offset = offset;
|
||||
const size_t sector_size = (is_blocked ? 0x900 : 0x800);
|
||||
const size_t extra_size = (is_blocked ? 0x18 : 0x00);
|
||||
const size_t frame_size = 0x80;
|
||||
const int sector_max = 3;
|
||||
const int skip_max = 32; /* videos interleave 7 or 15 sectors + 1 audio sector, maybe 31 too */
|
||||
|
||||
uint8_t frame_hdr[0x10];
|
||||
int sector = 0, skip = 0;
|
||||
uint32_t test_offset = offset;
|
||||
|
||||
/* test frames inside CD sectors */
|
||||
while (sector < sector_max) {
|
||||
if (is_blocked) {
|
||||
@ -164,11 +165,11 @@ static int xa_check_format(STREAMFILE *sf, off_t offset, int is_blocked) {
|
||||
|
||||
test_offset += extra_size; /* header */
|
||||
|
||||
for (i = 0; i < (sector_size / frame_size); i++) {
|
||||
for (int i = 0; i < (sector_size / frame_size); i++) {
|
||||
read_streamfile(frame_hdr, test_offset, sizeof(frame_hdr), sf);
|
||||
|
||||
/* XA frame checks: filter indexes should be 0..3, and shifts 0..C */
|
||||
for (j = 0; j < 16; j++) {
|
||||
for (int j = 0; j < 16; j++) {
|
||||
uint8_t header = get_u8(frame_hdr + j);
|
||||
if (((header >> 4) & 0xF) > 0x03)
|
||||
goto fail;
|
||||
@ -180,11 +181,10 @@ static int xa_check_format(STREAMFILE *sf, off_t offset, int is_blocked) {
|
||||
if (get_u32be(frame_hdr+0x00) != get_u32be(frame_hdr+0x04) ||
|
||||
get_u32be(frame_hdr+0x08) != get_u32be(frame_hdr+0x0c))
|
||||
goto fail;
|
||||
/* blank frames should always use 0x0c0c0c0c due to how shift works, (in rare/unused file-channels it may be blank though) */
|
||||
if (get_u32be(frame_hdr+0x00) == 0 &&
|
||||
get_u32be(frame_hdr+0x04) == 0 &&
|
||||
get_u32be(frame_hdr+0x08) == 0 &&
|
||||
get_u32be(frame_hdr+0x0c) == 0)
|
||||
/* blank frames should always use 0x0c0c0c0c due to how shift works, (in rare file-channels some frames may be blank though) */
|
||||
if (i == 0 &&
|
||||
get_u32be(frame_hdr+0x00) == 0 && get_u32be(frame_hdr+0x04) == 0 &&
|
||||
get_u32be(frame_hdr+0x08) == 0 && get_u32be(frame_hdr+0x0c) == 0)
|
||||
goto fail;
|
||||
|
||||
test_offset += 0x80;
|
||||
@ -201,7 +201,7 @@ fail:
|
||||
}
|
||||
|
||||
|
||||
#define XA_MAX_CHANNELS 16
|
||||
#define XA_MAX_CHANNELS 32 /* usually 08-16, seen ~24 in Langrisser V (PS1) */
|
||||
|
||||
typedef struct {
|
||||
uint32_t info;
|
||||
@ -270,9 +270,10 @@ static int xa_read_subsongs(STREAMFILE* sf, int target_subsong, uint32_t start,
|
||||
|
||||
if (xa_chan >= XA_MAX_CHANNELS) {
|
||||
VGM_LOG("XA: too many channels: %x\n", xa_chan);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
VGM_ASSERT((xa_submode & 0x01), "XA: end of audio at %x\n", offset); /* rare, signals last sector [Tetris (CD-i)] */
|
||||
//;VGM_ASSERT((xa_submode & 0x01), "XA: end of audio at %x\n", offset); /* rare, signals last sector [Tetris (CD-i), Langrisser V (PS1)] */
|
||||
//;VGM_ASSERT(is_eof, "XA: eof %02x%02x at %x\n", xa_file, xa_chan, offset); /* this sector still has data */
|
||||
//;VGM_ASSERT(!is_audio, "XA: not audio at %x\n", offset);
|
||||
|
||||
|
@ -51,3 +51,11 @@ size_t align_size_to_block(size_t value, size_t block_align) {
|
||||
if (extra_size == 0) return value;
|
||||
return (value + block_align - extra_size);
|
||||
}
|
||||
|
||||
bool check_subsongs(int* target_subsong, int total_subsongs) {
|
||||
if (*target_subsong == 0)
|
||||
*target_subsong = 1;
|
||||
if (*target_subsong < 0 || *target_subsong > total_subsongs || total_subsongs < 1)
|
||||
return false;
|
||||
return true;
|
||||
}
|
@ -53,4 +53,7 @@ void concatn(int length, char * dst, const char * src);
|
||||
|
||||
size_t align_size_to_block(size_t value, size_t block_align);
|
||||
|
||||
/* checks max subsongs and setups target */
|
||||
bool check_subsongs(int* target_subsong, int total_subsongs);
|
||||
|
||||
#endif
|
||||
|
@ -127,7 +127,6 @@ init_vgmstream_t init_vgmstream_functions[] = {
|
||||
init_vgmstream_ngc_pdt_split,
|
||||
init_vgmstream_ngc_pdt,
|
||||
init_vgmstream_mus_krome,
|
||||
init_vgmstream_dc_asd,
|
||||
init_vgmstream_spsd,
|
||||
init_vgmstream_rsd,
|
||||
init_vgmstream_bgw,
|
||||
@ -524,6 +523,7 @@ init_vgmstream_t init_vgmstream_functions[] = {
|
||||
init_vgmstream_adm2,
|
||||
init_vgmstream_nxof,
|
||||
init_vgmstream_gwb_gwd,
|
||||
init_vgmstream_s_pack,
|
||||
|
||||
/* lower priority metas (no clean header identity, somewhat ambiguous, or need extension/companion file to identify) */
|
||||
init_vgmstream_scd_pcm,
|
||||
@ -535,6 +535,7 @@ init_vgmstream_t init_vgmstream_functions[] = {
|
||||
init_vgmstream_seb,
|
||||
init_vgmstream_tgc,
|
||||
init_vgmstream_ivaud,
|
||||
init_vgmstream_asd_naxat,
|
||||
/* need companion files */
|
||||
init_vgmstream_pos,
|
||||
init_vgmstream_sli_loops,
|
||||
|
@ -370,11 +370,11 @@ typedef enum {
|
||||
meta_FAG, /* Jackie Chan - Stuntmaster */
|
||||
meta_PS2_MIHB, /* Merged MIH+MIB */
|
||||
meta_NGC_PDT, /* Mario Party 6 */
|
||||
meta_DC_ASD, /* Miss Moonligh */
|
||||
meta_ASD_NAXAT,
|
||||
meta_SPSD,
|
||||
meta_RSD,
|
||||
meta_PS2_ASS, /* ASS */
|
||||
meta_SEG, /* Eragon */
|
||||
meta_PS2_ASS,
|
||||
meta_SEG,
|
||||
meta_NDS_STRM_FFTA2, /* Final Fantasy Tactics A2 */
|
||||
meta_KNON,
|
||||
meta_ZWDSP, /* Zack and Wiki */
|
||||
|
Loading…
Reference in New Issue
Block a user