Updated VGMStream to r1667-70-g3f281cdf

CQTexperiment
Christopher Snowhill 2021-11-01 18:18:09 -07:00 committed by Christopher Snowhill
parent d522730fd3
commit a93510dd8b
60 changed files with 2503 additions and 1737 deletions

View File

@ -233,6 +233,11 @@
8351F32D2212B57000A606E4 /* 208.c in Sources */ = {isa = PBXBuildFile; fileRef = 8351F32A2212B57000A606E4 /* 208.c */; };
8351F32E2212B57000A606E4 /* ubi_bao_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 8351F32B2212B57000A606E4 /* ubi_bao_streamfile.h */; };
8351F32F2212B57000A606E4 /* dsf.c in Sources */ = {isa = PBXBuildFile; fileRef = 8351F32C2212B57000A606E4 /* dsf.c */; };
835B9B8F2730BF2D00F87EE3 /* ast_mv.c in Sources */ = {isa = PBXBuildFile; fileRef = 835B9B8A2730BF2C00F87EE3 /* ast_mv.c */; };
835B9B902730BF2D00F87EE3 /* ast_mmv.c in Sources */ = {isa = PBXBuildFile; fileRef = 835B9B8B2730BF2C00F87EE3 /* ast_mmv.c */; };
835B9B912730BF2D00F87EE3 /* lopu_fb.c in Sources */ = {isa = PBXBuildFile; fileRef = 835B9B8C2730BF2C00F87EE3 /* lopu_fb.c */; };
835B9B922730BF2D00F87EE3 /* hca_bf.h in Headers */ = {isa = PBXBuildFile; fileRef = 835B9B8D2730BF2D00F87EE3 /* hca_bf.h */; };
835B9B932730BF2D00F87EE3 /* lpcm_shade.c in Sources */ = {isa = PBXBuildFile; fileRef = 835B9B8E2730BF2D00F87EE3 /* lpcm_shade.c */; };
835C883622CC17BE001B4B3F /* bwav.c in Sources */ = {isa = PBXBuildFile; fileRef = 835C883122CC17BD001B4B3F /* bwav.c */; };
835C883722CC17BE001B4B3F /* ogg_vorbis_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 835C883522CC17BE001B4B3F /* ogg_vorbis_streamfile.h */; };
836C052B23F62F1A00FA07C7 /* libatrac9.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 835FC6C623F62AEF006960FA /* libatrac9.framework */; };
@ -346,7 +351,6 @@
836F6FCA18BDC2190095E648 /* ps2_adm.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E8E18BDC2180095E648 /* ps2_adm.c */; };
836F6FCB18BDC2190095E648 /* ads.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E8F18BDC2180095E648 /* ads.c */; };
836F6FCD18BDC2190095E648 /* ps2_ass.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E9118BDC2180095E648 /* ps2_ass.c */; };
836F6FCE18BDC2190095E648 /* ps2_ast.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E9218BDC2180095E648 /* ps2_ast.c */; };
836F6FD018BDC2190095E648 /* ps2_b1s.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E9418BDC2180095E648 /* ps2_b1s.c */; };
836F6FD118BDC2190095E648 /* ps2_bg00.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E9518BDC2180095E648 /* ps2_bg00.c */; };
836F6FD218BDC2190095E648 /* ps2_bmdx.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E9618BDC2180095E648 /* ps2_bmdx.c */; };
@ -363,7 +367,6 @@
836F6FE018BDC2190095E648 /* ps2_joe.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EA418BDC2180095E648 /* ps2_joe.c */; };
836F6FE218BDC2190095E648 /* ps2_kces.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EA618BDC2180095E648 /* ps2_kces.c */; };
836F6FE418BDC2190095E648 /* ps2_leg.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EA818BDC2180095E648 /* ps2_leg.c */; };
836F6FE518BDC2190095E648 /* ps2_lpcm.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EA918BDC2180095E648 /* ps2_lpcm.c */; };
836F6FE618BDC2190095E648 /* ps2_mcg.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EAA18BDC2180095E648 /* ps2_mcg.c */; };
836F6FE818BDC2190095E648 /* ps2_mic.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EAC18BDC2180095E648 /* ps2_mic.c */; };
836F6FE918BDC2190095E648 /* ps2_mihb.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EAD18BDC2180095E648 /* ps2_mihb.c */; };
@ -519,7 +522,6 @@
83997F5B22D9569E00633184 /* rad.c in Sources */ = {isa = PBXBuildFile; fileRef = 83997F5722D9569E00633184 /* rad.c */; };
839B54521EEE1D9600048A2D /* ngc_ulw.c in Sources */ = {isa = PBXBuildFile; fileRef = 831BD11F1EEE1CF200198540 /* ngc_ulw.c */; };
839C3D27270D49FF00E13653 /* lpcm_fb.c in Sources */ = {isa = PBXBuildFile; fileRef = 839C3D22270D49FF00E13653 /* lpcm_fb.c */; };
839C3D28270D49FF00E13653 /* lopu.c in Sources */ = {isa = PBXBuildFile; fileRef = 839C3D26270D49FF00E13653 /* lopu.c */; };
839E21E01F2EDAF100EE54D7 /* vorbis_custom_data_fsb.h in Headers */ = {isa = PBXBuildFile; fileRef = 839E21D61F2EDAF000EE54D7 /* vorbis_custom_data_fsb.h */; };
839E21E11F2EDAF100EE54D7 /* vorbis_custom_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 839E21D71F2EDAF000EE54D7 /* vorbis_custom_decoder.c */; };
839E21E21F2EDAF100EE54D7 /* vorbis_custom_utils_wwise.c in Sources */ = {isa = PBXBuildFile; fileRef = 839E21D81F2EDAF000EE54D7 /* vorbis_custom_utils_wwise.c */; };
@ -1045,6 +1047,11 @@
8351F32A2212B57000A606E4 /* 208.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = 208.c; sourceTree = "<group>"; };
8351F32B2212B57000A606E4 /* ubi_bao_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ubi_bao_streamfile.h; sourceTree = "<group>"; };
8351F32C2212B57000A606E4 /* dsf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dsf.c; sourceTree = "<group>"; };
835B9B8A2730BF2C00F87EE3 /* ast_mv.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ast_mv.c; sourceTree = "<group>"; };
835B9B8B2730BF2C00F87EE3 /* ast_mmv.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ast_mmv.c; sourceTree = "<group>"; };
835B9B8C2730BF2C00F87EE3 /* lopu_fb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = lopu_fb.c; sourceTree = "<group>"; };
835B9B8D2730BF2D00F87EE3 /* hca_bf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = hca_bf.h; sourceTree = "<group>"; };
835B9B8E2730BF2D00F87EE3 /* lpcm_shade.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = lpcm_shade.c; sourceTree = "<group>"; };
835C883122CC17BD001B4B3F /* bwav.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bwav.c; sourceTree = "<group>"; };
835C883522CC17BE001B4B3F /* ogg_vorbis_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ogg_vorbis_streamfile.h; sourceTree = "<group>"; };
835FC6C123F62AEE006960FA /* libatrac9.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = libatrac9.xcodeproj; path = ../libatrac9/libatrac9.xcodeproj; sourceTree = "<group>"; };
@ -1159,7 +1166,6 @@
836F6E8E18BDC2180095E648 /* ps2_adm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_adm.c; sourceTree = "<group>"; };
836F6E8F18BDC2180095E648 /* ads.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ads.c; sourceTree = "<group>"; };
836F6E9118BDC2180095E648 /* ps2_ass.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_ass.c; sourceTree = "<group>"; };
836F6E9218BDC2180095E648 /* ps2_ast.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_ast.c; sourceTree = "<group>"; };
836F6E9418BDC2180095E648 /* ps2_b1s.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_b1s.c; sourceTree = "<group>"; };
836F6E9518BDC2180095E648 /* ps2_bg00.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_bg00.c; sourceTree = "<group>"; };
836F6E9618BDC2180095E648 /* ps2_bmdx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_bmdx.c; sourceTree = "<group>"; };
@ -1176,7 +1182,6 @@
836F6EA418BDC2180095E648 /* ps2_joe.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_joe.c; sourceTree = "<group>"; };
836F6EA618BDC2180095E648 /* ps2_kces.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_kces.c; sourceTree = "<group>"; };
836F6EA818BDC2180095E648 /* ps2_leg.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_leg.c; sourceTree = "<group>"; };
836F6EA918BDC2180095E648 /* ps2_lpcm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_lpcm.c; sourceTree = "<group>"; };
836F6EAA18BDC2180095E648 /* ps2_mcg.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_mcg.c; sourceTree = "<group>"; };
836F6EAC18BDC2180095E648 /* ps2_mic.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_mic.c; sourceTree = "<group>"; };
836F6EAD18BDC2180095E648 /* ps2_mihb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_mihb.c; sourceTree = "<group>"; };
@ -1331,7 +1336,6 @@
8399335E2591E8C0001855AF /* sbk.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sbk.c; sourceTree = "<group>"; };
83997F5722D9569E00633184 /* rad.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = rad.c; sourceTree = "<group>"; };
839C3D22270D49FF00E13653 /* lpcm_fb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = lpcm_fb.c; sourceTree = "<group>"; };
839C3D26270D49FF00E13653 /* lopu.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = lopu.c; sourceTree = "<group>"; };
839E21D61F2EDAF000EE54D7 /* vorbis_custom_data_fsb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vorbis_custom_data_fsb.h; sourceTree = "<group>"; };
839E21D71F2EDAF000EE54D7 /* vorbis_custom_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vorbis_custom_decoder.c; sourceTree = "<group>"; };
839E21D81F2EDAF000EE54D7 /* vorbis_custom_utils_wwise.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vorbis_custom_utils_wwise.c; sourceTree = "<group>"; };
@ -1827,6 +1831,8 @@
834FE0C7215C79E7000A5D3D /* apc.c */,
836F6E3418BDC2180095E648 /* apple_caff.c */,
8315958820FEC83F007002F0 /* asf.c */,
835B9B8B2730BF2C00F87EE3 /* ast_mmv.c */,
835B9B8A2730BF2C00F87EE3 /* ast_mv.c */,
836F6E3518BDC2180095E648 /* ast.c */,
8306B0D520984590000302D4 /* atsl.c */,
83A21F7C201D897F000F04B9 /* atx.c */,
@ -1919,6 +1925,7 @@
83709DFF1ECBC1A4005C03D3 /* gtd.c */,
8342469020C4D22F00926E48 /* h4m.c */,
836F6E5218BDC2180095E648 /* halpst.c */,
835B9B8D2730BF2D00F87EE3 /* hca_bf.h */,
832BF81A21E0514A006F50F1 /* hca_keys_awb.h */,
83AA5D211F6E2F9C0020821C /* hca_keys.h */,
8323894F1D2246C300482226 /* hca.c */,
@ -1949,8 +1956,9 @@
83D20074248DDB760048BD24 /* ktsr.c */,
830EBE122004656E0023AA10 /* ktss.c */,
8373342423F60CDB00DE14DC /* kwb.c */,
839C3D26270D49FF00E13653 /* lopu.c */,
835B9B8C2730BF2C00F87EE3 /* lopu_fb.c */,
839C3D22270D49FF00E13653 /* lpcm_fb.c */,
835B9B8E2730BF2D00F87EE3 /* lpcm_shade.c */,
8373341F23F60CDB00DE14DC /* lrmd_streamfile.h */,
8373342223F60CDB00DE14DC /* lrmd.c */,
836F6E5A18BDC2180095E648 /* lsf.c */,
@ -2041,7 +2049,6 @@
836F6E8D18BDC2180095E648 /* ps2_2pfs.c */,
836F6E8E18BDC2180095E648 /* ps2_adm.c */,
836F6E9118BDC2180095E648 /* ps2_ass.c */,
836F6E9218BDC2180095E648 /* ps2_ast.c */,
836F6E9418BDC2180095E648 /* ps2_b1s.c */,
836F6E9518BDC2180095E648 /* ps2_bg00.c */,
836F6E9618BDC2180095E648 /* ps2_bmdx.c */,
@ -2059,7 +2066,6 @@
836F6EA418BDC2180095E648 /* ps2_joe.c */,
836F6EA618BDC2180095E648 /* ps2_kces.c */,
836F6EA818BDC2180095E648 /* ps2_leg.c */,
836F6EA918BDC2180095E648 /* ps2_lpcm.c */,
836F6EAA18BDC2180095E648 /* ps2_mcg.c */,
836F6EAC18BDC2180095E648 /* ps2_mic.c */,
836F6EAD18BDC2180095E648 /* ps2_mihb.c */,
@ -2354,6 +2360,7 @@
839E21E01F2EDAF100EE54D7 /* vorbis_custom_data_fsb.h in Headers */,
83031EDB243C510500C3F3E0 /* xnb_lz4mg.h in Headers */,
836F705418BDC2190095E648 /* streamfile.h in Headers */,
835B9B922730BF2D00F87EE3 /* hca_bf.h in Headers */,
83A3F0761E3AD8B900D6A794 /* stack_alloc.h in Headers */,
8373342723F60CDC00DE14DC /* lrmd_streamfile.h in Headers */,
83C7281122BC893D00678B4A /* 9tav_streamfile.h in Headers */,
@ -2763,7 +2770,6 @@
83AA7F812519C042004C5298 /* silence.c in Sources */,
834FE0B3215C798C000A5D3D /* acm_decoder_util.c in Sources */,
837CEA7A23487E2500E62A4A /* ffmpeg_decoder_utils.c in Sources */,
839C3D28270D49FF00E13653 /* lopu.c in Sources */,
837CEAF823487F2C00E62A4A /* xmv_valve.c in Sources */,
836F6F6718BDC2190095E648 /* acm.c in Sources */,
834FE0FD215C79ED000A5D3D /* vpk.c in Sources */,
@ -2774,6 +2780,7 @@
8306B0A720984552000302D4 /* blocked_xvas.c in Sources */,
8349A9101FE6258200E26435 /* ea_eaac.c in Sources */,
836F700F18BDC2190095E648 /* ps2_xa30.c in Sources */,
835B9B912730BF2D00F87EE3 /* lopu_fb.c in Sources */,
8346D97B25BF838C00D1A8B0 /* ktac.c in Sources */,
8349A8ED1FE6253900E26435 /* blocked_ea_sns.c in Sources */,
836F6F6F18BDC2190095E648 /* akb.c in Sources */,
@ -2892,7 +2899,6 @@
836F6F8818BDC2190095E648 /* fsb.c in Sources */,
83F2CCE525A5B41600F46FA8 /* acx.c in Sources */,
83FC176D23AC58D100E1025F /* xma_ue3.c in Sources */,
836F6FE518BDC2190095E648 /* ps2_lpcm.c in Sources */,
8375737321F9507D00F01AF5 /* oki_decoder.c in Sources */,
836F6FB318BDC2190095E648 /* ngc_lps.c in Sources */,
836F6FC018BDC2190095E648 /* p3d.c in Sources */,
@ -2946,7 +2952,6 @@
836F6FFA18BDC2190095E648 /* spm.c in Sources */,
83C7281B22BC893D00678B4A /* strm_abylight.c in Sources */,
834D3A6E19F47C98001C54F6 /* g1l.c in Sources */,
836F6FCE18BDC2190095E648 /* ps2_ast.c in Sources */,
832BF82A21E0514B006F50F1 /* vs_square.c in Sources */,
834FE0B4215C798C000A5D3D /* ffmpeg_decoder_custom_opus.c in Sources */,
8349A9091FE6258200E26435 /* pc_ast.c in Sources */,
@ -2986,6 +2991,7 @@
8349A9161FE6258200E26435 /* flx.c in Sources */,
832BF82921E0514B006F50F1 /* msf_banpresto.c in Sources */,
834FE0BE215C79A9000A5D3D /* blocked_xa_aiff.c in Sources */,
835B9B902730BF2D00F87EE3 /* ast_mmv.c in Sources */,
836F702A18BDC2190095E648 /* sd9.c in Sources */,
836F6FB418BDC2190095E648 /* ngc_nst_dsp.c in Sources */,
836F6FDB18BDC2190095E648 /* ps2_hsf.c in Sources */,
@ -3050,6 +3056,7 @@
836F6FEE18BDC2190095E648 /* ps2_p2bt.c in Sources */,
836F702618BDC2190095E648 /* s14_sss.c in Sources */,
834FE102215C79ED000A5D3D /* rfrm.c in Sources */,
835B9B8F2730BF2D00F87EE3 /* ast_mv.c in Sources */,
83AA5D1D1F6E2F800020821C /* blocked_vgs.c in Sources */,
836F702E18BDC2190095E648 /* sli.c in Sources */,
83AA7F822519C042004C5298 /* ktsc.c in Sources */,
@ -3099,6 +3106,7 @@
836F6F4118BDC2190095E648 /* blocked.c in Sources */,
836F6F3B18BDC2190095E648 /* ws_decoder.c in Sources */,
839933612591E8C1001855AF /* sbk.c in Sources */,
835B9B932730BF2D00F87EE3 /* lpcm_shade.c in Sources */,
838BDB6E1D3B043C0022CA6F /* ffmpeg.c in Sources */,
832BF82B21E0514B006F50F1 /* xopus.c in Sources */,
836F6F3418BDC2190095E648 /* nwa_decoder.c in Sources */,

View File

@ -0,0 +1,140 @@
/* Copyright (c) 2003-2008 Timothy B. Terriberry
Copyright (c) 2008 Xiph.Org Foundation */
/*
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of the Xiph.org Foundation nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*Some common macros for potential platform-specific optimization.*/
#include <math.h>
#include <limits.h>
#if !defined(_ecintrin_H)
# define _ecintrin_H (1)
/*Some specific platforms may have optimized intrinsic or inline assembly
versions of these functions which can substantially improve performance.
We define macros for them to allow easy incorporation of these non-ANSI
features.*/
/*Note that we do not provide a macro for abs(), because it is provided as a
library function, which we assume is translated into an intrinsic to avoid
the function call overhead and then implemented in the smartest way for the
target platform.
With modern gcc (4.x), this is true: it uses cmov instructions if the
architecture supports it and branchless bit-twiddling if it does not (the
speed difference between the two approaches is not measurable).
Interestingly, the bit-twiddling method was patented in 2000 (US 6,073,150)
by Sun Microsystems, despite prior art dating back to at least 1996:
http://web.archive.org/web/19961201174141/www.x86.org/ftp/articles/pentopt/PENTOPT.TXT
On gcc 3.x, however, our assumption is not true, as abs() is translated to a
conditional jump, which is horrible on deeply piplined architectures (e.g.,
all consumer architectures for the past decade or more) when the sign cannot
be reliably predicted.*/
/*Modern gcc (4.x) can compile the naive versions of min and max with cmov if
given an appropriate architecture, but the branchless bit-twiddling versions
are just as fast, and do not require any special target architecture.
Earlier gcc versions (3.x) compiled both code to the same assembly
instructions, because of the way they represented ((_b)>(_a)) internally.*/
#define EC_MAXI(_a,_b) ((_a)-((_a)-(_b)&-((_b)>(_a))))
#define EC_MINI(_a,_b) ((_a)+((_b)-(_a)&-((_b)<(_a))))
/*This has a chance of compiling branchless, and is just as fast as the
bit-twiddling method, which is slightly less portable, since it relies on a
sign-extended rightshift, which is not guaranteed by ANSI (but present on
every relevant platform).*/
#define EC_SIGNI(_a) (((_a)>0)-((_a)<0))
/*Slightly more portable than relying on a sign-extended right-shift (which is
not guaranteed by ANSI), and just as fast, since gcc (3.x and 4.x both)
compile it into the right-shift anyway.*/
#define EC_SIGNMASK(_a) (-((_a)<0))
/*Clamps an integer into the given range.
If _a>_c, then the lower bound _a is respected over the upper bound _c (this
behavior is required to meet our documented API behavior).
_a: The lower bound.
_b: The value to clamp.
_c: The upper boud.*/
#define EC_CLAMPI(_a,_b,_c) (EC_MAXI(_a,EC_MINI(_b,_c)))
/*Count leading zeros.
This macro should only be used for implementing ec_ilog(), if it is defined.
All other code should use EC_ILOG() instead.*/
#if defined(_MSC_VER)
# include <intrin.h>
static __inline int ec_bsr(unsigned long _x){
unsigned long ret;
_BitScanReverse(&ret,_x);
return (int)ret;
}
# define EC_CLZ0 (1)
# define EC_CLZ(_x) (-ec_bsr(_x))
#elif defined(ENABLE_TI_DSPLIB)
# include "dsplib.h"
# define EC_CLZ0 (31)
# define EC_CLZ(_x) (_lnorm(x))
#elif defined(__GNUC_PREREQ)
# if __GNUC_PREREQ(3,4)
# if INT_MAX>=2147483647
# define EC_CLZ0 ((int)sizeof(unsigned)*CHAR_BIT)
# define EC_CLZ(_x) (__builtin_clz(_x))
# elif LONG_MAX>=2147483647L
# define EC_CLZ0 ((int)sizeof(unsigned long)*CHAR_BIT)
# define EC_CLZ(_x) (__builtin_clzl(_x))
# endif
# endif
#endif
#if defined(EC_CLZ)
/*Note that __builtin_clz is not defined when _x==0, according to the gcc
documentation (and that of the BSR instruction that implements it on x86).
The majority of the time we can never pass it zero.
When we need to, it can be special cased.*/
# define EC_ILOG(_x) (EC_CLZ0-EC_CLZ(_x))
#else
static int ec_ilog2(celt_uint32 _v){
int ret;
int m;
ret=!!_v;
m=!!(_v&0xFFFF0000)<<4;
_v>>=m;
ret|=m;
m=!!(_v&0xFF00)<<3;
_v>>=m;
ret|=m;
m=!!(_v&0xF0)<<2;
_v>>=m;
ret|=m;
m=!!(_v&0xC)<<1;
_v>>=m;
ret|=m;
ret+=!!(_v&0x2);
return ret;
}
# define EC_ILOG(_x) (ec_ilog2(_x))
#endif
#endif

View File

@ -115,7 +115,7 @@ void decode_hevag(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing
/* xa_decoder */
void decode_xa(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_xa8);
void decode_xa(VGMSTREAM* v, sample_t* outbuf, int32_t samples_to_do);
size_t xa_bytes_to_samples(size_t bytes, int channels, int is_blocked, int is_form2, int bps);
@ -172,6 +172,7 @@ int msadpcm_check_coefs(STREAMFILE* sf, off_t offset);
/* yamaha_decoder */
void decode_aica(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo);
void decode_cp_ym(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo);
void decode_aska(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, size_t frame_size);
void decode_nxap(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
size_t yamaha_bytes_to_samples(size_t bytes, int channels);
@ -324,9 +325,21 @@ void decode_hca(hca_codec_data* data, sample_t* outbuf, int32_t samples_to_do);
void reset_hca(hca_codec_data* data);
void loop_hca(hca_codec_data* data, int32_t num_sample);
void free_hca(hca_codec_data* data);
int test_hca_key(hca_codec_data* data, unsigned long long keycode);
void hca_set_encryption_key(hca_codec_data* data, uint64_t keycode);
clHCA_stInfo* hca_get_info(hca_codec_data* data);
typedef struct {
/* config + output */
uint64_t key;
uint16_t subkey;
uint64_t best_key;
int best_score;
/* internals */
uint32_t start_offset;
} hca_keytest_t;
void test_hca_key(hca_codec_data* data, hca_keytest_t* hk);
void hca_set_encryption_key(hca_codec_data* data, uint64_t keycode);
STREAMFILE* hca_get_streamfile(hca_codec_data* data);
@ -465,7 +478,6 @@ void decode_mpeg(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do,
void reset_mpeg(mpeg_codec_data* data);
void seek_mpeg(VGMSTREAM* vgmstream, int32_t num_sample);
void free_mpeg(mpeg_codec_data* data);
void flush_mpeg(mpeg_codec_data* data);
int mpeg_get_sample_rate(mpeg_codec_data* data);
long mpeg_bytes_to_samples(long bytes, const mpeg_codec_data* data);

View File

@ -1,7 +1,8 @@
#include "coding.h"
#include <g719/g719.h>
#ifdef VGM_USE_G719
#include <g719/g719.h>
#define G719_MAX_CODES ((1280/8)) /* in int16, so max frame size is (value/8)*2 (0xF0=common, 0x140=decoder max 2560b, rare) */
struct g719_codec_data {

View File

@ -1,7 +1,8 @@
#include "coding.h"
#include "g7221_decoder_lib.h"
#ifdef VGM_USE_G7221
#include "g7221_decoder_lib.h"
#define G7221_MAX_FRAME_SIZE 0x78 /* 960/8 */
#define G7221_MAX_FRAME_SAMPLES 640 /* 32000/50 */

View File

@ -3,7 +3,7 @@
struct hca_codec_data {
STREAMFILE* streamfile;
STREAMFILE* sf;
clHCA_stInfo info;
signed short* sample_buffer;
@ -58,8 +58,8 @@ hca_codec_data* init_hca(STREAMFILE* sf) {
if (!data->sample_buffer) goto fail;
/* load streamfile for reads */
data->streamfile = reopen_streamfile(sf, 0);
if (!data->streamfile) goto fail;
data->sf = reopen_streamfile(sf, 0);
if (!data->sf) goto fail;
/* set initial values */
reset_hca(data);
@ -115,7 +115,7 @@ void decode_hca(hca_codec_data* data, sample_t* outbuf, int32_t samples_to_do) {
}
/* read frame */
bytes = read_streamfile(data->data_buffer, offset, blockSize, data->streamfile);
bytes = read_streamfile(data->data_buffer, offset, blockSize, data->sf);
if (bytes != blockSize) {
VGM_LOG("HCA: read %x vs expected %x bytes at %x\n", bytes, blockSize, (uint32_t)offset);
break;
@ -171,7 +171,7 @@ void loop_hca(hca_codec_data* data, int32_t num_sample) {
void free_hca(hca_codec_data* data) {
if (!data) return;
close_streamfile(data->streamfile);
close_streamfile(data->sf);
clHCA_done(data->handle);
free(data->handle);
free(data->data_buffer);
@ -179,6 +179,14 @@ void free_hca(hca_codec_data* data) {
free(data);
}
clHCA_stInfo* hca_get_info(hca_codec_data* data) {
return &data->info;
}
STREAMFILE* hca_get_streamfile(hca_codec_data* data) {
if (!data) return NULL;
return data->sf;
}
/* ************************************************************************* */
@ -192,19 +200,23 @@ void free_hca(hca_codec_data* data) {
/* ignores beginning frames (~10 is not uncommon, Dragalia Lost vocal layers have lots) */
#define HCA_KEY_MAX_SKIP_BLANKS 1200
/* 5~15 should be enough, but almost silent or badly mastered files may need tweaks
* (ex. newer Tales of the Rays files clip a lot and need +6 as some keys give almost-ok results) */
#define HCA_KEY_MIN_TEST_FRAMES 7
#define HCA_KEY_MAX_TEST_FRAMES 12
* (ex. newer Tales of the Rays files clip a lot) */
#define HCA_KEY_MIN_TEST_FRAMES 3 //7
#define HCA_KEY_MAX_TEST_FRAMES 7 //12
/* score of 10~30 isn't uncommon in a single frame, too many frames over that is unlikely */
#define HCA_KEY_MAX_FRAME_SCORE 150
#define HCA_KEY_MAX_TOTAL_SCORE (HCA_KEY_MAX_TEST_FRAMES * 50*HCA_KEY_SCORE_SCALE)
/* Test a number of frames if key decrypts correctly.
* Returns score: <0: error/wrong, 0: unknown/silent file, >0: good (the closest to 1 the better). */
int test_hca_key(hca_codec_data* data, unsigned long long keycode) {
static int test_hca_score(hca_codec_data* data, hca_keytest_t* hk, unsigned long long keycode) {
size_t test_frames = 0, current_frame = 0, blank_frames = 0;
int total_score = 0, found_regular_frame = 0;
const unsigned int blockSize = data->info.blockSize;
int total_score = 0;
const unsigned int block_size = data->info.blockSize;
uint32_t offset = hk->start_offset;
if (!offset)
offset = data->info.headerSize;
/* Due to the potentially large number of keys this must be tuned for speed.
* Buffered IO seems fast enough (not very different reading a large block once vs frame by frame).
@ -216,19 +228,25 @@ int test_hca_key(hca_codec_data* data, unsigned long long keycode) {
/* A final score of 0 (=silent) is only possible for short files with all blank frames */
while (test_frames < HCA_KEY_MAX_TEST_FRAMES && current_frame < data->info.blockCount) {
off_t offset = data->info.headerSize + current_frame * blockSize;
int score;
size_t bytes;
/* read and test frame */
bytes = read_streamfile(data->data_buffer, offset, blockSize, data->streamfile);
if (bytes != blockSize) {
bytes = read_streamfile(data->data_buffer, offset, block_size, data->sf);
if (bytes != block_size) {
/* normally this shouldn't happen, but pre-fetch ACB stop with frames in half, so just keep score */
//total_score = -1;
break;
}
score = clHCA_TestBlock(data->handle, (void*)(data->data_buffer), blockSize);
score = clHCA_TestBlock(data->handle, data->data_buffer, block_size);
/* get first non-blank frame */
if (!hk->start_offset && score != 0) {
hk->start_offset = offset;
}
offset += bytes;
if (score < 0 || score > HCA_KEY_MAX_FRAME_SCORE) {
total_score = -1;
break;
@ -236,13 +254,13 @@ int test_hca_key(hca_codec_data* data, unsigned long long keycode) {
current_frame++;
/* ignore silent frames at the beginning, up to a point */
if (score == 0 && blank_frames < HCA_KEY_MAX_SKIP_BLANKS && !found_regular_frame) {
/* ignore silent frames at the beginning, up to a point (keep skipping as
* in rare cases there are one non-blank frame then a bunch, that skew results) */
if (score == 0 && blank_frames < HCA_KEY_MAX_SKIP_BLANKS /*&& !hk->start_offset*/) {
blank_frames++;
continue;
}
found_regular_frame = 1;
test_frames++;
/* scale values to make scores of perfect frames more detectable */
@ -254,7 +272,6 @@ int test_hca_key(hca_codec_data* data, unsigned long long keycode) {
total_score += score;
/* don't bother checking more frames, other keys will get better scores */
if (total_score > HCA_KEY_MAX_TOTAL_SCORE)
break;
@ -270,15 +287,37 @@ int test_hca_key(hca_codec_data* data, unsigned long long keycode) {
return total_score;
}
void test_hca_key(hca_codec_data* data, hca_keytest_t* hk) {
int score;
uint64_t key = hk->key;
uint16_t subkey = hk->subkey;
//;VGM_LOG("HCA: test key=%08x%08x, subkey=%04x\n",
// (uint32_t)((key >> 32) & 0xFFFFFFFF), (uint32_t)(key & 0xFFFFFFFF), subkey);
if (subkey) {
key = key * ( ((uint64_t)subkey << 16u) | ((uint16_t)~subkey + 2u) );
}
score = test_hca_score(data, hk, (unsigned long long)key);
//;VGM_LOG("HCA: test key=%08x%08x, subkey=%04x, score=%i\n",
// (uint32_t)((key >> 32) & 0xFFFFFFFF), (uint32_t)(key & 0xFFFFFFFF), subkey, score);
/* wrong key */
if (score < 0)
return;
//;VGM_LOG("HCA: ok key=%08x%08x, subkey=%04x, score=%i\n",
// (uint32_t)((key >> 32) & 0xFFFFFFFF), (uint32_t)(key & 0xFFFFFFFF), subkey, score);
/* update if something better is found */
if (hk->best_score <= 0 || (score < hk->best_score && score > 0)) {
hk->best_score = score;
hk->best_key = key;
}
}
void hca_set_encryption_key(hca_codec_data* data, uint64_t keycode) {
clHCA_SetKey(data->handle, (unsigned long long)keycode);
}
clHCA_stInfo* hca_get_info(hca_codec_data* data) {
return &data->info;
}
STREAMFILE* hca_get_streamfile(hca_codec_data* data) {
if (!data) return NULL;
return data->streamfile;
}

View File

@ -57,9 +57,9 @@
#define HCA_MAX_FRAME_SIZE 0xFFFF /* lib max */
#define HCA_MASK 0x7F7F7F7F /* chunk obfuscation when the HCA is encrypted with key */
#define HCA_SUBFRAMES_PER_FRAME 8
#define HCA_SUBFRAMES 8
#define HCA_SAMPLES_PER_SUBFRAME 128 /* also spectrum points/etc */
#define HCA_SAMPLES_PER_FRAME (HCA_SUBFRAMES_PER_FRAME*HCA_SAMPLES_PER_SUBFRAME)
#define HCA_SAMPLES_PER_FRAME (HCA_SUBFRAMES*HCA_SAMPLES_PER_SUBFRAME)
#define HCA_MDCT_BITS 7 /* (1<<7) = 128 */
#define HCA_MIN_CHANNELS 1
@ -88,7 +88,7 @@ typedef struct stChannel {
unsigned int coded_count; /* encoded scales/resolutions/coefs */
/* subframe state */
unsigned char intensity[HCA_SUBFRAMES_PER_FRAME]; /* intensity indexes for joins stereo (value max: 15 / 4b) */
unsigned char intensity[HCA_SUBFRAMES]; /* intensity indexes for joins stereo (value max: 15 / 4b) */
unsigned char scalefactors[HCA_SAMPLES_PER_SUBFRAME]; /* scale indexes (value max: 64 / 6b)*/
unsigned char resolution[HCA_SAMPLES_PER_SUBFRAME]; /* resolution indexes (value max: 15 / 4b) */
unsigned char noises[HCA_SAMPLES_PER_SUBFRAME]; /* indexes to coefs that need noise fill + coefs that don't (value max: 128 / 8b) */
@ -96,13 +96,14 @@ typedef struct stChannel {
unsigned int valid_count; /* resolutions with valid values saved in 'noises' */
float gain[HCA_SAMPLES_PER_SUBFRAME]; /* gain to apply to quantized spectral data */
float spectra[HCA_SAMPLES_PER_SUBFRAME]; /* resulting dequantized data */
float spectra[HCA_SUBFRAMES][HCA_SAMPLES_PER_SUBFRAME]; /* resulting dequantized data */
float temp[HCA_SAMPLES_PER_SUBFRAME]; /* temp for DCT-IV */
float dct[HCA_SAMPLES_PER_SUBFRAME]; /* result of DCT-IV */
float imdct_previous[HCA_SAMPLES_PER_SUBFRAME]; /* IMDCT */
/* frame state */
float wave[HCA_SUBFRAMES_PER_FRAME][HCA_SAMPLES_PER_SUBFRAME]; /* resulting samples */
float wave[HCA_SUBFRAMES][HCA_SAMPLES_PER_SUBFRAME]; /* resulting samples */
} stChannel;
typedef struct clHCA {
@ -333,7 +334,7 @@ void clHCA_ReadSamples16(clHCA* hca, signed short *samples) {
unsigned int i, j, k;
/* PCM output is generally unused, but lib functions seem to use SIMD for f32 to s32 + round to zero */
for (i = 0; i < HCA_SUBFRAMES_PER_FRAME; i++) {
for (i = 0; i < HCA_SUBFRAMES; i++) {
for (j = 0; j < HCA_SAMPLES_PER_SUBFRAME; j++) {
for (k = 0; k < hca->channels; k++) {
f = hca->channel[k].wave[i][j];
@ -989,8 +990,12 @@ void clHCA_SetKey(clHCA* hca, unsigned long long keycode) {
}
}
static int clHCA_DecodeBlock_unpack(clHCA* hca, void *data, unsigned int size);
static void clHCA_DecodeBlock_transform(clHCA* hca);
int clHCA_TestBlock(clHCA* hca, void *data, unsigned int size) {
const int frame_samples = HCA_SUBFRAMES_PER_FRAME * HCA_SAMPLES_PER_SUBFRAME;
const int frame_samples = HCA_SUBFRAMES * HCA_SAMPLES_PER_SUBFRAME;
const float scale = 32768.0f;
unsigned int i, ch, sf, s;
int status;
@ -1014,7 +1019,7 @@ int clHCA_TestBlock(clHCA* hca, void *data, unsigned int size) {
}
/* return if decode fails (happens often with wrong keys due to bad bitstream values) */
status = clHCA_DecodeBlock(hca, data, size);
status = clHCA_DecodeBlock_unpack(hca, data, size);
if (status < 0)
return -1;
@ -1042,8 +1047,9 @@ int clHCA_TestBlock(clHCA* hca, void *data, unsigned int size) {
}
/* check decode results as (rarely) bad keys may still get here */
clHCA_DecodeBlock_transform(hca);
for (ch = 0; ch < hca->channels; ch++) {
for (sf = 0; sf < HCA_SUBFRAMES_PER_FRAME; sf++) {
for (sf = 0; sf < HCA_SUBFRAMES; sf++) {
for (s = 0; s < HCA_SAMPLES_PER_SUBFRAME; s++) {
float fsample = hca->channel[ch].wave[sf][s];
@ -1095,15 +1101,15 @@ void clHCA_DecodeReset(clHCA * hca) {
stChannel* ch = &hca->channel[i];
/* most values get overwritten during decode */
//memset(ch->intensity, 0, sizeof(ch->intensity[0]) * HCA_SUBFRAMES_PER_FRAME);
//memset(ch->intensity, 0, sizeof(ch->intensity[0]) * HCA_SUBFRAMES);
//memset(ch->scalefactors, 0, sizeof(ch->scalefactors[0]) * HCA_SAMPLES_PER_SUBFRAME);
//memset(ch->resolution, 0, sizeof(ch->resolution[0]) * HCA_SAMPLES_PER_SUBFRAME);
//memset(ch->gain, 0, sizeof(ch->gain[0]) * HCA_SAMPLES_PER_SUBFRAME);
//memset(ch->spectra, 0, sizeof(ch->spectra[0]) * HCA_SAMPLES_PER_SUBFRAME);
//memset(ch->spectra, 0, sizeof(ch->spectra[0]) * HCA_SUBFRAMES * HCA_SAMPLES_PER_SUBFRAME);
//memset(ch->temp, 0, sizeof(ch->temp[0]) * HCA_SAMPLES_PER_SUBFRAME);
//memset(ch->dct, 0, sizeof(ch->dct[0]) * HCA_SAMPLES_PER_SUBFRAME);
memset(ch->imdct_previous, 0, sizeof(ch->imdct_previous[0]) * HCA_SAMPLES_PER_SUBFRAME);
//memset(ch->wave, 0, sizeof(ch->wave[0][0]) * HCA_SUBFRAMES_PER_FRAME * HCA_SUBFRAMES_PER_FRAME);
//memset(ch->wave, 0, sizeof(ch->wave[0][0]) * HCA_SUBFRAMES * HCA_SUBFRAMES);
}
}
@ -1119,23 +1125,21 @@ static void calculate_resolution(stChannel* ch, unsigned int packed_noise_level,
static void calculate_gain(stChannel* ch);
static void dequantize_coefficients(stChannel* ch, clData* br);
static void dequantize_coefficients(stChannel* ch, clData* br, int subframe);
static void reconstruct_noise(stChannel* ch, unsigned int min_resolution, unsigned int ms_stereo, unsigned int* random_p);
static void reconstruct_noise(stChannel* ch, unsigned int min_resolution, unsigned int ms_stereo, unsigned int* random_p, int subframe);
static void reconstruct_high_frequency(stChannel* ch, unsigned int hfr_group_count, unsigned int bands_per_hfr_group,
unsigned int stereo_band_count, unsigned int base_band_count, unsigned int total_band_count, unsigned int version);
unsigned int stereo_band_count, unsigned int base_band_count, unsigned int total_band_count, unsigned int version, int subframe);
static void apply_intensity_stereo(stChannel* ch_pair, int subframe, unsigned int base_band_count, unsigned int total_band_count);
static void apply_ms_stereo(stChannel* ch_pair, unsigned int ms_stereo, unsigned int base_band_count, unsigned int total_band_count);
static void apply_ms_stereo(stChannel* ch_pair, unsigned int ms_stereo, unsigned int base_band_count, unsigned int total_band_count, int subframe);
static void imdct_transform(stChannel* ch, int subframe);
/* takes HCA data and decodes all of a frame's samples */
//hcadecoder_decode_block
int clHCA_DecodeBlock(clHCA* hca, void *data, unsigned int size) {
static int clHCA_DecodeBlock_unpack(clHCA* hca, void *data, unsigned int size) {
clData br;
unsigned short sync;
unsigned int subframe, ch;
@ -1180,19 +1184,29 @@ int clHCA_DecodeBlock(clHCA* hca, void *data, unsigned int size) {
}
/* lib seems to use a state value to skip parts (unpacking/subframe N/etc) as needed */
for (subframe = 0; subframe < HCA_SUBFRAMES_PER_FRAME; subframe++) {
for (subframe = 0; subframe < HCA_SUBFRAMES; subframe++) {
/* unpack channel data and get dequantized spectra */
for (ch = 0; ch < hca->channels; ch++){
dequantize_coefficients(&hca->channel[ch], &br);
dequantize_coefficients(&hca->channel[ch], &br, subframe);
}
/* original code transforms subframe here, but we have it for later */
}
return br.bit; /* numbers of read bits for validations */
}
static void clHCA_DecodeBlock_transform(clHCA* hca) {
unsigned int subframe, ch;
for (subframe = 0; subframe < HCA_SUBFRAMES; subframe++) {
/* restore missing bands from spectra */
for (ch = 0; ch < hca->channels; ch++) {
reconstruct_noise(&hca->channel[ch], hca->min_resolution, hca->ms_stereo, &hca->random);
reconstruct_noise(&hca->channel[ch], hca->min_resolution, hca->ms_stereo, &hca->random, subframe);
reconstruct_high_frequency(&hca->channel[ch], hca->hfr_group_count, hca->bands_per_hfr_group,
hca->stereo_band_count, hca->base_band_count, hca->total_band_count, hca->version);
hca->stereo_band_count, hca->base_band_count, hca->total_band_count, hca->version, subframe);
}
/* restore missing joint stereo bands */
@ -1200,7 +1214,7 @@ int clHCA_DecodeBlock(clHCA* hca, void *data, unsigned int size) {
for (ch = 0; ch < hca->channels - 1; ch++) {
apply_intensity_stereo(&hca->channel[ch], subframe, hca->base_band_count, hca->total_band_count);
apply_ms_stereo(&hca->channel[ch], hca->ms_stereo, hca->base_band_count, hca->total_band_count);
apply_ms_stereo(&hca->channel[ch], hca->ms_stereo, hca->base_band_count, hca->total_band_count, subframe);
}
}
@ -1209,9 +1223,27 @@ int clHCA_DecodeBlock(clHCA* hca, void *data, unsigned int size) {
imdct_transform(&hca->channel[ch], subframe);
}
}
}
return br.bit; /* numbers of read bits for validations */
/* takes HCA data and decodes all of a frame's samples */
//hcadecoder_decode_block
int clHCA_DecodeBlock(clHCA* hca, void *data, unsigned int size) {
int res;
/* Original HCA code doesn't separate unpack + transform (unpacks most data,
* reads a subframe's spectra, transforms that subframe.
*
* Unpacking first takes a bit more memory (1 spectra per subframe) but test keys faster
* (since unpack may fail with bad keys we can skip transform). For regular decoding, this
* way somehow is slightly faster? (~3-5%, extra compiler optimizations with reduced scope?) */
res = clHCA_DecodeBlock_unpack(hca, data, size);
if (res < 0)
return res;
clHCA_DecodeBlock_transform(hca);
return res;
}
//--------------------------------------------------
@ -1330,7 +1362,7 @@ static int unpack_intensity(stChannel* ch, clData* br, unsigned int hfr_group_co
ch->intensity[0] = value;
if (value < 15) {
bitreader_skip(br, 4);
for (i = 1; i < HCA_SUBFRAMES_PER_FRAME; i++) {
for (i = 1; i < HCA_SUBFRAMES; i++) {
ch->intensity[i] = bitreader_read(br, 4);
}
}
@ -1352,7 +1384,7 @@ static int unpack_intensity(stChannel* ch, clData* br, unsigned int hfr_group_co
ch->intensity[0] = value;
if (delta_bits == 3) { /* 3+1 = 4b */
/* fixed intensities */
for (i = 1; i < HCA_SUBFRAMES_PER_FRAME; i++) {
for (i = 1; i < HCA_SUBFRAMES; i++) {
ch->intensity[i] = bitreader_read(br, 4);
}
}
@ -1361,7 +1393,7 @@ static int unpack_intensity(stChannel* ch, clData* br, unsigned int hfr_group_co
unsigned char bmax = (2 << delta_bits) - 1;
unsigned char bits = delta_bits + 1;
for (i = 1; i < HCA_SUBFRAMES_PER_FRAME; i++) {
for (i = 1; i < HCA_SUBFRAMES; i++) {
unsigned char delta = bitreader_read(br, bits);
if (delta == bmax) {
value = bitreader_read(br, 4); /* encoded */
@ -1378,7 +1410,7 @@ static int unpack_intensity(stChannel* ch, clData* br, unsigned int hfr_group_co
}
else {
bitreader_skip(br, 4);
for (i = 0; i < HCA_SUBFRAMES_PER_FRAME; i++) {
for (i = 0; i < HCA_SUBFRAMES; i++) {
ch->intensity[i] = 7;
}
}
@ -1498,7 +1530,7 @@ static const float hcatbdecoder_read_val_table[128] = {
};
/* read spectral coefficients in the bitstream */
static void dequantize_coefficients(stChannel* ch, clData* br) {
static void dequantize_coefficients(stChannel* ch, clData* br, int subframe) {
int i;
unsigned int cc_count = ch->coded_count;
@ -1524,11 +1556,11 @@ static void dequantize_coefficients(stChannel* ch, clData* br) {
}
/* dequantize coef with gain */
ch->spectra[i] = ch->gain[i] * qc;
ch->spectra[subframe][i] = ch->gain[i] * qc;
}
/* clean rest of spectra */
memset(&ch->spectra[cc_count], 0, sizeof(ch->spectra[0]) * (HCA_SAMPLES_PER_SUBFRAME - cc_count));
memset(&ch->spectra[subframe][cc_count], 0, sizeof(ch->spectra[subframe][0]) * (HCA_SAMPLES_PER_SUBFRAME - cc_count));
}
@ -1560,7 +1592,7 @@ static const float* hcadecoder_scale_conversion_table = (const float*)hcadecoder
/* recreate resolution 0 coefs (not encoded) with pseudo-random noise based on
* other coefs/scales (probably similar to AAC's perceptual noise substitution) */
static void reconstruct_noise(stChannel* ch, unsigned int min_resolution, unsigned int ms_stereo, unsigned int* random_p) {
static void reconstruct_noise(stChannel* ch, unsigned int min_resolution, unsigned int ms_stereo, unsigned int* random_p, int subframe) {
if (min_resolution > 0) /* added in v3.0 */
return;
if (ch->valid_count <= 0 || ch->noise_count <= 0)
@ -1587,7 +1619,8 @@ static void reconstruct_noise(stChannel* ch, unsigned int min_resolution, unsign
sf_valid = ch->scalefactors[valid_index];
sc_index = (sf_noise - sf_valid + 62) & ~((sf_noise - sf_valid + 62) >> 31);
ch->spectra[noise_index] = hcadecoder_scale_conversion_table[sc_index] * ch->spectra[valid_index];
ch->spectra[subframe][noise_index] =
hcadecoder_scale_conversion_table[sc_index] * ch->spectra[subframe][valid_index];
}
*random_p = random; /* lib saves this in the bitreader, maybe for simplified passing around */
@ -1596,7 +1629,7 @@ static void reconstruct_noise(stChannel* ch, unsigned int min_resolution, unsign
/* recreate missing coefs in high bands based on lower bands (probably similar to AAC's spectral band replication) */
static void reconstruct_high_frequency(stChannel* ch, unsigned int hfr_group_count, unsigned int bands_per_hfr_group,
unsigned int stereo_band_count, unsigned int base_band_count, unsigned int total_band_count, unsigned int version) {
unsigned int stereo_band_count, unsigned int base_band_count, unsigned int total_band_count, unsigned int version, int subframe) {
if (bands_per_hfr_group == 0) /* added in v2.0, skipped in v2.0 files with 0 bands too */
return;
if (ch->type == STEREO_SECONDARY)
@ -1630,7 +1663,7 @@ static void reconstruct_high_frequency(stChannel* ch, unsigned int hfr_group_cou
sc_index = hfr_scales[group] - ch->scalefactors[lowband] + 63;
sc_index = sc_index & ~(sc_index >> 31); /* clamped in v3.0 lib (in theory 6b sf are 0..128) */
ch->spectra[highband] = hcadecoder_scale_conversion_table[sc_index] * ch->spectra[lowband];
ch->spectra[subframe][highband] = hcadecoder_scale_conversion_table[sc_index] * ch->spectra[subframe][lowband];
highband += 1;
lowband -= lowband_sub;
@ -1638,7 +1671,7 @@ static void reconstruct_high_frequency(stChannel* ch, unsigned int hfr_group_cou
}
/* last spectrum coefficient is 0 (normally highband = 128, but perhaps could 'break' before) */
ch->spectra[highband - 1] = 0.0f;
ch->spectra[subframe][highband - 1] = 0.0f;
}
}
@ -1661,8 +1694,8 @@ static void apply_intensity_stereo(stChannel* ch_pair, int subframe, unsigned in
int band;
float ratio_l = hcadecoder_intensity_ratio_table[ ch_pair[1].intensity[subframe] ];
float ratio_r = 2.0f - ratio_l; /* correct, though other decoders substract 2.0 (it does use 'fsubr 2.0' and such) */
float* sp_l = ch_pair[0].spectra;
float* sp_r = ch_pair[1].spectra;
float* sp_l = &ch_pair[0].spectra[subframe][0];
float* sp_r = &ch_pair[1].spectra[subframe][0];
for (band = base_band_count; band < total_band_count; band++) {
float coef_l = sp_l[band] * ratio_l;
@ -1674,7 +1707,7 @@ static void apply_intensity_stereo(stChannel* ch_pair, int subframe, unsigned in
}
/* restore L/R bands based on mid channel + side differences */
static void apply_ms_stereo(stChannel* ch_pair, unsigned int ms_stereo, unsigned int base_band_count, unsigned int total_band_count) {
static void apply_ms_stereo(stChannel* ch_pair, unsigned int ms_stereo, unsigned int base_band_count, unsigned int total_band_count, int subframe) {
if (!ms_stereo) /* added in v3.0 */
return;
if (ch_pair[0].type != STEREO_PRIMARY)
@ -1683,8 +1716,8 @@ static void apply_ms_stereo(stChannel* ch_pair, unsigned int ms_stereo, unsigned
{
int band;
const float ratio = 0.70710676908493; /* 0x3F3504F3 */
float* sp_l = ch_pair[0].spectra;
float* sp_r = ch_pair[1].spectra;
float* sp_l = &ch_pair[0].spectra[subframe][0];
float* sp_r = &ch_pair[1].spectra[subframe][0];
for (band = base_band_count; band < total_band_count; band++) {
float coef_l = (sp_l[band] + sp_r[band]) * ratio;
@ -1867,8 +1900,8 @@ static void imdct_transform(stChannel* ch, int subframe) {
{
unsigned int count1 = 1;
unsigned int count2 = half;
float* temp1 = ch->spectra;
float* temp2 = ch->temp;
float* temp1 = &ch->spectra[subframe][0];
float* temp2 = &ch->temp[0];
for (i = 0; i < mdct_bits; i++) {
float* swap;
@ -1897,8 +1930,8 @@ static void imdct_transform(stChannel* ch, int subframe) {
{
unsigned int count1 = half;
unsigned int count2 = 1;
float* temp1 = ch->temp;
float* temp2 = ch->spectra;
float* temp1 = &ch->temp[0];
float* temp2 = &ch->spectra[subframe][0];
for (i = 0; i < mdct_bits; i++) {
const float* sin_table = (const float*) sin_tables_hex[i];//todo cleanup
@ -1934,15 +1967,15 @@ static void imdct_transform(stChannel* ch, int subframe) {
/* copy dct */
/* (with the above optimization spectra is already modified, so this is redundant) */
for (i = 0; i < size; i++) {
ch->dct[i] = ch->spectra[i];
ch->dct[i] = ch->spectra[subframe][i];
}
#endif
}
/* update output/imdct with overlapped window (lib fuses this with the above) */
{
const float* dct = ch->spectra; //ch->dct;
const float* prev = ch->imdct_previous;
const float* dct = &ch->spectra[subframe][0]; //ch->dct;
const float* prev = &ch->imdct_previous[0];
for (i = 0; i < half; i++) {
ch->wave[subframe][i] = hcaimdct_window_float[i] * dct[i + half] + prev[i];

View File

@ -87,8 +87,10 @@ int mpeg_custom_setup_init_default(STREAMFILE* sf, off_t start_offset, mpeg_code
//case MPEG_P3D: data->skip_samples = info.frame_samples; break; /* matches Radical ADPCM (PC) output */
/* FSBs (with FMOD DLLs) don't seem to need it. Particularly a few games (all from Wayforward?)
* contain audible garbage at the beginning, but it's actually there in-game too */
//case MPEG_FSB: data->skip_samples = 0; break;
* contain audible garbage at the beginning, but it's actually there in-game too.
* Games doing full loops also must not have delay (reuses mpeg state on loop) */
case MPEG_FSB:
data->skip_samples = 0; break;
case MPEG_XVAG: /* set in header and needed for gapless looping */
data->skip_samples = data->config.skip_samples; break;

View File

@ -481,6 +481,8 @@ decode_fail:
/* UTILS */
/*********/
static void flush_mpeg(mpeg_codec_data* data, int is_loop);
void free_mpeg(mpeg_codec_data* data) {
if (!data)
return;
@ -513,7 +515,7 @@ void free_mpeg(mpeg_codec_data* data) {
void reset_mpeg(mpeg_codec_data* data) {
if (!data) return;
flush_mpeg(data);
flush_mpeg(data, 0);
#if 0
/* flush_mpeg properly resets mpg123 with mpg123_open_feed, and
@ -550,7 +552,7 @@ void seek_mpeg(VGMSTREAM* vgmstream, int32_t num_sample) {
else {
int i;
flush_mpeg(data);
flush_mpeg(data, 1);
/* restart from 0 and manually discard samples, since we don't really know the correct offset */
for (i = 0; i < data->streams_size; i++) {
@ -566,7 +568,7 @@ void seek_mpeg(VGMSTREAM* vgmstream, int32_t num_sample) {
}
/* resets mpg123 decoder and its internals without seeking, useful when a new MPEG substream starts */
void flush_mpeg(mpeg_codec_data* data) {
static void flush_mpeg(mpeg_codec_data* data, int is_loop) {
if (!data)
return;
@ -578,7 +580,10 @@ void flush_mpeg(mpeg_codec_data* data) {
int i;
/* re-start from 0 */
for (i=0; i < data->streams_size; i++) {
mpg123_open_feed(data->streams[i]->m);
/* On loop FSB retains MDCT state so it mixes with next/loop frame (confirmed with recordings).
* This only matters on full loops and if there is no encoder delay (since loops use discard right now) */
if (is_loop && data->custom && !(data->type == MPEG_FSB))
mpg123_open_feed(data->streams[i]->m);
data->streams[i]->bytes_in_buffer = 0;
data->streams[i]->buffer_full = 0;
data->streams[i]->buffer_used = 0;

View File

@ -206,7 +206,7 @@ static uint32_t read_ubits(uint8_t bits, uint32_t offset, uint8_t* buf) {
shift = offset - 8 * (offset / 8);
mask = (1 << bits) - 1;
pos = offset / 8;
val = (buf[pos+0]) | (buf[pos+1]<<8) | (buf[pos+2]<<16) | (buf[pos+3]<<24);
val = ((uint32_t)buf[pos+0]) | ((uint32_t)buf[pos+1]<<8) | ((uint32_t)buf[pos+2]<<16) | ((uint32_t)buf[pos+3]<<24);
return (val >> shift) & mask;
}

View File

@ -1037,15 +1037,15 @@ static uint16_t crc16(const uint8_t* data, int length) {
/* ************************************************************************* */
static uint32_t get_u32be(const uint8_t* mem) {
return (mem[0] << 24) | (mem[1] << 16) | (mem[2] << 8) | mem[3];
return ((uint32_t)mem[0] << 24) | ((uint32_t)mem[1] << 16) | ((uint32_t)mem[2] << 8) | (uint32_t)mem[3];
}
static uint32_t get_u32le(const uint8_t* mem) {
return (mem[3] << 24) | (mem[2] << 16) | (mem[1] << 8) | mem[0];
return ((uint32_t)mem[3] << 24) | ((uint32_t)mem[2] << 16) | ((uint32_t)mem[1] << 8) | (uint32_t)mem[0];
}
static uint16_t get_u16le(const uint8_t* mem) {
return (mem[1] << 8) | mem[0];
return ((uint16_t)mem[1] << 8) | (uint16_t)mem[0];
}
static int init_header(tac_header_t* header, const uint8_t* buf) {

View File

@ -162,73 +162,57 @@ static int build_header_setup(uint8_t* buf, size_t bufsize, uint32_t setup_id, S
static int load_fvs_file_single(uint8_t* buf, size_t bufsize, uint32_t setup_id, STREAMFILE* sf) {
STREAMFILE* sf_setup = NULL;
/* get from artificial external file (used if compiled without codebooks) */
{
char setupname[PATH_LIMIT];
char pathname[PATH_LIMIT];
char *path;
char setupname[0x20];
/* read "(dir/).fvs_{setup_id}" */
sf->get_name(sf,pathname,sizeof(pathname));
path = strrchr(pathname,DIR_SEPARATOR);
if (path)
*(path+1) = '\0';
else
pathname[0] = '\0';
snprintf(setupname,PATH_LIMIT,"%s.fvs_%08x", pathname, setup_id);
sf_setup = sf->open(sf,setupname,STREAMFILE_DEFAULT_BUFFER_SIZE);
snprintf(setupname, sizeof(setupname), ".fvs_%08x", setup_id);
sf_setup = open_streamfile_by_filename(sf, setupname);
}
/* get codebook and copy to buffer */
if (sf_setup) {
/* file found, get contents into the buffer */
size_t bytes = sf_setup->get_size(sf_setup);
if (bytes > bufsize) goto fail;
if (read_streamfile(buf, 0, bytes, sf_setup) != bytes)
goto fail;
sf_setup->close(sf_setup);
close_streamfile(sf_setup);
return bytes;
}
fail:
if (sf_setup) sf_setup->close(sf_setup);
close_streamfile(sf_setup);
return 0;
}
static int load_fvs_file_multi(uint8_t* buf, size_t bufsize, uint32_t setup_id, STREAMFILE* sf) {
STREAMFILE* sf_setup = NULL;
/* from to get from artificial external file (used if compiled without codebooks) */
{
char setupname[PATH_LIMIT];
char pathname[PATH_LIMIT];
char* path;
char setupname[0x20];
/* read "(dir/).fvs" */
sf->get_name(sf,pathname,sizeof(pathname));
path = strrchr(pathname,DIR_SEPARATOR);
if (path)
*(path+1) = '\0';
else
pathname[0] = '\0';
snprintf(setupname,PATH_LIMIT,"%s.fvs", pathname);
sf_setup = sf->open(sf,setupname,STREAMFILE_DEFAULT_BUFFER_SIZE);
snprintf(setupname, sizeof(setupname), ".fvs");
sf_setup = open_streamfile_by_filename(sf, setupname);
}
/* find codebook in mini-header (format by bnnm, feel free to change) */
if (sf_setup) {
/* file found: read mini-header (format by bnnm, feel free to change) and locate FVS */
int entries, i;
uint32_t offset = 0, size = 0;
if (read_32bitBE(0x0, sf_setup) != 0x56465653) goto fail; /* "VFVS" */
entries = read_32bitLE(0x08, sf_setup); /* 0x04=v0, 0x0c-0x20: reserved */
if (!is_id32be(0x00, sf_setup, "VFVS"))
goto fail;
entries = read_u32le(0x08, sf_setup); /* 0x04=v0, 0x0c-0x20: reserved */
if (entries <= 0) goto fail;
for (i=0; i < entries; i++) { /* entry = id, offset, size, reserved */
if ((uint32_t)read_32bitLE(0x20 + i*0x10, sf_setup) == setup_id) {
offset = read_32bitLE(0x24 + i*0x10, sf_setup);
size = read_32bitLE(0x28 + i*0x10, sf_setup);
for (i = 0; i < entries; i++) { /* entry = id, offset, size, reserved */
if (read_u32le(0x20 + i*0x10, sf_setup) == setup_id) {
offset = read_u32le(0x24 + i*0x10, sf_setup);
size = read_u32le(0x28 + i*0x10, sf_setup);
break;
}
}

View File

@ -1144,27 +1144,18 @@ static int load_wvc(uint8_t* ibuf, size_t ibufsize, uint32_t codebook_id, wwise_
}
static int load_wvc_file(uint8_t* buf, size_t bufsize, uint32_t codebook_id, STREAMFILE* sf) {
STREAMFILE* sfWvc = NULL;
STREAMFILE* sf_setup = NULL;
size_t wvc_size = 0;
/* get from artificial external file (used if compiled without codebooks) */
{
char setupname[PATH_LIMIT];
char pathname[PATH_LIMIT];
char *path;
char setupname[0x20];
/* read "(dir/).wvc" */
sf->get_name(sf,pathname,sizeof(pathname));
path = strrchr(pathname,DIR_SEPARATOR);
if (path)
*(path+1) = '\0';
else
pathname[0] = '\0';
snprintf(setupname, sizeof(setupname), ".wvc");
sf_setup = open_streamfile_by_filename(sf, setupname);
if (!sf_setup) goto fail;
snprintf(setupname,PATH_LIMIT,"%s.wvc", pathname);
sfWvc = sf->open(sf,setupname,STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!sfWvc) goto fail;
wvc_size = sfWvc->get_size(sfWvc);
wvc_size = get_streamfile_size(sf_setup);
}
/* find codebook and copy to buffer */
@ -1174,24 +1165,24 @@ static int load_wvc_file(uint8_t* buf, size_t bufsize, uint32_t codebook_id, STR
int codebook_count;
/* at the end of the WVC is an offset table, and we need to find codebook id (number) offset */
table_start = read_u32le(wvc_size - 4, sfWvc); /* last offset */
table_start = read_u32le(wvc_size - 4, sf_setup); /* last offset */
codebook_count = ((wvc_size - table_start) / 4) - 1;
if (table_start > wvc_size || codebook_id >= codebook_count) goto fail;
codebook_offset = read_u32le(table_start + codebook_id*4, sfWvc);
codebook_size = read_u32le(table_start + codebook_id*4 + 4, sfWvc) - codebook_offset;
codebook_offset = read_u32le(table_start + codebook_id*4, sf_setup);
codebook_size = read_u32le(table_start + codebook_id*4 + 4, sf_setup) - codebook_offset;
if (codebook_size > bufsize) goto fail;
if (read_streamfile(buf, codebook_offset, codebook_size, sfWvc) != codebook_size)
if (read_streamfile(buf, codebook_offset, codebook_size, sf_setup) != codebook_size)
goto fail;
sfWvc->close(sfWvc);
close_streamfile(sf_setup);
return codebook_size;
}
fail:
if (sfWvc) sfWvc->close(sfWvc);
close_streamfile(sf_setup);
return 0;
}

View File

@ -1,130 +1,99 @@
#include "coding.h"
#include "../util.h"
// todo this is based on Kazzuya's old code; different emus (PCSX, Mame, Mednafen, etc) do
// XA coefs int math in different ways (see comments below), not 100% accurate.
// May be implemented like the SNES/SPC700 BRR.
/* XA ADPCM gain values */
#if 0
static const float K0[4] = { 0.0, 0.9375, 1.796875, 1.53125 };
static const float K1[4] = { 0.0, 0.0, -0.8125, -0.859375 };
//#define XA_FLOAT 1
#if XA_FLOAT
/* floats as defined by the spec, but PS1's SPU would use int math */
static const float K0[4+12] = { 0.0, 0.9375, 1.796875, 1.53125 };
static const float K1[4+12] = { 0.0, 0.0, -0.8125, -0.859375 };
#else
/* K0/1 floats to int with N=6: K*2^6 = K*(1<<6) = K*64 (upper ranges are supposedly 0)*/
static const int K0[4+12] = { 0, 60, 115, 98 };
static const int K1[4+12] = { 0, 0, -52, -55 };
#endif
/* K0/1 floats to int, -K*2^10 = -K*(1<<10) = -K*1024 */
static const int IK0[4] = { 0, -960, -1840, -1568 };
static const int IK1[4] = { 0, 0, 832, 880 };
/* EA's extended XA (N=8), reverse engineered from SAT exes. Basically the same with minor
* diffs and extra steps probably for the SH2 CPU (only does 1/2/8 shifts) */
static const int16_t EA_TABLE[16][2] = {
{ 0, 0 },
{ 240, 0 },
{ 460, -208 },
{ 392, -220 },
{ 488, -240 },
{ 328, -208 },
{ 440, -168 },
{ 420, -188 },
{ 432, -176 },
{ 240, -16 },
{ 416, -192 },
{ 424, -160 },
{ 288, -8 },
{ 436, -188 },
{ 224, -1 },
{ 272, -16 },
};
/* Sony XA ADPCM, defined for CD-DA/CD-i in the "Red Book" (private) or "Green Book" (public) specs.
* The algorithm basically is BRR (Bit Rate Reduction) from the SNES SPC700, while the data layout is new.
*
* Decoding is defined in diagrams, roughly as:
* pcm = clamp( signed_nibble * 2^(12-range) + K0[index]*hist1 + K1[index]*hist2 )
* - Range (12-range=shift) and filter index are renewed every ~28 samples.
* - nibble is expanded to a signed 16b sample, reimplemented as:
* short sample = ((nibble << 12) & 0xf000) >> shift
* or: int sample = ((nibble << 28) & 0xf0000000) >> (shift + N)
* - K0/K1 are float coefs are typically redefined with int math in various ways, with non-equivalent rounding:
* (sample + K0*2^N*hist1 + K1*2^N*hist2 + [(2^N)/2]) / 2^N
* (sample + K0*2^N*hist1 + K1*2^N*hist2 + [(2^N)/2]) >> N
* sample + (K0*2^N*hist1 + K1*2^N*hist2)>>N
* sample + (K0*2^N*hist1)>>N + (K1*2^N*hist2)>>N
* etc
* (rounding differences should be inaudible, so public implementations may be approximations)
*
* Various XA descendants (PS-ADPCM, EA-XA, NGC DTK, FADPCM, etc) do filters/rounding slightly
* differently, maybe using one of the above methods in software/CPU, but in XA's case may be done
* like the SNES/SPC700 BRR, with specific per-filter ops.
* int coef tables commonly use N = 6 or 8, so K0 0.9375*64 = 60 or 0.9375*256 = 240
* PS1 XA is apparently upsampled and interpolated to 44100, vgmstream doesn't simulate this.
*
* XA has an 8-bit decoding and "emphasis" modes, that no PS1 game actually uses, but apparently
* are supported by the CD hardware and will play if found. Some odd CD-i game does use 8-bit mode.
* Official "sound quality level" modes:
* - Level A: 37.8hz, 8-bit
* - Level B: 37.8hz, 4-bit
* - Level C: 18.9hz, 4-bit
*
* Info (Green Book): https://www.lscdweb.com/data/downloadables/2/8/cdi_may94_r2.pdf
* BRR info (no$sns): http://problemkaputt.de/fullsnes.htm#snesapudspbrrsamples
* (bsnes): https://github.com/byuu/bsnes/blob/master/bsnes/sfc/dsp/SPC_DSP.cpp#L316
*/
* See end for accuracy information and layout info. */
/* data layout (mono):
* - CD-XA audio is divided into sectors ("audio blocks"), each with 18*0x80 frames
* (sectors handled externally, this decoder only sees N frames)
* - each frame ("sound group") is divided into 8 interleaved subframes ("sound unit"), with
* 8*0x01 subframe headers x2 ("sound parameters") first then 28*0x04 subframe nibbles ("sound data")
* - subframe headers: 0..3 + repeat 0..3 + 4..7 + repeat 4..7 (where N = subframe N header)
* (repeats may be for error correction, though probably unused)
* header has a "range" N (gain of 2^N, or simplified as a shift) and a "filter" (control gains K0 and K1)
* - subframe nibbles: 32b with nibble0 for subframes 0..8, 32b with nibble1 for subframes 0..8, etc
* (low first: 32b = sf1-n0 sf0-n0 sf3-n0 sf2-n0 sf5-n0 sf4-n0 sf7-n0 sf6-n0, etc)
*
* stereo layout is the same but alternates channels: subframe 0/2/4/6=L, subframe 1/3/5/7=R
*
* example:
* subframe 0: header @ 0x00 or 0x04, 28 nibbles (low) @ 0x10,14,18,1c,20 ... 7c
* subframe 1: header @ 0x01 or 0x05, 28 nibbles (high) @ 0x10,14,18,1c,20 ... 7c
* subframe 2: header @ 0x02 or 0x06, 28 nibbles (low) @ 0x11,15,19,1d,21 ... 7d
* ...
* subframe 7: header @ 0x0b or 0x0f, 28 nibbles (high) @ 0x13,17,1b,1f,23 ... 7f
*
* 8-bit layout is similar but only has 4 subframes + subframe bytes, so half the samples:
* subframe 0: header @ 0x00 or 0x04/08/0c, 28 bytes @ 0x10,14,18,1c,20 ... 7c
* subframe 1: header @ 0x01 or 0x05/09/0d, 28 bytes @ 0x11,16,19,1d,21 ... 7d
* ...
* subframe 3: header @ 0x03 or 0x07/0b/0f, 28 bytes @ 0x13,17,1b,1f,23 ... 7f
*/
typedef struct {
uint8_t frame[0x80];
int16_t* sbuf;
int channels;
int32_t hist1;
int32_t hist2;
int subframes;
int is_xa8;
int is_ea;
} xa_t;
void decode_xa(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_xa8) {
uint8_t frame[0x80] = {0};
off_t frame_offset;
int i,j, sp_pos, frames_in, samples_done = 0, sample_count = 0;
size_t bytes_per_frame, samples_per_frame;
int32_t hist1 = stream->adpcm_history1_32;
int32_t hist2 = stream->adpcm_history2_32;
int subframes = (is_xa8) ? 4 : 8;
/* external interleave (fixed size), mono/stereo */
bytes_per_frame = 0x80;
samples_per_frame = 28*subframes / channelspacing;
frames_in = first_sample / samples_per_frame;
first_sample = first_sample % samples_per_frame;
/* parse frame header */
frame_offset = stream->offset + bytes_per_frame * frames_in;
read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */
VGM_ASSERT(get_u32be(frame+0x0) != get_u32be(frame+0x4) || get_u32be(frame+0x8) != get_u32be(frame+0xC),
"bad frames at %x\n", (uint32_t)frame_offset);
static void decode_xa_frame(xa_t* xa, int32_t first_sample, int32_t samples_to_do, int channel) {
int i,j, samples_done = 0, sample_count = 0;
int shift_max = xa->is_xa8 ? 8 : 12;
int shift_limit = xa->is_xa8 ? 8 : 9; /* from Nocash PSX docs (in 8-bit mode max range should be 8 though) */
/* decode subframes */
for (i = 0; i < subframes / channelspacing; i++) {
for (i = 0; i < xa->subframes / xa->channels; i++) {
uint8_t sp;
int index, shift, sp_pos;
#ifdef XA_FLOAT
float coef1, coef2;
#else
int32_t coef1, coef2;
uint8_t coef_index, shift_factor;
#endif
/* parse current subframe (sound unit)'s header (sound parameters) */
sp_pos = is_xa8 ?
i*channelspacing + channel:
0x04 + i*channelspacing + channel;
coef_index = (frame[sp_pos] >> 4) & 0xf;
shift_factor = (frame[sp_pos] >> 0) & 0xf;
sp_pos = xa->is_xa8 ?
i*xa->channels + channel :
0x04 + i*xa->channels + channel;
sp = xa->frame[sp_pos];
index = (sp >> 4) & 0xf;
shift = (sp >> 0) & 0xf;
/* mastered values like 0xFF exist [Micro Machines (CDi), demo and release] */
VGM_ASSERT(coef_index > 4 || shift_factor > (is_xa8 ? 8 : 12), "XA: incorrect coefs/shift at %x\n", (uint32_t)frame_offset + sp_pos);
if (coef_index > 4)
coef_index = 0; /* only 4 filters are used, rest is apparently 0 */
if (shift_factor > (is_xa8 ? 8 : 12))
shift_factor = (is_xa8 ? 8 : 9); /* supposedly, from Nocash PSX docs (in 8-bit mode max range should be 8 though) */
VGM_ASSERT_ONCE(shift > shift_max, "XA: incorrect shift %x\n", sp);
if (shift > shift_max)
shift = shift_limit;
coef1 = IK0[coef_index];
coef2 = IK1[coef_index];
if (xa->is_ea) {
coef1 = EA_TABLE[index][0];
coef2 = EA_TABLE[index][1];
}
else {
VGM_ASSERT_ONCE(index > 4, "XA: incorrect coefs %x\n", sp);
coef1 = K0[index];
coef2 = K1[index];
}
/* decode subframe nibbles */
for(j = 0; j < 28; j++) {
int32_t sample;
uint8_t su;
/* skip half decodes to make sure hist isn't touched (kinda hack-ish) */
if (!(sample_count >= first_sample && samples_done < samples_to_do)) {
@ -132,47 +101,97 @@ void decode_xa(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, i
continue;
}
if (is_xa8) {
int su_pos = (channelspacing==1) ?
if (xa->is_xa8) {
int su_pos = (xa->channels==1) ?
0x10 + j*0x04 + i : /* mono */
0x10 + j*0x04 + i*2 + channel; /* stereo */
sample = frame[su_pos];
sample = (int16_t)((sample << 8) & 0xff00) >> shift_factor; /* 16b sign extend + scale */
su = xa->frame[su_pos];
sample = (int16_t)((su << 8) & 0xff00) >> shift; /* 16b sign extend + scale */
}
else {
uint8_t nibbles;
int su_pos = (channelspacing==1) ?
int su_pos = (xa->channels==1) ?
0x10 + j*0x04 + (i/2) : /* mono */
0x10 + j*0x04 + i; /* stereo */
int get_high_nibble = (channelspacing==1) ?
int get_high_nibble = (xa->channels==1) ?
(i&1) : /* mono (even subframes = low, off subframes = high) */
(channel == 1); /* stereo (L channel / even subframes = low, R channel / odd subframes = high) */
nibbles = frame[su_pos];
sample = get_high_nibble ?
(nibbles >> 4) & 0x0f :
(nibbles >> 0) & 0x0f;
sample = (int16_t)((sample << 12) & 0xf000) >> shift_factor; /* 16b sign extend + scale */
su = xa->frame[su_pos];
su = get_high_nibble ?
(su >> 4) & 0x0f :
(su >> 0) & 0x0f;
sample = (int16_t)((su << 12) & 0xf000) >> shift; /* 16b sign extend + scale */
}
sample = sample << 4; /* scale for current IK */
sample = sample - ((coef1*hist1 + coef2*hist2) >> 10);
#if XA_FLOAT
sample = sample + (coef1 * xa->hist1 + coef2 * xa->hist2);
#else
if (xa->is_ea) /* sample << 8 actually but UB on negatives */
sample = (sample * 256 + coef1 * xa->hist1 + coef2 * xa->hist2) >> 8;
else
sample = sample + ((coef1 * xa->hist1 + coef2 * xa->hist2 + 32) >> 6);
#endif
hist2 = hist1;
hist1 = sample; /* must go before clamp, somehow */
sample = sample >> 4;
sample = clamp16(sample);
xa->hist2 = xa->hist1;
xa->hist1 = sample;
sample = clamp16(sample); /* don't clamp hist */
if (xa->is_ea)
xa->hist1 = sample; /* do clamp hist */
outbuf[samples_done * channelspacing] = sample;
xa->sbuf[samples_done * xa->channels] = sample;
samples_done++;
sample_count++;
}
}
}
stream->adpcm_history1_32 = hist1;
stream->adpcm_history2_32 = hist2;
void decode_xa(VGMSTREAM* v, sample_t* outbuf, int32_t samples_to_do) {
uint32_t offset = v->ch[0].offset; /* L/R share offsets */
STREAMFILE* sf = v->ch[0].streamfile;
int ch;
int frames_in, bytes, samples_per_frame;
uint32_t frame_offset, bytes_per_frame;
int32_t first_sample = v->samples_into_block;
xa_t xa;
xa.channels = v->channels > 1 ? 2 : 1; /* only stereo/mono modes */
xa.is_xa8 = (v->coding_type == coding_XA8);
xa.is_ea = (v->coding_type == coding_XA_EA);
xa.subframes = (xa.is_xa8) ? 4 : 8;
/* external interleave (fixed size), mono/stereo */
bytes_per_frame = sizeof(xa.frame);
samples_per_frame = 28 * xa.subframes / v->channels;
frames_in = first_sample / samples_per_frame;
first_sample = first_sample % samples_per_frame;
/* parse frame header */
frame_offset = offset + bytes_per_frame * frames_in;
bytes = read_streamfile(xa.frame, frame_offset, bytes_per_frame, sf);
if (bytes != sizeof(xa.frame)) /* ignore EOF errors */
memset(xa.frame + bytes, 0, bytes_per_frame - bytes);
/* headers should repeat in pairs, except in EA's modified XA */
VGM_ASSERT_ONCE(!xa.is_ea &&
(get_u32be(xa.frame+0x0) != get_u32be(xa.frame+0x4) || get_u32be(xa.frame+0x8) != get_u32be(xa.frame+0xC)),
"bad frames at %x\n", frame_offset);
for (ch = 0; ch < xa.channels; ch++) {
VGMSTREAMCHANNEL* stream = &v->ch[ch];
xa.sbuf = outbuf+ch;
xa.hist1 = stream->adpcm_history1_32;
xa.hist2 = stream->adpcm_history2_32;
decode_xa_frame(&xa, first_sample, samples_to_do, ch);
stream->adpcm_history1_32 = xa.hist1;
stream->adpcm_history2_32 = xa.hist2;
}
}
@ -185,3 +204,85 @@ size_t xa_bytes_to_samples(size_t bytes, int channels, int is_blocked, int is_fo
return (bytes / 0x80) * (28*subframes / channels);
}
}
/*
* Official XA decoding is defined in diagrams (not implementation), roughly as:
* pcm = signed_nibble * 2^(12-range) + K0[index]*hist1 + K1[index]*hist2
* - K0 = 0.0, 0.9375, 1.796875, 1.53125 / K1 = 0.0, 0.0, -0.8125, -0.859375
* - int coef tables commonly use N = 6 or 8, so K0 0.9375*64 = 60 or 0.9375*256 = 240
* - Range (12-range=shift) and filter index are renewed every ~28 samples.
* - nibble is expanded to a signed 16b sample, reimplemented as:
* short sample = ((nibble << 12) & 0xf000) >> shift
* or: int sample = ((nibble << 28) & 0xf0000000) >> (shift + N)
* - K0/K1 are float coefs are typically redefined with int math in various ways, with non-equivalent rounding:
* (sample + K0*2^N*hist1 + K1*2^N*hist2 + [(2^N)/2]) / 2^N
* (sample + K0*2^N*hist1 + K1*2^N*hist2 + [(2^N)/2]) >> N
* sample + (K0*2^N*hist1 + K1*2^N*hist2)>>N
* sample + (K0*2^N*hist1)>>N + (K1*2^N*hist2)>>N
* ...
* (rounding differences should be inaudible)
*
* There isn't an official implementation, but supposedly CD-ROM controller (which reads CD-XA) pushes
* audio data to the SPU directly (http://wiki.psxdev.ru/index.php/SPU), so probably the same as PS-ADPCM.
* Emus all seem to use approximations:
* - duckstation: N=6, "s + ((h1 * K0) + (h2 * K1) + 32) / 64"; no hist clamp; s clamp;
* - mednafen: N=6, "s + ((h1 * K0) >> 6) + ((h2 * K1) >> 6)"; hist / s clamp;
* - mame: N=6, "s + ((h1 * K0) + (h2 * K1) + 32) >> 6)"; no hist / s clamp;
* - peops: N=10, "s + ((h1 * K0) + (h2 * K1) + 32) >> 10)"; no hist / s clamp;
* - pcsc: N=10, "s + ((h1 * K0) + (h2 * K1)) >> 10)"; hist / s clamp;
* - cedimu: f32, "s + (h1 * k0 + h2 * k1)"; no hist / s clamp
* It's not clear from the diagrams if hist should be clamped (seemingly not), but reportedly not
* clamping improves output waveform spikes, while N=6 seems most common, and +32
* (note <-0 / 64 != <0 >> 6)
*
* PS1 XA is apparently upsampled and interpolated to 44100, vgmstream doesn't simulate this.
* Various XA descendants (PS-ADPCM, EA-XA, NGC DTK, FADPCM, etc) do filters/rounding slightly
* differently too, maybe using one of the above methods in software/CPU, but in XA's case may
* be done like the SNES/SPC700 BRR, with specific per-filter ops rather than a formula.
*
* XA has an 8-bit decoding and "emphasis" modes, that no PS1 game actually uses, but apparently
* are supported by the CD hardware and will play if found. Some odd CD-i game does use 8-bit mode.
* Official "sound quality level" modes:
* - Level A: 37.8hz, 8-bit
* - Level B: 37.8hz, 4-bit
* - Level C: 18.9hz, 4-bit
*
* Info (Green Book): https://www.lscdweb.com/data/downloadables/2/8/cdi_may94_r2.pdf
* BRR info (no$sns): http://problemkaputt.de/fullsnes.htm#snesapudspbrrsamples
* (bsnes): https://github.com/byuu/bsnes/blob/master/bsnes/sfc/dsp/SPC_DSP.cpp#L316
* Note that this is just called "ADPCM" in the "CD-ROM XA" spec (rather than "XA ADPCM" being the actual name).
*/
/* data layout (mono):
* - CD XA audio is divided into sectors ("audio blocks"), each with 18*0x80 frames
* (sectors are handled externally, this decoder only sees 0x80 frames)
* - each frame ("sound group") is divided into 8 interleaved subframes ("sound unit"), with
* 8*0x01 subframe headers x2 ("sound parameters") first then 28*0x04 subframe nibbles ("sound data")
* - subframe headers: 0..3 + repeat 0..3 + 4..7 + repeat 4..7 (where N = subframe N header)
* (repeats may be for error correction, though probably unused)
* header has a "range" N (gain of 2^N, or simplified as a shift) and a "filter" (control gains K0 and K1)
* - subframe nibbles: 32b with nibble0 for subframes 0..8, 32b with nibble1 for subframes 0..8, etc
* (low first: 32b = sf1-n0 sf0-n0 sf3-n0 sf2-n0 sf5-n0 sf4-n0 sf7-n0 sf6-n0, etc)
*
* stereo layout is the same but alternates channels: subframe 0/2/4/6=L, subframe 1/3/5/7=R
*
* example:
* 2A29282A 2A29282A 29292929 29292929
* 0D0D20FB 0D8B011C 0EFA103B 5FEC2F42
* ...
*
* subframe 0: header @ 0x00 or 0x04, 28 nibbles (low) @ 0x10,14,18,1c,20 ... 7c
* subframe 1: header @ 0x01 or 0x05, 28 nibbles (high) @ 0x10,14,18,1c,20 ... 7c
* subframe 2: header @ 0x02 or 0x06, 28 nibbles (low) @ 0x11,15,19,1d,21 ... 7d
* ...
* subframe 7: header @ 0x0b or 0x0f, 28 nibbles (high) @ 0x13,17,1b,1f,23 ... 7f
*
* 8-bit layout is similar but only has 4 subframes + subframe bytes, so half the samples:
* subframe 0: header @ 0x00 or 0x04/08/0c, 28 bytes @ 0x10,14,18,1c,20 ... 7c
* subframe 1: header @ 0x01 or 0x05/09/0d, 28 bytes @ 0x11,16,19,1d,21 ... 7d
* ...
* subframe 3: header @ 0x03 or 0x07/0b/0f, 28 bytes @ 0x13,17,1b,1f,23 ... 7f
*
* XA-EA found in EA SAT games set subframes header like: 0..3 null + 0..3 ok + 4..7 ok + 4..7 null
* so decoder only reads those.
*/

View File

@ -12,6 +12,10 @@ static const int scale_step_adpcmb[16] = {
57, 57, 57, 57, 77, 102, 128, 153,
};
static const int scale_step_capcom[8] = {
58982, 58982, 58982, 58982, 78643, 104858, 131072, 157286,
};
/* look-up for 'mul' IMA's sign*((code&7) * 2 + 1) for every code */
static const int scale_delta[16] = {
1, 3, 5, 7, 9, 11, 13, 15,
@ -59,6 +63,31 @@ static void yamaha_aica_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offs
*hist1 = sample;
}
/* Capcom's version of Yamaha expand */
static void yamaha_capcom_expand_nibble(uint8_t byte, int shift, int32_t* hist1, int32_t* step_size, int16_t *out_sample) {
int code, ucode, delta, sample;
const int scale = 0x200; /* ADPCM state var, but seemingly fixed */
code = (byte >> shift) & 0xf;
ucode = code & 0x7;
delta = (ucode * (*step_size)) >> 2; /* custom (SH2 CPU can't do odd shifts in one op, it seems) */
if (code & 8)
delta = -delta;
sample = *hist1 + delta;
sample = (short)sample; /* clamp not done, but output is always low-ish */
*step_size = ((*step_size) * scale_step_capcom[ucode]) >> 16;
if (*step_size < 0x80) *step_size = 0x80; /* unlike usual 0x7f */
else if (*step_size > 0x6000) *step_size = 0x6000;
*hist1 = sample;
/* OG code adds out sample, but seems to be always 0 (used for mono downmix?) */
sample = ((scale * sample) >> 8) /*+ *out_sample */;
*out_sample = sample;
}
/* info about Yamaha ADPCM as created by official yadpcm.acm (in 'Yamaha-ADPCM-ACM-Driver-100-j')
* - possibly RIFF codec 0x20
@ -107,6 +136,37 @@ void decode_aica(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacin
stream->adpcm_step_index = step_size;
}
/* Capcom/Saturn Yamaha ADPCM, reverse engineered from the exe (codec has no apparent name so CP_YM = Capcom Yamaha) */
void decode_cp_ym(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo) {
int i, sample_count = 0;
int16_t out_sample;
int32_t hist1 = stream->adpcm_history1_16;
int step_size = stream->adpcm_step_index;
/* no header (external setup), pre-clamp for wrong values */
if (step_size < 0x80) step_size = 0x80;
if (step_size > 0x6000) step_size = 0x6000;
for (i = first_sample; i < first_sample + samples_to_do; i++) {
uint8_t byte;
uint32_t offset = is_stereo ?
stream->offset + i : /* stereo: one nibble per channel */
stream->offset + i/2; /* mono: consecutive nibbles */
int shift = is_stereo ?
(!(channel&1) ? 0:4) : /* even = low/L, odd = high/R */
(!(i&1) ? 0:4); /* low nibble first */
byte = read_u8(offset, stream->streamfile);
yamaha_capcom_expand_nibble(byte, shift, &hist1, &step_size, &out_sample);
outbuf[sample_count] = out_sample;
sample_count += channelspacing;
}
stream->adpcm_history1_16 = hist1;
stream->adpcm_step_index = step_size;
}
/* tri-Ace Aska ADPCM, Yamaha ADPCM-B with headered frames (reversed from Android SO's .so)
* implements table with if-else/switchs too but that's too goofy */
void decode_aska(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, size_t frame_size) {

View File

@ -442,6 +442,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM* vgmstream) {
return 0; /* variable (block-controlled) */
case coding_XA:
case coding_XA_EA:
return 28*8 / vgmstream->channels; /* 8 subframes per frame, mono/stereo */
case coding_XA8:
return 28*4 / vgmstream->channels; /* 4 subframes per frame, mono/stereo */
@ -471,6 +472,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM* vgmstream) {
case coding_WS: /* only works if output sample size is 8 bit, which always is for WS ADPCM */
return vgmstream->ws_output_size;
case coding_AICA:
case coding_CP_YM:
return 1;
case coding_AICA_int:
return 2;
@ -659,6 +661,7 @@ int get_vgmstream_frame_size(VGMSTREAM* vgmstream) {
return 0x00; /* variable (block-controlled) */
case coding_XA:
case coding_XA_EA:
case coding_XA8:
return 0x80;
case coding_PSX:
@ -690,6 +693,7 @@ int get_vgmstream_frame_size(VGMSTREAM* vgmstream) {
return vgmstream->current_block_size;
case coding_AICA:
case coding_AICA_int:
case coding_CP_YM:
return 0x01;
case coding_ASKA:
return vgmstream->frame_size;
@ -1005,12 +1009,9 @@ void decode_vgmstream(VGMSTREAM* vgmstream, int samples_written, int samples_to_
}
break;
case coding_XA:
case coding_XA_EA:
case coding_XA8: {
int is_xa8 = (vgmstream->coding_type == coding_XA8);
for (ch = 0; ch < vgmstream->channels; ch++) {
decode_xa(&vgmstream->ch[ch], buffer+ch,
vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch, is_xa8);
}
decode_xa(vgmstream, buffer, samples_to_do);
break;
}
case coding_EA_XA:
@ -1331,6 +1332,15 @@ void decode_vgmstream(VGMSTREAM* vgmstream, int samples_written, int samples_to_
}
break;
}
case coding_CP_YM: {
int is_stereo = (vgmstream->channels > 1);
for (ch = 0; ch < vgmstream->channels; ch++) {
decode_cp_ym(&vgmstream->ch[ch], buffer+ch,
vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch,
is_stereo);
}
break;
}
case coding_ASKA:
for (ch = 0; ch < vgmstream->channels; ch++) {
decode_aska(&vgmstream->ch[ch], buffer+ch,

View File

@ -58,9 +58,7 @@ static const char* extension_list[] = {
//"aif", //common
"aif-Loop",
"aifc", //common?
"aifcl", //fake extension for .aif???
//"aiff", //common
"aiffl", //fake extension for .aif???
"aix",
"akb",
"al",
@ -85,6 +83,7 @@ static const char* extension_list[] = {
"aud",
"audio", //txth/reserved [Grimm Echoes (Android)]
"aus",
"awa", //txth/reserved [Missing Parts Side A (PS2)]
"awb",
"awc",
@ -256,6 +255,7 @@ static const char* extension_list[] = {
"kovs", //fake extension/header id for .kvs
"kno",
"kns",
"koe",
"kraw",
"ktac",
"ktsl2asbin",
@ -335,6 +335,7 @@ static const char* extension_list[] = {
"msa",
"msb",
"msd",
"mse",
"msf",
"mss",
"msv",
@ -360,6 +361,7 @@ static const char* extension_list[] = {
"nop",
"nps",
"npsf", //fake extension/header id for .nps (in bigfiles)
"nsa",
"nsopus",
"nub",
"nub2",
@ -370,6 +372,7 @@ static const char* extension_list[] = {
"nxa",
//"ogg", //common
"ogg_",
"ogl",
"ogv",
"oma", //FFmpeg/not parsed (ATRAC3/ATRAC3PLUS/MP3/LPCM/WMA)
@ -431,6 +434,7 @@ static const char* extension_list[] = {
"sab",
"sad",
"saf",
"sam", //txth/reserved [Lost Kingdoms 2 (GC)]
"sap",
"sb0",
"sb1",
@ -532,6 +536,7 @@ static const char* extension_list[] = {
"txtp",
"tydsp",
"u0",
"ue4opus",
"ulw",
"um3",
@ -551,6 +556,7 @@ static const char* extension_list[] = {
"vbx", //txth/reserved [THE Taxi 2 (PS2)]
"vds",
"vdm",
"vgi", //txth/reserved [Time Crisis II (PS2)]
"vgm", //txth/reserved [Maximo (PS2)]
"vgs",
"vgv",
@ -567,6 +573,7 @@ static const char* extension_list[] = {
"vsv",
"vxn",
"w",
"waa",
"wac",
"wad",
@ -586,6 +593,7 @@ static const char* extension_list[] = {
"wd",
"wem",
"wii",
"wic", //txth/reserved [Road Rash (SAT)-videos]
"wip", //txth/reserved [Colin McRae DiRT (PC)]
"wlv", //txth/reserved [ToeJam & Earl III: Mission to Earth (DC)]
"wmus",
@ -593,6 +601,7 @@ static const char* extension_list[] = {
"wpd",
"wsd",
"wsi",
"wst", //txth/reserved [3jigen Shoujo o Hogo Shimashita (PC)]
"wua",
"wv2",
"wv6",
@ -735,6 +744,7 @@ static const coding_info coding_info_list[] = {
{coding_XA, "CD-ROM XA 4-bit ADPCM"},
{coding_XA8, "CD-ROM XA 8-bit ADPCM"},
{coding_XA_EA, "Electronic Arts XA 4-bit ADPCM"},
{coding_PSX, "Playstation 4-bit ADPCM"},
{coding_PSX_badflags, "Playstation 4-bit ADPCM (bad flags)"},
{coding_PSX_cfg, "Playstation 4-bit ADPCM (configurable)"},
@ -785,6 +795,7 @@ static const coding_info coding_info_list[] = {
{coding_WS, "Westwood Studios VBR ADPCM"},
{coding_AICA, "Yamaha AICA 4-bit ADPCM"},
{coding_AICA_int, "Yamaha AICA 4-bit ADPCM (mono/interleave)"},
{coding_CP_YM, "Capcom Yamaha 4-bit ADPCM"},
{coding_ASKA, "tri-Ace Aska 4-bit ADPCM"},
{coding_NXAP, "Nex NXAP 4-bit ADPCM"},
{coding_TGC, "Tiger Game.com 4-bit ADPCM"},
@ -1118,7 +1129,8 @@ static const meta_info meta_info_list[] = {
{meta_XBOX_HLWAV, "Half-Life 2 .WAV header"},
{meta_MYSPD, "U-Sing .MYSPD header"},
{meta_HIS, "Her Interactive HIS header"},
{meta_PS2_AST, "KOEI AST header"},
{meta_AST_MV, "MicroVision AST header"},
{meta_AST_MMV, "Marvelous AST header"},
{meta_CAPDSP, "Capcom DSP header"},
{meta_DMSG, "Microsoft RIFF DMSG header"},
{meta_PONA_3DO, "Policenauts BGM header"},
@ -1146,7 +1158,7 @@ static const meta_info meta_info_list[] = {
{meta_DSP_XIII, "XIII dsp header"},
{meta_DSP_CABELAS, "Cabelas games .DSP header"},
{meta_PS2_ADM, "Dragon Quest V .ADM raw header"},
{meta_PS2_LPCM, "LPCM header"},
{meta_LPCM_SHADE, "Shade LPCM header"},
{meta_PS2_VMS, "VMS Header"},
{meta_XAU, "XPEC XAU header"},
{meta_GH3_BAR, "Guitar Hero III Mobile .bar"},

View File

@ -1,6 +1,7 @@
#include "layout.h"
#include "../coding/coding.h"
#include "../vgmstream.h"
#include "../util/endianness.h"
/* set up for the block at the given offset */
void block_update_ea_1snh(off_t block_offset, VGMSTREAM* vgmstream) {
@ -8,7 +9,7 @@ void block_update_ea_1snh(off_t block_offset, VGMSTREAM* vgmstream) {
int i;
uint32_t block_id;
size_t block_size = 0, block_header = 0, audio_size = 0;
int32_t (*read_32bit)(off_t,STREAMFILE*) = vgmstream->codec_endian ? read_32bitBE : read_32bitLE;
read_s32_t read_s32 = vgmstream->codec_endian ? read_s32be : read_s32le;
/* EOF reads: signal we have nothing and let the layout fail */
@ -19,27 +20,29 @@ void block_update_ea_1snh(off_t block_offset, VGMSTREAM* vgmstream) {
return;
}
block_id = read_32bitBE(block_offset + 0x00, sf);
block_id = read_u32be(block_offset + 0x00, sf);
/* BE in SAT, but one file may have both BE and LE chunks [FIFA 98 (SAT): movie LE, audio BE] */
if (guess_endianness32bit(block_offset + 0x04, sf))
block_size = read_32bitBE(block_offset + 0x04, sf);
if (guess_endian32(block_offset + 0x04, sf))
block_size = read_u32be(block_offset + 0x04, sf);
else
block_size = read_32bitLE(block_offset + 0x04, sf);
block_size = read_u32le(block_offset + 0x04, sf);
block_header = 0;
if (block_id == 0x31534E68 || block_id == 0x53454144) { /* "1SNh" "SEAD" audio header */
int is_sead = (block_id == 0x53454144);
int is_eacs = read_32bitBE(block_offset + 0x08, sf) == 0x45414353;
int is_zero = read_32bitBE(block_offset + 0x08, sf) == 0x00;
if (block_id == get_id32be("1SNh") || block_id == get_id32be("SEAD")) { /* audio header */
int is_sead = (block_id == get_id32be("SEAD"));
int is_eacs = is_id32be(block_offset + 0x08, sf, "EACS");
int is_zero = read_u32be(block_offset + 0x08, sf) == 0x00;
block_header = (is_eacs || is_zero) ? 0x28 : (is_sead ? 0x14 : 0x2c);
if (block_header >= block_size) /* sometimes has audio data after header */
block_header = 0;
} else if (block_id == 0x31534E64 || block_id == 0x534E4443) { /* "1SNd" "SNDC" audio data */
}
else if (block_id == get_id32be("1SNd") || block_id == get_id32be("SNDC")) {
block_header = 0x08;
} else if (block_id == 0x00000000 || block_id == 0xFFFFFFFF || block_id == 0x31534E65) { /* EOF or "1SNe" */
}
else if (block_id == 0x00000000 || block_id == 0xFFFFFFFF || block_id == get_id32be("1SNe")) { /* EOF */
vgmstream->current_block_samples = -1;
return;
}
@ -72,20 +75,25 @@ void block_update_ea_1snh(off_t block_offset, VGMSTREAM* vgmstream) {
break;
case coding_PSX:
if (vgmstream->codec_config == 1) {/* extra field */
block_header += 0x04;
audio_size -= 0x04;
}
vgmstream->current_block_samples = ps_bytes_to_samples(audio_size, vgmstream->channels);
for (i=0;i<vgmstream->channels;i++) {
for (i = 0; i < vgmstream->channels; i++) {
vgmstream->ch[i].offset = block_offset + block_header + i*(audio_size/vgmstream->channels);
}
break;
case coding_DVI_IMA:
if (vgmstream->codec_config == 1) { /* ADPCM hist */
vgmstream->current_block_samples = read_32bit(block_offset + block_header, sf);
vgmstream->current_block_samples = read_s32(block_offset + block_header, sf);
for(i = 0; i < vgmstream->channels; i++) {
off_t adpcm_offset = block_offset + block_header + 0x04;
vgmstream->ch[i].adpcm_step_index = read_32bit(adpcm_offset + i*0x04 + 0x00*vgmstream->channels, sf);
vgmstream->ch[i].adpcm_history1_32 = read_32bit(adpcm_offset + i*0x04 + 0x04*vgmstream->channels, sf);
vgmstream->ch[i].adpcm_step_index = read_s32(adpcm_offset + i*0x04 + 0x00*vgmstream->channels, sf);
vgmstream->ch[i].adpcm_history1_32 = read_s32(adpcm_offset + i*0x04 + 0x04*vgmstream->channels, sf);
vgmstream->ch[i].offset = adpcm_offset + 0x08*vgmstream->channels;
}

View File

@ -44,36 +44,9 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) {
block_samples = 0; /* layout ignores this */
}
#ifdef VGM_USE_MPEG
/* "SCHl" start block, when decoding multi files pasted together */
if (block_id == 0x5343486C) {
switch(vgmstream->coding_type) {
case coding_MPEG_custom:
case coding_MPEG_layer1:
case coding_MPEG_layer2:
case coding_MPEG_layer3:
case coding_MPEG_ealayer3:
/* need to reset MPEG decoder to reset discards and trailing samples in the buffers */
flush_mpeg(vgmstream->codec_data);
break;
default:
break;
}
}
#endif
/* padding between "SCEl" and next "SCHl" (when subfiles exist) */
if (block_id == 0x00000000)
block_size = 0x04;
/* guard against errors (happens in bad rips/endianness, observed max is vid ~0x20000) */
if (block_size == 0x00 || block_size > 0xFFFFF || block_samples > 0xFFFF) {
block_size = 0x04;
block_samples = 0;
}
/* "SCEl" end chunk should be 32b-aligned, fixes some multi-SCHl [ex. Need for Speed 2 (PC) .eam] */
if (((block_offset + block_size) % 0x04) && block_id == 0x5343456C) {
block_size += 0x04 - ((block_offset + block_size) % 0x04);
if (block_id == 0x00000000 || block_id == 0xFFFFFFFF || block_id == 0x5343456C) { /* EOF */
vgmstream->current_block_samples = -1;
return;
}
}

View File

@ -14,6 +14,9 @@ VGMSTREAM* init_vgmstream_ads(STREAMFILE* sf) {
/* checks */
if (!is_id32be(0x00,sf,"SShd"))
goto fail;
/* .ads: actual extension
* .ss2: demuxed videos (fake?)
* .pcm: Taisho Mononoke Ibunroku (PS2)
@ -23,20 +26,20 @@ VGMSTREAM* init_vgmstream_ads(STREAMFILE* sf) {
if (!check_extensions(sf, "ads,ss2,pcm,adx,,800"))
goto fail;
if (!is_id32be(0x00,sf,"SShd") &&
!is_id32be(0x20,sf,"SSbd"))
goto fail;
if (read_32bitLE(0x04,sf) != 0x18 && /* standard header size */
read_32bitLE(0x04,sf) != 0x20) /* True Fortune (PS2) */
if (read_u32le(0x04,sf) != 0x18 && /* standard header size */
read_u32le(0x04,sf) != 0x20 && /* True Fortune (PS2) */
read_u32le(0x04,sf) != get_streamfile_size(sf) - 0x08) /* Katamari Damacy videos */
goto fail;
if (!is_id32be(0x20,sf,"SSbd"))
goto fail;
/* base values (a bit unorderly since devs hack ADS too much and detection is messy) */
{
codec = read_32bitLE(0x08,sf);
sample_rate = read_32bitLE(0x0C,sf);
channels = read_32bitLE(0x10,sf); /* up to 4 [Eve of Extinction (PS2)] */
interleave = read_32bitLE(0x14,sf); /* set even when mono */
codec = read_u32le(0x08,sf);
sample_rate = read_s32le(0x0C,sf);
channels = read_s32le(0x10,sf); /* up to 4 [Eve of Extinction (PS2)] */
interleave = read_s32le(0x14,sf); /* set even when mono */
switch(codec) {
@ -61,7 +64,7 @@ VGMSTREAM* init_vgmstream_ads(STREAMFILE* sf) {
case 0x00: /* PCM16BE from official docs, probably never used */
default:
VGM_LOG("ADS: unknown codec\n");
vgm_logi("ADS: unknown codec\n");
goto fail;
}
}
@ -127,8 +130,8 @@ VGMSTREAM* init_vgmstream_ads(STREAMFILE* sf) {
{
uint32_t loop_start, loop_end;
loop_start = read_32bitLE(0x18,sf);
loop_end = read_32bitLE(0x1C,sf);
loop_start = read_u32le(0x18,sf);
loop_end = read_u32le(0x1C,sf);
loop_flag = 0;
@ -144,7 +147,7 @@ VGMSTREAM* init_vgmstream_ads(STREAMFILE* sf) {
loop_start_offset = loop_start * 0x10;
ignore_silent_frame_capcom = 1;
}
else if (read_32bitBE(0x28,sf) == 0x50414421) { /* "PAD!" padding until 0x800 */
else if (is_id32be(0x28,sf, "PAD!")) { /* padding until 0x800 */
/* Super Galdelic Hour: loop_start is PCM bytes */
loop_flag = 1;
loop_start_sample = loop_start / 2 / channels;

View File

@ -80,14 +80,14 @@ VGMSTREAM* init_vgmstream_adx_subkey(STREAMFILE* sf, uint16_t subkey) {
/* encryption */
if (version == 0x0408) {
if (find_adx_key(sf, 8, &xor_start, &xor_mult, &xor_add, 0)) {
if (!find_adx_key(sf, 8, &xor_start, &xor_mult, &xor_add, 0)) {
vgm_logi("ADX: decryption keystring not found\n");
}
coding_type = coding_CRI_ADX_enc_8;
version = 0x0400;
}
else if (version == 0x0409) {
if (find_adx_key(sf, 9, &xor_start, &xor_mult, &xor_add, subkey)) {
if (!find_adx_key(sf, 9, &xor_start, &xor_mult, &xor_add, subkey)) {
vgm_logi("ADX: decryption keycode not found\n");
}
coding_type = coding_CRI_ADX_enc_9;

View File

@ -6,23 +6,24 @@ VGMSTREAM* init_vgmstream_aif_asobo(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
off_t start_offset;
size_t data_size;
int loop_flag, channel_count;
int loop_flag, channels, sample_rate;
/* checks */
/* aif: standard, .laif/aiffl: for plugins */
if ( !check_extensions(sf,"aif,laif,aiffl") )
if (read_u16le(0x00,sf) != 0x69) /* fmt chunk with Xbox codec */
goto fail;
if ((uint16_t)read_16bitLE(0x00,sf) != 0x69) /* Xbox codec */
/* aif: standard, .laif: for plugins */
if ( !check_extensions(sf,"aif,laif") )
goto fail;
channel_count = read_16bitLE(0x02,sf); /* assumed, only stereo is known */
if (channel_count != 2) goto fail;
channels = read_u16le(0x02,sf); /* assumed, only stereo is known */
if (channels != 2) goto fail;
/* 0x08: ? */
if ((uint16_t)read_16bitLE(0x0c,sf) != 0x24*channel_count) /* Xbox block */
sample_rate = read_u32le(0x04,sf);
/* 0x08: bitrate */
if (read_u16le(0x0c,sf) != 0x24 * channels) /* Xbox block */
goto fail;
if ((uint16_t)read_16bitLE(0x0e,sf) != 0x04) /* Xbox bps */
if (read_u16le(0x0e,sf) != 0x04) /* Xbox bps */
goto fail;
loop_flag = 0;
@ -31,17 +32,17 @@ VGMSTREAM* init_vgmstream_aif_asobo(STREAMFILE* sf) {
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_AIF_ASOBO;
vgmstream->sample_rate = read_32bitLE(0x04,sf);
vgmstream->num_samples = xbox_ima_bytes_to_samples(data_size,channel_count);
vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = xbox_ima_bytes_to_samples(data_size, channels);
vgmstream->coding_type = coding_XBOX_IMA;
vgmstream->layout_type = layout_none;
if ( !vgmstream_open_stream(vgmstream, sf, start_offset) )
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
goto fail;
return vgmstream;

View File

@ -81,9 +81,11 @@ VGMSTREAM* init_vgmstream_aifc(STREAMFILE* sf) {
/* checks */
if (!is_id32be(0x00,sf, "FORM"))
goto fail;
/* .aif: common (AIFF or AIFC), .aiff: common AIFF, .aifc: common AIFC
* .laif/laifc/laiff: for plugins
* .aifcl/aiffl: for plugins?
* .laif/laiff/laifc: for plugins
* .cbd2: M2 games
* .bgm: Super Street Fighter II Turbo (3DO)
* .acm: Crusader - No Remorse (SAT)
@ -91,15 +93,17 @@ VGMSTREAM* init_vgmstream_aifc(STREAMFILE* sf) {
* .ai: Dragon Force (SAT)
* (extensionless: Doom (3DO)
* .fda: Homeworld 2 (PC)
* .n64: Turok (N64) src */
* .n64: Turok (N64) src
* .pcm: Road Rash (SAT)
*/
if (check_extensions(sf, "aif,laif,")) {
is_aifc_ext = 1;
is_aiff_ext = 1;
}
else if (check_extensions(sf, "aifc,laifc,aifcl,afc,cbd2,bgm,fda,n64")) {
else if (check_extensions(sf, "aifc,laifc,afc,cbd2,bgm,fda,n64")) {
is_aifc_ext = 1;
}
else if (check_extensions(sf, "aiff,laiff,acm,adp,ai,aiffl")) {
else if (check_extensions(sf, "aiff,laiff,acm,adp,ai,pcm")) {
is_aiff_ext = 1;
}
else {
@ -107,17 +111,16 @@ VGMSTREAM* init_vgmstream_aifc(STREAMFILE* sf) {
}
file_size = get_streamfile_size(sf);
if (read_u32be(0x00,sf) != 0x464F524D && /* "FORM" */
read_u32be(0x04,sf)+0x08 != file_size)
if (read_u32be(0x04,sf) + 0x08 != file_size)
goto fail;
/* AIFF originally allowed only PCM (non-compressed) audio, so newer AIFC was added,
* though some AIFF with other codecs exist */
if (read_u32be(0x08,sf) == 0x41494643) { /* "AIFC" */
if (is_id32be(0x08,sf, "AIFC")) {
if (!is_aifc_ext) goto fail;
is_aifc = 1;
}
else if (read_u32be(0x08,sf) == 0x41494646) { /* "AIFF" */
else if (is_id32be(0x08,sf, "AIFF")) {
if (!is_aiff_ext) goto fail;
is_aiff = 1;
}
@ -160,9 +163,7 @@ VGMSTREAM* init_vgmstream_aifc(STREAMFILE* sf) {
if (comm_found) goto fail;
comm_found = 1;
channels = read_u16be(offset + 0x00,sf);
if (channels <= 0) goto fail;
channels = read_u16be(offset + 0x00,sf);
sample_count = read_u32be(offset + 0x02,sf); /* sample_frames in theory, depends on codec */
sample_size = read_u16be(offset + 0x06,sf);
sample_rate = read_f80be(offset + 0x08,sf);
@ -233,7 +234,7 @@ VGMSTREAM* init_vgmstream_aifc(STREAMFILE* sf) {
coding_type = coding_PCM16BE;
interleave = 2;
break;
case 4: /* Crusader: No Remorse (SAT), Road Rash (3DO) */
case 4: /* Crusader: No Remorse (SAT), Road Rash (3DO/SAT) */
coding_type = coding_XA;
break;
default:

View File

@ -0,0 +1,55 @@
#include "meta.h"
#include "../coding/coding.h"
/* AST - from Marvelous(?) games [Katekyou Hitman Reborn! Dream Hyper Battle! (PS2), Binchou-tan: Shiawasegoyomi (PS2)] */
VGMSTREAM* init_vgmstream_ast_mmv(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
uint32_t start_offset, data_size;
int loop_flag, channels, sample_rate, interleave;
/* checks */
if (!is_id32be(0x00,sf, "AST\0"))
goto fail;
/* .ast: from executables (usually found in bigfiles) */
if (!check_extensions(sf,"ast"))
goto fail;
data_size = read_u32le(0x04, sf);
if (data_size != get_streamfile_size(sf))
goto fail;
sample_rate = read_s32le(0x08,sf);
channels = read_32bitLE(0x0C,sf);
interleave = read_u32le(0x10,sf);
/* 0x14: number of blocks */
/* 0x18: ? (not fully related to size/time) */
/* 0x1c: f32 time */
loop_flag = 0;
start_offset = 0x100;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channels,loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_AST_MMV;
vgmstream->num_samples = ps_bytes_to_samples(data_size - start_offset, channels);
vgmstream->sample_rate = sample_rate;
vgmstream->interleave_block_size = interleave;
vgmstream->layout_type = layout_interleave;
vgmstream->coding_type = coding_PSX;
read_string(vgmstream->stream_name,0x20, 0x20,sf);
if (!vgmstream_open_stream(vgmstream,sf,start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -0,0 +1,53 @@
#include "meta.h"
#include "../coding/coding.h"
/* AST - from MicroVision lib games [P.T.O. IV (PS2), Naval Ops: Warship Gunner (PS2)] */
VGMSTREAM* init_vgmstream_ast_mv(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
uint32_t start_offset, data_size, check;
int loop_flag, channels, interleave, sample_rate;
/* checks */
if (!is_id32be(0x00,sf, "AST\0"))
goto fail;
if (!check_extensions(sf,"ast"))
goto fail;
channels = 2;
sample_rate = read_s32le(0x04, sf);
interleave = read_u32le(0x08,sf);
data_size = read_u32le(0x0c,sf); /* may have garbage */
check = read_u32be(0x10, sf);
/* rest: null/garbage */
loop_flag = 0;
start_offset = 0x800;
/* there is a variation in .ikm (Zwei), with loops and different start offset */
if (check != 0x20002000 && /* NO:WG (garbage up to start) */
check != 0x00000000) /* PTO4 */
goto fail;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = ps_bytes_to_samples(data_size - start_offset, channels);
vgmstream->interleave_block_size = interleave;
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_interleave;
vgmstream->meta_type = meta_AST_MV;
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -1,35 +1,34 @@
#include "meta.h"
#include "../coding/coding.h"
/* .BAF - Bizarre Creations bank file [Blur (PS3), Project Gotham Racing 4 (X360), Geometry Wars (PC)] */
VGMSTREAM * init_vgmstream_baf(STREAMFILE *streamFile) {
VGMSTREAM * init_vgmstream_baf(STREAMFILE *sf) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset, header_offset, name_offset;
size_t stream_size;
int loop_flag, channel_count, sample_rate, version, codec, tracks;
int total_subsongs, target_subsong = streamFile->stream_index;
int32_t (*read_32bit)(off_t,STREAMFILE*);
uint32_t channel_count, sample_rate, num_samples, version, codec, tracks;
int loop_flag, total_subsongs, target_subsong = sf->stream_index;
uint32_t (*read_u32)(off_t,STREAMFILE*);
/* checks */
if (!check_extensions(streamFile, "baf"))
if (!is_id32be(0x00, sf, "BANK"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x42414E4B) /* "BANK" */
if (!check_extensions(sf, "baf"))
goto fail;
/* use BANK size to check endianness */
if (guess_endianness32bit(0x04,streamFile)) {
read_32bit = read_32bitBE;
if (guess_endianness32bit(0x04,sf)) {
read_u32 = read_u32be;
} else {
read_32bit = read_32bitLE;
read_u32 = read_u32le;
}
/* 0x04: bank size */
version = read_32bit(0x08,streamFile);
version = read_u32(0x08,sf);
if (version != 0x03 && version != 0x04 && version != 0x05)
goto fail;
total_subsongs = read_32bit(0x0c,streamFile);
total_subsongs = read_u32(0x0c,sf);
if (target_subsong == 0) target_subsong = 1;
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
/* - in v3 */
@ -43,83 +42,96 @@ VGMSTREAM * init_vgmstream_baf(STREAMFILE *streamFile) {
/* find target WAVE chunk */
{
int i;
off_t offset = read_32bit(0x04, streamFile);
off_t offset = read_u32(0x04, sf);
for (i = 0; i < total_subsongs; i++) {
if (i+1 == target_subsong)
break;
offset += read_32bit(offset+0x04, streamFile); /* WAVE size, variable per codec */
offset += read_u32(offset+0x04, sf); /* WAVE size, variable per codec */
/* skip companion "CUE " (found in 007: Blood Stone, contains segment cues) */
if (read_32bitBE(offset+0x00, streamFile) == 0x43554520) {
offset += read_32bit(offset+0x04, streamFile); /* CUE size */
if (is_id32be(offset+0x00, sf, "CUE ")) {
offset += read_u32(offset+0x04, sf); /* CUE size */
}
}
header_offset = offset;
}
/* parse header */
if (read_32bitBE(header_offset+0x00, streamFile) != 0x57415645) /* "WAVE" */
if (!is_id32be(header_offset+0x00, sf, "WAVE"))
goto fail;
codec = read_32bit(header_offset+0x08, streamFile);
codec = read_u32(header_offset+0x08, sf);
name_offset = header_offset + 0x0c;
start_offset = read_32bit(header_offset+0x2c, streamFile);
stream_size = read_32bit(header_offset+0x30, streamFile);
start_offset = read_u32(header_offset+0x2c, sf);
stream_size = read_u32(header_offset+0x30, sf);
tracks = 0;
switch(codec) {
case 0x03:
case 0x03: /* PCM16LE */
switch(version) {
case 0x03: /* Geometry Wars (PC) */
sample_rate = read_32bit(header_offset+0x38, streamFile);
channel_count = read_32bit(header_offset+0x40, streamFile);
sample_rate = read_u32(header_offset+0x38, sf);
channel_count = read_u32(header_offset+0x40, sf);
/* no actual flag, just loop +15sec songs */
loop_flag = (pcm_bytes_to_samples(stream_size, channel_count, 16) > 15*sample_rate);
break;
case 0x04: /* Project Gotham Racing 4 (X360) */
sample_rate = read_32bit(header_offset+0x3c, streamFile);
channel_count = read_32bit(header_offset+0x44, streamFile);
loop_flag = read_8bit(header_offset+0x4b, streamFile);
sample_rate = read_u32(header_offset+0x3c, sf);
channel_count = read_u32(header_offset+0x44, sf);
loop_flag = read_u8(header_offset+0x4b, sf);
break;
default:
goto fail;
}
num_samples = pcm_bytes_to_samples(stream_size, channel_count, 16);
break;
case 0x07:
switch(version) {
case 0x07: /* PSX ADPCM (0x21 frame size) */
if (version == 0x04 && read_u32(header_offset + 0x3c, sf) != 0) {
/* Blur (Prototype) (PS3) */
sample_rate = read_u32(header_offset+0x3c, sf);
channel_count = read_u32(header_offset+0x44, sf);
loop_flag = read_u8(header_offset+0x4b, sf);
/* mini-header at the start of the stream */
num_samples = read_u32le(start_offset+0x04, sf) / 0x02; /* PCM size? */
start_offset += read_u32le(start_offset+0x00, sf);
break;
}
switch (version) {
case 0x04: /* Blur (PS3) */
case 0x05: /* James Bond 007: Blood Stone (X360) */
sample_rate = read_32bit(header_offset+0x40, streamFile);
loop_flag = read_8bit(header_offset+0x48, streamFile);
tracks = read_8bit(header_offset+0x49, streamFile);
channel_count = read_8bit(header_offset+0x4b, streamFile);
sample_rate = read_u32(header_offset+0x40, sf);
num_samples = read_u32(header_offset+0x44, sf);
loop_flag = read_u8(header_offset+0x48, sf);
tracks = read_u8(header_offset+0x49, sf);
channel_count = read_u8(header_offset+0x4b, sf);
if (tracks) {
channel_count = channel_count * tracks;
}
break;
default:
goto fail;
}
break;
case 0x08:
case 0x08: /* XMA1 */
switch(version) {
case 0x04: /* Project Gotham Racing (X360) */
sample_rate = read_32bit(header_offset+0x3c, streamFile);
channel_count = read_32bit(header_offset+0x44, streamFile);
loop_flag = read_8bit(header_offset+0x54, streamFile) != 0;
sample_rate = read_u32(header_offset+0x3c, sf);
channel_count = read_u32(header_offset+0x44, sf);
loop_flag = read_u8(header_offset+0x54, sf) != 0;
break;
case 0x05: /* James Bond 007: Blood Stone (X360) */
sample_rate = read_32bit(header_offset+0x40, streamFile);
channel_count = read_32bit(header_offset+0x48, streamFile);
loop_flag = read_8bit(header_offset+0x58, streamFile) != 0;
sample_rate = read_u32(header_offset+0x40, sf);
channel_count = read_u32(header_offset+0x48, sf);
loop_flag = read_u8(header_offset+0x58, sf) != 0;
break;
default:
@ -150,9 +162,9 @@ VGMSTREAM * init_vgmstream_baf(STREAMFILE *streamFile) {
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x02;
vgmstream->num_samples = pcm_bytes_to_samples(stream_size, channel_count, 16);
vgmstream->num_samples = num_samples;
vgmstream->loop_start_sample = 0;
vgmstream->loop_end_sample = vgmstream->num_samples;
vgmstream->loop_end_sample = num_samples;
break;
case 0x07:
@ -160,9 +172,9 @@ VGMSTREAM * init_vgmstream_baf(STREAMFILE *streamFile) {
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x21;
vgmstream->num_samples = read_32bit(header_offset+0x44, streamFile);
vgmstream->num_samples = num_samples;
vgmstream->loop_start_sample = 0;
vgmstream->loop_end_sample = vgmstream->num_samples;
vgmstream->loop_end_sample = num_samples;
break;
#ifdef VGM_USE_FFMPEG
@ -170,8 +182,8 @@ VGMSTREAM * init_vgmstream_baf(STREAMFILE *streamFile) {
uint8_t buf[0x100];
int bytes;
bytes = ffmpeg_make_riff_xma1(buf,0x100, vgmstream->num_samples, stream_size, vgmstream->channels, vgmstream->sample_rate, 0);
vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,stream_size);
bytes = ffmpeg_make_riff_xma1(buf,0x100, 0, stream_size, vgmstream->channels, vgmstream->sample_rate, 0);
vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf,bytes, start_offset,stream_size);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
@ -185,18 +197,18 @@ VGMSTREAM * init_vgmstream_baf(STREAMFILE *streamFile) {
msd.data_offset = start_offset;
msd.data_size = stream_size;
msd.loop_flag = loop_flag;
msd.loop_start_b = read_32bit(header_offset+0x4c, streamFile);
msd.loop_end_b = read_32bit(header_offset+0x50, streamFile);
msd.loop_start_subframe = (read_8bit(header_offset+0x55, streamFile) >> 0) & 0x0f;
msd.loop_end_subframe = (read_8bit(header_offset+0x55, streamFile) >> 4) & 0x0f;
xma_get_samples(&msd, streamFile);
msd.loop_start_b = read_u32(header_offset+0x4c, sf);
msd.loop_end_b = read_u32(header_offset+0x50, sf);
msd.loop_start_subframe = (read_u8(header_offset+0x55, sf) >> 0) & 0x0f;
msd.loop_end_subframe = (read_u8(header_offset+0x55, sf) >> 4) & 0x0f;
xma_get_samples(&msd, sf);
vgmstream->num_samples = msd.num_samples; /* also at 0x58, but unreliable? */
vgmstream->loop_start_sample = msd.loop_start_sample;
vgmstream->loop_end_sample = msd.loop_end_sample;
}
xma_fix_raw_samples_ch(vgmstream, streamFile, start_offset, stream_size, channel_count, 1,1);
xma_fix_raw_samples_ch(vgmstream, sf, start_offset, stream_size, channel_count, 1,1);
break;
}
#endif
@ -206,10 +218,10 @@ VGMSTREAM * init_vgmstream_baf(STREAMFILE *streamFile) {
goto fail;
}
read_string(vgmstream->stream_name,0x20+1, name_offset,streamFile);
read_string(vgmstream->stream_name,0x20+1, name_offset,sf);
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
goto fail;
return vgmstream;
@ -264,7 +276,7 @@ VGMSTREAM * init_vgmstream_baf_badrip(STREAMFILE *streamFile) {
vgmstream->interleave_block_size = frame_size;
vgmstream->meta_type = meta_BAF;
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
if (!vgmstream_open_stream(vgmstream, streamFile, start_offset))
goto fail;
return vgmstream;

View File

@ -21,7 +21,7 @@ typedef struct _BARSTREAMFILE {
/*static*/ STREAMFILE *wrap_bar_STREAMFILE(STREAMFILE *file);
static size_t read_bar(BARSTREAMFILE *streamFile, uint8_t *dest, off_t offset, size_t length) {
static size_t read_bar(BARSTREAMFILE *streamFile, uint8_t *dest, offv_t offset, size_t length) {
off_t i;
size_t read_length = streamFile->real_file->read(streamFile->real_file, dest, offset, length);
@ -36,7 +36,7 @@ static size_t get_size_bar(BARSTREAMFILE *streamFile) {
return streamFile->real_file->get_size(streamFile->real_file);
}
static size_t get_offset_bar(BARSTREAMFILE *streamFile) {
static offv_t get_offset_bar(BARSTREAMFILE *streamFile) {
return streamFile->real_file->get_offset(streamFile->real_file);
}

View File

@ -1,41 +1,38 @@
#include <math.h>
#include "meta.h"
#include "../coding/coding.h"
#include "../util/endianness.h"
typedef enum { PSX, PCM16, ATRAC9, HEVAG } bnk_codec;
/* .BNK - Sony's Scream Tool bank format [Puyo Puyo Tetris (PS4), NekoBuro: Cats Block (Vita)] */
VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
off_t start_offset, stream_offset, name_offset = 0;
size_t stream_size, interleave = 0;
off_t sblk_offset, data_offset;
size_t data_size;
int channel_count = 0, loop_flag, sample_rate, parts, sblk_version, big_endian;
uint32_t start_offset, stream_offset, name_offset = 0;
uint32_t sblk_offset, data_offset;
uint32_t stream_size, data_size, interleave = 0;
int channels = 0, loop_flag, sample_rate, parts, sblk_version, big_endian;
int loop_start = 0, loop_end = 0;
uint32_t pitch, flags;
uint32_t atrac9_info = 0;
int total_subsongs, target_subsong = sf->stream_index;
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL;
float (*read_f32)(off_t,STREAMFILE*) = NULL;
read_u16_t read_u16;
read_u32_t read_u32;
read_f32_t read_f32;
bnk_codec codec;
/* checks */
if (!check_extensions(sf, "bnk"))
goto fail;
/* bnk version */
if (read_32bitBE(0x00,sf) == 0x00000003) { /* PS3 */
read_32bit = read_32bitBE;
read_16bit = read_16bitBE;
if (read_u32be(0x00,sf) == 0x03) { /* PS3 */
read_u32 = read_u32be;
read_u16 = read_u16be;
read_f32 = read_f32be;
big_endian = 1;
}
else if (read_32bitBE(0x00,sf) == 0x03000000) { /* PS2/Vita/PS4 */
read_32bit = read_32bitLE;
read_16bit = read_16bitLE;
else if (read_u32le(0x00,sf) == 0x03) { /* PS2/Vita/PS4 */
read_u32 = read_u32le;
read_u16 = read_u16le;
read_f32 = read_f32le;
big_endian = 0;
}
@ -43,13 +40,18 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) {
goto fail;
}
parts = read_32bit(0x04,sf);
/* checks */
if (!check_extensions(sf, "bnk"))
goto fail;
parts = read_u32(0x04,sf);
if (parts < 2 || parts > 3) goto fail;
sblk_offset = read_32bit(0x08,sf);
sblk_offset = read_u32(0x08,sf);
/* 0x0c: sklb size */
data_offset = read_32bit(0x10,sf);
data_size = read_32bit(0x14,sf);
data_offset = read_u32(0x10,sf);
data_size = read_u32(0x14,sf);
/* when sblk_offset >= 0x20: */
/* 0x18: ZLSD small footer, rare [Yakuza 6's Puyo Puyo (PS4)] */
/* 0x1c: ZLSD size */
@ -60,30 +62,30 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) {
/* SBlk part: parse header */
if (read_32bit(sblk_offset+0x00,sf) != 0x6B6C4253) /* "klBS" (SBlk = sample block?) */
if (read_u32(sblk_offset+0x00,sf) != get_id32be("klBS")) /* SBlk = sample block? */
goto fail;
sblk_version = read_32bit(sblk_offset+0x04,sf);
sblk_version = read_u32(sblk_offset+0x04,sf);
/* 0x08: possibly when sblk_version=0x0d, 0x03=Vita, 0x06=PS4 */
//;VGM_LOG("BNK: sblk_offset=%lx, data_offset=%lx, sblk_version %x\n", sblk_offset, data_offset, sblk_version);
{
int i;
off_t table1_offset, table2_offset, table3_offset, table4_offset;
size_t section_entries, material_entries, stream_entries;
size_t table1_entry_size;
off_t table1_suboffset, table2_suboffset;
off_t table2_entry_offset = 0, table3_entry_offset = 0;
uint32_t table1_offset, table2_offset, table3_offset, table4_offset;
uint32_t section_entries, material_entries, stream_entries;
uint32_t table1_entry_size;
uint32_t table1_suboffset, table2_suboffset;
uint32_t table2_entry_offset = 0, table3_entry_offset = 0;
int table4_entry_id = -1;
off_t table4_entries_offset, table4_names_offset;
uint32_t table4_entries_offset, table4_names_offset;
switch(sblk_version) {
case 0x01: /* Ratchet & Clank (PS2) */
section_entries = (uint16_t)read_16bit(sblk_offset+0x16,sf); /* entry size: ~0x0c */
material_entries = (uint16_t)read_16bit(sblk_offset+0x18,sf); /* entry size: ~0x28 */
stream_entries = (uint16_t)read_16bit(sblk_offset+0x1a,sf); /* entry size: none (count) */
table1_offset = sblk_offset + read_32bit(sblk_offset+0x1c,sf);
table2_offset = sblk_offset + read_32bit(sblk_offset+0x20,sf);
section_entries = read_u16(sblk_offset+0x16,sf); /* entry size: ~0x0c */
material_entries = read_u16(sblk_offset+0x18,sf); /* entry size: ~0x28 */
stream_entries = read_u16(sblk_offset+0x1a,sf); /* entry size: none (count) */
table1_offset = sblk_offset + read_u32(sblk_offset+0x1c,sf);
table2_offset = sblk_offset + read_u32(sblk_offset+0x20,sf);
table3_offset = table2_offset; /* mixed table in this version */
table4_offset = 0; /* not included */
@ -95,14 +97,15 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) {
case 0x03: /* Yu-Gi-Oh! GX - The Beginning of Destiny (PS2) */
case 0x04: /* Test banks */
case 0x05: /* Ratchet & Clank (PS3) */
case 0x08: /* Playstation Home Arcade (Vita) */
case 0x09: /* Puyo Puyo Tetris (PS4) */
section_entries = (uint16_t)read_16bit(sblk_offset+0x16,sf); /* entry size: ~0x0c */
material_entries = (uint16_t)read_16bit(sblk_offset+0x18,sf); /* entry size: ~0x08 */
stream_entries = (uint16_t)read_16bit(sblk_offset+0x1a,sf); /* entry size: ~0x18 + variable */
table1_offset = sblk_offset + read_32bit(sblk_offset+0x1c,sf);
table2_offset = sblk_offset + read_32bit(sblk_offset+0x20,sf);
table3_offset = sblk_offset + read_32bit(sblk_offset+0x34,sf);
table4_offset = sblk_offset + read_32bit(sblk_offset+0x38,sf);
section_entries = read_u16(sblk_offset+0x16,sf); /* entry size: ~0x0c */
material_entries = read_u16(sblk_offset+0x18,sf); /* entry size: ~0x08 */
stream_entries = read_u16(sblk_offset+0x1a,sf); /* entry size: ~0x18 + variable */
table1_offset = sblk_offset + read_u32(sblk_offset+0x1c,sf);
table2_offset = sblk_offset + read_u32(sblk_offset+0x20,sf);
table3_offset = sblk_offset + read_u32(sblk_offset+0x34,sf);
table4_offset = sblk_offset + read_u32(sblk_offset+0x38,sf);
table1_entry_size = 0x0c;
table1_suboffset = 0x08;
@ -111,13 +114,13 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) {
case 0x0d: /* Polara (Vita), Crypt of the Necrodancer (Vita) */
case 0x0e: /* Yakuza 6's Puyo Puyo (PS4) */
table1_offset = sblk_offset + read_32bit(sblk_offset+0x18,sf);
table2_offset = sblk_offset + read_32bit(sblk_offset+0x1c,sf);
table3_offset = sblk_offset + read_32bit(sblk_offset+0x2c,sf);
table4_offset = sblk_offset + read_32bit(sblk_offset+0x30,sf);
section_entries = (uint16_t)read_16bit(sblk_offset+0x38,sf); /* entry size: ~0x24 */
material_entries = (uint16_t)read_16bit(sblk_offset+0x3a,sf); /* entry size: ~0x08 */
stream_entries = (uint16_t)read_16bit(sblk_offset+0x3c,sf); /* entry size: ~0x5c + variable */
table1_offset = sblk_offset + read_u32(sblk_offset+0x18,sf);
table2_offset = sblk_offset + read_u32(sblk_offset+0x1c,sf);
table3_offset = sblk_offset + read_u32(sblk_offset+0x2c,sf);
table4_offset = sblk_offset + read_u32(sblk_offset+0x30,sf);
section_entries = read_u16(sblk_offset+0x38,sf); /* entry size: ~0x24 */
material_entries = read_u16(sblk_offset+0x3a,sf); /* entry size: ~0x08 */
stream_entries = read_u16(sblk_offset+0x3c,sf); /* entry size: ~0x5c + variable */
table1_entry_size = 0x24;
table1_suboffset = 0x0c;
@ -125,11 +128,11 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) {
break;
default:
VGM_LOG("BNK: unknown version %x\n", sblk_version);
vgm_logi("BNK: unknown version %x (report)\n", sblk_version);
goto fail;
}
//;VGM_LOG("BNK: table offsets=%lx, %lx, %lx, %lx\n", table1_offset,table2_offset,table3_offset,table4_offset);
//;VGM_LOG("BNK: table offsets=%x, %x, %x, %x\n", table1_offset,table2_offset,table3_offset,table4_offset);
//;VGM_LOG("BNK: table entries=%i, %i, %i\n", section_entries,material_entries,stream_entries);
@ -159,7 +162,7 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) {
*/
for (i = 0; i < material_entries; i++) {
uint8_t table2_type = read_32bit(table2_offset + (i*0x28) + 0x00, sf);
uint32_t table2_type = read_u32(table2_offset + (i*0x28) + 0x00, sf);
if (table2_type != 0x01)
continue;
@ -179,7 +182,7 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) {
for (i = 0; i < material_entries; i++) {
uint32_t table2_value, table2_subinfo, table2_subtype;
table2_value = (uint32_t)read_32bit(table2_offset+(i*0x08)+table2_suboffset+0x00,sf);
table2_value = read_u32(table2_offset+(i*0x08)+table2_suboffset+0x00,sf);
table2_subinfo = (table2_value >> 0) & 0xFFFF;
table2_subtype = (table2_value >> 16) & 0xFFFF;
if (table2_subtype != 0x100)
@ -214,54 +217,31 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) {
case 0x03:
case 0x04:
case 0x05:
case 0x08:
case 0x09:
pitch = (uint8_t)read_8bit(table3_offset+table3_entry_offset+0x02,sf);
flags = (uint8_t)read_8bit(table3_offset+table3_entry_offset+0x0f,sf);
stream_offset = read_32bit(table3_offset+table3_entry_offset+0x10,sf);
stream_size = read_32bit(table3_offset+table3_entry_offset+0x14,sf);
pitch = read_u8(table3_offset+table3_entry_offset+0x02,sf);
flags = read_u8(table3_offset+table3_entry_offset+0x0f,sf);
stream_offset = read_u32(table3_offset+table3_entry_offset+0x10,sf);
stream_size = read_u32(table3_offset+table3_entry_offset+0x14,sf);
/* must use some log/formula but whatevs */
switch(pitch) {
case 0xC6: sample_rate = 50000; break; //?
case 0xC4: sample_rate = 48000; break;
case 0xC3: sample_rate = 46000; break; //?
case 0xC2: sample_rate = 44100; break;
case 0xBC: sample_rate = 36000; break; //?
case 0xBA: sample_rate = 32000; break; //?
case 0xB9: sample_rate = 30000; break; //?
case 0xB8: sample_rate = 28000; break; //?
case 0xB6: sample_rate = 22050; break;
case 0xB4: sample_rate = 18000; break; //?
case 0xB2: sample_rate = 16000; break; //?
case 0xB0: sample_rate = 15000; break; //?
case 0xAF: sample_rate = 14000; break; //?
case 0xAE: sample_rate = 13000; break; //?
case 0xAD: sample_rate = 12500; break; //?
case 0xAC: sample_rate = 12000; break; //?
case 0xAB: sample_rate = 11050; break; //?
case 0xAA: sample_rate = 11025; break;
case 0xA9: sample_rate = 10000; break; //?
case 0xA8: sample_rate = 9000; break; //?
case 0xA7: sample_rate = 8000; break; //?
case 0xA6: sample_rate = 7000; break; //?
case 0xA5: sample_rate = 6500; break; //?
case 0xA4: sample_rate = 6000; break; //?
case 0xA3: sample_rate = 5800; break; //?
case 0xA2: sample_rate = 5400; break; //?
case 0xA1: sample_rate = 5000; break; //?
case 0x9d: sample_rate = 4000; break; //?
case 0x9c: sample_rate = 3500; break; //?
default:
VGM_LOG("BNK: unknown pitch %x\n", pitch);
goto fail;
/* approximate note-to-hz, not sure about real formula (observed from 0x9a to 0xc6)
* using (rate) * 2 ^ ((pitch - note) / 12) but not correct? (may be rounded) */
sample_rate = 614.0 * pow(2.0, (float)(pitch - 0x70) / 12.0);
break;
}
break;
case 0x0d:
case 0x0e:
flags = (uint8_t)read_8bit(table3_offset+table3_entry_offset+0x12,sf);
stream_offset = read_32bit(table3_offset+table3_entry_offset+0x44,sf);
stream_size = read_32bit(table3_offset+table3_entry_offset+0x48,sf);
flags = read_u8 (table3_offset+table3_entry_offset+0x12,sf);
stream_offset = read_u32(table3_offset+table3_entry_offset+0x44,sf);
stream_size = read_u32(table3_offset+table3_entry_offset+0x48,sf);
sample_rate = (int)read_f32(table3_offset+table3_entry_offset+0x4c,sf);
break;
@ -275,12 +255,13 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) {
switch(sblk_version) {
//case 0x03: /* different format? */
//case 0x04: /* different format? */
case 0x08:
case 0x09:
case 0x0d:
case 0x0e:
/* find if this sound has an assigned name in table1 */
for (i = 0; i < section_entries; i++) {
off_t entry_offset = (uint16_t)read_16bit(table1_offset+(i*table1_entry_size)+table1_suboffset+0x00,sf);
uint32_t entry_offset = read_u16(table1_offset+(i*table1_entry_size)+table1_suboffset+0x00,sf);
/* rarely (ex. Polara sfx) one name applies to multiple materials,
* from current entry_offset to next entry_offset (section offsets should be in order) */
@ -296,15 +277,15 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) {
/* 0x0c: table4 size */
/* variable: entries */
/* variable: names (null terminated) */
table4_entries_offset = table4_offset + read_32bit(table4_offset+0x08, sf);
table4_entries_offset = table4_offset + read_u32(table4_offset+0x08, sf);
table4_names_offset = table4_entries_offset + (0x10*section_entries);
//;VGM_LOG("BNK: t4_entries=%lx, t4_names=%lx\n", table4_entries_offset, table4_names_offset);
/* get assigned name from table4 names */
for (i = 0; i < section_entries; i++) {
int entry_id = read_32bit(table4_entries_offset+(i*0x10)+0x0c, sf);
int entry_id = read_u32(table4_entries_offset+(i*0x10)+0x0c, sf);
if (entry_id == table4_entry_id) {
name_offset = table4_names_offset + read_32bit(table4_entries_offset+(i*0x10)+0x00, sf);
name_offset = table4_names_offset + read_u32(table4_entries_offset+(i*0x10)+0x00, sf);
break;
}
}
@ -329,24 +310,24 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) {
case 0x03:
case 0x04:
case 0x05:
channel_count = 1;
channels = 1;
/* early versions don't have PS-ADPCM size, could check next offset but it's all kind of loopy */
if (sblk_version <= 0x03 && stream_size == 0 && (flags & 0x80) == 0) {
off_t offset;
off_t max_offset = get_streamfile_size(sf);
uint32_t offset;
uint32_t max_offset = get_streamfile_size(sf);
stream_size += 0x10;
for (offset = data_offset + stream_offset + 0x10; offset < max_offset; offset += 0x10) {
/* beginning frame (if file loops won't have end frame) */
if (read_32bitBE(offset + 0x00, sf) == 0x00000000 && read_32bitBE(offset + 0x04, sf) == 0x00000000)
if (read_u32be(offset + 0x00, sf) == 0x00000000 && read_u32be(offset + 0x04, sf) == 0x00000000)
break;
stream_size += 0x10;
/* end frame */
if (read_32bitBE(offset + 0x00, sf) == 0x00077777 && read_32bitBE(offset + 0x04, sf) == 0x77777777)
if (read_u32be(offset + 0x00, sf) == 0x00077777 && read_u32be(offset + 0x04, sf) == 0x77777777)
break;
}
@ -356,17 +337,17 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) {
/* hack for PS3 files that use dual subsongs as stereo */
if (total_subsongs == 2 && stream_size * 2 == data_size) {
channel_count = 2;
stream_size = stream_size*channel_count;
channels = 2;
stream_size = stream_size*channels;
total_subsongs = 1;
}
interleave = stream_size / channel_count;
interleave = stream_size / channels;
if (flags & 0x80) {
codec = PCM16; /* rare [Wipeout HD (PS3)] */
if ((flags & 0x80) && sblk_version <= 3) { /* PS Home Arcade has other flags? */
codec = PCM16; /* rare [Wipeout HD (PS3)]-v3 */
}
else {
loop_flag = ps_find_loop_offsets(sf, start_offset, stream_size, channel_count, interleave, &loop_start, &loop_end);
loop_flag = ps_find_loop_offsets(sf, start_offset, stream_size, channels, interleave, &loop_start, &loop_end);
loop_flag = (flags & 0x40); /* no loops values in sight so may only apply to PS-ADPCM flags */
codec = PSX;
@ -375,54 +356,68 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) {
//postdata_size = 0x10; /* last frame may be garbage */
break;
case 0x08:
case 0x09:
type = read_16bit(start_offset+0x00,sf);
extradata_size = 0x08 + read_32bit(start_offset+0x04,sf); /* 0x14 for AT9 */
type = read_u16(start_offset+0x00,sf);
extradata_size = 0x08 + read_u32(start_offset+0x04,sf); /* 0x14 for AT9 */
switch(type) {
case 0x00:
channels = 1;
codec = PSX;
interleave = 0x10;
break;
case 0x01:
channels = 1;
codec = PCM16;
interleave = 0x01;
break;
case 0x02: /* ATRAC9 mono */
case 0x05: /* ATRAC9 stereo */
if (read_32bit(start_offset+0x08,sf) + 0x08 != extradata_size) /* repeat? */
if (read_u32(start_offset+0x08,sf) + 0x08 != extradata_size) /* repeat? */
goto fail;
channel_count = (type == 0x02) ? 1 : 2;
channels = (type == 0x02) ? 1 : 2;
atrac9_info = (uint32_t)read_32bitBE(start_offset+0x0c,sf);
atrac9_info = read_u32be(start_offset+0x0c,sf);
/* 0x10: null? */
loop_length = read_32bit(start_offset+0x14,sf);
loop_start = read_32bit(start_offset+0x18,sf);
loop_length = read_u32(start_offset+0x14,sf);
loop_start = read_u32(start_offset+0x18,sf);
loop_end = loop_start + loop_length; /* loop_start is -1 if not set */
codec = ATRAC9;
break;
default:
VGM_LOG("BNK: unknown type %x\n", type);
vgm_logi("BNK: unknown type %x (report)\n", type);
goto fail;
}
break;
case 0x0d:
case 0x0e:
type = read_16bit(start_offset+0x00,sf);
if (read_32bit(start_offset+0x04,sf) != 0x01) /* type? */
type = read_u16(start_offset+0x00,sf);
if (read_u32(start_offset+0x04,sf) != 0x01) /* type? */
goto fail;
extradata_size = 0x10 + read_32bit(start_offset+0x08,sf); /* 0x80 for AT9, 0x10 for PCM/PS-ADPCM */
extradata_size = 0x10 + read_u32(start_offset+0x08,sf); /* 0x80 for AT9, 0x10 for PCM/PS-ADPCM */
/* 0x0c: null? */
switch(type) {
case 0x02: /* ATRAC9 mono */
case 0x05: /* ATRAC9 stereo */
if (read_32bit(start_offset+0x10,sf) + 0x10 != extradata_size) /* repeat? */
if (read_u32(start_offset+0x10,sf) + 0x10 != extradata_size) /* repeat? */
goto fail;
channel_count = (type == 0x02) ? 1 : 2;
channels = (type == 0x02) ? 1 : 2;
atrac9_info = (uint32_t)read_32bitBE(start_offset+0x14,sf);
atrac9_info = read_u32be(start_offset+0x14,sf);
/* 0x18: null? */
/* 0x1c: channels? */
/* 0x20: null? */
loop_length = read_32bit(start_offset+0x24,sf);
loop_start = read_32bit(start_offset+0x28,sf);
loop_length = read_u32(start_offset+0x24,sf);
loop_start = read_u32(start_offset+0x28,sf);
loop_end = loop_start + loop_length; /* loop_start is -1 if not set */
codec = ATRAC9;
@ -431,11 +426,11 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) {
case 0x01: /* PCM16LE mono? (NekoBuro/Polara sfx) */
case 0x04: /* PCM16LE stereo? (NekoBuro/Polara sfx) */
/* 0x10: null? */
channel_count = read_32bit(start_offset+0x14,sf);
channels = read_u32(start_offset+0x14,sf);
interleave = 0x02;
loop_start = read_32bit(start_offset+0x18,sf);
loop_length = read_32bit(start_offset+0x1c,sf);
loop_start = read_u32(start_offset+0x18,sf);
loop_length = read_u32(start_offset+0x1c,sf);
loop_end = loop_start + loop_length; /* loop_start is -1 if not set */
codec = PCM16;
@ -443,24 +438,24 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) {
case 0x00: /* PS-ADPCM (test banks) */
/* 0x10: null? */
channel_count = read_32bit(start_offset+0x14,sf);
channels = read_u32(start_offset+0x14,sf);
interleave = 0x02;
loop_start = read_32bit(start_offset+0x18,sf);
loop_length = read_32bit(start_offset+0x1c,sf);
loop_start = read_u32(start_offset+0x18,sf);
loop_length = read_u32(start_offset+0x1c,sf);
loop_end = loop_start + loop_length; /* loop_start is -1 if not set */
codec = HEVAG;
break;
default:
VGM_LOG("BNK: unknown type %x\n", type);
vgm_logi("BNK: unknown type %x (report)\n", type);
goto fail;
}
break;
default:
VGM_LOG("BNK: unknown data version %x\n", sblk_version);
vgm_logi("BNK: unknown data version %x (report)\n", sblk_version);
goto fail;
}
@ -474,7 +469,7 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) {
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
vgmstream = allocate_vgmstream(channels,loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = sample_rate;
@ -518,7 +513,7 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) {
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = interleave;
vgmstream->num_samples = ps_bytes_to_samples(stream_size,channel_count);
vgmstream->num_samples = ps_bytes_to_samples(stream_size,channels);
vgmstream->loop_start_sample = loop_start;
vgmstream->loop_end_sample = loop_end;
break;
@ -528,7 +523,7 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) {
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = interleave;
vgmstream->num_samples = ps_bytes_to_samples(stream_size,channel_count);
vgmstream->num_samples = ps_bytes_to_samples(stream_size,channels);
vgmstream->loop_start_sample = loop_start;
vgmstream->loop_end_sample = loop_end;
break;

View File

@ -1,6 +1,6 @@
#include "meta.h"
#include "../coding/coding.h"
#include "../util.h"
#include "../util/chunks.h"
#include "bnsf_keys.h"
@ -13,55 +13,76 @@ static void find_bnsf_key(STREAMFILE *sf, off_t start, g7221_codec_data *data, u
#endif
/* BNSF - Bandai Namco Sound Format/File [Tales of Graces (Wii), Tales of Berseria (PS4)] */
VGMSTREAM * init_vgmstream_bnsf(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset = 0, first_offset = 0x0C;
int loop_flag = 0, channel_count = 0, sample_rate;
int num_samples, loop_start = 0, loop_end = 0, loop_adjust, block_samples;
VGMSTREAM* init_vgmstream_bnsf(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
off_t start_offset = 0;
int loop_flag = 0, channels = 0, sample_rate = 0;
uint32_t num_samples = 0, loop_start = 0, loop_end = 0, loop_adjust = 0, block_samples = 0;
uint32_t codec, flags = 0;
size_t bnsf_size, sdat_size, block_size;
off_t loop_chunk = 0, sfmt_chunk, sdat_chunk;
size_t bnsf_size, block_size = 0;
/* checks */
if (!check_extensions(streamFile,"bnsf"))
if (!is_id32be(0x00,sf, "BNSF"))
goto fail;
if (read_32bitBE(0,streamFile) != 0x424E5346) /* "BNSF" */
if (!check_extensions(sf,"bnsf"))
goto fail;
bnsf_size = read_32bitBE(0x04,streamFile);
codec = read_32bitBE(0x08,streamFile);
bnsf_size = read_u32be(0x04,sf);
codec = read_u32be(0x08,sf);
if (bnsf_size + (codec == 0x49533232 ? 0x00 : 0x08) != get_streamfile_size(streamFile)) /* IS22 uses full size */
if (codec != get_id32be("IS22")) /* uses full size */
bnsf_size += 0x08;
if (bnsf_size != get_streamfile_size(sf))
goto fail;
if (!find_chunk_be(streamFile, 0x73666d74,first_offset,0, &sfmt_chunk,NULL)) /* "sfmt" */
goto fail;
if (!find_chunk_be(streamFile, 0x73646174,first_offset,0, &sdat_chunk,&sdat_size)) /* "sdat" */
goto fail;
if ( find_chunk_be(streamFile, 0x6C6F6F70,first_offset,0, &loop_chunk,NULL)) { /* "loop" */
loop_flag = 1;
loop_start = read_32bitBE(loop_chunk+0x00,streamFile); /* block-aligned */
loop_end = read_32bitBE(loop_chunk+0x04,streamFile) + 1;
{
enum {
CHUNK_sfmt = 0x73666d74,
CHUNK_sdat = 0x73646174,
CHUNK_loop = 0x6C6F6F70,
};
chunk_t rc = {0};
rc.be_size = 1;
rc.current = 0x0C;
while (next_chunk(&rc, sf)) {
switch(rc.type) {
case CHUNK_sfmt:
flags = read_u16be(rc.offset+0x00,sf);
channels = read_u16be(rc.offset+0x02,sf);
sample_rate = read_s32be(rc.offset+0x04,sf);
num_samples = read_s32be(rc.offset+0x08,sf);
loop_adjust = read_s32be(rc.offset+0x0c,sf); /* 0 when no loop */
block_size = read_u16be(rc.offset+0x10,sf);
block_samples = read_u16be(rc.offset+0x12,sf);
//max_samples = sdat_size / block_size * block_samples;
break;
case CHUNK_loop:
loop_flag = 1;
loop_start = read_s32be(rc.offset+0x00,sf); /* block-aligned */
loop_end = read_s32be(rc.offset+0x04,sf) + 1;
break;
case CHUNK_sdat:
start_offset = rc.offset;
break;
default:
break;
}
}
}
flags = read_16bitBE(sfmt_chunk+0x00,streamFile);
channel_count = read_16bitBE(sfmt_chunk+0x02,streamFile);
sample_rate = read_32bitBE(sfmt_chunk+0x04,streamFile);
num_samples = read_32bitBE(sfmt_chunk+0x08,streamFile);
loop_adjust = read_32bitBE(sfmt_chunk+0x0c,streamFile); /* 0 when no loop */
block_size = read_16bitBE(sfmt_chunk+0x10,streamFile);
block_samples = read_16bitBE(sfmt_chunk+0x12,streamFile);
//max_samples = sdat_size / block_size * block_samples;
start_offset = sdat_chunk;
if (loop_adjust >= block_samples) /* decoder can't handle this */
goto fail;
if (!start_offset)
goto fail;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag);
vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = sample_rate;
@ -71,7 +92,7 @@ VGMSTREAM * init_vgmstream_bnsf(STREAMFILE *streamFile) {
vgmstream->meta_type = meta_BNSF;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = block_size / channel_count;
vgmstream->interleave_block_size = block_size / channels;
switch (codec) {
#ifdef VGM_USE_G7221
@ -80,20 +101,20 @@ VGMSTREAM * init_vgmstream_bnsf(STREAMFILE *streamFile) {
vgmstream->codec_data = init_g7221(vgmstream->channels, vgmstream->interleave_block_size);
if (!vgmstream->codec_data) goto fail;
/* get decryption key in .bnsfkey file or list, for later games' voices
* [The Idolm@ster 2 (PS3/X360), Tales of Zestiria (PS3/PC)] */
/* get decryption key in .bnsfkey file or list, for later games' voices and some odd BGM
* [THE iDOL@STER 2 (PS3/X360), Tales of Zestiria (PS3/PC)] */
if (flags != 0) { /* only known value is 0x02 though */
size_t keysize;
uint8_t key[24] = {0}; /* keystring 0-padded to 192-bit */
keysize = read_key_file(key, sizeof(key), streamFile);
keysize = read_key_file(key, sizeof(key), sf);
#ifdef BNSF_BRUTEFORCE
if (1) {
bruteforce_bnsf_key(streamFile, start_offset, vgmstream->codec_data, key);
bruteforce_bnsf_key(sf, start_offset, vgmstream->codec_data, key);
} else
#endif
if (keysize <= 0 || keysize > sizeof(key)) {
find_bnsf_key(streamFile, start_offset, vgmstream->codec_data, key);
find_bnsf_key(sf, start_offset, vgmstream->codec_data, key);
}
set_key_g7221(vgmstream->codec_data, key);
}
@ -118,10 +139,9 @@ VGMSTREAM * init_vgmstream_bnsf(STREAMFILE *streamFile) {
}
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
@ -187,9 +207,9 @@ static void bruteforce_bnsf_key(STREAMFILE* sf, off_t start, g7221_codec_data* d
sf_keys = open_streamfile_by_filename(sf, "keys.txt");
if (!sf_keys) goto done;
keys_size = get_streamfile_size(sf_keys);
offset = 0x00;
while (offset < keys_size) {
int line_len;
@ -204,7 +224,7 @@ static void bruteforce_bnsf_key(STREAMFILE* sf, off_t start, g7221_codec_data* d
for (j = i + BNSF_MIN_KEY_LEN; j <= line_len; j++) {
int keylen = j - i;
const char* key = &line[i];
test_key(sf, start, data, key, keylen, &best_score, best_key);
if (best_score == 1) {
VGM_ASSERT(best_score > 0, "BNSF: good key=%.24s (score=%i)\n", best_key, best_score);

View File

@ -1,177 +1,179 @@
#include "meta.h"
/* .cks - Cricket Audio stream [Part Time UFO (Android), Mega Man 1-6 (Android)] */
VGMSTREAM * init_vgmstream_cks(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
int loop_flag, channel_count, codec, sample_rate;
int32_t num_samples, loop_start, loop_end;
size_t block_size;
/* checks */
if (!check_extensions(streamFile, "cks"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x636B6D6B) /* "ckmk" */
goto fail;
/* 0x04(4): platform bitflags (from LSB: iOS, Android, OS X, Windows, WP8, Linux, tvOS, undefined/ignored) */
if (read_32bitLE(0x08,streamFile) != 0x00) /* expected file type (0x00: stream, 0x01: bank, 0x02+: unknown) */
goto fail;
if (read_32bitLE(0x0c,streamFile) != 0x02) /* file version (always 0x02) */
goto fail;
codec = read_8bit(0x10,streamFile);
channel_count = read_8bit(0x11,streamFile);
sample_rate = (uint16_t)read_16bitLE(0x12,streamFile);
num_samples = read_32bitLE(0x14,streamFile) * read_16bitLE(0x1a,streamFile); /* number_of_blocks * samples_per_frame */
block_size = read_16bitLE(0x18,streamFile);
/* 0x1c(2): volume */
/* 0x1e(2): pan */
loop_start = read_32bitLE(0x20,streamFile);
loop_end = read_32bitLE(0x24,streamFile);
loop_flag = read_16bitLE(0x28,streamFile) != 0; /* loop count (-1 = forever) */
/* 0x2a(2): unused? */
start_offset = 0x2c;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = num_samples;
vgmstream->loop_start_sample = loop_start;
vgmstream->loop_end_sample = loop_end;
vgmstream->meta_type = meta_CKS;
switch(codec) {
case 0x00: /* pcm16 [from tests] */
vgmstream->coding_type = coding_PCM16LE;
break;
case 0x01: /* pcm8 [from tests] */
vgmstream->coding_type = coding_PCM8;
break;
case 0x02: /* adpcm [Part Time UFO (Android), Mega Man 1-6 (Android)] */
vgmstream->coding_type = coding_MSADPCM_ck;
vgmstream->frame_size = block_size / channel_count; /* always 0x18 */
break;
default:
goto fail;
}
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = block_size / channel_count;
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}
/* .ckb - Cricket Audio bank [Fire Emblem Heroes (Android), Mega Man 1-6 (Android)] */
VGMSTREAM * init_vgmstream_ckb(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset, name_offset = 0;
int loop_flag, channel_count, codec, sample_rate;
int32_t num_samples, loop_start, loop_end;
size_t block_size, stream_size;
int total_subsongs, target_subsong = streamFile->stream_index;
/* checks */
if (!check_extensions(streamFile, "ckb"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x636B6D6B) /* "ckmk" */
goto fail;
/* 0x04(4): platform bitflags (from LSB: iOS, Android, OS X, Windows, WP8, Linux, tvOS, undefined/ignored) */
if (read_32bitLE(0x08,streamFile) != 0x01) /* expected file type (0x00: stream, 0x01: bank, 0x02+: unknown) */
goto fail;
if (read_32bitLE(0x0c,streamFile) != 0x02) /* file version (always 0x02) */
goto fail;
/* 0x10: bank name (size 0x1c+1) */
/* 0x30/34: reserved? */
total_subsongs = read_32bitLE(0x38,streamFile);
if (target_subsong == 0) target_subsong = 1;
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
/* 0x3c: total_subsongs again? (ignored) */
/* 0x40/44: unknown (ignored) */
/* get subsong (stream offset isn't given so must calc manually) */
{
int i;
off_t header_offset = 0x48;
off_t stream_offset = 0x48 + total_subsongs*0x48;
for (i = 0; i < total_subsongs; i++) {
name_offset = header_offset+0x00; /* stream name (size 0x1c+1) */
codec = read_8bit(header_offset+0x20,streamFile);
channel_count = read_8bit(header_offset+0x21,streamFile);
sample_rate = (uint16_t)read_16bitLE(header_offset+0x22,streamFile);
num_samples = read_32bitLE(header_offset+0x24,streamFile) * read_16bitLE(header_offset+0x2a,streamFile); /* number_of_blocks * samples_per_frame */
block_size = read_16bitLE(header_offset+0x28,streamFile);
/* 0x2c(2): volume */
/* 0x2e(2): pan */
loop_start = read_32bitLE(header_offset+0x30,streamFile);
loop_end = read_32bitLE(header_offset+0x34,streamFile);
loop_flag = read_16bitLE(header_offset+0x38,streamFile) != 0; /* loop count (-1 = forever) */
/* 0x3a(2): unused? */
stream_size = read_32bitLE(header_offset+0x3c,streamFile);
/* 0x40/44(4): unused? */
if (target_subsong == (i+1))
break;
header_offset += 0x48;
stream_offset += stream_size;
}
start_offset = stream_offset;
}
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = num_samples;
vgmstream->loop_start_sample = loop_start;
vgmstream->loop_end_sample = loop_end;
vgmstream->num_streams = total_subsongs;
vgmstream->stream_size = stream_size;
read_string(vgmstream->stream_name,0x1c+1, name_offset,streamFile);
vgmstream->meta_type = meta_CKB;
switch(codec) {
case 0x00: /* pcm16 [Mega Man 1-6 (Android)] */
vgmstream->coding_type = coding_PCM16LE;
break;
case 0x01: /* pcm8 */
vgmstream->coding_type = coding_PCM8;
break;
case 0x02: /* adpcm [Fire Emblem Heroes (Android)] */
vgmstream->coding_type = coding_MSADPCM_ck;
/* frame_size is always 0x18 */
break;
default:
goto fail;
}
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = block_size / channel_count;
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}
#include "meta.h"
/* .cks - Cricket Audio stream [Part Time UFO (Android), Mega Man 1-6 (Android)] */
VGMSTREAM* init_vgmstream_cks(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
off_t start_offset;
int loop_flag, channels, codec, sample_rate;
int32_t num_samples, loop_start, loop_end;
size_t block_size;
/* checks */
if (!is_id32be(0x00,sf, "ckmk"))
goto fail;
if (!check_extensions(sf, "cks"))
goto fail;
/* 0x04(4): platform bitflags (from LSB: iOS, Android, OS X, Windows, WP8, Linux, tvOS, undefined/ignored) */
if (read_u32le(0x08,sf) != 0x00) /* expected file type (0x00: stream, 0x01: bank, 0x02+: unknown) */
goto fail;
if (read_u32le(0x0c,sf) != 0x02) /* file version (always 0x02) */
goto fail;
codec = read_u8(0x10,sf);
channels = read_u8(0x11,sf);
sample_rate = read_u16le(0x12,sf);
num_samples = read_s32le(0x14,sf) * read_u16le(0x1a,sf); /* number_of_blocks * samples_per_frame */
block_size = read_u16le(0x18,sf);
/* 0x1c(2): volume */
/* 0x1e(2): pan */
loop_start = read_s32le(0x20,sf);
loop_end = read_s32le(0x24,sf);
loop_flag = read_s16le(0x28,sf) != 0; /* loop count (-1 = forever) */
/* 0x2a(2): unused? */
start_offset = 0x2c;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = num_samples;
vgmstream->loop_start_sample = loop_start;
vgmstream->loop_end_sample = loop_end;
vgmstream->meta_type = meta_CKS;
switch(codec) {
case 0x00: /* pcm16 [from tests] */
vgmstream->coding_type = coding_PCM16LE;
break;
case 0x01: /* pcm8 [from tests] */
vgmstream->coding_type = coding_PCM8;
break;
case 0x02: /* adpcm [Part Time UFO (Android), Mega Man 1-6 (Android)] */
vgmstream->coding_type = coding_MSADPCM_ck;
vgmstream->frame_size = block_size / channels; /* always 0x18 */
break;
default:
goto fail;
}
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = block_size / channels;
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}
/* .ckb - Cricket Audio bank [Fire Emblem Heroes (Android), Mega Man 1-6 (Android)] */
VGMSTREAM* init_vgmstream_ckb(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
off_t start_offset, name_offset = 0;
int loop_flag = 0, channels = 0, codec = 0, sample_rate = 0;
int32_t num_samples = 0, loop_start = 0, loop_end = 0;
size_t block_size = 0, stream_size = 0;
int total_subsongs, target_subsong = sf->stream_index;
/* checks */
if (!is_id32be(0x00,sf, "ckmk"))
goto fail;
if (!check_extensions(sf, "ckb"))
goto fail;
/* 0x04(4): platform bitflags (from LSB: iOS, Android, OS X, Windows, WP8, Linux, tvOS, undefined/ignored) */
if (read_u32le(0x08,sf) != 0x01) /* expected file type (0x00: stream, 0x01: bank, 0x02+: unknown) */
goto fail;
if (read_u32le(0x0c,sf) != 0x02) /* file version (always 0x02) */
goto fail;
/* 0x10: bank name (size 0x1c+1) */
/* 0x30/34: reserved? */
total_subsongs = read_u32le(0x38,sf);
if (target_subsong == 0) target_subsong = 1;
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
/* 0x3c: total_subsongs again? (ignored) */
/* 0x40/44: unknown (ignored) */
/* get subsong (stream offset isn't given so must calc manually) */
{
int i;
off_t header_offset = 0x48;
off_t stream_offset = 0x48 + total_subsongs*0x48;
for (i = 0; i < total_subsongs; i++) {
name_offset = header_offset+0x00; /* stream name (size 0x1c+1) */
codec = read_8bit(header_offset+0x20,sf);
channels = read_8bit(header_offset+0x21,sf);
sample_rate = (uint16_t)read_16bitLE(header_offset+0x22,sf);
num_samples = read_32bitLE(header_offset+0x24,sf) * read_16bitLE(header_offset+0x2a,sf); /* number_of_blocks * samples_per_frame */
block_size = read_16bitLE(header_offset+0x28,sf);
/* 0x2c(2): volume */
/* 0x2e(2): pan */
loop_start = read_32bitLE(header_offset+0x30,sf);
loop_end = read_32bitLE(header_offset+0x34,sf);
loop_flag = read_16bitLE(header_offset+0x38,sf) != 0; /* loop count (-1 = forever) */
/* 0x3a(2): unused? */
stream_size = read_32bitLE(header_offset+0x3c,sf);
/* 0x40/44(4): unused? */
if (target_subsong == (i+1))
break;
header_offset += 0x48;
stream_offset += stream_size;
}
start_offset = stream_offset;
}
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = num_samples;
vgmstream->loop_start_sample = loop_start;
vgmstream->loop_end_sample = loop_end;
vgmstream->num_streams = total_subsongs;
vgmstream->stream_size = stream_size;
read_string(vgmstream->stream_name,0x1c+1, name_offset,sf);
vgmstream->meta_type = meta_CKB;
switch(codec) {
case 0x00: /* pcm16 [Mega Man 1-6 (Android)] */
vgmstream->coding_type = coding_PCM16LE;
break;
case 0x01: /* pcm8 */
vgmstream->coding_type = coding_PCM8;
break;
case 0x02: /* adpcm [Fire Emblem Heroes (Android)] */
vgmstream->coding_type = coding_MSADPCM_ck;
/* frame_size is always 0x18 */
break;
default:
goto fail;
}
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = block_size / channels;
if (!vgmstream_open_stream(vgmstream,sf,start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -1,6 +1,7 @@
#include "meta.h"
#include "../coding/coding.h"
#include "../layout/layout.h"
#include "../util/endianness.h"
#define EA_CODEC_PCM 0x00
#define EA_CODEC_ULAW 0x01
@ -17,7 +18,9 @@ typedef struct {
int32_t loop_start;
int32_t loop_end;
int32_t loop_start_offset;
int32_t data_offset;
uint32_t data_offset;
uint32_t base_size;
int big_endian;
int loop_flag;
@ -27,7 +30,7 @@ typedef struct {
int total_subsongs;
} eacs_header;
static int parse_header(STREAMFILE* sf, eacs_header* ea, off_t begin_offset);
static int parse_header(STREAMFILE* sf, eacs_header* ea, uint32_t begin_offset);
static VGMSTREAM* init_vgmstream_main(STREAMFILE* sf, eacs_header* ea);
static void set_ea_1snh_num_samples(VGMSTREAM* vgmstream, STREAMFILE* sf, eacs_header* ea, int find_loop);
@ -35,24 +38,12 @@ static int get_ea_1snh_ima_version(STREAMFILE* sf, off_t start_offset, const eac
/* EA 1SNh - from early EA games, stream (~1996, ex. Need for Speed) */
VGMSTREAM* init_vgmstream_ea_1snh(STREAMFILE* sf) {
eacs_header ea = { 0 };
off_t offset, eacs_offset;
eacs_header ea = {0};
off_t offset = 0x00, eacs_offset;
VGMSTREAM* vgmstream = NULL;
/* checks */
/* .asf/as4: common,
* .lasf: fake for plugins
* .sng: fake for plugins (for .asf issues)
* .cnk: some PS1 games
* .uv/tgq: some SAT videos
* .tgv: videos
* (extensionless): Need for Speed (SAT) videos */
if (!check_extensions(sf, "asf,lasf,sng,as4,cnk,uv,tgq,tgv,"))
goto fail;
offset = 0x00;
/* in TGV videos, either TGVk or 1SNh block comes first */
if (is_id32be(0x00, sf, "TGVk")) {
offset = read_u32be(0x04, sf);
@ -64,14 +55,28 @@ VGMSTREAM* init_vgmstream_ea_1snh(STREAMFILE* sf) {
!is_id32be(offset + 0x00, sf, "SEAD"))
goto fail;
/* .asf/as4: common,
* .lasf: fake for plugins
* .sng: fake for plugins (for .asf issues)
* .cnk: some PS1 games [Triple Play 97 (PS1), FIFA 97 (PS1)]
* .uv/tgq: some SAT videos
* .tgv: videos
* (extensionless): Need for Speed (SAT) videos */
if (!check_extensions(sf, "asf,lasf,sng,as4,cnk,uv,tgq,tgv,"))
goto fail;
/* stream is divided into blocks/chunks: 1SNh=audio header, 1SNd=data xN, 1SNl=loop end, 1SNe=end.
* Video uses various blocks (TGVk/TGVf/MUVf/etc) and sometimes alt audio blocks (SEAD/SNDC/SEND). */
ea.is_sead = is_id32be(offset + 0x00, sf, "SEAD");
if (!ea.is_sead)
ea.base_size = read_u32le(offset + 0x04, sf);
eacs_offset = offset + 0x08; /* after 1SNh block id+size */
if (!parse_header(sf, &ea, eacs_offset))
goto fail;
vgmstream = init_vgmstream_main(sf, &ea);
if (!vgmstream) goto fail;
@ -98,17 +103,19 @@ VGMSTREAM* init_vgmstream_ea_eacs(STREAMFILE* sf) {
/* checks */
if (!is_id32be(0x00,sf, "EACS") &&
read_u32be(0x00,sf) != 0 && !is_id32be(0x228,sf, "EACS"))
goto fail;
/* .eas: single bank [Need for Speed (PC)]
* .bnk: multi bank [Need for Speed (PC)] */
if (!check_extensions(sf,"eas,bnk"))
* .bnk: multi bank [Need for Speed (PC)]
* .as4: single [NBA Live 96 (PC)] */
if (!check_extensions(sf,"eas,bnk,as4"))
goto fail;
/* plain data without blocks, can contain N*(EACS header) + N*(data), or N (EACS header + data) */
ea.is_bank = 1;
/* use ??? as endian marker (Saturn = BE) */
//ea.big_endian = guess_endianness32bit(0x04,sf);
if (is_id32be(0x00,sf, "EACS")) {
/* single bank variant */
eacs_offset = 0x00;
@ -121,8 +128,8 @@ VGMSTREAM* init_vgmstream_ea_eacs(STREAMFILE* sf) {
if (target_subsong == 0) target_subsong = 1;
if (target_subsong < 0) goto fail;
/* offsets to EACSs are scattered in the first 0x200
* this looks dumb but seems like the only way */
/* offsets to EACSs are scattered in the first 0x200, then 0x28 info + EACS per subsong.
* This looks dumb but seems like the only way. */
eacs_offset = 0;
for (i = 0x00; i < 0x200; i += 0x04) {
off_t bank_offset = read_u32le(i, sf);
@ -134,7 +141,7 @@ VGMSTREAM* init_vgmstream_ea_eacs(STREAMFILE* sf) {
/* parse mini bank header */
if (ea.total_subsongs == target_subsong) {
/* 0x00: some id or flags? */
eacs_offset = read_u32le(bank_offset + 0x04, sf);
eacs_offset = read_u32le(bank_offset + 0x04, sf); /* always after 0x28 from bank_offset */
if (!is_id32be(eacs_offset, sf, "EACS"))
goto fail;
/* rest: not sure if part of this header */
@ -193,10 +200,11 @@ static VGMSTREAM* init_vgmstream_main(STREAMFILE* sf, eacs_header* ea) {
case EA_CODEC_PSX: /* Need for Speed (PS1) */
vgmstream->coding_type = coding_PSX;
vgmstream->codec_config = ea->codec_config;
break;
default:
VGM_LOG("EA EACS: unknown codec 0x%02x\n", ea->codec);
vgm_logi("EA EACS: unknown codec 0x%02x\n", ea->codec);
goto fail;
}
@ -210,13 +218,13 @@ fail:
return NULL;
}
static int parse_header(STREAMFILE* sf, eacs_header* ea, off_t offset) {
static int parse_header(STREAMFILE* sf, eacs_header* ea, uint32_t offset) {
/* audio header endianness doesn't always match block headers, use sample rate to detect */
int32_t (*read_s32)(off_t,STREAMFILE*);
read_s32_t read_s32;
if (is_id32be(offset+0x00,sf, "EACS")) {
/* EACS subheader (PC, SAT) */
ea->big_endian = guess_endianness32bit(offset + 0x04, sf);
ea->big_endian = guess_endian32(offset + 0x04, sf);
read_s32 = ea->big_endian ? read_s32be : read_s32le;
ea->sample_rate = read_s32(offset+0x04, sf);
@ -236,9 +244,37 @@ static int parse_header(STREAMFILE* sf, eacs_header* ea, off_t offset) {
ea->codec_config = get_ea_1snh_ima_version(sf, 0x00, ea);
/* EACS banks with empty values exist but will be rejected later */
}
else if (read_u32be(offset + 0x00, sf) == 0x00) {
else if (ea->is_sead) {
/* alt subheader (found in some PC videos) */
ea->big_endian = guess_endian32(offset + 0x00, sf);
read_s32 = ea->big_endian ? read_s32be : read_s32le;
ea->sample_rate = read_s32(offset+0x00, sf);
ea->channels = read_s32(offset+0x04, sf);
ea->codec = read_s32(offset+0x08, sf);
if (ea->codec == EA_CODEC_IMA)
ea->codec_config = get_ea_1snh_ima_version(sf, 0x00, ea);
}
else if (ea->base_size == 0x2c) {
/* [NBA Live 96 (PS1), Need for Speed (PS1)] */
ea->sample_rate = read_s32le(offset+0x00, sf);
ea->channels = read_u8(offset+0x18, sf);
ea->codec = EA_CODEC_PSX;
ea->codec_config = 0;
}
else if (ea->base_size == 0x30) {
/* [FIFA 97 (PS1), Triple Play 97 (PS1)] */
/* 0x00: 0 or some id? (same for N files) */
ea->sample_rate = read_s32le(offset+0x04, sf);
ea->channels = read_u8(offset+0x1c, sf);
ea->codec = EA_CODEC_PSX;
ea->codec_config = 1;
}
else {
//TODO: test
/* found in early videos, similar to EACS */
ea->big_endian = guess_endianness32bit(offset + 0x04, sf);
ea->big_endian = guess_endian32(offset + 0x04, sf);
read_s32 = ea->big_endian ? read_s32be : read_s32le;
ea->sample_rate = read_s32(offset + 0x04, sf);
@ -250,27 +286,6 @@ static int parse_header(STREAMFILE* sf, eacs_header* ea, off_t offset) {
if (ea->codec == EA_CODEC_IMA)
ea->codec_config = get_ea_1snh_ima_version(sf, 0x00, ea);
}
else if (ea->is_sead) {
/* alt subheader (found in some PC videos) */
ea->big_endian = guess_endianness32bit(offset + 0x00, sf);
read_s32 = ea->big_endian ? read_s32be : read_s32le;
ea->sample_rate = read_s32(offset+0x00, sf);
ea->channels = read_s32(offset+0x04, sf);
ea->codec = read_s32(offset+0x08, sf);
if (ea->codec == EA_CODEC_IMA)
ea->codec_config = get_ea_1snh_ima_version(sf, 0x00, ea);
}
else {
/* alt subheader (PS1) */
ea->big_endian = guess_endianness32bit(offset + 0x00, sf);
read_s32 = ea->big_endian ? read_s32be : read_s32le;
ea->sample_rate = read_s32(offset+0x00, sf);
ea->channels = read_u8(offset+0x18, sf);
ea->codec = EA_CODEC_PSX;
}
ea->loop_flag = (ea->loop_end > 0);
@ -281,26 +296,28 @@ static int parse_header(STREAMFILE* sf, eacs_header* ea, off_t offset) {
static void set_ea_1snh_num_samples(VGMSTREAM *vgmstream, STREAMFILE* sf, eacs_header* ea, int find_loop) {
int32_t num_samples = 0, block_id;
size_t file_size;
int32_t(*read_s32)(off_t, STREAMFILE *) = ea->big_endian ? read_s32be : read_s32le;
read_s32_t read_s32 = ea->big_endian ? read_s32be : read_s32le;
int loop_end_found = 0;
file_size = get_streamfile_size(sf);
vgmstream->next_block_offset = ea->data_offset;
while (vgmstream->next_block_offset < file_size) {
block_update_ea_1snh(vgmstream->next_block_offset, vgmstream);
block_update(vgmstream->next_block_offset, vgmstream);
if (vgmstream->current_block_samples < 0)
break;
block_id = read_u32be(vgmstream->current_block_offset, sf);
if (find_loop) {
if (vgmstream->current_block_offset == ea->loop_start_offset) {
ea->loop_start = num_samples;
ea->loop_flag = 1;
block_update_ea_1snh(ea->data_offset, vgmstream);
block_update(ea->data_offset, vgmstream);
return;
}
} else {
}
else {
if (block_id == get_id32be("1SNl") ) { /* loop point found */
ea->loop_start_offset = read_s32(vgmstream->current_block_offset + 0x08, sf);
ea->loop_end = num_samples;
@ -314,7 +331,7 @@ static void set_ea_1snh_num_samples(VGMSTREAM *vgmstream, STREAMFILE* sf, eacs_h
ea->num_samples = num_samples;
/* reset once we're done */
block_update_ea_1snh(ea->data_offset, vgmstream);
block_update(ea->data_offset, vgmstream);
if (loop_end_found) {
/* recurse to find loop start sample */
@ -326,21 +343,27 @@ static void set_ea_1snh_num_samples(VGMSTREAM *vgmstream, STREAMFILE* sf, eacs_h
static int get_ea_1snh_ima_version(STREAMFILE* sf, off_t start_offset, const eacs_header* ea) {
off_t block_offset = start_offset;
size_t file_size = get_streamfile_size(sf);
int32_t (*read_s32)(off_t,STREAMFILE*) = ea->big_endian ? read_s32be : read_s32le;
read_s32_t read_s32 = ea->big_endian ? read_s32be : read_s32le;
if (ea->type == 0xFF) /* bnk */
return 0;
while (block_offset < file_size) {
uint32_t id = read_u32be(block_offset+0x00,sf);
size_t block_size;
/* BE in SAT, but one file may have both BE and LE chunks */
if (guess_endianness32bit(block_offset + 0x04, sf))
if (guess_endian32(block_offset + 0x04, sf))
block_size = read_u32be(block_offset + 0x04, sf);
else
block_size = read_u32le(block_offset + 0x04, sf);
if (id == 0x31534E64 || id == 0x534E4443) { /* "1SNd" "SNDC" audio data */
size_t ima_samples = read_s32(block_offset + 0x08, sf);
size_t expected_samples = (block_size - 0x08 - 0x04 - 0x08*ea->channels) * 2 / ea->channels;
if (block_size == 0 || block_size == -1)
break;
if (id == get_id32be("1SNd") || id == get_id32be("SNDC")) {
int32_t ima_samples = read_s32(block_offset + 0x08, sf);
int32_t expected_samples = (block_size - 0x08 - 0x04 - 0x08*ea->channels) * 2 / ea->channels;
if (ima_samples == expected_samples) {
return 1; /* has ADPCM hist (hopefully) */

View File

@ -260,8 +260,10 @@ static VGMSTREAM* parse_s10a_header(STREAMFILE* sf, off_t offset, uint16_t targe
} else {
/* streamed asset */
astFile = open_streamfile_by_ext(sf, "ast");
if (!astFile)
if (!astFile) {
vgm_logi("EA ABK: .ast file not found (find and put together)\n");
goto fail;
}
if (read_32bitBE(0x00, astFile) != 0x53313053) /* "S10S" */
goto fail;
@ -604,7 +606,7 @@ static STREAMFILE *open_mapfile_pair(STREAMFILE* sf, int track /*, int num_track
}
}
VGM_LOG("No MPF/MUS pair specified for %s.\n", file_name);
vgm_logi("EA MPF: .mus file not found (find and put together)\n");
return NULL;
}

View File

@ -127,8 +127,8 @@ static VGMSTREAM * parse_bnk_header(STREAMFILE* sf, off_t offset, int target_str
static int parse_variable_header(STREAMFILE* sf, ea_header* ea, off_t begin_offset, int max_length, int bnk_version);
static uint32_t read_patch(STREAMFILE* sf, off_t* offset);
static off_t get_ea_stream_mpeg_start_offset(STREAMFILE* sf, off_t start_offset, const ea_header* ea);
static VGMSTREAM* init_vgmstream_ea_variable_header(STREAMFILE* sf, ea_header* ea, off_t start_offset, int is_bnk, int standalone);
static void update_ea_stream_size_and_samples(STREAMFILE* sf, off_t start_offset, VGMSTREAM* vgmstream, int standalone);
static VGMSTREAM* init_vgmstream_ea_variable_header(STREAMFILE* sf, ea_header* ea, off_t start_offset, int is_bnk);
static void update_ea_stream_size(STREAMFILE* sf, off_t start_offset, VGMSTREAM* vgmstream);
/* EA SCHl with variable header - from EA games (roughly 1997~2010); generated by EA Canada's sx.exe/Sound eXchange */
VGMSTREAM* init_vgmstream_ea_schl(STREAMFILE* sf) {
@ -150,9 +150,8 @@ VGMSTREAM* init_vgmstream_ea_schl(STREAMFILE* sf) {
* .hab: GoldenEye - Rogue Agent (inside .big)
* .xsf: 007 - Agent Under Fire (Xbox)
* .gsf: 007 - Everything or Nothing (GC)
* .mus: map/mpf+mus only?
* (extensionless): SSX (PS2) (inside .big) */
if (!check_extensions(sf,"asf,lasf,str,chk,eam,exa,sng,aud,sx,xa,strm,stm,hab,xsf,gsf,mus,"))
if (!check_extensions(sf,"asf,lasf,str,chk,eam,exa,sng,aud,sx,xa,strm,stm,hab,xsf,gsf,"))
goto fail;
/* check header */
@ -284,10 +283,9 @@ VGMSTREAM* init_vgmstream_ea_bnk(STREAMFILE* sf) {
/* check extension */
/* .bnk: common
* .sdt: Harry Potter games
* .mus: streams/jingles (rare)
* .abk: GoldenEye - Rogue Agent
* .ast: FIFA 2004 (inside .big) */
if (!check_extensions(sf,"bnk,sdt,mus,abk,ast"))
if (!check_extensions(sf,"bnk,sdt,abk,ast"))
goto fail;
if (target_stream == 0) target_stream = 1;
@ -428,8 +426,10 @@ VGMSTREAM* init_vgmstream_ea_abk(STREAMFILE* sf) {
case 0x02:
astData = open_streamfile_by_ext(sf, "ast");
if (!astData)
if (!astData) {
vgm_logi("EA ABK: .ast file not found (find and put together)\n");
goto fail;
}
/* looped sounds basically consist of two independent segments
* the first one is loop start, the second one is loop body */
@ -737,7 +737,7 @@ static STREAMFILE* open_mapfile_pair(STREAMFILE* sf, int track /*, int num_track
}
}
VGM_LOG("No MPF/MUS pair specified for %s.\n", file_name);
vgm_logi("EA MPF: .mus file not found (find and put together)\n");
return NULL;
}
@ -1130,7 +1130,7 @@ static VGMSTREAM* parse_schl_block(STREAMFILE* sf, off_t offset, int standalone)
start_offset = offset + header_size; /* starts in "SCCl" (skipped in block layout) or very rarely "SCDl" and maybe movie blocks */
/* rest is common */
return init_vgmstream_ea_variable_header(sf, &ea, start_offset, 0, standalone);
return init_vgmstream_ea_variable_header(sf, &ea, start_offset, 0);
fail:
return NULL;
@ -1221,7 +1221,7 @@ static VGMSTREAM* parse_bnk_header(STREAMFILE* sf, off_t offset, int target_stre
start_offset = ea.offsets[0]; /* first channel, presumably needed for MPEG */
/* rest is common */
vgmstream = init_vgmstream_ea_variable_header(sf, &ea, start_offset, bnk_version, 0);
vgmstream = init_vgmstream_ea_variable_header(sf, &ea, start_offset, bnk_version);
if (!vgmstream) goto fail;
if (!is_embedded) {
vgmstream->num_streams = real_bnk_sounds;
@ -1234,7 +1234,7 @@ fail:
}
/* inits VGMSTREAM from a EA header */
static VGMSTREAM* init_vgmstream_ea_variable_header(STREAMFILE* sf, ea_header* ea, off_t start_offset, int bnk_version, int standalone) {
static VGMSTREAM* init_vgmstream_ea_variable_header(STREAMFILE* sf, ea_header* ea, off_t start_offset, int bnk_version) {
VGMSTREAM* vgmstream = NULL;
int i, ch;
int is_bnk = bnk_version;
@ -1490,7 +1490,7 @@ static VGMSTREAM* init_vgmstream_ea_variable_header(STREAMFILE* sf, ea_header* e
/* TODO: Figure out how to get stream size for BNK sounds */
} else {
update_ea_stream_size_and_samples(sf, start_offset, vgmstream, standalone);
update_ea_stream_size(sf, start_offset, vgmstream);
}
return vgmstream;
@ -1901,18 +1901,18 @@ fail:
return 0;
}
static void update_ea_stream_size_and_samples(STREAMFILE* sf, off_t start_offset, VGMSTREAM *vgmstream, int standalone) {
static void update_ea_stream_size(STREAMFILE* sf, off_t start_offset, VGMSTREAM *vgmstream) {
uint32_t block_id;
int32_t num_samples = 0;
size_t stream_size = 0, file_size;
int multiple_schl = 0;
file_size = get_streamfile_size(sf);
/* formats with custom codecs */
if (vgmstream->layout_type != layout_blocked_ea_schl) {
if (vgmstream->layout_type != layout_blocked_ea_schl)
return;
}
if (vgmstream->stream_size != 0)
return;
file_size = get_streamfile_size(sf);
/* manually read totals */
vgmstream->next_block_offset = start_offset;
@ -1923,20 +1923,10 @@ static void update_ea_stream_size_and_samples(STREAMFILE* sf, off_t start_offset
block_id = read_32bitBE(vgmstream->current_block_offset + 0x00, sf);
if (block_id == EA_BLOCKID_END) { /* banks should never contain movie "SHxx" */
if (!standalone)
break;
}
else if (block_id == EA_BLOCKID_HEADER) { /* "SCHl" start block (movie "SHxx" shouldn't use multi files) */
multiple_schl = 1;
break;
}
if (vgmstream->current_block_samples > 0) {
/* HACK: fix num_samples for streams with multiple SCHl. Need to eventually get rid of this.
* Get total samples by parsing block headers, needed when multiple files are stitched together.
* Some EA files (.mus/eam/sng/etc) concat many small subfiles, used for interactive/mapped
* music (.map/lin). Subfiles always share header, except num_samples. */
num_samples += vgmstream->current_block_samples;
/* stream size is almost never provided in bank files so we have to calc it manually */
stream_size += vgmstream->next_block_offset - vgmstream->ch[0].offset;
}
@ -1944,17 +1934,7 @@ static void update_ea_stream_size_and_samples(STREAMFILE* sf, off_t start_offset
/* reset once we're done */
block_update_ea_schl(start_offset, vgmstream);
/* only use calculated samples with multiple subfiles (rarely header samples may be less due to padding) */
if (standalone && multiple_schl) {
VGM_LOG("EA SCHl: multiple SCHl found\n");
if (num_samples > vgmstream->num_samples) {
vgmstream->num_samples = num_samples;
}
}
if (vgmstream->stream_size == 0)
vgmstream->stream_size = stream_size;
vgmstream->stream_size = stream_size;
}
/* find data start offset inside the first SCDl; not very elegant but oh well */

View File

@ -1,118 +1,155 @@
#include "meta.h"
#include "../layout/layout.h"
#include "../coding/coding.h"
/* Possibly the same as EA_CODEC_x in variable SCHl */
#define EA_CODEC_PCM 0x00
#define EA_CODEC_IMA 0x02
typedef struct {
int8_t version;
int8_t bps;
int8_t channels;
int8_t codec;
int16_t sample_rate;
int32_t num_samples;
int big_endian;
int loop_flag;
} ea_fixed_header;
static int parse_fixed_header(STREAMFILE* streamFile, ea_fixed_header* ea, off_t begin_offset);
/* EA SCHl with fixed header - from EA games (~1997? ex. NHL 97 PC) */
VGMSTREAM * init_vgmstream_ea_schl_fixed(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
size_t header_size;
ea_fixed_header ea = {0};
/* checks */
/* .asf: original
* .lasf: fake for plugins */
if (!check_extensions(streamFile,"asf,lasf"))
goto fail;
/* check header (see ea_schl.c for more info about blocks) */
if (read_32bitBE(0x00,streamFile) != 0x5343486C) /* "SCHl" */
goto fail;
header_size = read_32bitLE(0x04,streamFile);
if (!parse_fixed_header(streamFile,&ea, 0x08))
goto fail;
start_offset = header_size;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(ea.channels, ea.loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = ea.sample_rate;
vgmstream->num_samples = ea.num_samples;
//vgmstream->loop_start_sample = ea.loop_start;
//vgmstream->loop_end_sample = ea.loop_end;
vgmstream->codec_endian = ea.big_endian;
vgmstream->meta_type = meta_EA_SCHL_fixed;
vgmstream->layout_type = layout_blocked_ea_schl;
switch (ea.codec) {
case EA_CODEC_PCM:
vgmstream->coding_type = ea.bps==8 ? coding_PCM8 : (ea.big_endian ? coding_PCM16BE : coding_PCM16LE);
break;
case EA_CODEC_IMA:
vgmstream->coding_type = coding_DVI_IMA; /* stereo/mono, high nibble first */
break;
default:
VGM_LOG("EA: unknown codec 0x%02x\n", ea.codec);
goto fail;
}
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}
static int parse_fixed_header(STREAMFILE* streamFile, ea_fixed_header* ea, off_t begin_offset) {
off_t offset = begin_offset;
if (read_32bitBE(offset+0x00, streamFile) != 0x5041546C && /* "PATl" */
read_32bitBE(offset+0x38, streamFile) != 0x544D706C) /* "TMpl" */
goto fail;
offset += 0x3c; /* after TMpl */
ea->version = read_8bit(offset+0x00, streamFile);
ea->bps = read_8bit(offset+0x01, streamFile);
ea->channels = read_8bit(offset+0x02, streamFile);
ea->codec = read_8bit(offset+0x03, streamFile);
VGM_ASSERT(read_16bitLE(offset+0x04, streamFile) != 0, "EA SCHl fixed: unknown1 found\n");
/* 0x04(16): unknown */
ea->sample_rate = (uint16_t)read_16bitLE(offset+0x06, streamFile);
ea->num_samples = read_32bitLE(offset+0x08, streamFile);
VGM_ASSERT(read_32bitLE(offset+0x0c, streamFile) != -1, "EA SCHl fixed: unknown2 found\n"); /* loop start? */
VGM_ASSERT(read_32bitLE(offset+0x10, streamFile) != -1, "EA SCHl fixed: unknown3 found\n"); /* loop end? */
VGM_ASSERT(read_32bitLE(offset+0x14, streamFile) != 0, "EA SCHl fixed: unknown4 found\n"); /* data start? */
VGM_ASSERT(read_32bitLE(offset+0x18, streamFile) != -1, "EA SCHl fixed: unknown5 found\n");
VGM_ASSERT(read_32bitLE(offset+0x1c, streamFile) != 0x7F, "EA SCHl fixed: unknown6 found\n");
//ea->loop_flag = (ea->loop_end_sample);
return 1;
fail:
return 0;
}
#include "meta.h"
#include "../layout/layout.h"
#include "../coding/coding.h"
/* Possibly the same as EA_CODEC_x in variable SCHl */
#define EA_CODEC_PCM 0x00
#define EA_CODEC_IMA 0x02
#define EA_CODEC_PSX 0x06
typedef struct {
int8_t version;
int8_t bps;
int8_t channels;
int8_t codec;
int sample_rate;
int32_t num_samples;
int big_endian;
int loop_flag;
} ea_fixed_header;
static int parse_fixed_header(STREAMFILE* sf, ea_fixed_header* ea);
/* EA SCHl with fixed header - from EA games (~1997?) */
VGMSTREAM* init_vgmstream_ea_schl_fixed(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
off_t start_offset;
size_t header_size;
ea_fixed_header ea = {0};
/* checks */
if (!is_id32be(0x00,sf, "SCHl"))
goto fail;
/* .asf: original [NHK 97 (PC)]
* .lasf: fake for plugins
* .cnk: ps1 [NBA Live 97 (PS1)] */
if (!check_extensions(sf,"asf,lasf,cnk"))
goto fail;
/* see ea_schl.c for more info about blocks */
//TODO: handle SCCl? [NBA Live 97 (PS1)]
header_size = read_u32le(0x04,sf);
if (!parse_fixed_header(sf, &ea))
goto fail;
start_offset = header_size;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(ea.channels, ea.loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = ea.sample_rate;
vgmstream->num_samples = ea.num_samples;
//vgmstream->loop_start_sample = ea.loop_start;
//vgmstream->loop_end_sample = ea.loop_end;
vgmstream->codec_endian = ea.big_endian;
vgmstream->meta_type = meta_EA_SCHL_fixed;
vgmstream->layout_type = layout_blocked_ea_schl;
switch (ea.codec) {
case EA_CODEC_PCM:
vgmstream->coding_type = ea.bps==8 ? coding_PCM8 : (ea.big_endian ? coding_PCM16BE : coding_PCM16LE);
break;
case EA_CODEC_IMA:
vgmstream->coding_type = coding_DVI_IMA; /* stereo/mono, high nibble first */
break;
case EA_CODEC_PSX:
vgmstream->coding_type = coding_PSX;
break;
default:
VGM_LOG("EA: unknown codec 0x%02x\n", ea.codec);
goto fail;
}
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}
static int parse_fixed_header(STREAMFILE* sf, ea_fixed_header* ea) {
uint32_t offset = 0x00, size = 0;
if (is_id32be(offset+0x08, sf, "PATl"))
offset = 0x08;
else if (is_id32be(offset+0x0c, sf, "PATl"))
offset = 0x0c; /* extra field in PS1 */
else
goto fail;
size = read_u32le(offset+0x34, sf);
if (size == 0x20 && is_id32be(offset+0x38, sf, "TMpl")) { /* PC LE? */
offset += 0x3c;
ea->version = read_u8 (offset+0x00, sf);
ea->bps = read_u8 (offset+0x01, sf);
ea->channels = read_u8 (offset+0x02, sf);
ea->codec = read_u8 (offset+0x03, sf);
/* 0x04: 0? */
ea->sample_rate = read_u16le(offset+0x06, sf);
ea->num_samples = read_s32le(offset+0x08, sf);
/* 0x0c: -1? loop_start? */
/* 0x10: -1? loop_end? */
/* 0x14: 0? data start? */
/* 0x18: -1? */
/* 0x1c: volume? (always 128) */
}
else if (size == 0x38 && is_id32be(offset+0x38, sf, "TMxl")) { /* PSX LE? */
offset += 0x3c;
ea->version = read_u8 (offset+0x00, sf);
ea->bps = read_u8 (offset+0x01, sf);
ea->channels = read_u8 (offset+0x02, sf);
ea->codec = read_u8 (offset+0x03, sf);
/* 0x04: 0? */
ea->sample_rate = read_u16le(offset+0x06, sf);
/* 0x08: 0x20C? */
ea->num_samples = read_s32le(offset+0x0c, sf);
/* 0x10: -1? loop_start? */
/* 0x14: -1? loop_end? */
/* 0x18: 0x20C? */
/* 0x1c: 0? */
/* 0x20: 0? */
/* 0x24: 0? */
/* 0x28: -1? */
/* 0x2c: -1? */
/* 0x30: -1? */
/* 0x34: volume? (always 128) */
}
else {
goto fail;
}
//ea->loop_flag = (ea->loop_end_sample);
return 1;
fail:
return 0;
}

View File

@ -36,7 +36,7 @@ VGMSTREAM* init_vgmstream_encrypted(STREAMFILE* sf) {
goto fail;
}
temp_sf = setup_ogg_vorbis_streamfile(sf, cfg);
temp_sf = setup_ogg_vorbis_streamfile(sf, &cfg);
if (!temp_sf) goto fail;
vgmstream = init_vgmstream_ogg_vorbis(temp_sf);
@ -53,7 +53,7 @@ VGMSTREAM* init_vgmstream_encrypted(STREAMFILE* sf) {
goto fail;
}
temp_sf = setup_ogg_vorbis_streamfile(sf, cfg);
temp_sf = setup_ogg_vorbis_streamfile(sf, &cfg);
if (!temp_sf) goto fail;
#ifdef VGM_USE_FFMPEG //TODO: allow MP3 without FFmpeg
@ -72,7 +72,7 @@ VGMSTREAM* init_vgmstream_encrypted(STREAMFILE* sf) {
goto fail;
}
temp_sf = setup_ogg_vorbis_streamfile(sf, cfg);
temp_sf = setup_ogg_vorbis_streamfile(sf, &cfg);
if (!temp_sf) goto fail;
vgmstream = init_vgmstream_riff(temp_sf);
@ -80,12 +80,13 @@ VGMSTREAM* init_vgmstream_encrypted(STREAMFILE* sf) {
return vgmstream;
}
if (check_extensions(sf,"bgm")) {
if (check_extensions(sf,"bgm,mse,koe")) {
/* Studio Ring games [Nanami to Konomi no Oshiete ABC (PC), Oyatsu no Jikan (PC)]
* .bgm: BGM, .mse: SE, .koe: Voice */
uint8_t keybuf[0x100];
size_t key_size;
off_t start;
/* Studio Ring games [Nanami to Konomi no Oshiete ABC (PC), Oyatsu no Jikan (PC)] */
if (id != get_id32be("RIFF"))
goto fail;

View File

@ -221,7 +221,7 @@ VGMSTREAM* init_vgmstream_fsb(STREAMFILE* sf) {
}
else {
/* subsong header for normal files */
stream_header_size = (uint16_t)read_16bitLE(header_offset+0x00,sf);
stream_header_size = read_u16le(header_offset+0x00,sf);
fsb.name_offset = header_offset+0x02;
fsb.name_size = 0x20-0x02;
fsb.num_samples = read_32bitLE(header_offset+0x20,sf);
@ -231,7 +231,7 @@ VGMSTREAM* init_vgmstream_fsb(STREAMFILE* sf) {
fsb.mode = read_32bitLE(header_offset+0x30,sf);
fsb.sample_rate = read_32bitLE(header_offset+0x34,sf);
/* 0x38: defvol, 0x3a: defpan, 0x3c: defpri */
fsb.channels = read_16bitLE(header_offset+0x3e,sf);
fsb.channels = read_u16le(header_offset+0x3e,sf);
/* FSB3.1/4:
* 0x40: mindistance, 0x44: maxdistance, 0x48: varfreq/size_32bits
* 0x4c: varvol, 0x4e: fsb.varpan */
@ -287,9 +287,9 @@ VGMSTREAM* init_vgmstream_fsb(STREAMFILE* sf) {
//;VGM_ASSERT(fsb.flags & FMOD_FSB_SOURCE_ENCRYPTED, "FSB ENCRYPTED found\n");
/* sometimes there is garbage at the end or missing bytes due to improper ripping */
VGM_ASSERT(fsb.base_header_size + fsb.sample_headers_size + fsb.sample_data_size != sf->get_size(sf),
vgm_asserti(fsb.base_header_size + fsb.sample_headers_size + fsb.sample_data_size != get_streamfile_size(sf),
"FSB wrong head/data_size found (expected 0x%x vs 0x%x)\n",
fsb.base_header_size + fsb.sample_headers_size + fsb.sample_data_size, sf->get_size(sf));
fsb.base_header_size + fsb.sample_headers_size + fsb.sample_data_size, get_streamfile_size(sf));
/* autodetect unwanted loops */
{
@ -319,7 +319,7 @@ VGMSTREAM* init_vgmstream_fsb(STREAMFILE* sf) {
fsb.loop_flag = !(fsb.mode & FSOUND_LOOP_OFF); /* disabled manually */
if (fsb.loop_flag && !enable_loop && full_loop && is_small) {
VGM_LOG("FSB: disable unwanted loop\n");
VGM_LOG("FSB: disabled unwanted loop\n");
fsb.loop_flag = 0;
}
}
@ -426,8 +426,8 @@ VGMSTREAM* init_vgmstream_fsb(STREAMFILE* sf) {
/* get libcelt version (set in the first subsong only, but try all extradata just in case) */
if (fsb.first_extradata_offset || fsb.extradata_offset) {
uint32_t lib = fsb.first_extradata_offset ?
(uint32_t)read_32bitLE(fsb.first_extradata_offset, sf) :
(uint32_t)read_32bitLE(fsb.extradata_offset, sf);;
read_u32le(fsb.first_extradata_offset, sf) :
read_u32le(fsb.extradata_offset, sf);
switch(lib) {
case 0x80000009: is_new_lib = 0; break; /* War Thunder (PC) */
case 0x80000010: is_new_lib = 1; break; /* Vessel (PC) */
@ -436,7 +436,7 @@ VGMSTREAM* init_vgmstream_fsb(STREAMFILE* sf) {
}
else {
/* split FSBs? try to guess from observed bitstreams */
uint16_t frame = (uint16_t)read_16bitBE(fsb.stream_offset+0x04+0x04,sf);
uint16_t frame = read_u16be(fsb.stream_offset+0x04+0x04,sf);
if ((frame & 0xF000) == 0x6000 || frame == 0xFFFE) {
is_new_lib = 1;
} else {

View File

@ -3,11 +3,14 @@
#include "../coding/coding.h"
#include "../coding/hca_decoder_clhca.h"
//#define HCA_BRUTEFORCE
#ifdef HCA_BRUTEFORCE
static void bruteforce_hca_key(STREAMFILE* sf, hca_codec_data* hca_data, unsigned long long* p_keycode, uint16_t subkey);
#ifdef VGM_DEBUG_OUTPUT
//#define HCA_BRUTEFORCE
#ifdef HCA_BRUTEFORCE
#include "hca_bf.h"
#endif
#endif
static void find_hca_key(hca_codec_data* hca_data, uint64_t* p_keycode, uint16_t subkey);
static int find_hca_key(hca_codec_data* hca_data, uint64_t* p_keycode, uint16_t subkey);
/* CRI HCA - streamed audio from CRI ADX2/Atom middleware */
@ -57,7 +60,9 @@ VGMSTREAM* init_vgmstream_hca_subkey(STREAMFILE* sf, uint16_t subkey) {
}
#ifdef HCA_BRUTEFORCE
else if (1) {
bruteforce_hca_key(sf, hca_data, &keycode, subkey);
int ok = find_hca_key(hca_data, &keycode, subkey);
if (!ok)
bruteforce_hca_key(sf, hca_data, &keycode, subkey);
}
#endif
else {
@ -123,60 +128,32 @@ fail:
}
static inline void test_key(hca_codec_data* hca_data, uint64_t key, uint16_t subkey, int* best_score, uint64_t* best_keycode) {
int score;
//;VGM_LOG("HCA: test key=%08x%08x, subkey=%04x\n",
// (uint32_t)((key >> 32) & 0xFFFFFFFF), (uint32_t)(key & 0xFFFFFFFF), subkey);
if (subkey) {
key = key * ( ((uint64_t)subkey << 16u) | ((uint16_t)~subkey + 2u) );
}
score = test_hca_key(hca_data, (unsigned long long)key);
//;VGM_LOG("HCA: test key=%08x%08x, subkey=%04x, score=%i\n",
// (uint32_t)((key >> 32) & 0xFFFFFFFF), (uint32_t)(key & 0xFFFFFFFF), subkey, score);
/* wrong key */
if (score < 0)
return;
//;VGM_LOG("HCA: ok key=%08x%08x, subkey=%04x, score=%i\n",
// (uint32_t)((key >> 32) & 0xFFFFFFFF), (uint32_t)(key & 0xFFFFFFFF), subkey, score);
/* update if something better is found */
if (*best_score <= 0 || (score < *best_score && score > 0)) {
*best_score = score;
*best_keycode = key;
}
}
/* try to find the decryption key from a list. */
static void find_hca_key(hca_codec_data* hca_data, uint64_t* p_keycode, uint16_t subkey) {
const size_t keys_length = sizeof(hcakey_list) / sizeof(hcakey_info);
int best_score = -1;
/* try to find the decryption key from a list */
static int find_hca_key(hca_codec_data* hca_data, uint64_t* p_keycode, uint16_t subkey) {
const size_t keys_length = sizeof(hcakey_list) / sizeof(hcakey_list[0]);
int i;
hca_keytest_t hk = {0};
*p_keycode = 0xCC55463930DBE1AB; /* defaults to PSO2 key, most common */
hk.best_key = 0xCC55463930DBE1AB; /* defaults to PSO2 key, most common */
hk.subkey = subkey;
for (i = 0; i < keys_length; i++) {
uint64_t key = hcakey_list[i].key;
hk.key = hcakey_list[i].key;
test_key(hca_data, key, subkey, &best_score, p_keycode);
if (best_score == 1)
test_hca_key(hca_data, &hk);
if (hk.best_score == 1)
goto done;
#if 0
{
int j;
size_t subkeys_size = hcakey_list[i].subkeys_size;
const uint16_t *subkeys = hcakey_list[i].subkeys;
const uint16_t* subkeys = hcakey_list[i].subkeys;
if (subkeys_size > 0 && subkey == 0) {
for (j = 0; j < subkeys_size; j++) {
test_key(hca_data, key, subkeys[j], &best_score, p_keycode);
if (best_score == 1)
hk.subkey = subkeys[j];
test_hca_key(hca_data, &hk);
if (hk.best_score == 1)
goto done;
}
}
@ -185,201 +162,9 @@ static void find_hca_key(hca_codec_data* hca_data, uint64_t* p_keycode, uint16_t
}
done:
VGM_ASSERT(best_score > 1, "HCA: best key=%08x%08x (score=%i)\n",
(uint32_t)((*p_keycode >> 32) & 0xFFFFFFFF), (uint32_t)(*p_keycode & 0xFFFFFFFF), best_score);
vgm_asserti(best_score < 0, "HCA: decryption key not found\n");
*p_keycode = hk.best_key;
VGM_ASSERT(hk.best_score > 1, "HCA: best key=%08x%08x (score=%i)\n",
(uint32_t)((*p_keycode >> 32) & 0xFFFFFFFF), (uint32_t)(*p_keycode & 0xFFFFFFFF), hk.best_score);
vgm_asserti(hk.best_score <= 0, "HCA: decryption key not found\n");
return hk.best_score > 0;
}
#ifdef HCA_BRUTEFORCE
typedef enum {
HBF_TYPE_64LE_1,
HBF_TYPE_64BE_1,
HBF_TYPE_32LE_1,
HBF_TYPE_32BE_1,
HBF_TYPE_64LE_4,
HBF_TYPE_64BE_4,
HBF_TYPE_32LE_4,
HBF_TYPE_32BE_4,
} HBF_type_t;
/* Bruteforce binary keys in executables and similar files, mainly for some mobile games.
* Kinda slow but acceptable for ~20MB exes, not very optimized. Unity usually has keys
* in plaintext (inside levelX or other base files) instead though. */
static void bruteforce_hca_key_bin_type(STREAMFILE* sf, hca_codec_data* hca_data, unsigned long long* p_keycode, uint16_t subkey, HBF_type_t type) {
STREAMFILE* sf_keys = NULL;
uint8_t* buf = NULL;
int best_score = 0xFFFFFF, cur_score;
off_t keys_size, bytes;
int pos;
uint64_t old_key = 0;
uint64_t key = 0;
/* load whole file in memory for performance (exes with keys shouldn't be too big) */
sf_keys = open_streamfile_by_filename(sf, "keys.bin");
if (!sf_keys) return;
VGM_LOG("HCA: test keys.bin (type %i)\n", type);
*p_keycode = 0;
keys_size = get_streamfile_size(sf_keys);
buf = malloc(keys_size);
if (!buf) goto done;
bytes = read_streamfile(buf, 0, keys_size, sf_keys);
if (bytes != keys_size) goto done;
VGM_LOG("HCA: start\n");
pos = 0;
while (pos < keys_size - 4) {
VGM_ASSERT(pos % 0x1000000 == 0, "HCA: pos %x...\n", pos);
/* keys are usually u64le but other orders may exist */
switch(type) {
case HBF_TYPE_64LE_1: key = get_u64le(buf + pos); pos += 0x01; break;
case HBF_TYPE_64BE_1: key = get_u64be(buf + pos); pos += 0x01; break;
case HBF_TYPE_32LE_1: key = get_u32le(buf + pos); pos += 0x01; break;
case HBF_TYPE_32BE_1: key = get_u32be(buf + pos); pos += 0x01; break;
case HBF_TYPE_64LE_4: key = get_u64le(buf + pos); pos += 0x04; break;
case HBF_TYPE_64BE_4: key = get_u64be(buf + pos); pos += 0x04; break;
case HBF_TYPE_32LE_4: key = get_u32le(buf + pos); pos += 0x04; break;
case HBF_TYPE_32BE_4: key = get_u32be(buf + pos); pos += 0x04; break;
default: key = 0; pos = keys_size; break;
}
if (key == 0 || key == old_key)
continue;
old_key = key;
cur_score = 0;
test_key(hca_data, key, subkey, &cur_score, p_keycode);
if (cur_score == 1) {
best_score = cur_score;
goto done;
}
if (cur_score > 0 && cur_score <= 500) {
VGM_LOG("HCA: possible key=%08x%08x (score=%i) at %x\n",
(uint32_t)((key >> 32) & 0xFFFFFFFF), (uint32_t)(key & 0xFFFFFFFF), cur_score, pos-0x04);
if (best_score > cur_score)
best_score = cur_score;
}
}
done:
if (best_score < 0 || best_score > 10000) {
*p_keycode = 0;
VGM_LOG("HCA: good key not found\n");
}
else {
/* print key as p_keycode includes subkey */
VGM_LOG("HCA: best key=%08x%08x (score=%i)\n",
(uint32_t)((key >> 32) & 0xFFFFFFFF), (uint32_t)(key & 0xFFFFFFFF), best_score);
}
close_streamfile(sf_keys);
free(buf);
}
static void bruteforce_hca_key_bin(STREAMFILE* sf, hca_codec_data* hca_data, unsigned long long* p_keycode, uint16_t subkey) {
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_64LE_1);
/*
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_32LE_1);
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_64BE_1);
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_32BE_1);
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_64LE_4);
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_32LE_4);
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_64BE_4);
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_32BE_4);
*/
}
#include <inttypes.h>
//#include <stdio.h>
/* same as the above but for txt lines. */
static void bruteforce_hca_key_txt(STREAMFILE* sf, hca_codec_data* hca_data, unsigned long long* p_keycode, uint16_t subkey) {
STREAMFILE* sf_keys = NULL;
uint8_t* buf = NULL;
int best_score = 0xFFFFFF, cur_score;
off_t keys_size, bytes;
int i = 0, pos;
char line[1024];
/* load whole file in memory for performance (exes with keys shouldn't be too big) */
sf_keys = open_streamfile_by_filename(sf, "keys.txt");
if (!sf_keys) return;
VGM_LOG("HCA: test keys.txt\n");
*p_keycode = 0;
keys_size = get_streamfile_size(sf_keys);
buf = malloc(keys_size);
if (!buf) goto done;
bytes = read_streamfile(buf, 0, keys_size, sf_keys);
if (bytes != keys_size) goto done;
VGM_LOG("HCA: start\n");
pos = 0;
while (pos < keys_size) {
int bytes_read, line_ok, count;
uint64_t key = 0;
bytes_read = read_line(line, sizeof(line), pos, sf_keys, &line_ok);
pos += bytes_read;
if (!line_ok) continue; /* line too long */
count = sscanf(line, "%" SCNd64, &key);
if (count != 1) continue;
VGM_ASSERT(pos % 10000 == 0, "HCA: count %i...\n", i);
if (key == 0)
continue;
i++;
cur_score = 0;
test_key(hca_data, key, subkey, &cur_score, p_keycode);
if (cur_score == 1)
goto done;
if (cur_score > 0 && cur_score <= 500) {
VGM_LOG("HCA: possible key=%08x%08x (score=%i) at %x\n",
(uint32_t)((key >> 32) & 0xFFFFFFFF), (uint32_t)(key & 0xFFFFFFFF), cur_score, pos-0x04);
if (best_score > cur_score)
best_score = cur_score;
}
}
done:
VGM_LOG("HCA: done %i keys.txt\n", i);
VGM_ASSERT(best_score > 0, "HCA: best key=%08x%08x (score=%i)\n",
(uint32_t)((*p_keycode >> 32) & 0xFFFFFFFF), (uint32_t)(*p_keycode & 0xFFFFFFFF), best_score);
VGM_ASSERT(best_score < 0, "HCA: key not found\n");
if (best_score < 0 || best_score > 10000)
*p_keycode = 0;
close_streamfile(sf_keys);
free(buf);
}
static void bruteforce_hca_key(STREAMFILE* sf, hca_codec_data* hca_data, unsigned long long* p_keycode, uint16_t subkey) {
bruteforce_hca_key_bin(sf, hca_data, p_keycode, subkey);
if (*p_keycode != 0)
return;
bruteforce_hca_key_txt(sf, hca_data, p_keycode, subkey);
if (*p_keycode != 0)
return;
}
#endif

View File

@ -0,0 +1,257 @@
#ifndef _HCA_BF_
#define _HCA_BF_
#include "meta.h"
#include "../coding/coding.h"
#ifdef HCA_BRUTEFORCE
static void bruteforce_process_result(hca_keytest_t* hk, unsigned long long* p_keycode) {
*p_keycode = hk->best_key;
if (hk->best_score < 0 || hk->best_score > 10000) {
VGM_LOG("HCA BF: no good key found\n");
}
else {
VGM_LOG("HCA BF: best key=%08x%08x (score=%i)\n",
(uint32_t)((*p_keycode >> 32) & 0xFFFFFFFF), (uint32_t)(*p_keycode & 0xFFFFFFFF), hk->best_score);
}
}
typedef enum {
HBF_TYPE_64LE_1,
HBF_TYPE_64BE_1,
HBF_TYPE_32LE_1,
HBF_TYPE_32BE_1,
HBF_TYPE_64LE_4,
HBF_TYPE_64BE_4,
HBF_TYPE_32LE_4,
HBF_TYPE_32BE_4,
} HBF_type_t;
/* Bruteforce binary keys in executables and similar files, mainly for some mobile games.
* Kinda slow but acceptable for ~100MB exes, not very optimized. Unity usually has keys
* in plaintext (inside levelX or other base files) instead though, use test below. */
static void bruteforce_hca_key_bin_type(STREAMFILE* sf, hca_codec_data* hca_data, unsigned long long* p_keycode, uint16_t subkey, HBF_type_t type) {
STREAMFILE* sf_keys = NULL;
uint8_t* buf = NULL;
uint32_t keys_size, bytes;
int pos, step;
uint64_t key = 0, old_key = 0;
hca_keytest_t hk = {0};
hk.subkey = subkey;
/* load whole file in memory for performance (exes with keys shouldn't be too big) */
sf_keys = open_streamfile_by_filename(sf, "keys.bin");
if (!sf_keys) return;
VGM_LOG("HCA BF: test keys.bin (type %i)\n", type);
*p_keycode = 0;
keys_size = get_streamfile_size(sf_keys);
buf = malloc(keys_size);
if (!buf) {
VGM_LOG("HCA BF: key file too big!\n");
goto done;
}
bytes = read_streamfile(buf, 0, keys_size, sf_keys);
if (bytes != keys_size) goto done;
VGM_LOG("HCA BF: start .bin\n");
switch(type) {
case HBF_TYPE_64LE_1:
case HBF_TYPE_64BE_1:
case HBF_TYPE_32LE_1:
case HBF_TYPE_32BE_1: step = 0x01; break;
case HBF_TYPE_64LE_4:
case HBF_TYPE_64BE_4:
case HBF_TYPE_32LE_4:
case HBF_TYPE_32BE_4: step = 0x04; break;
default: goto done;
}
pos = 0;
while (pos < keys_size - 8) {
VGM_ASSERT(pos % 0x1000000 == 0, "HCA: pos %x...\n", pos);
/* keys are usually u64le but other orders may exist */
switch(type) {
case HBF_TYPE_64LE_1: key = get_u64le(buf + pos); break;
case HBF_TYPE_64BE_1: key = get_u64be(buf + pos); break;
case HBF_TYPE_32LE_1: key = get_u32le(buf + pos); break;
case HBF_TYPE_32BE_1: key = get_u32be(buf + pos); break;
case HBF_TYPE_64LE_4: key = get_u64le(buf + pos); break;
case HBF_TYPE_64BE_4: key = get_u64be(buf + pos); break;
case HBF_TYPE_32LE_4: key = get_u32le(buf + pos); break;
case HBF_TYPE_32BE_4: key = get_u32be(buf + pos); break;
default: goto done;
}
pos += step;
if (key == 0 || key == old_key)
continue;
old_key = key;
hk.key = key;
test_hca_key(hca_data, &hk);
if (hk.best_score == 1)
goto done;
}
done:
bruteforce_process_result(&hk, p_keycode);
close_streamfile(sf_keys);
free(buf);
}
static void bruteforce_hca_key_bin(STREAMFILE* sf, hca_codec_data* hca_data, unsigned long long* p_keycode, uint16_t subkey) {
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_64LE_1);
/*
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_32LE_1);
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_64BE_1);
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_32BE_1);
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_64LE_4);
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_32LE_4);
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_64BE_4);
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_32BE_4);
*/
}
#include <inttypes.h>
//#include <stdio.h>
/* same as the above but for txt lines. */
static void bruteforce_hca_key_txt(STREAMFILE* sf, hca_codec_data* hca_data, unsigned long long* p_keycode, uint16_t subkey) {
STREAMFILE* sf_keys = NULL;
uint8_t* buf = NULL;
uint32_t keys_size, bytes;
char line[1024];
int i = 0, pos;
uint64_t key = 0;
hca_keytest_t hk = {0};
hk.subkey = subkey;
/* load whole file in memory for performance (exes with keys shouldn't be too big) */
sf_keys = open_streamfile_by_filename(sf, "keys.txt");
if (!sf_keys) return;
VGM_LOG("HCA BF: test keys.txt\n");
*p_keycode = 0;
keys_size = get_streamfile_size(sf_keys);
buf = malloc(keys_size);
if (!buf) {
VGM_LOG("HCA BF: key file too big!\n");
goto done;
}
bytes = read_streamfile(buf, 0, keys_size, sf_keys);
if (bytes != keys_size) goto done;
VGM_LOG("HCA BF: start .txt\n");
pos = 0;
while (pos < keys_size) {
int bytes_read, line_ok, count;
key = 0;
bytes_read = read_line(line, sizeof(line), pos, sf_keys, &line_ok);
pos += bytes_read;
if (!line_ok) continue; /* line too long */
count = sscanf(line, "%" SCNd64, &key);
if (count != 1) continue;
VGM_ASSERT(pos % 10000 == 0, "HCA: count %i...\n", i);
if (key == 0)
continue;
i++;
hk.key = key;
test_hca_key(hca_data, &hk);
if (hk.best_score == 1)
goto done;
}
done:
bruteforce_process_result(&hk, p_keycode);
close_streamfile(sf_keys);
free(buf);
}
/* same as the above but good ol' bruteforce numbers (useful for games with keys that are dates) */
static void bruteforce_hca_key_num(STREAMFILE* sf, hca_codec_data* hca_data, unsigned long long* p_keycode, uint16_t subkey) {
STREAMFILE* sf_keys = NULL;
uint32_t keys_size;
uint64_t min, max;
uint64_t key = 0;
hca_keytest_t hk = {0};
hk.subkey = subkey;
/* load whole file in memory for performance (exes with keys shouldn't be too big) */
sf_keys = open_streamfile_by_filename(sf, "keys.num");
if (!sf_keys) return;
VGM_LOG("HCA BF: test keys.num\n");
*p_keycode = 0;
keys_size = get_streamfile_size(sf_keys);
/* don't set too high as it does ~70000 keys per second, do the math */
if (keys_size < 0x10) {
min = 0;
max = 0xFFFFFFFF;
}
else {
min = read_u64be(0x00, sf_keys);
max = read_u64be(0x08, sf_keys);
}
VGM_LOG("HCA BF: start .num\n");
while (min < max) {
key = min;
min++;
VGM_ASSERT(min % 0x100000 == 0, "HCA: count %x...\n", (uint32_t)min);
hk.key = key;
test_hca_key(hca_data, &hk);
if (hk.best_score == 1)
goto done;
}
done:
bruteforce_process_result(&hk, p_keycode);
close_streamfile(sf_keys);
}
static void bruteforce_hca_key(STREAMFILE* sf, hca_codec_data* hca_data, unsigned long long* p_keycode, uint16_t subkey) {
bruteforce_hca_key_bin(sf, hca_data, p_keycode, subkey);
if (*p_keycode != 0)
return;
bruteforce_hca_key_txt(sf, hca_data, p_keycode, subkey);
if (*p_keycode != 0)
return;
bruteforce_hca_key_num(sf, hca_data, p_keycode, subkey);
if (*p_keycode != 0)
return;
}
#endif
#endif /*_HCA_BF_*/

View File

@ -48,7 +48,7 @@ static const hcakey_info hcakey_list[] = {
// - Ro-Kyu-Bu! Naisho no Shutter Chance (PSVita)
{1234253142}, // 0000000049913556
// Idolm@ster Cinderella Stage (iOS/Android)
// THE iDOLM@STER Cinderella Girls: Starlight Stage (iOS/Android)
// Shadowverse (iOS/Android)
{59751358413602}, // 00003657F27E3B22
@ -67,16 +67,15 @@ static const hcakey_info hcakey_list[] = {
// Sonic Runners (iOS/Android)
{19910623}, // 00000000012FCFDF
// Fate/Grand Order (iOS/Android) base assets
{12345}, // 0000000000003039
// Fate/Grand Order (iOS/Android) download assets *unconfirmed
{9117927877783581796}, // 7E89631892EBF464
// Fate/Grand Order (iOS/Android)
{12345}, // 0000000000003039 - base assets
{9117927877783581796}, // 7E89631892EBF464 - downloaded assets *unconfirmed
// Raramagi (iOS/Android)
{45719322}, // 0000000002B99F1A
// Idolm@ster Million Live (iOS/Android)
// THE iDOLM@STER Million Live! (iOS/Android)
// THE iDOLM@STER SideM GROWING STARS (Android)
{765765765765765}, // 0002B875BC731A85
// Kurokishi to Shiro no Maou (iOS/Android)
@ -775,7 +774,48 @@ static const hcakey_info hcakey_list[] = {
// Nogizaka 46 Fractal (Android)
{984635491346198130}, // 0DAA20C336EEAE72
// NEO: The World Ends With You (PC)
{53346933792338754}, // 00BD86C0EE8C7342
// THE iDOLM@STER Starlit Season (PS4/PC)
{0x1e03b570b6145d1d}, // BGM
{0x1da915aaa181a461}, // SE
{0x1c82b6ab7487a5ec}, // Voice
{0x6d275d3666c2f9c8}, // Sng001
{0x0f53815df3044e6d}, // Sng002
{0x158778e2e2fab347}, // Sng003
{0x16b75e8b5247d46b}, // Sng004
{0x157df8a6047048fc}, // Sng005
{0x184d358b50b658d0}, // Sng006
{0x157fb75af4ddd983}, // Sng007
{0x404ba38c3e470827}, // Sng008
{0x01d0b788a3b60d48}, // Sng009
{0x021718d55d0960c9}, // Sng010
{0x0021c5993d2b901c}, // Sng011
{0x08237bcb9b711087}, // Sng012
{0x01af60402e1228a5}, // Sng013
{0x4eec18ab73a1a634}, // Sng014
{0x1855099898b11ad9}, // Sng015
{0x57ef8f2ea5d54db5}, // Sng016
{0x17cc6975d67e2a1f}, // Sng017
{0x0a5d0fc8cc5c4502}, // Sng018
{0x198ea1a17416050b}, // Sng019
{0x2aa3b8abad207a1e}, // Sng020
{0x33d98a3a9f9bfdef}, // Sng026
{0x2284fd5ca82c78f4}, // Sng027
{0x178a76b6436d20f0}, // Sng028
{0x3ff99f2fed65a1ed}, // Sng030
// Ulala: Idle Adventure (Android)
{20191022}, // 000000000134172E
// Girls' Frontline: Project Neural Cloud (Android)
{210222522032314}, // 0000BF323EBFE0BA
// Super Robot Wars 30 (PC)
{6734488621090458}, // 0017ECFB5201069A
};
#endif/*_HCA_KEYS_H_*/

View File

@ -2,74 +2,50 @@
#include "../coding/coding.h"
/* IKM - MiCROViSiON PS2 container [Zwei (PS2)] */
VGMSTREAM* init_vgmstream_ikm_ps2(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
static VGMSTREAM* init_vgmstream_ikm_ps2(STREAMFILE* sf) {
VGMSTREAM* v = NULL;
off_t start_offset;
int loop_flag, channel_count;
int loop_flag, channels;
/* checks */
if (!is_id32be(0x00,sf, "IKM\0"))
goto fail;
if (!check_extensions(sf,"ikm"))
goto fail;
/* 0x20: type 03? */
if (!is_id32be(0x40,sf, "AST\0"))
goto fail;
loop_flag = (read_s32le(0x14, sf) > 0);
channel_count = read_s32le(0x50, sf);
channels = read_s32le(0x50, sf);
start_offset = 0x800;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag);
if (!vgmstream) goto fail;
v = allocate_vgmstream(channels, loop_flag);
if (!v) goto fail;
vgmstream->meta_type = meta_IKM;
vgmstream->sample_rate = read_s32le(0x44, sf);
vgmstream->num_samples = ps_bytes_to_samples(read_s32le(0x4c, sf), channel_count);
vgmstream->loop_start_sample = read_s32le(0x14, sf);
vgmstream->loop_end_sample = read_s32le(0x18, sf);
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x10; /* @0x40 / channels */
v->meta_type = meta_IKM;
v->sample_rate = read_s32le(0x44, sf);
v->num_samples = ps_bytes_to_samples(read_s32le(0x4c, sf), channels);
v->loop_start_sample = read_s32le(0x14, sf);
v->loop_end_sample = read_s32le(0x18, sf);
v->coding_type = coding_PSX;
v->layout_type = layout_interleave;
v->interleave_block_size = 0x10; /* @0x40 / channels */
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
if (!vgmstream_open_stream(v, sf, start_offset))
goto fail;
return vgmstream;
return v;
fail:
close_vgmstream(vgmstream);
close_vgmstream(v);
return NULL;
}
/* IKM - MiCROViSiON PC container [Chaos Legion (PC), Legend of Galactic Heroes (PC)] */
VGMSTREAM* init_vgmstream_ikm_pc(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
static VGMSTREAM* init_vgmstream_ikm_pc(STREAMFILE* sf) {
VGMSTREAM* v = NULL;
off_t start_offset;
/* checks */
if (!is_id32be(0x00,sf, "IKM\0"))
goto fail;
if (!check_extensions(sf,"ikm"))
goto fail;
/* 0x20: type 01? */
/* find "OggS" start */
if (is_id32be(0x30,sf, "OggS")) {
if (is_id32be(0x30,sf, "OggS"))
start_offset = 0x30; /* Chaos Legion (PC) */
}
else if (is_id32be(0x800,sf, "OggS")) {
else
start_offset = 0x800; /* Legend of Galactic Heroes (PC) */
}
else {
goto fail;
}
{
ogg_vorbis_meta_info_t ovmi = {0};
@ -81,31 +57,23 @@ VGMSTREAM* init_vgmstream_ikm_pc(STREAMFILE* sf) {
ovmi.loop_flag = ovmi.loop_end > 0;
ovmi.stream_size = read_s32le(0x24, sf);
vgmstream = init_vgmstream_ogg_vorbis_config(sf, start_offset, &ovmi);
v = init_vgmstream_ogg_vorbis_config(sf, start_offset, &ovmi);
if (!v) goto fail;
}
return vgmstream;
return v;
fail:
close_vgmstream(vgmstream);
close_vgmstream(v);
return NULL;
}
/* IKM - MiCROViSiON PSP container [The Legend of Heroes: A Tear of Vermillion (PSP)] */
VGMSTREAM* init_vgmstream_ikm_psp(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
static VGMSTREAM* init_vgmstream_ikm_psp(STREAMFILE* sf) {
VGMSTREAM* v = NULL;
STREAMFILE* temp_sf = NULL;
off_t start_offset;
size_t data_size;
/* checks */
if (!is_id32be(0x00,sf, "IKM\0"))
goto fail;
if (!check_extensions(sf,"ikm"))
goto fail;
/* 0x20: type 00? */
if (!is_id32be(0x800,sf, "RIFF"))
goto fail;
@ -116,16 +84,43 @@ VGMSTREAM* init_vgmstream_ikm_psp(STREAMFILE* sf) {
temp_sf = setup_subfile_streamfile(sf, start_offset, data_size, "at3");
if (!temp_sf) goto fail;
vgmstream = init_vgmstream_riff(temp_sf);
if (!vgmstream) goto fail;
v = init_vgmstream_riff(temp_sf);
if (!v) goto fail;
vgmstream->meta_type = meta_IKM;
v->meta_type = meta_IKM;
close_streamfile(temp_sf);
return vgmstream;
return v;
fail:
close_streamfile(temp_sf);
close_vgmstream(vgmstream);
close_vgmstream(v);
return NULL;
}
/* IKM - MiCROViSiON container */
VGMSTREAM* init_vgmstream_ikm(STREAMFILE* sf) {
uint32_t type;
/* checks */
if (!is_id32be(0x00,sf, "IKM\0"))
goto fail;
if (!check_extensions(sf,"ikm"))
goto fail;
type = read_u32le(0x20, sf);
switch(type) {
case 0x00: /* The Legend of Heroes: A Tear of Vermillion (PSP) */
return init_vgmstream_ikm_psp(sf);
case 0x01: /* Chaos Legion (PC), Legend of Galactic Heroes (PC) */
return init_vgmstream_ikm_pc(sf);
case 0x03: /* Zwei (PS2) */
return init_vgmstream_ikm_ps2(sf);
default:
goto fail;
}
fail:
return NULL;
}

View File

@ -30,7 +30,13 @@ VGMSTREAM* init_vgmstream_lopu_fb(STREAMFILE* sf) {
/* rest: null */
loop_flag = (loop_end > 0); /* -1 if no loop */
/* Must remove skip or some files decode past limit. loop_end equals to PC (.ogg) version's max
* samples, but in some case (stage_park) goes slightly past max but is still valid.
* (loops shouldn't remove skip as they wouldn't match PC/bgm.txt loop times) */
num_samples -= skip;
if (num_samples < loop_end)
num_samples = loop_end;
/* build the VGMSTREAM */

View File

@ -0,0 +1,51 @@
#include "meta.h"
/* LPCM - from Shade's 'Shade game library' (ShdLib) [Ah! My Goddess (PS2), Warship Gunner (PS2)] */
VGMSTREAM* init_vgmstream_lpcm_shade(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
off_t start_offset;
uint32_t loop_flag, channels;
/* checks */
if (!is_id32be(0x00, sf, "LPCM"))
goto fail;
/* .w: real extension
* .lpcm: fake (header id) */
if (!check_extensions(sf, "w,lpcm"))
goto fail;
/* extra checks since header is kind of simple */
if (read_s32le(0x04,sf) * 0x02 * 2 > get_streamfile_size(sf)) /* data size is less than total samples */
goto fail;
if (read_u32le(0x10,sf) != 0) /* just in case */
goto fail;
start_offset = 0x800; /* assumed, closer to num_samples */
loop_flag = read_s32le(0x8,sf) != 0;
channels = 2;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_LPCM_SHADE;
vgmstream->sample_rate = 48000;
vgmstream->num_samples = read_s32le(0x4,sf);
vgmstream->loop_start_sample = read_s32le(0x8,sf);
vgmstream->loop_end_sample = read_s32le(0xc,sf);
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:
if (vgmstream) close_vgmstream(vgmstream);
return NULL;
}

View File

@ -222,9 +222,7 @@ VGMSTREAM * init_vgmstream_leg(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_filp(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ikm_ps2(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ikm_pc(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ikm_psp(STREAMFILE * streamFile);
VGMSTREAM* init_vgmstream_ikm(STREAMFILE* sf);
VGMSTREAM * init_vgmstream_sfs(STREAMFILE * streamFile);
@ -444,7 +442,9 @@ VGMSTREAM * init_vgmstream_myspd(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_his(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_ps2_ast(STREAMFILE* streamFile);
VGMSTREAM* init_vgmstream_ast_mv(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_ast_mmv(STREAMFILE* sf);
VGMSTREAM * init_vgmstream_dmsg(STREAMFILE* streamFile);
@ -480,7 +480,7 @@ VGMSTREAM * init_vgmstream_ps2_wad(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_ps2_adm(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_ps2_lpcm(STREAMFILE* streamFile);
VGMSTREAM* init_vgmstream_lpcm_shade(STREAMFILE* sf);
VGMSTREAM * init_vgmstream_dsp_bdsp(STREAMFILE* streamFile);

View File

@ -131,13 +131,38 @@ static const uint32_t xiph_mappings[] = {
};
/* Ogg Vorbis - standard .ogg with (possibly) loop comments/metadata */
static VGMSTREAM* _init_vgmstream_ogg_vorbis(STREAMFILE* sf) {
static VGMSTREAM* _init_vgmstream_ogg_vorbis_cfg_ovmi(STREAMFILE* sf, ogg_vorbis_io_config_data* cfg, ogg_vorbis_meta_info_t* ovmi) {
VGMSTREAM* vgmstream = NULL;
STREAMFILE* temp_sf = NULL;
if (cfg->is_encrypted) {
temp_sf = setup_ogg_vorbis_streamfile(sf, cfg);
if (!temp_sf) goto fail;
}
if (ovmi->meta_type == 0) {
if (cfg->is_encrypted || ovmi->decryption_callback != NULL)
ovmi->meta_type = meta_OGG_encrypted;
else
ovmi->meta_type = meta_OGG_VORBIS;
}
vgmstream = _init_vgmstream_ogg_vorbis_config(temp_sf != NULL ? temp_sf : sf, cfg->start, ovmi);
close_streamfile(temp_sf);
return vgmstream;
fail:
close_streamfile(temp_sf);
return NULL;
}
/* 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};
off_t start_offset = 0;
int is_ogg = 0;
int is_um3 = 0;
@ -171,7 +196,8 @@ static VGMSTREAM* _init_vgmstream_ogg_vorbis(STREAMFILE* sf) {
is_sngw = 1;
} else if (check_extensions(sf,"isd")) { /* .isd: Inti Creates PC games */
is_isd = 1;
} else if (check_extensions(sf,"rpgmvo")) { /* .rpgmvo: RPG Maker MV games (PC) */
} else if (check_extensions(sf,"rpgmvo,ogg_")) {
/* .rpgmvo: RPG Maker MV games (PC), .ogg_: RPG Maker MZ games (PC) */
is_rpgmvo = 1;
} else if (check_extensions(sf,"eno")) { /* .eno: Metronomicon (PC) */
is_eno = 1;
@ -238,7 +264,7 @@ static VGMSTREAM* _init_vgmstream_ogg_vorbis(STREAMFILE* sf) {
ovmi.decryption_callback = kovs_ogg_decryption_callback;
ovmi.meta_type = meta_OGG_KOVS;
start_offset = 0x20;
cfg.start = 0x20;
}
if (is_sngw) { /* [Capcom's MT Framework PC games] */
@ -321,7 +347,7 @@ static VGMSTREAM* _init_vgmstream_ogg_vorbis(STREAMFILE* sf) {
if (sf_isl) {
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) {
off_t loop_offset;
char basename[PATH_LIMIT];
@ -353,22 +379,22 @@ static VGMSTREAM* _init_vgmstream_ogg_vorbis(STREAMFILE* sf) {
}
}
if (is_rpgmvo) { /* [RPG Maker MV (PC)] */
if (is_rpgmvo) { /* [RPG Maker MV (PC), RPG Maker MZ (PC)] */
if (read_32bitBE(0x00,sf) != 0x5250474D && /* "RPGM" */
read_32bitBE(0x00,sf) != 0x56000000) { /* "V\0\0\0" */
goto fail;
}
ovmi.decryption_callback = rpgmvo_ogg_decryption_callback;
start_offset = 0x10;
cfg.start = 0x10;
}
if (is_eno) { /* [Metronomicon (PC)] */
/* first byte probably derives into key, but this works too */
cfg.key[0] = (uint8_t)read_8bit(0x05,sf); /* regular ogg have a zero at this offset = easy key */
cfg.key[0] = read_u8(0x05,sf); /* regular ogg have a zero at this offset = easy key */
cfg.key_len = 1;
cfg.is_encrypted = 1;
start_offset = 0x01; /* "OggS" starts after key-thing */
cfg.start = 0x01; /* "OggS" starts after key-thing */
}
if (is_gwm) { /* [Adagio: Cloudburst (PC)] */
@ -397,7 +423,7 @@ static VGMSTREAM* _init_vgmstream_ogg_vorbis(STREAMFILE* sf) {
else { /* [Operation Babel: New Tokyo Legacy (PC), Labyrinth of Refrain: Coven of Dusk (PC)] */
int i;
/* found at file_size-1 but this works too (same key for most files but can vary) */
uint8_t base_key = (uint8_t)read_8bit(0x04,sf) - 0x04;
uint8_t base_key = read_u8(0x04,sf) - 0x04;
cfg.key_len = 256;
for (i = 0; i < cfg.key_len; i++) {
@ -408,13 +434,13 @@ static VGMSTREAM* _init_vgmstream_ogg_vorbis(STREAMFILE* sf) {
}
if (is_bgm) { /* [Fortissimo (PC)] */
size_t file_size = get_streamfile_size(sf);
uint32_t file_size = get_streamfile_size(sf);
uint8_t key[0x04];
uint32_t xor_be;
put_32bitLE(key, (uint32_t)file_size);
put_u32le(key, file_size);
xor_be = get_u32be(key);
if ((read_32bitBE(0x00,sf) ^ xor_be) == 0x4F676753) { /* "OggS" */
if ((read_u32be(0x00,sf) ^ xor_be) == get_id32be("OggS")) {
int i;
cfg.key_len = 4;
for (i = 0; i < cfg.key_len; i++) {
@ -425,28 +451,80 @@ static VGMSTREAM* _init_vgmstream_ogg_vorbis(STREAMFILE* sf) {
}
if (cfg.is_encrypted) {
temp_sf = setup_ogg_vorbis_streamfile(sf, cfg);
if (!temp_sf) goto fail;
}
if (ovmi.meta_type == 0) {
if (cfg.is_encrypted || ovmi.decryption_callback != NULL)
ovmi.meta_type = meta_OGG_encrypted;
else
ovmi.meta_type = meta_OGG_VORBIS;
}
vgmstream = _init_vgmstream_ogg_vorbis_config(temp_sf != NULL ? temp_sf : sf, start_offset, &ovmi);
close_streamfile(temp_sf);
return vgmstream;
return _init_vgmstream_ogg_vorbis_cfg_ovmi(sf, &cfg, &ovmi);
fail:
close_streamfile(temp_sf);
return NULL;
}
/* Ogg Vorbis - encrypted .ogg [Yumekoi Tensei (PC)] */
static VGMSTREAM* _init_vgmstream_ogg_vorbis_tink(STREAMFILE* sf) {
ogg_vorbis_io_config_data cfg = {0};
ogg_vorbis_meta_info_t ovmi = {0};
uint32_t start;
/* checks */
if (is_id32be(0x00, sf, "Tink")) {
start = 0x00;
}
else if (is_id32be(0x0c, sf, "Tink")) {
ovmi.loop_start = read_u32le(0x00, sf);
ovmi.loop_end = read_u32le(0x04, sf);
ovmi.loop_flag = read_u32le(0x0c, sf);
ovmi.loop_end_found = 1;
start = 0x0c;
}
else {
goto fail;
}
if (!check_extensions(sf,"u0"))
goto fail;
cfg.is_encrypted = 1;
cfg.is_header_swap = 1;
cfg.start = start;
cfg.max_offset = 0xE1F;
cfg.key_len = 0xE1F;
if (sizeof(cfg.key) < cfg.key_len)
goto fail;
/* copy key */
{
static const char* keystring = "BB3206F-F171-4885-A131-EC7FBA6FF491 Copyright 2004 Cyberworks \"TinkerBell\"., all rights reserved.";
int i, keystring_len;
start = 0;
memset(cfg.key, 0, 0x04);
put_u8 (cfg.key + 0x04, 0x44);
keystring_len = strlen(keystring) + 1; /* including null */
for (i = 0x05; i < cfg.key_len; i += keystring_len) {
int copy = keystring_len;
if (i + copy > cfg.key_len)
copy = cfg.key_len - i;
memcpy(cfg.key + i, keystring, copy);
}
}
return _init_vgmstream_ogg_vorbis_cfg_ovmi(sf, &cfg, &ovmi);
fail:
return NULL;
}
static VGMSTREAM* _init_vgmstream_ogg_vorbis(STREAMFILE* sf) {
VGMSTREAM* v;
v = _init_vgmstream_ogg_vorbis_common(sf);
if (v) return v;
v = _init_vgmstream_ogg_vorbis_tink(sf);
if (v) return v;
return NULL;
}
static VGMSTREAM* _init_vgmstream_ogg_vorbis_config(STREAMFILE* sf, off_t start, const ogg_vorbis_meta_info_t* ovmi) {
VGMSTREAM* vgmstream = NULL;
ogg_vorbis_codec_data* data = NULL;

View File

@ -5,10 +5,12 @@
typedef struct {
int is_encrypted;
uint8_t key[0x100];
uint8_t key[0x1000];
size_t key_len;
int is_nibble_swap;
int is_header_swap;
uint32_t start;
uint32_t max_offset;
} ogg_vorbis_io_config_data;
typedef struct {
@ -19,20 +21,35 @@ typedef struct {
static size_t ogg_vorbis_io_read(STREAMFILE *sf, uint8_t *dest, off_t offset, size_t length, ogg_vorbis_io_data* data) {
static const uint8_t header_swap[4] = { 0x4F,0x67,0x67,0x53 }; /* "OggS" */
static const size_t header_size = 0x04;
int i;
size_t bytes = read_streamfile(dest, offset, length, sf);
if (data->cfg.is_encrypted) {
for (i = 0; i < bytes; i++) {
if (data->cfg.is_header_swap && (offset + i) < header_size) {
dest[i] = header_swap[(offset + i) % header_size];
int max = bytes;
int header_end = 0x04 + data->cfg.start;
if (data->cfg.max_offset + data->cfg.start) {
if (offset > data->cfg.max_offset + data->cfg.start) {
max = 0;
}
else {
max = data->cfg.max_offset + data->cfg.start - offset;
if (max > bytes)
max = bytes;
}
}
for (i = 0; i < max; i++) {
if (data->cfg.is_header_swap &&
(offset + i) >= data->cfg.start &&
(offset + i) < header_end) {
dest[i] = header_swap[(offset + i) % 0x04];
}
else {
if (!data->cfg.key_len && !data->cfg.is_nibble_swap)
break;
if (data->cfg.key_len)
dest[i] ^= data->cfg.key[(offset + i) % data->cfg.key_len];
if (data->cfg.key_len && (offset + i) >= data->cfg.start)
dest[i] ^= data->cfg.key[(offset + i - data->cfg.start) % data->cfg.key_len];
if (data->cfg.is_nibble_swap)
dest[i] = ((dest[i] << 4) & 0xf0) | ((dest[i] >> 4) & 0x0f);
}
@ -44,11 +61,11 @@ static size_t ogg_vorbis_io_read(STREAMFILE *sf, uint8_t *dest, off_t offset, si
//todo maybe use generic decryption streamfile
/* Decrypts Ogg Vorbis streams */
static STREAMFILE* setup_ogg_vorbis_streamfile(STREAMFILE *sf, ogg_vorbis_io_config_data cfg) {
static STREAMFILE* setup_ogg_vorbis_streamfile(STREAMFILE *sf, ogg_vorbis_io_config_data* cfg) {
STREAMFILE *new_sf = NULL;
ogg_vorbis_io_data io_data = {0};
io_data.cfg = cfg; /* memcpy */
io_data.cfg = *cfg; /* memcpy */
new_sf = open_wrap_streamfile(sf);
new_sf = open_io_streamfile_f(new_sf, &io_data, sizeof(ogg_vorbis_io_data), ogg_vorbis_io_read, NULL);

View File

@ -30,7 +30,7 @@ static VGMSTREAM* init_vgmstream_opus(STREAMFILE* sf, meta_t meta_type, off_t of
/* 0x80000002: 'offset info' chunk (seek table?), not seen */
/* 'context info' chunk, rare [Famicom Detective Club (Switch)] */
/* 'context info' chunk, rare [Famicom Detective Club (Switch), SINce Memories (Switch)] */
if (context_offset && read_u32le(offset + context_offset, sf) == 0x80000003) {
/* maybe should give priority to external info? */
context_offset += offset;
@ -52,6 +52,12 @@ static VGMSTREAM* init_vgmstream_opus(STREAMFILE* sf, meta_t meta_type, off_t of
multistream_offset = offset + 0x20;
}
/* Opus can only do 48000 but some games store original rate [Grandia HD Collection, Lego Marvel] */
if (sample_rate != 48000) {
VGM_LOG("OPUS: ignored non-standard sample rate of %i\n", sample_rate);
sample_rate = 48000;
}
/* 'data info' chunk */
data_offset += offset;
@ -69,8 +75,6 @@ static VGMSTREAM* init_vgmstream_opus(STREAMFILE* sf, meta_t meta_type, off_t of
vgmstream->meta_type = meta_type;
vgmstream->sample_rate = sample_rate;
if (vgmstream->sample_rate == 16000)
vgmstream->sample_rate = 48000; // Grandia HD Collection contains a false sample_rate in header
vgmstream->num_samples = num_samples;
vgmstream->loop_start_sample = loop_start;
vgmstream->loop_end_sample = loop_end;
@ -124,6 +128,9 @@ VGMSTREAM* init_vgmstream_opus_std(STREAMFILE* sf) {
int num_samples, loop_start, loop_end;
/* checks */
if (read_u32le(0x00,sf) != 0x80000001) /* 'basic info' chunk */
goto fail;
/* .opus: standard
* .bgm: Cotton Reboot (Switch) */
if (!check_extensions(sf,"opus,lopus,bgm"))
@ -157,11 +164,11 @@ VGMSTREAM* init_vgmstream_opus_n1(STREAMFILE* sf) {
int num_samples, loop_start, loop_end;
/* checks */
if (!check_extensions(sf,"opus,lopus"))
goto fail;
if (!((read_u32be(0x04,sf) == 0x00000000 && read_u32be(0x0c,sf) == 0x00000000) ||
(read_u32be(0x04,sf) == 0xFFFFFFFF && read_u32be(0x0c,sf) == 0xFFFFFFFF)))
goto fail;
if (!check_extensions(sf,"opus,lopus"))
goto fail;
offset = 0x10;
num_samples = 0;
@ -181,7 +188,7 @@ VGMSTREAM* init_vgmstream_opus_capcom(STREAMFILE* sf) {
int channels;
/* checks */
if ( !check_extensions(sf,"opus,lopus"))
if (!check_extensions(sf,"opus,lopus"))
goto fail;
channels = read_32bitLE(0x04,sf);
@ -259,10 +266,10 @@ VGMSTREAM* init_vgmstream_opus_nop(STREAMFILE* sf) {
int num_samples, loop_start = 0, loop_end = 0, loop_flag;
/* checks */
if (!check_extensions(sf,"nop"))
if (!is_id32be(0x00, sf, "sadf") ||
!is_id32be(0x08, sf, "opus"))
goto fail;
if (read_32bitBE(0x00, sf) != 0x73616466 || /* "sadf" */
read_32bitBE(0x08, sf) != 0x6f707573) /* "opus" */
if (!check_extensions(sf,"nop"))
goto fail;
offset = read_32bitLE(0x1c, sf);
@ -284,9 +291,9 @@ VGMSTREAM* init_vgmstream_opus_shinen(STREAMFILE* sf) {
int num_samples, loop_start, loop_end;
/* checks */
if ( !check_extensions(sf,"opus,lopus"))
if (read_u32be(0x08,sf) != 0x01000080)
goto fail;
if (read_32bitBE(0x08,sf) != 0x01000080)
if ( !check_extensions(sf,"opus,lopus"))
goto fail;
offset = 0x08;
@ -308,11 +315,12 @@ VGMSTREAM* init_vgmstream_opus_nus3(STREAMFILE* sf) {
int num_samples = 0, loop_start = 0, loop_end = 0, loop_flag;
/* checks */
if (!is_id32be(0x00, sf, "OPUS"))
goto fail;
/* .opus: header ID (they only exist inside .nus3bank) */
if (!check_extensions(sf, "opus,lopus"))
goto fail;
if (read_32bitBE(0x00, sf) != 0x4F505553) /* "OPUS" */
goto fail;
/* Here's an interesting quirk, OPUS header contains big endian values
while the Nintendo Opus header and data that follows remain little endian as usual */
@ -337,13 +345,13 @@ VGMSTREAM* init_vgmstream_opus_sps_n1(STREAMFILE* sf) {
int num_samples, loop_start = 0, loop_end = 0, loop_flag;
/* checks */
if (read_u32be(0x00, sf) != 0x09000000) /* file type (see other N1 SPS) */
goto fail;
/* .sps: Labyrinth of Refrain: Coven of Dusk (Switch)
* .nlsd: Disgaea Refine (Switch), Ys VIII (Switch)
* .at9: void tRrLM(); //Void Terrarium (Switch) */
if (!check_extensions(sf, "sps,nlsd,at9"))
goto fail;
if (read_32bitBE(0x00, sf) != 0x09000000) /* file type (see other N1 SPS) */
goto fail;
num_samples = read_32bitLE(0x0C, sf);
@ -382,9 +390,9 @@ VGMSTREAM* init_vgmstream_opus_opusx(STREAMFILE* sf) {
float modifier;
/* checks */
if (!check_extensions(sf, "opusx"))
if (!is_id32be(0x00, sf, "OPUS"))
goto fail;
if (read_32bitBE(0x00, sf) != 0x4F505553) /* "OPUS" */
if (!check_extensions(sf, "opusx"))
goto fail;
offset = 0x10;
@ -414,10 +422,11 @@ VGMSTREAM* init_vgmstream_opus_prototype(STREAMFILE* sf) {
int num_samples = 0, loop_start = 0, loop_end = 0, loop_flag;
/* checks */
if (!is_id32be(0x00, sf, "OPUS"))
goto fail;
if (!check_extensions(sf, "opus,lopus"))
goto fail;
if (read_32bitBE(0x00, sf) != 0x4F505553 || /* "OPUS" */
read_32bitBE(0x18, sf) != 0x01000080)
if (read_32bitBE(0x18, sf) != 0x01000080)
goto fail;
offset = 0x18;
@ -441,9 +450,9 @@ VGMSTREAM* init_vgmstream_opus_opusnx(STREAMFILE* sf) {
int num_samples = 0, loop_start = 0, loop_end = 0;
/* checks */
if (!check_extensions(sf, "opus,lopus"))
if (!is_id64be(0x00, sf,"OPUSNX\0\0"))
goto fail;
if (read_64bitBE(0x00, sf) != 0x4F5055534E580000) /* "OPUSNX\0\0" */
if (!check_extensions(sf, "opus,lopus"))
goto fail;
offset = 0x10;
@ -462,9 +471,9 @@ VGMSTREAM* init_vgmstream_opus_nsopus(STREAMFILE* sf) {
int num_samples = 0, loop_start = 0, loop_end = 0;
/* checks */
if (!check_extensions(sf, "nsopus"))
if (!is_id32be(0x00, sf,"EWNO"))
goto fail;
if (read_u32be(0x00, sf) != 0x45574E4F) /* "EWNO" */
if (!check_extensions(sf, "nsopus"))
goto fail;
offset = 0x08;
@ -481,12 +490,12 @@ VGMSTREAM* init_vgmstream_opus_sqex(STREAMFILE* sf) {
int num_samples = 0, loop_start = 0, loop_end = 0, loop_flag;
/* checks */
/* .wav: default
* .opus: fake? */
if (!check_extensions(sf, "wav,lwav,opus,lopus"))
goto fail;
if (read_u32be(0x00, sf) != 0x01000000)
goto fail;
/* .wav: original */
if (!check_extensions(sf, "wav,lwav"))
goto fail;
/* 0x04: channels */
/* 0x08: data_size */
offset = read_32bitLE(0x0C, sf);

View File

@ -1,62 +0,0 @@
#include "meta.h"
#include "../coding/coding.h"
/* AST - from Koei and Marvelous games (same internal dev?) */
VGMSTREAM * init_vgmstream_ps2_ast(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
int loop_flag, channel_count, variant_type;
/* check extension */
if (!check_extensions(streamFile,"ast")) goto fail;
/* check header */
if (read_32bitBE(0x00,streamFile) != 0x41535400) /* "AST\0" */
goto fail;
/* determine variant (after 0x10 is garbage/code data in type 1 until 0x800, but consistent in all songs) */
if (read_32bitBE(0x10,streamFile) == 0x00000000 || read_32bitBE(0x10,streamFile) == 0x20002000) {
variant_type = 1; /* Koei: P.T.O. IV (0x00000000), Naval Ops: Warship Gunner (0x20002000) */
channel_count = 2;
}
else {
variant_type = 2; /* Marvelous: Katekyoo Hitman Reborn! Dream Hyper Battle!, Binchou-tan: Shiawasegoyomi */
channel_count = read_32bitLE(0x0C,streamFile);
}
loop_flag = 0;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
if (variant_type == 1) {
start_offset = 0x800;
vgmstream->sample_rate = read_32bitLE(0x04,streamFile);
vgmstream->num_samples = ps_bytes_to_samples(read_32bitLE(0x0C,streamFile)-start_offset,channel_count);
vgmstream->interleave_block_size = read_32bitLE(0x08,streamFile);
}
else if (variant_type == 2) {
start_offset = 0x100;
vgmstream->sample_rate = read_32bitLE(0x08,streamFile);
vgmstream->num_samples = ps_bytes_to_samples(read_32bitLE(0x04,streamFile)-start_offset,channel_count);
vgmstream->interleave_block_size = read_32bitLE(0x10,streamFile);
}
else {
goto fail;
}
vgmstream->layout_type = layout_interleave;
vgmstream->coding_type = coding_PSX;
vgmstream->meta_type = meta_PS2_AST;
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -1,64 +0,0 @@
#include "meta.h"
#include "../util.h"
/* LPCM (from Ah! My Goddess (PS2)) */
VGMSTREAM * init_vgmstream_ps2_lpcm(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
off_t start_offset;
int loop_flag;
int channel_count;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("lpcm",filename_extension(filename))) goto fail;
/* check header */
if (read_32bitBE(0,streamFile) != 0x4C50434D) /* LPCM */
goto fail;
loop_flag = read_32bitLE(0x8,streamFile);
channel_count = 2;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
start_offset = 0x10;
vgmstream->channels = channel_count;
vgmstream->sample_rate = 48000;
vgmstream->coding_type = coding_PCM16LE;
vgmstream->num_samples = read_32bitLE(0x4,streamFile);
if (loop_flag) {
vgmstream->loop_start_sample = read_32bitLE(0x8,streamFile);
vgmstream->loop_end_sample = read_32bitLE(0xc,streamFile);
}
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 2;
vgmstream->meta_type = meta_PS2_LPCM;
/* 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;
fail:
/* clean up anything we may have opened */
if (vgmstream) close_vgmstream(vgmstream);
return NULL;
}

View File

@ -353,8 +353,9 @@ VGMSTREAM* init_vgmstream_riff(STREAMFILE* sf) {
* .saf: Whacked! (Xbox)
* .mwv: Level-5 games [Dragon Quest VIII (PS2), Rogue Galaxy (PS2)]
* .ima: Baja: Edge of Control (PS3/X360)
* .nsa: Studio Ring games that uses NScripter [Hajimete no Otetsudai (PC)]
*/
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,saf,ima") ) {
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,saf,ima,nsa") ) {
;
}
else if ( check_extensions(sf, "mwv") ) {

View File

@ -272,6 +272,31 @@ static int parse_header(STREAMFILE* sf_h, STREAMFILE* sf_b, strwav_header* strwa
return 1;
}
/* Taz Wanted demo (PC)[2003] */
if ( read_u32be(0x04,sf_h) == 0x00000900 &&
read_u32le(0x24,sf_h) == read_u32le(0xfc,sf_h) && /* sample rate repeat */
read_u32le(0x10c,sf_h) == header_size
) {
/* 0x08: null */
/* 0x0c: hashname */
strwav->num_samples = read_s32le(0x20,sf_h);
strwav->sample_rate = read_s32le(0x24,sf_h);
/* 0x28: 16 bps */
strwav->flags = read_u32le(0x2c,sf_h);
strwav->loop_end = read_s32le(0x30,sf_h);
strwav->loop_start = read_s32le(0x38,sf_h);
/* 0x58: number of chunks */
strwav->tracks = read_s32le(0xD8,sf_h);
/* 0xfc: sample rate 2 */
/* 0x100: ? */
/* 0x10c: header size */
strwav->codec = IMA;
strwav->interleave = strwav->tracks > 1 ? 0x8000 : 0x10000;
;VGM_LOG("STR+WAV: header TAZd (PC)\n");
return 1;
}
/* The Fairly OddParents - Breakin' da Rules (Xbox)[2003] */
if ( read_u32be(0x04,sf_h) == 0x00000900 &&
read_u32le(0x24,sf_h) == read_u32le(0xb0,sf_h) && /* sample rate repeat */
@ -479,12 +504,14 @@ static int parse_header(STREAMFILE* sf_h, STREAMFILE* sf_b, strwav_header* strwa
return 1;
}
/* Taz Wanted (PC)[2002] */
/* Zapper: One Wicked Cricket! Beta (Xbox)[2002] */
if ( read_u32be(0x04,sf_h) == 0x00000900 &&
read_u32le(0x0c,sf_h) != header_size &&
read_u32le(0x24,sf_h) != 0 &&
read_u32le(0x24,sf_h) == read_u32le(0x90,sf_h) && /* sample rate repeat */
read_u32le(0xa0,sf_h) == header_size /* ~0xC0 */
(read_u32le(0xa0,sf_h) == header_size || /* Zapper */
read_u32le(0xa0,sf_h) + 0x50 == header_size) /* Taz */
) {
/* 0x08: null */
/* 0x0c: hashname */
@ -612,7 +639,7 @@ static int parse_header(STREAMFILE* sf_h, STREAMFILE* sf_b, strwav_header* strwa
strwav->codec = DSP;
strwav->dsps_table = 0xf0;
strwav->interleave = strwav->tracks > 2 ? 0x8000 : 0x10000;
strwav->interleave = strwav->tracks >= 2 ? 0x8000 : 0x10000;
;VGM_LOG("STR+WAV: header SBCKK (GC)\n");
return 1;
}

View File

@ -40,6 +40,9 @@ typedef enum {
ASF = 30, /* Argonaut ASF 4-bit ADPCM */
EAXA = 31, /* Electronic Arts EA-XA 4-bit ADPCM v1 */
OKI4S = 32, /* OKI ADPCM with 16-bit output (unlike OKI/VOX/Dialogic ADPCM's 12-bit) */
XA,
XA_EA,
CP_YM,
UNKNOWN = 99,
} txth_codec_t;
@ -117,11 +120,15 @@ typedef struct {
uint32_t chunk_count;
uint32_t chunk_header_size;
uint32_t chunk_data_size;
uint32_t chunk_value;
uint32_t chunk_size_offset;
uint32_t chunk_be;
int chunk_start_set;
int chunk_size_set;
int chunk_count_set;
uint32_t base_offset;
uint32_t is_offset_absolute;
uint32_t name_values[16];
int name_values_count;
@ -256,6 +263,9 @@ VGMSTREAM* init_vgmstream_txth(STREAMFILE* sf) {
case TGC: coding = coding_TGC; break;
case ASF: coding = coding_ASF; break;
case EAXA: coding = coding_EA_XA; break;
case XA: coding = coding_XA; break;
case XA_EA: coding = coding_XA_EA; break;
case CP_YM: coding = coding_CP_YM; break;
default:
goto fail;
}
@ -367,6 +377,9 @@ VGMSTREAM* init_vgmstream_txth(STREAMFILE* sf) {
case coding_OKI16:
case coding_OKI4S:
case coding_XA:
case coding_XA_EA:
case coding_CP_YM:
vgmstream->layout_type = layout_none;
break;
@ -448,7 +461,7 @@ VGMSTREAM* init_vgmstream_txth(STREAMFILE* sf) {
{
int16_t (*read_16bit)(off_t, STREAMFILE*) = txth.coef_big_endian ? read_16bitBE : read_16bitLE;
int16_t (*get_16bit)(const uint8_t* p) = txth.coef_big_endian ? get_16bitBE : get_16bitLE;
VGM_LOG("coef=%x\n",txth.coef_offset );
for (i = 0; i < vgmstream->channels; i++) {
if (txth.coef_mode == 0) { /* normal coefs */
for (j = 0; j < 16; j++) {
@ -648,7 +661,7 @@ static VGMSTREAM* init_subfile(txth_header* txth) {
* - etc
* to avoid it we set a particular fake extension and detect it when reading .txth
*/
strcpy(extension, "subfile_txth.");
strcpy(extension, ".subfile_txth.");
strcat(extension, txth->subfile_extension);
sf_sub = setup_subfile_streamfile(txth->sf_body, txth->subfile_offset, txth->subfile_size, extension);
@ -722,50 +735,43 @@ fail:
static STREAMFILE* open_txth(STREAMFILE* sf) {
char basename[PATH_LIMIT];
char filename[PATH_LIMIT];
char fileext[PATH_LIMIT];
const char *subext;
const char* base_ext;
const char* txth_ext;
STREAMFILE* sf_text;
/* try "(path/)(name.ext).txth" */
get_streamfile_name(sf,filename,PATH_LIMIT);
if (strstr(filename, "subfile_txth") != NULL)
get_streamfile_name(sf, filename, sizeof(filename));
if (strstr(filename, ".subfile_txth") != NULL)
return NULL; /* detect special case of subfile-within-subfile */
strcat(filename, ".txth");
sf_text = open_streamfile(sf,filename);
if (sf_text) return sf_text;
/* try "(path/)(.sub.ext).txth" */
get_streamfile_basename(sf,basename,PATH_LIMIT);
subext = filename_extension(basename);
if (subext != NULL && subext[0] != '\0') {
get_streamfile_path(sf,filename,PATH_LIMIT);
get_streamfile_ext(sf,fileext,PATH_LIMIT);
strcat(filename,".");
strcat(filename, subext);
strcat(filename,".");
strcat(filename, fileext);
strcat(filename, ".txth");
base_ext = filename_extension(filename);
concatn(sizeof(filename), filename, ".txth");
txth_ext = filename_extension(filename);
sf_text = open_streamfile(sf,filename);
/* try "(path/)(name.ext).txth" */
{
/* full filename, already prepared */
sf_text = open_streamfile(sf, filename);
if (sf_text) return sf_text;
}
/* try "(path/)(.ext).txth" */
get_streamfile_path(sf,filename,PATH_LIMIT);
get_streamfile_ext(sf,fileext,PATH_LIMIT);
strcat(filename,".");
strcat(filename, fileext);
strcat(filename, ".txth");
sf_text = open_streamfile(sf,filename);
if (sf_text) return sf_text;
if (base_ext) {
base_ext--; //get_streamfile_path(sf, filename, sizeof(filename));
sf_text = open_streamfile_by_filename(sf, base_ext);
if (sf_text) return sf_text;
}
/* try "(path/).txth" */
get_streamfile_path(sf,filename,PATH_LIMIT);
strcat(filename, ".txth");
sf_text = open_streamfile(sf,filename);
if (sf_text) return sf_text;
if (txth_ext) {
txth_ext--; /* points to "txth" due to the concat */
sf_text = open_streamfile_by_filename(sf, txth_ext);
if (sf_text) return sf_text;
}
/* not found */
return NULL;
@ -788,7 +794,9 @@ static void set_body_chunk(txth_header* txth) {
//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)
return;
if (txth->chunk_size == 0 || txth->chunk_start > txth->data_size || txth->chunk_count == 0)
if ((txth->chunk_size == 0 && ! txth->chunk_size_offset) ||
txth->chunk_start > txth->data_size ||
txth->chunk_count == 0)
return;
if (!txth->sf_body)
return;
@ -804,18 +812,22 @@ static void set_body_chunk(txth_header* txth) {
{
txth_io_config_data cfg = {0};
cfg.chunk_start = txth->chunk_start;
cfg.chunk_number = txth->chunk_number - 1; /* 1-index to 0-index */
cfg.chunk_header_size = txth->chunk_header_size;
cfg.chunk_data_size = txth->chunk_data_size;
cfg.chunk_value = txth->chunk_value;
cfg.chunk_size_offset = txth->chunk_size_offset;
cfg.chunk_be = txth->chunk_be;
cfg.chunk_start = txth->chunk_start;
cfg.chunk_size = txth->chunk_size;
cfg.chunk_count = txth->chunk_count;
cfg.chunk_number = txth->chunk_number - 1; /* 1-index to 0-index */
temp_sf = setup_txth_streamfile(txth->sf_body, cfg, txth->sf_body_opened);
if (!temp_sf) return;
}
/* closing is handled by temp_sf */
//if (txth->sf_body_opened) {
// close_streamfile(txth->sf_body);
@ -939,6 +951,9 @@ static txth_codec_t parse_codec(txth_header* txth, const char* val) {
else if (is_string(val,"GCOM_ADPCM")) return TGC;
else if (is_string(val,"ASF")) return ASF;
else if (is_string(val,"EAXA")) return EAXA;
else if (is_string(val,"XA")) return XA;
else if (is_string(val,"XA_EA")) return XA_EA;
else if (is_string(val,"CP_YM")) return CP_YM;
/* special handling */
else if (is_string(val,"name_value")) return txth->name_values[0];
else if (is_string(val,"name_value1")) return txth->name_values[0];
@ -949,6 +964,19 @@ static txth_codec_t parse_codec(txth_header* txth, const char* val) {
return UNKNOWN;
}
static int parse_be(txth_header* txth, const char* val, uint32_t* p_value) {
if (is_string(val, "BE"))
*p_value = 1;
else if (is_string(val, "LE"))
*p_value = 0;
else
if (!parse_num(txth->sf_head,txth,val, p_value))
goto fail;
return 1;
fail:
return 0;
}
static int parse_keyval(STREAMFILE* sf_, txth_header* txth, const char* key, char* val) {
//;VGM_LOG("TXTH: key=%s, val=%s\n", key, val);
@ -1169,23 +1197,26 @@ static int parse_keyval(STREAMFILE* sf_, txth_header* txth, const char* key, cha
goto fail;
}
else if (is_string(key,"offset_absolute")) {
if (!parse_num(txth->sf_head,txth,val, &txth->is_offset_absolute)) goto fail;
}
/* COEFS */
else if (is_string(key,"coef_offset")) {
if (!parse_num(txth->sf_head,txth,val, &txth->coef_offset)) goto fail;
/* special adjustments */
VGM_LOG("coef norm=%x\n",txth->coef_offset );
txth->coef_offset += txth->base_offset;
if (txth->subsong_spacing)
VGM_LOG("coef+base=%x\n",txth->coef_offset );
if (txth->subsong_spacing && !txth->is_offset_absolute)
txth->coef_offset += txth->subsong_spacing * (txth->target_subsong - 1);
VGM_LOG("coef+spac=%x\n",txth->coef_offset );
}
else if (is_string(key,"coef_spacing")) {
if (!parse_num(txth->sf_head,txth,val, &txth->coef_spacing)) goto fail;
}
else if (is_string(key,"coef_endianness")) {
if (is_string(val, "BE"))
txth->coef_big_endian = 1;
else if (is_string(val, "LE"))
txth->coef_big_endian = 0;
else if (!parse_num(txth->sf_head,txth,val, &txth->coef_big_endian)) goto fail;
if (!parse_be(txth, val, &txth->coef_big_endian)) goto fail;
}
else if (is_string(key,"coef_mode")) {
if (!parse_num(txth->sf_head,txth,val, &txth->coef_mode)) goto fail;
@ -1201,18 +1232,14 @@ static int parse_keyval(STREAMFILE* sf_, txth_header* txth, const char* key, cha
txth->hist_set = 1;
/* special adjustment */
txth->hist_offset += txth->hist_offset;
if (txth->subsong_spacing)
if (txth->subsong_spacing && !txth->is_offset_absolute)
txth->hist_offset += txth->subsong_spacing * (txth->target_subsong - 1);
}
else if (is_string(key,"hist_spacing")) {
if (!parse_num(txth->sf_head,txth,val, &txth->hist_spacing)) goto fail;
}
else if (is_string(key,"hist_endianness")) {
if (is_string(val, "BE"))
txth->hist_big_endian = 1;
else if (is_string(val, "LE"))
txth->hist_big_endian = 0;
else if (!parse_num(txth->sf_head,txth,val, &txth->hist_big_endian)) goto fail;
if (!parse_be(txth, val, &txth->hist_big_endian)) goto fail;
}
/* SUBSONGS */
@ -1227,10 +1254,10 @@ static int parse_keyval(STREAMFILE* sf_, txth_header* txth, const char* key, cha
txth->name_offset_set = 1;
/* special adjustment */
txth->name_offset += txth->base_offset;
if (txth->subsong_spacing)
if (txth->subsong_spacing && !txth->is_offset_absolute)
txth->name_offset += txth->subsong_spacing * (txth->target_subsong - 1);
}
else if (is_string(key,"name_offset_absolute")) {
else if (is_string(key,"name_offset_absolute")) { //TODO: remove
if (!parse_num(txth->sf_head,txth,val, &txth->name_offset)) goto fail;
txth->name_offset_set = 1;
/* special adjustment */
@ -1336,34 +1363,41 @@ static int parse_keyval(STREAMFILE* sf_, txth_header* txth, const char* key, cha
}
/* CHUNKS */
else if (is_string(key,"chunk_number")) {
if (!parse_num(txth->sf_head,txth,val, &txth->chunk_number)) goto fail;
else if (is_string(key,"chunk_count")) {
if (!parse_num(txth->sf_head,txth,val, &txth->chunk_count)) goto fail;
txth->chunk_count_set = 1;
set_body_chunk(txth);
}
else if (is_string(key,"chunk_start")) {
if (!parse_num(txth->sf_head,txth,val, &txth->chunk_start)) goto fail;
txth->chunk_start_set = 1;
set_body_chunk(txth);
}
else if (is_string(key,"chunk_header_size")) {
if (!parse_num(txth->sf_head,txth,val, &txth->chunk_header_size)) goto fail;
//txth->chunk_header_size_set = 1;
//set_body_chunk(txth); /* optional and should go before chunk_size */
}
else if (is_string(key,"chunk_data_size")) {
if (!parse_num(txth->sf_head,txth,val, &txth->chunk_data_size)) goto fail;
//txth->chunk_data_size_set = 1;
//set_body_chunk(txth); /* optional and should go before chunk_size */
}
else if (is_string(key,"chunk_size")) {
if (!parse_num(txth->sf_head,txth,val, &txth->chunk_size)) goto fail;
txth->chunk_size_set = 1;
set_body_chunk(txth);
}
else if (is_string(key,"chunk_count")) {
if (!parse_num(txth->sf_head,txth,val, &txth->chunk_count)) goto fail;
txth->chunk_count_set = 1;
set_body_chunk(txth);
/* optional and should go before the above */
else if (is_string(key,"chunk_number")) {
if (!parse_num(txth->sf_head,txth,val, &txth->chunk_number)) goto fail;
}
else if (is_string(key,"chunk_header_size")) {
if (!parse_num(txth->sf_head,txth,val, &txth->chunk_header_size)) goto fail;
}
else if (is_string(key,"chunk_data_size")) {
if (!parse_num(txth->sf_head,txth,val, &txth->chunk_data_size)) goto fail;
}
else if (is_string(key,"chunk_value")) {
if (!parse_num(txth->sf_head,txth,val, &txth->chunk_value)) goto fail;
}
else if (is_string(key,"chunk_size_offset")) {
if (!parse_num(txth->sf_head,txth,val, &txth->chunk_size_offset)) goto fail;
}
else if (is_string(key,"chunk_endianness")) {
if (!parse_be(txth, val, &txth->chunk_be)) goto fail;
}
/* BASE OFFSET */
else if (is_string(key,"base_offset")) {
@ -1564,6 +1598,16 @@ fail:
return 0;
}
static void string_trim(char* str) {
int str_len = strlen(str);
int i;
for (i = str_len - 1; i >= 0; i--) {
if (str[i] != ' ')
break;
str[i] = '\0';
}
}
static int read_name_table_keyval(txth_header* txth, const char* line, char* key, char* val) {
int ok;
int subsong;
@ -1576,8 +1620,10 @@ static int read_name_table_keyval(txth_header* txth, const char* line, char* key
return 0;
/* try "(name): (val))" */
ok = sscanf(line, " %[^\t#:] : %[^\t#\r\n] ", key, val);
if (ok == 2) {
string_trim(key); /* otherwise includes end spaces before : */
//;VGM_LOG("TXTH: name %s get\n", key);
return 1;
}
@ -1628,32 +1674,27 @@ fail:
return 0;
}
static int parse_name_table(txth_header* txth, char* name_list) {
static int parse_name_table(txth_header* txth, char* set_name) {
STREAMFILE* sf_names = NULL;
off_t txt_offset, file_size;
char fullname[PATH_LIMIT];
char filename[PATH_LIMIT];
char basename[PATH_LIMIT];
const char* table_name;
/* just in case */
if (!txth->sf_text || !txth->sf_body)
goto fail;
/* trim name_list just in case */
{
int name_list_len = strlen(name_list);
int i;
for (i = name_list_len - 1; i >= 0; i--) {
if (name_list[i] != ' ')
break;
name_list[i] = '\0';
}
}
//;VGM_LOG("TXTH: name_list='%s'\n", name_list);
/* trim just in case */
string_trim(set_name);
if (is_string(set_name,"*"))
table_name = ".names.txt";
else
table_name = set_name;
/* open companion file near .txth */
sf_names = open_streamfile_by_filename(txth->sf_text, name_list);
sf_names = open_streamfile_by_filename(txth->sf_text, table_name);
if (!sf_names) goto fail;
get_streamfile_name(txth->sf_body, fullname, sizeof(filename));
@ -1893,6 +1934,8 @@ static int parse_num(STREAMFILE* sf, txth_header* txth, const char* val, uint32_
else if ((n = is_string_field(val,"subfile_offset"))) value = txth->subfile_offset;
else if ((n = is_string_field(val,"subfile_size"))) value = txth->subfile_size;
else if ((n = is_string_field(val,"base_offset"))) value = txth->base_offset;
else if ((n = is_string_field(val,"coef_offset"))) value = txth->coef_offset;
else if ((n = is_string_field(val,"hist_offset"))) value = txth->hist_offset;
//todo whatever, improve
else if ((n = is_string_field(val,"name_value"))) value = txth->name_values[0];
else if ((n = is_string_field(val,"name_value1"))) value = txth->name_values[0];
@ -2001,6 +2044,9 @@ static int get_bytes_to_samples(txth_header* txth, uint32_t bytes) {
return asf_bytes_to_samples(bytes, txth->channels);
case EAXA:
return ea_xa_bytes_to_samples(bytes, txth->channels);
case XA:
case XA_EA:
return xa_bytes_to_samples(bytes, txth->channels, 0, 0, 4);
/* XMA bytes-to-samples is done at the end as the value meanings are a bit different */
case XMA1:
@ -2011,6 +2057,7 @@ static int get_bytes_to_samples(txth_header* txth, uint32_t bytes) {
case DVI_IMA:
return ima_bytes_to_samples(bytes, txth->channels);
case AICA:
case CP_YM:
return yamaha_bytes_to_samples(bytes, txth->channels);
case PCFX:
case OKI16:

View File

@ -1,30 +1,36 @@
#ifndef _TXTH_STREAMFILE_H_
#define _TXTH_STREAMFILE_H_
#include "../streamfile.h"
#include "../util/endianness.h"
typedef struct {
off_t chunk_start;
size_t chunk_size;
size_t chunk_header_size;
size_t chunk_data_size;
uint32_t chunk_start;
uint32_t chunk_size;
uint32_t chunk_header_size;
uint32_t chunk_data_size;
int chunk_count;
int chunk_number;
uint32_t chunk_value;
uint32_t chunk_size_offset;
int chunk_be;
} txth_io_config_data;
typedef struct {
/* config */
txth_io_config_data cfg;
size_t stream_size;
uint32_t stream_size;
/* state */
off_t logical_offset; /* fake offset */
off_t physical_offset; /* actual offset */
size_t block_size; /* current size */
size_t skip_size; /* size from block start to reach data */
size_t data_size; /* usable size in a block */
uint32_t logical_offset; /* fake offset */
uint32_t physical_offset; /* actual offset */
uint32_t block_size; /* current size */
uint32_t skip_size; /* size from block start to reach data */
uint32_t data_size; /* usable size in a block */
size_t logical_size;
uint32_t logical_size;
} txth_io_data;
@ -64,6 +70,22 @@ static size_t txth_io_read(STREAMFILE* sf, uint8_t* dest, off_t offset, size_t l
data->data_size = data->cfg.chunk_data_size;
}
/* chunk size reader (overwrites the above) */
if (data->cfg.chunk_header_size && data->cfg.chunk_size_offset) {
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->data_size = data->block_size - data->cfg.chunk_header_size;
/* skip chunk if doesn't match expected header value */
if (data->cfg.chunk_value) {
uint32_t value = read_u32(data->physical_offset + 0x00, sf);
if (value != data->cfg.chunk_value) {
data->data_size = 0;
}
}
}
/* clamp for games where last block is smaller */ //todo not correct for all cases
if (data->physical_offset + data->block_size > data->cfg.chunk_start + data->stream_size) {
data->block_size = (data->cfg.chunk_start + data->stream_size) - data->physical_offset;

View File

@ -645,7 +645,7 @@ static VGMSTREAM *init_vgmstream_ubi_dat_main(ubi_sb_header *sb, STREAMFILE *sf_
if (!sf_data) {
/* play silence if external file is not found since Rayman 2 seems to rely on this behavior */
vgm_logi("UBI DAT: external file '%s' not found (put together)\n", sb->resource_name);
strncat(sb->readable_name, " (missing)", sizeof(sb->readable_name));
concatn(sizeof(sb->readable_name), sb->readable_name, " (missing)");
sb->duration = (float)pcm_bytes_to_samples(sb->stream_size, sb->channels, 16) / (float)sb->sample_rate;
return init_vgmstream_ubi_sb_silence(sb);
}
@ -1301,8 +1301,11 @@ static VGMSTREAM* init_vgmstream_ubi_sb_audio(ubi_sb_header* sb, STREAMFILE* sf_
if (sb->is_external) {
sf_data = open_streamfile_by_filename(sf, sb->resource_name);
if (sf_data == NULL) {
/* play silence if external file is not found */
vgm_logi("UBI SB: external file '%s' not found (put together)\n", sb->resource_name);
goto fail;
concatn(sizeof(sb->readable_name), sb->readable_name, " (missing)");
sb->duration = 1.0f;
return init_vgmstream_ubi_sb_silence(sb);
}
}
else {
@ -1342,8 +1345,11 @@ static VGMSTREAM* init_vgmstream_ubi_sb_layer(ubi_sb_header* sb, STREAMFILE* sf_
if (sb->is_external) {
sf_data = open_streamfile_by_filename(sf,sb->resource_name);
if (sf_data == NULL) {
/* play silence if external file is not found */
vgm_logi("UBI SB: external file '%s' not found (put together)\n", sb->resource_name);
goto fail;
concatn(sizeof(sb->readable_name), sb->readable_name, " (missing)");
sb->duration = 1.0f;
return init_vgmstream_ubi_sb_silence(sb);
}
}
else {

View File

@ -6,53 +6,45 @@
static int xa_read_subsongs(STREAMFILE* sf, int target_subsong, off_t start, uint16_t* p_stream_config, off_t* p_stream_offset, size_t* p_stream_size, int* p_form2);
static int xa_check_format(STREAMFILE* sf, off_t offset, int is_blocked);
/* XA - from Sony PS1 and Philips CD-i CD audio, also Saturn streams */
/* XA - from Sony PS1 and Philips CD-i CD audio */
VGMSTREAM* init_vgmstream_xa(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
off_t start_offset;
int loop_flag = 0, channels, sample_rate, bps;
int is_riff = 0, is_blocked = 0, is_form2 = 0;
int is_riff = 0, is_form2 = 0, is_blocked;
size_t stream_size = 0;
int total_subsongs = 0, target_subsong = sf->stream_index;
uint16_t target_config = 0;
/* checks */
/* .xa: common
* .str: often videos and sometimes speech/music
* .adp: Phantasy Star Collection (SAT) raw XA
* .pxa: Mortal Kombat 4 (PS1)
* .grn: Micro Machines (CDi)
* (extensionless): bigfiles [Castlevania: Symphony of the Night (PS1)] */
if (!check_extensions(sf,"xa,str,adp,pxa,grn,"))
goto fail;
/* Proper XA comes in raw (BIN 2352 mode2/form2) CD sectors, that contain XA subheaders.
* Also has minimal support for headerless (ISO 2048 mode1/data) mode. */
/* check RIFF header = raw (optional, added when ripping and not part of the CD data) */
if (read_u32be(0x00,sf) == 0x52494646 && /* "RIFF" */
read_u32be(0x08,sf) == 0x43445841 && /* "CDXA" */
read_u32be(0x0C,sf) == 0x666D7420) { /* "fmt " */
if (read_u32be(0x00,sf) == 0x00FFFFFF && read_u32be(0x04,sf) == 0xFFFFFFFF && read_u32be(0x08,sf) == 0xFFFFFF00) {
/* sector sync word = raw data */
is_blocked = 1;
start_offset = 0x00;
}
else if (is_id32be(0x00,sf, "RIFF") && is_id32be(0x08,sf, "CDXA") && is_id32be(0x0C,sf, "fmt ")) {
/* RIFF header = raw with header (optional, added by CD drivers when copying and not part of the CD data) */
is_blocked = 1;
is_riff = 1;
start_offset = 0x2c; /* after "data", ignore RIFF values as often are wrong */
}
else {
/* sector sync word = raw */
if (read_u32be(0x00,sf) == 0x00FFFFFF &&
read_u32be(0x04,sf) == 0xFFFFFFFF &&
read_u32be(0x08,sf) == 0xFFFFFF00) {
is_blocked = 1;
start_offset = 0x00;
}
else {
/* headerless or possibly incorrectly ripped */
start_offset = 0x00;
}
/* non-blocked (ISO 2048 mode1/data) or incorrectly ripped: use TXTH */
goto fail;
}
/* .xa: common
* .str: often videos and sometimes speech/music
* .pxa: Mortal Kombat 4 (PS1)
* .grn: Micro Machines (CDi)
* (extensionless): bigfiles [Castlevania: Symphony of the Night (PS1)] */
if (!check_extensions(sf,"xa,str,pxa,grn,"))
goto fail;
/* 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. */
/* test for XA data, since format is raw-ish (with RIFF it's assumed to be ok) */
if (!is_riff && !xa_check_format(sf, start_offset, is_blocked))
goto fail;
@ -89,42 +81,18 @@ VGMSTREAM* init_vgmstream_xa(STREAMFILE* sf) {
switch((xa_header >> 6) & 1) { /* 6: emphasis (applies a filter) */
case 0: break;
default: /* shouldn't be used by games */
VGM_LOG("XA: unknown emphasis found\n");
break;
vgm_logi("XA: unknown emphasis found\n");
goto fail;
}
switch((xa_header >> 7) & 1) { /* 7: reserved */
case 0: break;
default:
VGM_LOG("XA: unknown reserved bit found\n");
break;
vgm_logi("XA: unknown reserved bit found\n");
goto fail;
}
}
else {
/* headerless */
if (check_extensions(sf,"adp")) {
/* Phantasy Star Collection (SAT) raw files */
/* most are stereo, though a few (mainly sfx banks, sometimes using .bin) are mono */
char filename[PATH_LIMIT] = {0};
get_streamfile_filename(sf, filename,PATH_LIMIT);
/* detect PS1 mono files, very lame but whatevs, no way to detect XA mono/stereo */
if (filename[0]=='P' && filename[1]=='S' && filename[2]=='1' && filename[3]=='S') {
channels = 1;
sample_rate = 22050;
}
else {
channels = 2;
sample_rate = 44100;
}
bps = 4;
}
else {
/* incorrectly ripped standard XA */
channels = 2;
sample_rate = 37800;
bps = 4;
}
goto fail;
}
/* untested */
@ -161,7 +129,9 @@ fail:
return NULL;
}
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);
@ -172,23 +142,27 @@ static int xa_check_format(STREAMFILE *sf, off_t offset, int is_blocked) {
/* test frames inside CD sectors */
while (sector < sector_max) {
uint8_t xa_submode = read_u8(test_offset + 0x12, sf);
int is_audio = !(xa_submode & 0x08) && (xa_submode & 0x04) && !(xa_submode & 0x02);
if (is_blocked) {
uint8_t xa_submode = read_u8(test_offset + 0x12, sf);
int is_audio = !(xa_submode & 0x08) && (xa_submode & 0x04) && !(xa_submode & 0x02);
if (is_blocked && !is_audio) {
skip++;
if (sector == 0 && skip > skip_max) /* no a single audio sector found */
goto fail;
test_offset += sector_size + extra_size + extra_size;
continue;
if (is_blocked && !is_audio) {
skip++;
if (sector == 0 && skip > skip_max) /* no a single audio sector found */
goto fail;
test_offset += sector_size + extra_size + extra_size;
continue;
}
}
test_offset += extra_size; /* header */
for (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++) {
uint8_t header = read_u8(test_offset + j, sf);
uint8_t header = get_u8(frame_hdr + j);
if (((header >> 4) & 0xF) > 0x03)
goto fail;
if (((header >> 0) & 0xF) > 0x0c)
@ -196,14 +170,14 @@ static int xa_check_format(STREAMFILE *sf, off_t offset, int is_blocked) {
}
/* XA headers pairs are repeated */
if (read_u32be(test_offset+0x00, sf) != read_u32be(test_offset+0x04, sf) ||
read_u32be(test_offset+0x08, sf) != read_u32be(test_offset+0x0c, sf))
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) */
if (read_u32be(test_offset+0x00, sf) == 0 &&
read_u32be(test_offset+0x04, sf) == 0 &&
read_u32be(test_offset+0x08, sf) == 0 &&
read_u32be(test_offset+0x0c, sf) == 0)
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)
goto fail;
test_offset += 0x80;

View File

@ -1,12 +1,22 @@
#include "streamfile.h"
#include "util.h"
#include "vgmstream.h"
#include <string.h>
/* for dup/fdopen in some systems */
#ifndef _MSC_VER
#include <unistd.h>
#endif
//TODO: move
#ifndef DIR_SEPARATOR
#if defined (_WIN32) || defined (WIN32)
#define DIR_SEPARATOR '\\'
#else
#define DIR_SEPARATOR '/'
#endif
#endif
/* For (rarely needed) +2GB file support we use fseek64/ftell64. Those are usually available
* but may depend on compiler.
* - MSVC: +VS2008 should work
@ -108,7 +118,7 @@ static size_t stdio_read(STDIO_STREAMFILE* sf, uint8_t* dst, offv_t offset, size
#ifdef VGM_DEBUG_OUTPUT
if (offset < sf->buf_offset && length > 0) {
VGM_LOG("stdio: rebuffer, requested %x vs %x (sf %x)\n", (uint32_t)offset, (uint32_t)sf->buf_offset, (uint32_t)sf);
//VGM_LOG("stdio: rebuffer, requested %x vs %x (sf %x)\n", (uint32_t)offset, (uint32_t)sf->buf_offset, (uint32_t)sf);
//sf->rebuffer++;
//if (rebuffer > N) ...
}
@ -341,7 +351,7 @@ static size_t buffer_read(BUFFER_STREAMFILE* sf, uint8_t* dst, offv_t offset, si
#ifdef VGM_DEBUG_OUTPUT
if (offset < sf->buf_offset) {
VGM_LOG("buffer: rebuffer, requested %x vs %x (sf %x)\n", (uint32_t)offset, (uint32_t)sf->buf_offset, (uint32_t)sf);
//VGM_LOG("buffer: rebuffer, requested %x vs %x (sf %x)\n", (uint32_t)offset, (uint32_t)sf->buf_offset, (uint32_t)sf);
}
#endif
@ -1263,7 +1273,6 @@ STREAMFILE* read_filemap_file_pos(STREAMFILE* sf, int file_num, int* p_pos) {
/* better way? */
if (strcmp(line, "#@reset-pos") == 0) {
file_pos = 0;
VGM_LOG("pos =%i\n", file_pos);
}
continue;
}

View File

@ -1,7 +1,6 @@
/*
* streamfile.h - definitions for buffered file reading with STREAMFILE
*/
#ifndef _STREAMFILE_H
#define _STREAMFILE_H
@ -9,9 +8,14 @@
#define _CRT_SECURE_NO_DEPRECATE
#endif
//TODO cleanup
//NULL, allocs
#include <stdlib.h>
//FILE
#include <stdio.h>
//string functions in meta and so on
#include <string.h>
//off_t
#include <sys/types.h>
#include "streamtypes.h"
#include "util.h"
@ -22,14 +26,6 @@
#include <io.h>
#endif
#ifndef DIR_SEPARATOR
#if defined (_WIN32) || defined (WIN32)
#define DIR_SEPARATOR '\\'
#else
#define DIR_SEPARATOR '/'
#endif
#endif
/* 64-bit offset is needed for banks that hit +2.5GB (like .fsb or .ktsl2stbin).
* Leave as typedef to toggle since it's theoretically slower when compiled as 32-bit.
* ATM it's only used in choice places until more performance tests are done.

View File

@ -12,19 +12,19 @@
/* host endian independent multi-byte integer reading */
static inline int16_t get_16bitBE(const uint8_t* p) {
return (p[0]<<8) | (p[1]);
return ((uint16_t)p[0]<<8) | ((uint16_t)p[1]);
}
static inline int16_t get_16bitLE(const uint8_t* p) {
return (p[0]) | (p[1]<<8);
return ((uint16_t)p[0]) | ((uint16_t)p[1]<<8);
}
static inline int32_t get_32bitBE(const uint8_t* p) {
return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | (p[3]);
return ((uint32_t)p[0]<<24) | ((uint32_t)p[1]<<16) | ((uint32_t)p[2]<<8) | ((uint32_t)p[3]);
}
static inline int32_t get_32bitLE(const uint8_t* p) {
return (p[0]) | (p[1]<<8) | (p[2]<<16) | (p[3]<<24);
return ((uint32_t)p[0]) | ((uint32_t)p[1]<<8) | ((uint32_t)p[2]<<16) | ((uint32_t)p[3]<<24);
}
static inline int64_t get_64bitBE(const uint8_t* p) {

View File

@ -6,5 +6,10 @@
typedef uint32_t (*read_u32_t)(off_t, STREAMFILE*);
typedef int32_t (*read_s32_t)(off_t, STREAMFILE*);
typedef uint16_t (*read_u16_t)(off_t, STREAMFILE*);
typedef float (*read_f32_t)(off_t, STREAMFILE*);
//todo move here
#define guess_endian32 guess_endianness32bit
#define guess_endian16 guess_endianness16bit
#endif

View File

@ -92,9 +92,7 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
init_vgmstream_musx,
init_vgmstream_leg,
init_vgmstream_filp,
init_vgmstream_ikm_ps2,
init_vgmstream_ikm_pc,
init_vgmstream_ikm_psp,
init_vgmstream_ikm,
init_vgmstream_sfs,
init_vgmstream_bg00,
init_vgmstream_sat_dvi,
@ -214,7 +212,8 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
init_vgmstream_xbox_hlwav,
init_vgmstream_myspd,
init_vgmstream_his,
init_vgmstream_ps2_ast,
init_vgmstream_ast_mmv,
init_vgmstream_ast_mv,
init_vgmstream_dmsg,
init_vgmstream_ngc_dsp_aaap,
init_vgmstream_ngc_dsp_konami,
@ -237,7 +236,7 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
init_vgmstream_dsp_xiii,
init_vgmstream_dsp_cabelas,
init_vgmstream_ps2_adm,
init_vgmstream_ps2_lpcm,
init_vgmstream_lpcm_shade,
init_vgmstream_dsp_bdsp,
init_vgmstream_ps2_vms,
init_vgmstream_xau,

View File

@ -96,6 +96,7 @@ typedef enum {
coding_XA, /* CD-ROM XA 4-bit */
coding_XA8, /* CD-ROM XA 8-bit */
coding_XA_EA, /* EA's Saturn XA (not to be confused with EA-XA) */
coding_PSX, /* Sony PS ADPCM (VAG) */
coding_PSX_badflags, /* Sony PS ADPCM with custom flag byte */
coding_PSX_cfg, /* Sony PS ADPCM with configurable frame size (int math) */
@ -147,6 +148,7 @@ typedef enum {
coding_AICA, /* Yamaha AICA ADPCM (stereo) */
coding_AICA_int, /* Yamaha AICA ADPCM (mono/interleave) */
coding_CP_YM, /* Capcom's Yamaha ADPCM (stereo/mono) */
coding_ASKA, /* Aska ADPCM */
coding_NXAP, /* NXAP ADPCM */
@ -516,7 +518,8 @@ typedef enum {
meta_PONA_3DO, /* Policenauts (3DO) */
meta_PONA_PSX, /* Policenauts (PSX) */
meta_XBOX_HLWAV, /* Half Life 2 (XBOX) */
meta_PS2_AST, /* Some KOEI game (PS2) */
meta_AST_MV,
meta_AST_MMV,
meta_DMSG, /* Nightcaster II - Equinox (XBOX) */
meta_NGC_DSP_AAAP, /* Turok: Evolution (NGC), Vexx (NGC) */
meta_PS2_STER, /* Juuni Kokuki: Kakukaku Taru Ou Michi Beni Midori no Uka */
@ -539,7 +542,7 @@ typedef enum {
meta_DSP_XIII, /* XIII, possibly more (Ubisoft header???) */
meta_DSP_CABELAS, /* Cabelas games */
meta_PS2_ADM, /* Dragon Quest V (PS2) */
meta_PS2_LPCM, /* Ah! My Goddess */
meta_LPCM_SHADE,
meta_DSP_BDSP, /* Ah! My Goddess */
meta_PS2_VMS, /* Autobahn Raser - Police Madness */
meta_XAU, /* XPEC Entertainment (Beat Down (PS2 Xbox), Spectral Force Chronicle (PS2)) */