mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-02-06 14:44:25 +01:00
support an improved form of ADX encryption
git-svn-id: https://vgmstream.svn.sourceforge.net/svnroot/vgmstream@967 51a99a44-fe44-0410-b1ba-c3e57ba2b86b
This commit is contained in:
parent
4db4ae8620
commit
8f7faebedd
@ -45,7 +45,7 @@ void decode_adx_enc(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspaci
|
|||||||
|
|
||||||
int framesin = first_sample/32;
|
int framesin = first_sample/32;
|
||||||
|
|
||||||
int32_t scale = (read_16bitBE(stream->offset+framesin*18,stream->streamfile) ^ stream->adx_xor) + 1;
|
int32_t scale = ((read_16bitBE(stream->offset+framesin*18,stream->streamfile) ^ stream->adx_xor)&0x1fff) + 1;
|
||||||
int32_t hist1 = stream->adpcm_history1_32;
|
int32_t hist1 = stream->adpcm_history1_32;
|
||||||
int32_t hist2 = stream->adpcm_history2_32;
|
int32_t hist2 = stream->adpcm_history2_32;
|
||||||
int coef1 = stream->adpcm_coef[0];
|
int coef1 = stream->adpcm_coef[0];
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
#include "../coding/coding.h"
|
#include "../coding/coding.h"
|
||||||
#include "../util.h"
|
#include "../util.h"
|
||||||
|
|
||||||
static int find_key(STREAMFILE *file, uint16_t *xor_start, uint16_t *xor_mult, uint16_t *xor_add);
|
static int find_key(STREAMFILE *file, uint8_t type, uint16_t *xor_start, uint16_t *xor_mult, uint16_t *xor_add);
|
||||||
|
|
||||||
VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) {
|
VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) {
|
||||||
VGMSTREAM * vgmstream = NULL;
|
VGMSTREAM * vgmstream = NULL;
|
||||||
@ -59,12 +59,20 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) {
|
|||||||
version_signature = read_16bitBE(0x12,streamFile);
|
version_signature = read_16bitBE(0x12,streamFile);
|
||||||
/* encryption */
|
/* encryption */
|
||||||
if (version_signature == 0x0408) {
|
if (version_signature == 0x0408) {
|
||||||
if (find_key(streamFile, &xor_start, &xor_mult, &xor_add))
|
if (find_key(streamFile, 8, &xor_start, &xor_mult, &xor_add))
|
||||||
{
|
{
|
||||||
coding_type = coding_CRI_ADX_enc;
|
coding_type = coding_CRI_ADX_enc_8;
|
||||||
version_signature = 0x0400;
|
version_signature = 0x0400;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (version_signature == 0x0409) {
|
||||||
|
if (find_key(streamFile, 9, &xor_start, &xor_mult, &xor_add))
|
||||||
|
{
|
||||||
|
coding_type = coding_CRI_ADX_enc_9;
|
||||||
|
version_signature = 0x0400;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (version_signature == 0x0300) { /* type 03 */
|
if (version_signature == 0x0300) { /* type 03 */
|
||||||
header_type = meta_ADX_03;
|
header_type = meta_ADX_03;
|
||||||
if (stream_offset-6 >= 0x2c) { /* enough space for loop info? */
|
if (stream_offset-6 >= 0x2c) { /* enough space for loop info? */
|
||||||
@ -157,7 +165,8 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) {
|
|||||||
vgmstream->ch[i].adpcm_coef[0] = coef1;
|
vgmstream->ch[i].adpcm_coef[0] = coef1;
|
||||||
vgmstream->ch[i].adpcm_coef[1] = coef2;
|
vgmstream->ch[i].adpcm_coef[1] = coef2;
|
||||||
|
|
||||||
if (coding_type == coding_CRI_ADX_enc)
|
if (coding_type == coding_CRI_ADX_enc_8 ||
|
||||||
|
coding_type == coding_CRI_ADX_enc_9)
|
||||||
{
|
{
|
||||||
int j;
|
int j;
|
||||||
vgmstream->ch[i].adx_channels = channel_count;
|
vgmstream->ch[i].adx_channels = channel_count;
|
||||||
@ -181,9 +190,10 @@ fail:
|
|||||||
|
|
||||||
/* guessadx stuff */
|
/* guessadx stuff */
|
||||||
|
|
||||||
|
/* type 8 keys */
|
||||||
static struct {
|
static struct {
|
||||||
uint16_t start,mult,add;
|
uint16_t start,mult,add;
|
||||||
} keys[] = {
|
} keys_8[] = {
|
||||||
/* Clover Studio (GOD HAND, Okami) */
|
/* Clover Studio (GOD HAND, Okami) */
|
||||||
/* I'm pretty sure this is right, based on a decrypted version of some GOD HAND tracks. */
|
/* I'm pretty sure this is right, based on a decrypted version of some GOD HAND tracks. */
|
||||||
/* Also it is the 2nd result from guessadx */
|
/* Also it is the 2nd result from guessadx */
|
||||||
@ -343,10 +353,20 @@ static struct {
|
|||||||
{0x4c73,0x4d8d,0x5827},
|
{0x4c73,0x4d8d,0x5827},
|
||||||
};
|
};
|
||||||
|
|
||||||
static const int key_count = sizeof(keys)/sizeof(keys[0]);
|
/* type 9 keys */
|
||||||
|
static struct {
|
||||||
|
uint16_t start,mult,add;
|
||||||
|
} keys_9[] = {
|
||||||
|
/* Phantasy Star Online 2
|
||||||
|
* guessed with degod */
|
||||||
|
{0x07d2,0x1ec5,0x0c7f},
|
||||||
|
};
|
||||||
|
|
||||||
|
static const int keys_8_count = sizeof(keys_8)/sizeof(keys_8[0]);
|
||||||
|
static const int keys_9_count = sizeof(keys_9)/sizeof(keys_9[0]);
|
||||||
|
|
||||||
/* return 0 if not found, 1 if found and set parameters */
|
/* return 0 if not found, 1 if found and set parameters */
|
||||||
static int find_key(STREAMFILE *file, uint16_t *xor_start, uint16_t *xor_mult, uint16_t *xor_add)
|
static int find_key(STREAMFILE *file, uint8_t type, uint16_t *xor_start, uint16_t *xor_mult, uint16_t *xor_add)
|
||||||
{
|
{
|
||||||
uint16_t * scales = NULL;
|
uint16_t * scales = NULL;
|
||||||
uint16_t * prescales = NULL;
|
uint16_t * prescales = NULL;
|
||||||
@ -423,39 +443,86 @@ static int find_key(STREAMFILE *file, uint16_t *xor_start, uint16_t *xor_mult, u
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* guess each of the keys */
|
if (type == 8)
|
||||||
for (key_id=0;key_id<key_count;key_id++) {
|
{
|
||||||
/* test pre-scales */
|
/* guess each of the keys */
|
||||||
uint16_t xor = keys[key_id].start;
|
for (key_id=0;key_id<keys_8_count;key_id++) {
|
||||||
uint16_t mult = keys[key_id].mult;
|
/* test pre-scales */
|
||||||
uint16_t add = keys[key_id].add;
|
uint16_t xor = keys_8[key_id].start;
|
||||||
int i;
|
uint16_t mult = keys_8[key_id].mult;
|
||||||
|
uint16_t add = keys_8[key_id].add;
|
||||||
|
int i;
|
||||||
|
|
||||||
for (i=0;i<bruteframe &&
|
for (i=0;i<bruteframe &&
|
||||||
((prescales[i]&0x6000)==(xor&0x6000) ||
|
((prescales[i]&0x6000)==(xor&0x6000) ||
|
||||||
prescales[i]==0);
|
prescales[i]==0);
|
||||||
i++) {
|
i++) {
|
||||||
xor = xor * mult + add;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i == bruteframe)
|
|
||||||
{
|
|
||||||
/* test */
|
|
||||||
for (i=0;i<scales_to_do &&
|
|
||||||
(scales[i]&0x6000)==(xor&0x6000);i++) {
|
|
||||||
xor = xor * mult + add;
|
xor = xor * mult + add;
|
||||||
}
|
}
|
||||||
if (i == scales_to_do)
|
|
||||||
|
if (i == bruteframe)
|
||||||
{
|
{
|
||||||
*xor_start = keys[key_id].start;
|
/* test */
|
||||||
*xor_mult = keys[key_id].mult;
|
for (i=0;i<scales_to_do &&
|
||||||
*xor_add = keys[key_id].add;
|
(scales[i]&0x6000)==(xor&0x6000);i++) {
|
||||||
|
xor = xor * mult + add;
|
||||||
rc = 1;
|
}
|
||||||
goto find_key_cleanup;
|
if (i == scales_to_do)
|
||||||
|
{
|
||||||
|
*xor_start = keys_8[key_id].start;
|
||||||
|
*xor_mult = keys_8[key_id].mult;
|
||||||
|
*xor_add = keys_8[key_id].add;
|
||||||
|
|
||||||
|
rc = 1;
|
||||||
|
goto find_key_cleanup;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (type == 9)
|
||||||
|
{
|
||||||
|
/* smarter XOR as seen in PSO2, can't do an exact match so we
|
||||||
|
* have to search for the lowest */
|
||||||
|
long best_score = MAX_FRAMES * 0x1fff;
|
||||||
|
|
||||||
|
/* guess each of the keys */
|
||||||
|
for (key_id=0;key_id<keys_9_count;key_id++) {
|
||||||
|
/* run past pre-scales */
|
||||||
|
uint16_t xor = keys_9[key_id].start;
|
||||||
|
uint16_t mult = keys_9[key_id].mult;
|
||||||
|
uint16_t add = keys_9[key_id].add;
|
||||||
|
int i;
|
||||||
|
long total_score = 0;
|
||||||
|
|
||||||
|
for (i=0;i<bruteframe;i++) {
|
||||||
|
xor = xor * mult + add;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == bruteframe)
|
||||||
|
{
|
||||||
|
/* test */
|
||||||
|
for (i=0;i<scales_to_do && total_score < best_score;i++) {
|
||||||
|
xor = xor * mult + add;
|
||||||
|
total_score += (scales[i]^xor)&0x1fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (total_score < best_score)
|
||||||
|
{
|
||||||
|
*xor_start = keys_9[key_id].start;
|
||||||
|
*xor_mult = keys_9[key_id].mult;
|
||||||
|
*xor_add = keys_9[key_id].add;
|
||||||
|
|
||||||
|
best_score = total_score;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* arbitrarily decide if we have won? */
|
||||||
|
if (best_score < scales_to_do * 0x1000)
|
||||||
|
{
|
||||||
|
rc = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
find_key_cleanup:
|
find_key_cleanup:
|
||||||
@ -463,3 +530,4 @@ find_key_cleanup:
|
|||||||
if (prescales) free(prescales);
|
if (prescales) free(prescales);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -781,7 +781,8 @@ void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstre
|
|||||||
int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
|
int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
|
||||||
switch (vgmstream->coding_type) {
|
switch (vgmstream->coding_type) {
|
||||||
case coding_CRI_ADX:
|
case coding_CRI_ADX:
|
||||||
case coding_CRI_ADX_enc:
|
case coding_CRI_ADX_enc_8:
|
||||||
|
case coding_CRI_ADX_enc_9:
|
||||||
case coding_L5_555:
|
case coding_L5_555:
|
||||||
return 32;
|
return 32;
|
||||||
case coding_NGC_DSP:
|
case coding_NGC_DSP:
|
||||||
@ -896,7 +897,8 @@ int get_vgmstream_samples_per_shortframe(VGMSTREAM * vgmstream) {
|
|||||||
int get_vgmstream_frame_size(VGMSTREAM * vgmstream) {
|
int get_vgmstream_frame_size(VGMSTREAM * vgmstream) {
|
||||||
switch (vgmstream->coding_type) {
|
switch (vgmstream->coding_type) {
|
||||||
case coding_CRI_ADX:
|
case coding_CRI_ADX:
|
||||||
case coding_CRI_ADX_enc:
|
case coding_CRI_ADX_enc_8:
|
||||||
|
case coding_CRI_ADX_enc_9:
|
||||||
case coding_L5_555:
|
case coding_L5_555:
|
||||||
return 18;
|
return 18;
|
||||||
case coding_NGC_DSP:
|
case coding_NGC_DSP:
|
||||||
@ -1016,7 +1018,8 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
|
|||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case coding_CRI_ADX_enc:
|
case coding_CRI_ADX_enc_8:
|
||||||
|
case coding_CRI_ADX_enc_9:
|
||||||
for (chan=0;chan<vgmstream->channels;chan++) {
|
for (chan=0;chan<vgmstream->channels;chan++) {
|
||||||
decode_adx_enc(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan,
|
decode_adx_enc(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan,
|
||||||
vgmstream->channels,vgmstream->samples_into_block,
|
vgmstream->channels,vgmstream->samples_into_block,
|
||||||
@ -1623,8 +1626,11 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) {
|
|||||||
case coding_CRI_ADX:
|
case coding_CRI_ADX:
|
||||||
snprintf(temp,TEMPSIZE,"CRI ADX 4-bit ADPCM");
|
snprintf(temp,TEMPSIZE,"CRI ADX 4-bit ADPCM");
|
||||||
break;
|
break;
|
||||||
case coding_CRI_ADX_enc:
|
case coding_CRI_ADX_enc_8:
|
||||||
snprintf(temp,TEMPSIZE,"encrypted CRI ADX 4-bit ADPCM");
|
snprintf(temp,TEMPSIZE,"encrypted (type 8) CRI ADX 4-bit ADPCM");
|
||||||
|
break;
|
||||||
|
case coding_CRI_ADX_enc_9:
|
||||||
|
snprintf(temp,TEMPSIZE,"encrypted (type 9) CRI ADX 4-bit ADPCM");
|
||||||
break;
|
break;
|
||||||
case coding_NDS_IMA:
|
case coding_NDS_IMA:
|
||||||
snprintf(temp,TEMPSIZE,"NDS-style 4-bit IMA ADPCM");
|
snprintf(temp,TEMPSIZE,"NDS-style 4-bit IMA ADPCM");
|
||||||
|
@ -50,7 +50,8 @@ typedef enum {
|
|||||||
/* 4-bit ADPCM */
|
/* 4-bit ADPCM */
|
||||||
coding_NDS_IMA, /* IMA ADPCM w/ NDS layout */
|
coding_NDS_IMA, /* IMA ADPCM w/ NDS layout */
|
||||||
coding_CRI_ADX, /* CRI ADX */
|
coding_CRI_ADX, /* CRI ADX */
|
||||||
coding_CRI_ADX_enc, /* encrypted CRI ADX */
|
coding_CRI_ADX_enc_8, /* encrypted CRI ADX, type 8 (God Hand) */
|
||||||
|
coding_CRI_ADX_enc_9, /* encrypted CRI ADX, type 9 (PSO2) */
|
||||||
coding_NGC_DSP, /* NGC ADPCM, called DSP */
|
coding_NGC_DSP, /* NGC ADPCM, called DSP */
|
||||||
coding_NGC_DTK, /* NGC hardware disc ADPCM, called DTK, TRK or ADP */
|
coding_NGC_DTK, /* NGC hardware disc ADPCM, called DTK, TRK or ADP */
|
||||||
coding_G721, /* CCITT G.721 ADPCM */
|
coding_G721, /* CCITT G.721 ADPCM */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user