Merge pull request #842 from bnnm/winamp-etc

- Split Winamp code
- Add ADX keys [Persona 5 Royal (PS4)]
- Add .vag variation [Gift (PS2)]
- Handle foobar2000 bug when reading paths with double slash
- Minor tweak in TXTP path handling
This commit is contained in:
bnnm 2021-04-18 13:33:57 +02:00 committed by GitHub
commit 76566c8d98
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 1428 additions and 1309 deletions

View File

@ -195,31 +195,38 @@ fail:
return NULL; return NULL;
} }
static STREAMFILE * open_foo_streamfile_buffer(const char * const filename, size_t buffersize, abort_callback * p_abort, t_filestats * stats) { static STREAMFILE* open_foo_streamfile_buffer(const char* const filename, size_t buffersize, abort_callback* p_abort, t_filestats* stats) {
STREAMFILE *streamFile; STREAMFILE* sf = NULL;
service_ptr_t<file> infile; service_ptr_t<file> infile;
bool infile_exists; bool infile_exists;
infile_exists = filesystem::g_exists(filename, *p_abort); try {
if(!infile_exists) { infile_exists = filesystem::g_exists(filename, *p_abort);
/* allow non-existing files in some cases */ if (!infile_exists) {
if (!vgmstream_is_virtual_filename(filename)) /* allow non-existing files in some cases */
return NULL; if (!vgmstream_is_virtual_filename(filename))
return NULL;
}
if (infile_exists) {
filesystem::g_open_read(infile, filename, *p_abort);
if(stats) *stats = infile->get_stats(*p_abort);
}
sf = open_foo_streamfile_buffer_by_file(infile, infile_exists, filename, buffersize, p_abort);
if (!sf) {
//m_file.release(); //refcounted and cleaned after it goes out of scope
}
} catch (...) {
/* somehow foobar2000 throws an exception on g_exists when filename has a double \
* (traditionally Windows treats that like a single slash and fopen handles it fine) */
return NULL;
} }
if (infile_exists) { return sf;
filesystem::g_open_read(infile,filename,*p_abort);
if(stats) *stats = infile->get_stats(*p_abort);
}
streamFile = open_foo_streamfile_buffer_by_file(infile, infile_exists, filename, buffersize, p_abort);
if (!streamFile) {
//m_file.release(); //refcounted and cleaned after it goes out of scope
}
return streamFile;
} }
STREAMFILE * open_foo_streamfile(const char * const filename, abort_callback * p_abort, t_filestats * stats) { STREAMFILE* open_foo_streamfile(const char* const filename, abort_callback* p_abort, t_filestats* stats) {
return open_foo_streamfile_buffer(filename,STREAMFILE_DEFAULT_BUFFER_SIZE, p_abort, stats); return open_foo_streamfile_buffer(filename, STREAMFILE_DEFAULT_BUFFER_SIZE, p_abort, stats);
} }

View File

@ -240,9 +240,15 @@ static const adxkey_info adxkey9_list[] = {
/* Detective Conan Runner / Case Closed Runner (Android) */ /* Detective Conan Runner / Case Closed Runner (Android) */
{0x0613,0x0e3d,0x6dff, NULL,1175268187653273344}, // 104f643098e3f700 {0x0613,0x0e3d,0x6dff, NULL,1175268187653273344}, // 104f643098e3f700
/* Persona 5 Royal (PS4) */ /* Persona 5 Royal (PS4) (asia?) **verified */
{0x0000,0x1c85,0x7043, NULL,29915170}, // 0000000001C87822 {0x0000,0x1c85,0x7043, NULL,29915170}, // 0000000001C87822
/* Persona 5 Royal (PS4) (japan?) **not verified */
{0x0000,0x0000,0x0000, NULL,10882899}, // 0000000000A60F53
/* Persona 5 Royal (PS4) (us/eu?) **not verified */
{0x0000,0x0000,0x0000, NULL,22759300}, // 00000000015B4784
/* Assault Lily Last Bullet (Android) */ /* Assault Lily Last Bullet (Android) */
{0x0000,0x0000,0x0000, NULL,6349046567469313}, // 00168E6C99510101 (+ AWB subkeys) {0x0000,0x0000,0x0000, NULL,6349046567469313}, // 00168E6C99510101 (+ AWB subkeys)

View File

@ -691,7 +691,7 @@ static STREAMFILE* open_txth(STREAMFILE* sf) {
/* try "(path/)(.sub.ext).txth" */ /* try "(path/)(.sub.ext).txth" */
get_streamfile_basename(sf,basename,PATH_LIMIT); get_streamfile_basename(sf,basename,PATH_LIMIT);
subext = filename_extension(basename); subext = filename_extension(basename);
if (subext != NULL) { if (subext != NULL && subext[0] != '\0') {
get_streamfile_path(sf,filename,PATH_LIMIT); get_streamfile_path(sf,filename,PATH_LIMIT);
get_streamfile_ext(sf,fileext,PATH_LIMIT); get_streamfile_ext(sf,fileext,PATH_LIMIT);
strcat(filename,"."); strcat(filename,".");

View File

@ -245,9 +245,13 @@ fail:
return 0; return 0;
} }
static int is_silent(txtp_entry* entry) { static int is_silent(const char* fn) {
/* should also contain "." in the filename for commands with seconds ("1.0") to work */ /* should also contain "." in the filename for commands with seconds ("1.0") to work */
return entry->filename[0] == '?'; return fn[0] == '?';
}
static int is_absolute(const char* fn) {
return fn[0] == '/' || fn[0] == '\\' || fn[1] == ':';
} }
/* open all entries and apply settings to resulting VGMSTREAMs */ /* open all entries and apply settings to resulting VGMSTREAMs */
@ -268,17 +272,23 @@ static int parse_entries(txtp_header* txtp, STREAMFILE* sf) {
/* open all entry files first as they'll be modified by modes */ /* open all entry files first as they'll be modified by modes */
for (i = 0; i < txtp->vgmstream_count; i++) { for (i = 0; i < txtp->vgmstream_count; i++) {
STREAMFILE* temp_sf = NULL; STREAMFILE* temp_sf = NULL;
const char* filename = txtp->entry[i].filename;
/* silent entry ignore */ /* silent entry ignore */
if (is_silent(&txtp->entry[i])) { if (is_silent(filename)) {
txtp->entry[i].silent = 1; txtp->entry[i].silent = 1;
has_silents = 1; has_silents = 1;
continue; continue;
} }
temp_sf = open_streamfile_by_filename(sf, txtp->entry[i].filename); /* absolute paths are detected for convenience, but since it's hard to unify all OSs
* and plugins, they aren't "officially" supported nor documented, thus may or may not work */
if (is_absolute(filename))
temp_sf = open_streamfile(sf, filename); /* from path as is */
else
temp_sf = open_streamfile_by_filename(sf, filename); /* from current path */
if (!temp_sf) { if (!temp_sf) {
VGM_LOG("TXTP: cannot open streamfile for %s\n", txtp->entry[i].filename); VGM_LOG("TXTP: cannot open streamfile for %s\n", filename);
goto fail; goto fail;
} }
temp_sf->stream_index = txtp->entry[i].subsong; temp_sf->stream_index = txtp->entry[i].subsong;
@ -286,7 +296,7 @@ static int parse_entries(txtp_header* txtp, STREAMFILE* sf) {
txtp->vgmstream[i] = init_vgmstream_from_STREAMFILE(temp_sf); txtp->vgmstream[i] = init_vgmstream_from_STREAMFILE(temp_sf);
close_streamfile(temp_sf); close_streamfile(temp_sf);
if (!txtp->vgmstream[i]) { if (!txtp->vgmstream[i]) {
VGM_LOG("TXTP: cannot open vgmstream for %s#%i\n", txtp->entry[i].filename, txtp->entry[i].subsong); VGM_LOG("TXTP: cannot open vgmstream for %s#%i\n", filename, txtp->entry[i].subsong);
goto fail; goto fail;
} }

View File

@ -1,351 +1,366 @@
#include "meta.h" #include "meta.h"
#include "../coding/coding.h" #include "../coding/coding.h"
/* VAGp - Sony SDK format, created by various tools */ /* VAGp - Sony SDK format, created by various official tools */
VGMSTREAM * init_vgmstream_vag(STREAMFILE *streamFile) { VGMSTREAM* init_vgmstream_vag(STREAMFILE* sf) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM* vgmstream = NULL;
off_t start_offset; off_t start_offset;
size_t file_size, channel_size, interleave, interleave_first = 0, interleave_first_skip = 0; size_t file_size, channel_size, interleave, interleave_first = 0, interleave_first_skip = 0;
meta_t meta_type; meta_t meta_type;
int channel_count = 0, loop_flag, sample_rate; int channels = 0, loop_flag, sample_rate;
uint32_t vag_id, version; uint32_t vag_id, version, reserved;
int32_t loop_start_sample = 0, loop_end_sample = 0; int32_t loop_start_sample = 0, loop_end_sample = 0;
int allow_dual_stereo = 0; int allow_dual_stereo = 0, has_interleave_last = 0;
/* checks */ /* checks */
/* .vag: standard /* .vag: standard
* .swag: Frantix (PSP) * .swag: Frantix (PSP)
* .str: Ben10 Galactic Racing * .str: Ben10 Galactic Racing
* .vig: MX vs. ATV Untamed (PS2) * .vig: MX vs. ATV Untamed (PS2)
* .l/r: Crash Nitro Kart (PS2), Gradius V (PS2) * .l/r: Crash Nitro Kart (PS2), Gradius V (PS2)
* .vas: Kingdom Hearts II (PS2) */ * .vas: Kingdom Hearts II (PS2) */
if ( !check_extensions(streamFile,"vag,swag,str,vig,l,r,vas") ) if ( !check_extensions(sf,"vag,swag,str,vig,l,r,vas") )
goto fail; goto fail;
/* check VAG Header */ if (((read_u32be(0x00,sf) & 0xFFFFFF00) != 0x56414700) && /* "VAG" */
if (((read_32bitBE(0x00,streamFile) & 0xFFFFFF00) != 0x56414700) && /* "VAG" */ ((read_u32le(0x00,sf) & 0xFFFFFF00) != 0x56414700))
((read_32bitLE(0x00,streamFile) & 0xFFFFFF00) != 0x56414700)) goto fail;
goto fail;
file_size = get_streamfile_size(sf);
file_size = get_streamfile_size(streamFile);
/* versions used to create the file:
/* version used to create the file: * - 00000000 = v1.8 PC
* - 00000000 = v1.8 PC, * - 00000002 = v1.3 Mac (used?)
* - 00000002 = v1.3 Mac (used?) * - 00000003 = v1.6+ Mac
* - 00000003 = v1.6+ Mac * - 00000020 = v2.0 PC (most common)
* - 00000020 = v2.0 PC (most common) * - 00000004 = ? (later games)
* - 00000004 = ? (later games) * - 00000006 = ? (vagconv)
* - 00000006 = ? (vagconv) * - 00020001 = v2.1 (vagconv2)
* - 00020001 = v2.1 (vagconv2) * - 00030000 = v3.0 (vagconv2) */
* - 00030000 = v3.0 (vagconv2) */
version = (uint32_t)read_32bitBE(0x04,streamFile); vag_id = read_u32be(0x00,sf);
/* 0x08-0c: reserved */ version = read_u32be(0x04,sf);
channel_size = read_32bitBE(0x0c,streamFile); reserved = read_u32be(0x08,sf);
sample_rate = read_32bitBE(0x10,streamFile); channel_size = read_u32be(0x0c,sf);
/* 0x14-20 reserved */ sample_rate = read_u32be(0x10,sf);
/* 0x20-30: name (optional) */ /* 0x14-20 reserved */
/* 0x30: data start (first 0x10 usually 0s to init SPU) */ /* 0x20-30: name (optional) */
/* 0x30: data start (first 0x10 usually 0s to init SPU) */
/* check variation */ /* check variation */
vag_id = read_32bitBE(0x00,streamFile); switch(vag_id) {
switch(vag_id) {
case 0x56414731: /* "VAG1" (1 channel) [Metal Gear Solid 3 (PS2)] */
case 0x56414731: /* "VAG1" (1 channel) [Metal Gear Solid 3 (PS2)] */ meta_type = meta_PS2_VAG1;
meta_type = meta_PS2_VAG1; start_offset = 0x40; /* 0x30 is extra data in VAG1 */
start_offset = 0x40; /* 0x30 is extra data in VAG1 */ channels = 1;
channel_count = 1; interleave = 0;
interleave = 0; loop_flag = 0;
loop_flag = 0; break;
break;
case 0x56414732: /* "VAG2" (2 channels) [Metal Gear Solid 3 (PS2)] */
case 0x56414732: /* "VAG2" (2 channels) [Metal Gear Solid 3 (PS2)] */ meta_type = meta_PS2_VAG2;
meta_type = meta_PS2_VAG2; start_offset = 0x40; /* 0x30 is extra data in VAG2 */
start_offset = 0x40; /* 0x30 is extra data in VAG2 */ channels = 2;
channel_count = 2; interleave = 0x800;
interleave = 0x800; loop_flag = 0;
loop_flag = 0; break;
break;
case 0x56414769: /* "VAGi" (interleaved) */
case 0x56414769: /* "VAGi" (interleaved) */ meta_type = meta_PS2_VAGi;
meta_type = meta_PS2_VAGi; start_offset = 0x800;
start_offset = 0x800; channels = 2;
channel_count = 2; interleave = read_u32le(0x08,sf);
interleave = read_32bitLE(0x08,streamFile); loop_flag = 0;
loop_flag = 0; break;
break;
case 0x70474156: /* pGAV (little endian / stereo) [Jak 3 (PS2), Jak X (PS2)] */
case 0x70474156: /* pGAV (little endian / stereo) [Jak 3 (PS2), Jak X (PS2)] */ meta_type = meta_PS2_pGAV;
meta_type = meta_PS2_pGAV; start_offset = 0x30;
start_offset = 0x30;
if (is_id32be(0x20,sf, "Ster")) {
if (read_32bitBE(0x20,streamFile) == 0x53746572) { /* "Ster" */ channels = 2;
channel_count = 2;
if (is_id32be(0x2000,sf, "pGAV"))
if (read_32bitLE(0x2000,streamFile) == 0x56414770) /* "pGAV" */ interleave = 0x2000; /* Jak 3 interleave, includes header */
interleave = 0x2000; /* Jak 3 interleave, includes header */ else if (is_id32be(0x1000,sf, "pGAV"))
else if (read_32bitLE(0x1000,streamFile) == 0x56414770) /* "pGAV" */ interleave = 0x1000; /* Jak X interleave, includes header */
interleave = 0x1000; /* Jak X interleave, includes header */ else
else interleave = 0x2000; /* Jak 3 interleave in rare files, no header */
interleave = 0x2000; /* Jak 3 interleave in rare files, no header */ interleave_first = interleave - start_offset; /* interleave includes header */
interleave_first = interleave - start_offset; /* interleave includes header */ interleave_first_skip = start_offset;
interleave_first_skip = start_offset; }
} else {
else { channels = 1;
channel_count = 1; interleave = 0;
interleave = 0; }
}
channel_size = read_u32le(0x0C,sf) / channels;
channel_size = read_32bitLE(0x0C,streamFile) / channel_count; sample_rate = read_s32le(0x10,sf);
sample_rate = read_32bitLE(0x10,streamFile); //todo adjust channel_size, includes part of header?
//todo adjust channel_size, includes part of header? loop_flag = 0;
loop_flag = 0; break;
break;
case 0x56414770: /* "VAGp" (standard and variations) */
case 0x56414770: /* "VAGp" (standard and variations) */ meta_type = meta_PS2_VAGp;
meta_type = meta_PS2_VAGp;
if (check_extensions(sf,"vig")) {
if (check_extensions(streamFile,"vig")) { /* MX vs. ATV Untamed (PS2) */
/* MX vs. ATV Untamed (PS2) */ start_offset = 0x800 - 0x20;
start_offset = 0x800 - 0x20; channels = 2;
channel_count = 2; interleave = 0x10;
interleave = 0x10; loop_flag = 0;
loop_flag = 0; }
} else if (check_extensions(sf,"swag")) { /* also "VAGp" at (file_size / channels) */
else if (check_extensions(streamFile,"swag")) { /* also "VAGp" at (file_size / channels) */ /* Frantix (PSP) */
/* Frantix (PSP) */ start_offset = 0x40; /* channel_size ignores empty frame */
start_offset = 0x40; /* channel_size ignores empty frame */ channels = 2;
channel_count = 2; interleave = file_size / channels;
interleave = file_size / channel_count;
channel_size = read_u32le(0x0c,sf);
channel_size = read_32bitLE(0x0c,streamFile); sample_rate = read_s32le(0x10,sf);
sample_rate = read_32bitLE(0x10,streamFile);
loop_flag = ps_find_loop_offsets(sf, start_offset, channel_size*channels, channels, interleave, &loop_start_sample, &loop_end_sample);
loop_flag = ps_find_loop_offsets(streamFile, start_offset, channel_size*channel_count, channel_count, interleave, &loop_start_sample, &loop_end_sample); }
} else if (is_id32be(0x6000,sf, "VAGp")) {
else if (read_32bitBE(0x6000,streamFile) == 0x56414770) { /* "VAGp" */ /* The Simpsons Wrestling (PS1) */
/* The Simpsons Wrestling (PS1) */ start_offset = 0x30;
start_offset = 0x30; channels = 2;
channel_count = 2; interleave = 0x6000;
interleave = 0x6000; interleave_first = interleave - start_offset; /* includes header */
interleave_first = interleave - start_offset; /* includes header */ interleave_first_skip = start_offset;
interleave_first_skip = start_offset;
loop_flag = 0;
loop_flag = 0; }
} else if (is_id32be(0x1000,sf, "VAGp")) {
else if (read_32bitBE(0x1000,streamFile) == 0x56414770) { /* "VAGp" */ /* Shikigami no Shiro (PS2) */
/* Shikigami no Shiro (PS2) */ start_offset = 0x30;
start_offset = 0x30; channels = 2;
channel_count = 2; interleave = 0x1000;
interleave = 0x1000; interleave_first = interleave - start_offset; /* includes header */
interleave_first = interleave - start_offset; /* includes header */ interleave_first_skip = start_offset;
interleave_first_skip = start_offset;
loop_flag = ps_find_loop_offsets(sf, start_offset, channel_size*channels, channels, interleave, &loop_start_sample, &loop_end_sample);
loop_flag = ps_find_loop_offsets(streamFile, start_offset, channel_size*channel_count, channel_count, interleave, &loop_start_sample, &loop_end_sample); }
} else if (version == 0x40000000) {
else if (version == 0x40000000) { /* Killzone (PS2) */
/* Killzone (PS2) */ start_offset = 0x30;
start_offset = 0x30; channels = 1;
channel_count = 1; interleave = 0;
interleave = 0;
channel_size = read_u32le(0x0C,sf) / channels;
channel_size = read_32bitLE(0x0C,streamFile) / channel_count; sample_rate = read_s32le(0x10,sf);
sample_rate = read_32bitLE(0x10,streamFile); loop_flag = 0;
loop_flag = 0; }
} else if (version == 0x00020001 || version == 0x00030000) {
else if (version == 0x00020001 || version == 0x00030000) { /* standard Vita/PS4 .vag [Chronovolt (Vita), Grand Kingdom (PS4)] */
/* standard Vita/PS4 .vag [Chronovolt (Vita), Grand Kingdom (PS4)] */ start_offset = 0x30;
start_offset = 0x30; interleave = 0x10;
interleave = 0x10;
/* channels are at 0x1e, except Ukiyo no Roushi (Vita), which has
/* channels are at 0x1e, except Ukiyo no Roushi (Vita), which has * loop start/end frame (but also uses PS-ADPCM flags) */
* loop start/end frame (but also uses PS-ADPCM flags) */ if (read_u32be(0x18,sf) == 0
if (read_32bitBE(0x18,streamFile) == 0 && (read_u32be(0x1c,sf) & 0xFFFF00FF) == 0
&& (read_32bitBE(0x1c,streamFile) & 0xFFFF00FF) == 0 && read_u8(0x1e,sf) < 16) {
&& read_8bit(0x1e,streamFile) < 16) { channels = read_u8(0x1e,sf);
channel_count = read_8bit(0x1e,streamFile); if (channels == 0)
if (channel_count == 0) channels = 1; /* ex. early games [Lumines (Vita)] */
channel_count = 1; /* ex. early games [Lumines (Vita)] */ }
} else {
else { channels = 1;
channel_count = 1; }
}
channel_size = channel_size / channels;
channel_size = channel_size / channel_count; loop_flag = ps_find_loop_offsets(sf, start_offset, channel_size*channels, channels, interleave, &loop_start_sample, &loop_end_sample);
loop_flag = ps_find_loop_offsets(streamFile, start_offset, channel_size*channel_count, channel_count, interleave, &loop_start_sample, &loop_end_sample); }
} else if (version == 0x00000004 && channel_size == file_size - 0x60 && read_u32be(0x1c, sf) != 0) { /* also .vas */
else if (version == 0x00000004 && channel_size == file_size - 0x60 && read_32bitBE(0x1c, streamFile) != 0) { /* also .vas */ /* Kingdom Hearts II (PS2) */
/* Kingdom Hearts II (PS2) */ start_offset = 0x60;
start_offset = 0x60; interleave = 0x10;
interleave = 0x10;
loop_start_sample = read_s32be(0x14,sf);
loop_start_sample = read_32bitBE(0x14,streamFile); loop_end_sample = read_s32be(0x18,sf);
loop_end_sample = read_32bitBE(0x18,streamFile); loop_flag = (loop_end_sample > 0); /* maybe at 0x1d */
loop_flag = (loop_end_sample > 0); /* maybe at 0x1d */ channels = read_u8(0x1e,sf);
channel_count = read_8bit(0x1e,streamFile); /* 0x1f: possibly volume */
/* 0x1f: possibly volume */ channel_size = channel_size / channels;
channel_size = channel_size / channel_count; /* mono files also have channel/volume, but start at 0x30 and are probably named .vag */
/* mono files also have channel/volume, but start at 0x30 and are probably named .vag */ }
} else if (version == 0x00000020 && is_id32be(0x30,sf, "STER") && is_id32be(0x34,sf, "EOVA") && is_id32be(0x38,sf, "G2K\0")) {
else if (read_32bitBE(0x30,streamFile) == 0x53544552 /* "STEREOVAG2K " */ /* The Simpsons Skateboarding (PS2) */
&& read_32bitBE(0x34,streamFile) == 0x454F5641 start_offset = 0x800;
&& read_32bitBE(0x38,streamFile) == 0x47324B00) { channels = 2;
/* The Simpsons Skateboarding (PS2) */ interleave = 0x800;
start_offset = 0x800; loop_flag = 0;
channel_count = 2; }
interleave = 0x800; else if (version == 0x00000002 && is_id32be(0x24, sf, "VAGx")) {
loop_flag = 0; /* Need for Speed: Hot Pursuit 2 (PS2) */
} start_offset = 0x30;
else if (version == 0x00000002 && read_32bitBE(0x24, streamFile) == 0x56414778) { /* "VAGx" */ channels = read_u32be(0x2c, sf);
/* Need for Speed: Hot Pursuit 2 (PS2) */ channel_size = channel_size / channels;
start_offset = 0x30; loop_flag = 0;
channel_count = read_32bitBE(0x2c, streamFile);
channel_size = channel_size / channel_count; if (file_size % 0x10 != 0)
loop_flag = 0; goto fail;
if (file_size % 0x10 != 0) goto fail; /* detect interleave using end markers */
interleave = 0;
/* detect interleave using end markers */
interleave = 0; if (channels > 1) {
off_t offset = file_size;
if (channel_count > 1) { off_t end_off = 0;
off_t offset = file_size; uint8_t flag;
off_t end_off = 0;
uint8_t flag; while (offset > start_offset) {
offset -= 0x10;
while (offset > start_offset) { flag = read_u8(offset + 0x01, sf);
offset -= 0x10; if (flag == 0x01) {
flag = read_8bit(offset + 0x01, streamFile); if (!end_off) {
if (flag == 0x01) { end_off = offset;
if (!end_off) { } else {
end_off = offset; interleave = end_off - offset;
} else { break;
interleave = end_off - offset; }
break; }
} }
}
} if (!interleave) goto fail;
}
if (!interleave) goto fail; }
} else if (version == 0x00000020 && channel_size == file_size - 0x800 && read_u32be(0x08, sf) == 0x01) {
} /* Garfield: Saving Arlene (PS2) */
else if (version == 0x00000020 && channel_size == file_size - 0x800 && read_32bitBE(0x08, streamFile) == 0x01) { start_offset = 0x800;
/* Garfield: Saving Arlene (PS2) */ channels = 2;
start_offset = 0x800; interleave = 0x400;
channel_count = 2; loop_flag = 0;
interleave = 0x400;
loop_flag = 0; channel_size -= ps_find_padding(sf, start_offset, channel_size, channels, interleave, 0);
channel_size = channel_size / channels;
channel_size -= ps_find_padding(streamFile, start_offset, channel_size, channel_count, interleave, 0); }
channel_size = channel_size / channel_count; else if (version == 0x00000020 && reserved == 0x01010101) {
} /* Gift (PS2) */
else { start_offset = 0x800;
/* standard PS1/PS2/PS3 .vag [Ecco the Dolphin (PS2), Legasista (PS3)] */ channels = 2; /* mono VAGs in this game are standard, without reserved value */
start_offset = 0x30; interleave = 0x2000;
interleave = 0; if (read_u32be(0x4800,sf) == 0x00000000) /* one file has bigger interleave, detectable with ch2's null frame */
interleave = 0x4000;
channel_count = 1;
if (version == 0x20 /* hack for repeating full loops that aren't too small */ channel_size = channel_size / channels;
&& ps_bytes_to_samples(channel_size, 1) > 20 * sample_rate) { has_interleave_last = 1;
loop_flag = ps_find_loop_offsets_full(streamFile, start_offset, channel_size*channel_count, channel_count, interleave, &loop_start_sample, &loop_end_sample);
} /* all files do full loops */
else { loop_flag = 1;
loop_flag = ps_find_loop_offsets(streamFile, start_offset, channel_size*channel_count, channel_count, interleave, &loop_start_sample, &loop_end_sample); loop_start_sample = 0;
} loop_end_sample = ps_bytes_to_samples(channel_size,1);
allow_dual_stereo = 1; /* often found with external L/R files */ }
} else {
break; /* standard PS1/PS2/PS3 .vag [Ecco the Dolphin (PS2), Legasista (PS3)] */
start_offset = 0x30;
default: interleave = 0;
goto fail;
} channels = 1;
if (version == 0x20 /* hack for repeating full loops that aren't too small */
&& ps_bytes_to_samples(channel_size, 1) > 20 * sample_rate) {
/* build the VGMSTREAM */ loop_flag = ps_find_loop_offsets_full(sf, start_offset, channel_size*channels, channels, interleave, &loop_start_sample, &loop_end_sample);
vgmstream = allocate_vgmstream(channel_count,loop_flag); }
if (!vgmstream) goto fail; else {
loop_flag = ps_find_loop_offsets(sf, start_offset, channel_size*channels, channels, interleave, &loop_start_sample, &loop_end_sample);
vgmstream->meta_type = meta_type; }
vgmstream->allow_dual_stereo = allow_dual_stereo; allow_dual_stereo = 1; /* often found with external L/R files */
}
vgmstream->sample_rate = sample_rate; break;
vgmstream->num_samples = ps_bytes_to_samples(channel_size,1);
vgmstream->loop_start_sample = loop_start_sample; default:
vgmstream->loop_end_sample = loop_end_sample; goto fail;
vgmstream->coding_type = coding_PSX; }
if (version == 0x00020001 || version == 0x00030000)
vgmstream->coding_type = coding_HEVAG;
vgmstream->layout_type = (channel_count == 1) ? layout_none : layout_interleave; /* build the VGMSTREAM */
vgmstream->interleave_block_size = interleave; vgmstream = allocate_vgmstream(channels, loop_flag);
vgmstream->interleave_first_block_size = interleave_first; if (!vgmstream) goto fail;
vgmstream->interleave_first_skip = interleave_first_skip;
vgmstream->meta_type = meta_type;
vgmstream->allow_dual_stereo = allow_dual_stereo;
read_string(vgmstream->stream_name,0x10+1, 0x20,streamFile); /* always, can be null */
vgmstream->sample_rate = sample_rate;
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) vgmstream->num_samples = ps_bytes_to_samples(channel_size,1);
goto fail; vgmstream->loop_start_sample = loop_start_sample;
return vgmstream; vgmstream->loop_end_sample = loop_end_sample;
vgmstream->coding_type = coding_PSX;
fail: if (version == 0x00020001 || version == 0x00030000)
close_vgmstream(vgmstream); vgmstream->coding_type = coding_HEVAG;
return NULL; vgmstream->layout_type = (channels == 1) ? layout_none : layout_interleave;
} vgmstream->interleave_block_size = interleave;
vgmstream->interleave_first_block_size = interleave_first;
/* AAAp - Acclaim Austin Audio VAG header [The Red Star (PS2)] */ vgmstream->interleave_first_skip = interleave_first_skip;
VGMSTREAM* init_vgmstream_vag_aaap(STREAMFILE* streamFile) { if (has_interleave_last && channels > 1 && interleave)
VGMSTREAM* vgmstream = NULL; vgmstream->interleave_last_block_size = channel_size % interleave;
off_t vag_offset, start_offset;
uint32_t channel_size, sample_rate; read_string(vgmstream->stream_name,0x10+1, 0x20,sf); /* always, can be null */
uint16_t interleave, channels;
uint32_t i; if (!vgmstream_open_stream(vgmstream, sf, start_offset))
int loop_flag; goto fail;
return vgmstream;
/* checks */
/* .vag - assumed, we don't know the original filenames */ fail:
if (!check_extensions(streamFile, "vag")) close_vgmstream(vgmstream);
goto fail; return NULL;
}
if (read_u32be(0x00, streamFile) != 0x41414170) /* "AAAp" */
goto fail; /* AAAp - Acclaim Austin Audio VAG header [The Red Star (PS2)] */
VGMSTREAM* init_vgmstream_vag_aaap(STREAMFILE* sf) {
interleave = read_u16le(0x04, streamFile); VGMSTREAM* vgmstream = NULL;
channels = read_u16le(0x06, streamFile); off_t vag_offset, start_offset;
vag_offset = 0x08; uint32_t channel_size, sample_rate;
uint16_t interleave, channels;
/* file has VAGp header for each channel */ uint32_t i;
for (i = 0; i < channels; i++) { int loop_flag;
if (read_u32be(vag_offset + i * 0x30, streamFile) != 0x56414770) /* "VAGp" */
goto fail; /* checks */
} /* .vag - assumed, we don't know the original filenames */
if (!check_extensions(sf, "vag"))
/* check version */ goto fail;
if (read_u32be(vag_offset + 0x04, streamFile) != 0x00000020)
goto fail; if (read_u32be(0x00, sf) != 0x41414170) /* "AAAp" */
goto fail;
channel_size = read_u32be(vag_offset + 0x0c, streamFile);
sample_rate = read_u32be(vag_offset + 0x10, streamFile); interleave = read_u16le(0x04, sf);
start_offset = vag_offset + channels * 0x30; channels = read_u16le(0x06, sf);
loop_flag = 0; vag_offset = 0x08;
/* build the VGMSTREAM */ /* file has VAGp header for each channel */
vgmstream = allocate_vgmstream(channels, loop_flag); for (i = 0; i < channels; i++) {
if (!vgmstream) goto fail; if (read_u32be(vag_offset + i * 0x30, sf) != 0x56414770) /* "VAGp" */
goto fail;
vgmstream->meta_type = meta_PS2_VAGp_AAAP; }
vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = ps_bytes_to_samples(channel_size, 1); /* check version */
vgmstream->coding_type = coding_PSX; if (read_u32be(vag_offset + 0x04, sf) != 0x00000020)
vgmstream->layout_type = layout_interleave; goto fail;
vgmstream->interleave_block_size = interleave;
channel_size = read_u32be(vag_offset + 0x0c, sf);
if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) sample_rate = read_u32be(vag_offset + 0x10, sf);
goto fail; start_offset = vag_offset + channels * 0x30;
return vgmstream; loop_flag = 0;
fail: /* build the VGMSTREAM */
close_vgmstream(vgmstream); vgmstream = allocate_vgmstream(channels, loop_flag);
return NULL; if (!vgmstream) goto fail;
}
vgmstream->meta_type = meta_PS2_VAGp_AAAP;
vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = ps_bytes_to_samples(channel_size, 1);
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = interleave;
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -1351,9 +1351,11 @@ static uint32_t hash_sf(STREAMFILE* sf) {
get_streamfile_name(sf, path, sizeof(path)); get_streamfile_name(sf, path, sizeof(path));
/* our favorite garbo hash a.k.a FNV-1 32b */ /* our favorite garbo hash a.k.a FNV-1 32b */
for (i = 0; i < strlen(path); i++) { i = 0;
while (path[i] != '\0') {
char c = tolower(path[i]); char c = tolower(path[i]);
hash = (hash * 16777619) ^ (uint8_t)c; hash = (hash * 16777619) ^ (uint8_t)c;
i++;
} }
return hash; return hash;

View File

@ -1,5 +1,7 @@
file(GLOB WINAMP_SDK_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/sdk/*.h") file(GLOB WINAMP_SDK_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/sdk/*.h")
set(RESOURCES ${CMAKE_CURRENT_SOURCE_DIR}/resource.rc) set(RESOURCES ${CMAKE_CURRENT_SOURCE_DIR}/resource.rc)
file(GLOB WINAMP_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/*.h")
file(GLOB WINAMP_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.c")
# Setup source groups, mainly for Visual Studio # Setup source groups, mainly for Visual Studio
source_group("Header Files\\sdk" FILES ${WINAMP_SDK_HEADERS}) source_group("Header Files\\sdk" FILES ${WINAMP_SDK_HEADERS})
@ -7,8 +9,8 @@ source_group("Resource Files" FILES ${RESOURCES})
add_library(in_vgmstream SHARED add_library(in_vgmstream SHARED
${WINAMP_SDK_HEADERS} ${WINAMP_SDK_HEADERS}
resource.h ${WINAMP_HEADERS}
in_vgmstream.c ${WINAMP_SOURCES}
${RESOURCES}) ${RESOURCES})
# Link to the vgmstream library # Link to the vgmstream library

View File

@ -27,12 +27,14 @@ TARGET_EXT_LIBS += $(LIBS_TARGET_EXT_LIBS)
export CFLAGS LDFLAGS export CFLAGS LDFLAGS
#SRC_SRCS = $(wildcard *.c)
SRC_SRCS = in_vgmstream.c in_streamfile.c in_config.c
############################################################################### ###############################################################################
### targets ### targets
in_vgmstream: libvgmstream.a $(TARGET_EXT_LIBS) resource.o in_vgmstream: libvgmstream.a $(TARGET_EXT_LIBS) resource.o
$(CC) -shared -static-libgcc $(CFLAGS) "-DVERSION=\"`../version.sh`\"" in_vgmstream.c resource.o $(LDFLAGS) -o $(OUTPUT_WINAMP) $(CC) -shared -static-libgcc $(CFLAGS) "-DVERSION=\"`../version.sh`\"" $(SRC_SRCS) resource.o $(LDFLAGS) -o $(OUTPUT_WINAMP)
$(STRIP) $(OUTPUT_WINAMP) $(STRIP) $(OUTPUT_WINAMP)
resource.o: resource.rc resource.h resource.o: resource.rc resource.h

372
winamp/in_config.c Normal file
View File

@ -0,0 +1,372 @@
/**
* config for Winamp
*/
#include "in_vgmstream.h"
/* ************************************* */
/* IN_CONFIG */
/* ************************************* */
/* config */
#define CONFIG_APP_NAME TEXT("vgmstream plugin")
#define CONFIG_INI_NAME TEXT("plugin.ini")
#define INI_FADE_TIME TEXT("fade_seconds")
#define INI_FADE_DELAY TEXT("fade_delay")
#define INI_LOOP_COUNT TEXT("loop_count")
#define INI_THREAD_PRIORITY TEXT("thread_priority")
#define INI_LOOP_FOREVER TEXT("loop_forever")
#define INI_IGNORE_LOOP TEXT("ignore_loop")
#define INI_DISABLE_SUBSONGS TEXT("disable_subsongs")
#define INI_DOWNMIX_CHANNELS TEXT("downmix_channels")
#define INI_TAGFILE_DISABLE TEXT("tagfile_disable")
#define INI_FORCE_TITLE TEXT("force_title")
#define INI_EXTS_UNKNOWN_ON TEXT("exts_unknown_on")
#define INI_EXTS_COMMON_ON TEXT("exts_common_on")
#define INI_GAIN_TYPE TEXT("gain_type")
#define INI_CLIP_TYPE TEXT("clip_type")
TCHAR *dlg_priority_strings[7] = {
TEXT("Idle"),
TEXT("Lowest"),
TEXT("Below Normal"),
TEXT("Normal"),
TEXT("Above Normal"),
TEXT("Highest (not recommended)"),
TEXT("Time Critical (not recommended)")
};
TCHAR *dlg_replaygain_strings[] = {
TEXT("None"),
TEXT("Album"),
TEXT("Peak")
};
int priority_values[7] = {
THREAD_PRIORITY_IDLE,
THREAD_PRIORITY_LOWEST,
THREAD_PRIORITY_BELOW_NORMAL,
THREAD_PRIORITY_NORMAL,
THREAD_PRIORITY_ABOVE_NORMAL,
THREAD_PRIORITY_HIGHEST,
THREAD_PRIORITY_TIME_CRITICAL
};
// todo finish UNICODE (requires IPC_GETINIDIRECTORYW from later SDKs to read the ini path properly)
/* Winamp INI reader */
static void ini_get_filename(In_Module* input_module, TCHAR *inifile) {
if (IsWindow(input_module->hMainWindow) && SendMessage(input_module->hMainWindow, WM_WA_IPC,0,IPC_GETVERSION) >= 0x5000) {
/* newer Winamp with per-user settings */
TCHAR *ini_dir = (TCHAR *)SendMessage(input_module->hMainWindow, WM_WA_IPC, 0, IPC_GETINIDIRECTORY);
cfg_strncpy(inifile, ini_dir, PATH_LIMIT);
cfg_strncat(inifile, TEXT("\\Plugins\\"), PATH_LIMIT);
/* can't be certain that \Plugins already exists in the user dir */
CreateDirectory(inifile,NULL);
cfg_strncat(inifile, CONFIG_INI_NAME, PATH_LIMIT);
}
else {
/* older winamp with single settings */
TCHAR *lastSlash;
GetModuleFileName(NULL, inifile, PATH_LIMIT);
lastSlash = cfg_strrchr(inifile, TEXT('\\'));
*(lastSlash + 1) = 0;
/* XMPlay doesn't have a "plugins" subfolder */
if (settings.is_xmplay)
cfg_strncat(inifile, CONFIG_INI_NAME,PATH_LIMIT);
else
cfg_strncat(inifile, TEXT("Plugins\\") CONFIG_INI_NAME,PATH_LIMIT);
/* Maybe should query IPC_GETINIDIRECTORY and use that, not sure what ancient Winamps need.
* There must be some proper way to handle dirs since other Winamp plugins save config in
* XMPlay correctly (this feels like archaeology, try later) */
}
}
static void ini_get_d(const char *inifile, const char *entry, double defval, double *p_val) {
TCHAR buf[256];
TCHAR defbuf[256];
int consumed, res;
cfg_sprintf(defbuf, TEXT("%.2lf"), defval);
GetPrivateProfileString(CONFIG_APP_NAME, entry, defbuf, buf, 256, inifile);
res = cfg_sscanf(buf, TEXT("%lf%n"), p_val, &consumed);
if (res < 1 || consumed != cfg_strlen(buf) || *p_val < 0) {
*p_val = defval;
}
}
static void ini_get_i(const char *inifile, const char *entry, int defval, int *p_val, int min, int max) {
*p_val = GetPrivateProfileInt(CONFIG_APP_NAME, entry, defval, inifile);
if (*p_val < min || *p_val > max) {
*p_val = defval;
}
}
static void ini_get_b(const char *inifile, const char *entry, int defval, int *p_val) {
ini_get_i(inifile, entry, defval, p_val, 0, 1);
}
static void ini_set_d(const char *inifile, const char *entry, double val) {
TCHAR buf[256];
cfg_sprintf(buf, TEXT("%.2lf"), val);
WritePrivateProfileString(CONFIG_APP_NAME, entry, buf, inifile);
}
static void ini_set_i(const char *inifile, const char *entry, int val) {
TCHAR buf[32];
cfg_sprintf(buf, TEXT("%d"), val);
WritePrivateProfileString(CONFIG_APP_NAME, entry, buf, inifile);
}
static void ini_set_b(const char *inifile, const char *entry, int val) {
ini_set_i(inifile, entry, val);
}
/*static*/ void load_defaults(winamp_settings_t* defaults) {
defaults->thread_priority = 3;
defaults->fade_time = 10.0;
defaults->fade_delay = 0.0;
defaults->loop_count = 2.0;
defaults->loop_forever = 0;
defaults->ignore_loop = 0;
defaults->disable_subsongs = 0;
defaults->downmix_channels = 0;
defaults->tagfile_disable = 0;
defaults->force_title = 0;
defaults->exts_unknown_on = 0;
defaults->exts_common_on = 0;
defaults->gain_type = 1;
defaults->clip_type = 2;
}
/*static*/ void load_config(In_Module* input_module, winamp_settings_t* settings, winamp_settings_t* defaults) {
TCHAR inifile[PATH_LIMIT];
ini_get_filename(input_module, inifile);
ini_get_i(inifile, INI_THREAD_PRIORITY, defaults->thread_priority, &settings->thread_priority, 0, 6);
ini_get_d(inifile, INI_FADE_TIME, defaults->fade_time, &settings->fade_time);
ini_get_d(inifile, INI_FADE_DELAY, defaults->fade_delay, &settings->fade_delay);
ini_get_d(inifile, INI_LOOP_COUNT, defaults->loop_count, &settings->loop_count);
ini_get_b(inifile, INI_LOOP_FOREVER, defaults->loop_forever, &settings->loop_forever);
ini_get_b(inifile, INI_IGNORE_LOOP, defaults->ignore_loop, &settings->ignore_loop);
ini_get_b(inifile, INI_DISABLE_SUBSONGS, defaults->disable_subsongs, &settings->disable_subsongs);
ini_get_i(inifile, INI_DOWNMIX_CHANNELS, defaults->downmix_channels, &settings->downmix_channels, 0, 64);
ini_get_b(inifile, INI_TAGFILE_DISABLE, defaults->tagfile_disable, &settings->tagfile_disable);
ini_get_b(inifile, INI_FORCE_TITLE, defaults->force_title, &settings->force_title);
ini_get_b(inifile, INI_EXTS_UNKNOWN_ON, defaults->exts_unknown_on, &settings->exts_unknown_on);
ini_get_b(inifile, INI_EXTS_COMMON_ON, defaults->exts_common_on, &settings->exts_common_on);
ini_get_i(inifile, INI_GAIN_TYPE, defaults->gain_type, (int*)&settings->gain_type, 0, 3);
ini_get_i(inifile, INI_CLIP_TYPE, defaults->clip_type, (int*)&settings->clip_type, 0, 3);
if (settings->loop_forever && settings->ignore_loop)
settings->ignore_loop = 0;
}
static void save_config(In_Module* input_module, winamp_settings_t* settings) {
TCHAR inifile[PATH_LIMIT];
ini_get_filename(input_module, inifile);
ini_set_i(inifile, INI_THREAD_PRIORITY, settings->thread_priority);
ini_set_d(inifile, INI_FADE_TIME, settings->fade_time);
ini_set_d(inifile, INI_FADE_DELAY, settings->fade_delay);
ini_set_d(inifile, INI_LOOP_COUNT, settings->loop_count);
ini_set_b(inifile, INI_LOOP_FOREVER, settings->loop_forever);
ini_set_b(inifile, INI_IGNORE_LOOP, settings->ignore_loop);
ini_set_b(inifile, INI_DISABLE_SUBSONGS, settings->disable_subsongs);
ini_set_i(inifile, INI_DOWNMIX_CHANNELS, settings->downmix_channels);
ini_set_b(inifile, INI_TAGFILE_DISABLE, settings->tagfile_disable);
ini_set_b(inifile, INI_FORCE_TITLE, settings->force_title);
ini_set_b(inifile, INI_EXTS_UNKNOWN_ON, settings->exts_unknown_on);
ini_set_b(inifile, INI_EXTS_COMMON_ON, settings->exts_common_on);
ini_set_i(inifile, INI_GAIN_TYPE, settings->gain_type);
ini_set_i(inifile, INI_CLIP_TYPE, settings->clip_type);
}
static void dlg_input_set_d(HWND hDlg, int idc, double val) {
TCHAR buf[256];
cfg_sprintf(buf, TEXT("%.2lf"), val);
SetDlgItemText(hDlg, idc, buf);
}
static void dlg_input_set_i(HWND hDlg, int idc, int val) {
TCHAR buf[32];
cfg_sprintf(buf, TEXT("%d"), val);
SetDlgItemText(hDlg, idc, buf);
}
static void dlg_check_set(HWND hDlg, int idc, int val) {
CheckDlgButton(hDlg, idc, val ? BST_CHECKED : BST_UNCHECKED);
}
static void cfg_combo_set(HWND hDlg, int idc, int val, TCHAR **list, int list_size) {
int i;
HANDLE hCombo = GetDlgItem(hDlg, idc);
for (i = 0; i < list_size; i++) {
SendMessage(hCombo, CB_ADDSTRING, 0, (LPARAM)list[i]);
}
SendMessage(hCombo, CB_SETCURSEL, val, 0);
}
static void cfg_slider_set(HWND hDlg, int idc1, int idc2, int val, int min, int max, TCHAR **list) {
HANDLE hSlider = GetDlgItem(hDlg, idc1);
SendMessage(hSlider, TBM_SETRANGE, (WPARAM)TRUE, (LPARAM)MAKELONG(min, max));
SendMessage(hSlider, TBM_SETPOS, (WPARAM)TRUE, (LPARAM)val+1);
SetDlgItemText(hDlg, idc2, list[val]);
}
static void dlg_input_get_d(HWND hDlg, int idc, double *p_val, LPCTSTR error, int *p_err) {
TCHAR buf[256];
int res, consumed;
double defval = *p_val;
GetDlgItemText(hDlg, idc, buf, 256);
res = cfg_sscanf(buf, TEXT("%lf%n"), p_val, &consumed);
if (res < 1 || consumed != cfg_strlen(buf) || *p_val < 0) {
MessageBox(hDlg, error, NULL, MB_OK|MB_ICONERROR);
*p_val = defval;
*p_err = 1;
}
}
static void dlg_input_get_i(HWND hDlg, int idc, int *p_val, LPCTSTR error, int *p_err) {
TCHAR buf[32];
int res, consumed;
int defval = *p_val;
GetDlgItemText(hDlg, idc, buf, 32);
res = cfg_sscanf(buf, TEXT("%d%n"), p_val, &consumed);
if (res < 1 || consumed != cfg_strlen(buf) || *p_val < 0) {
MessageBox(hDlg, error, NULL, MB_OK|MB_ICONERROR);
*p_val = defval;
*p_err = 1;
}
}
static void dlg_check_get(HWND hDlg, int idc, int *p_val) {
*p_val = (IsDlgButtonChecked(hDlg, idc) == BST_CHECKED);
}
static void dlg_combo_get(HWND hDlg, int idc, int *p_val) {
*p_val = SendMessage(GetDlgItem(hDlg, idc), CB_GETCURSEL, 0, 0);
}
static int dlg_load_form(HWND hDlg, winamp_settings_t* settings) {
int err = 0;
dlg_input_get_d(hDlg, IDC_FADE_TIME, &settings->fade_time, TEXT("Fade Length must be a positive number"), &err);
dlg_input_get_d(hDlg, IDC_FADE_DELAY, &settings->fade_delay, TEXT("Fade Delay must be a positive number"), &err);
dlg_input_get_d(hDlg, IDC_LOOP_COUNT, &settings->loop_count, TEXT("Loop Count must be a positive number"), &err);
dlg_check_get(hDlg, IDC_LOOP_FOREVER, &settings->loop_forever);
dlg_check_get(hDlg, IDC_IGNORE_LOOP, &settings->ignore_loop);
dlg_check_get(hDlg, IDC_DISABLE_SUBSONGS, &settings->disable_subsongs);
dlg_input_get_i(hDlg, IDC_DOWNMIX_CHANNELS, &settings->downmix_channels, TEXT("Downmix must be a positive integer number"), &err);
dlg_check_get(hDlg, IDC_TAGFILE_DISABLE, &settings->tagfile_disable);
dlg_check_get(hDlg, IDC_FORCE_TITLE, &settings->force_title);
dlg_check_get(hDlg, IDC_EXTS_UNKNOWN_ON, &settings->exts_unknown_on);
dlg_check_get(hDlg, IDC_EXTS_COMMON_ON, &settings->exts_common_on);
dlg_combo_get(hDlg, IDC_GAIN_TYPE, (int*)&settings->gain_type);
dlg_combo_get(hDlg, IDC_CLIP_TYPE, (int*)&settings->clip_type);
return err ? 0 : 1;
}
static void dlg_save_form(HWND hDlg, winamp_settings_t* settings, int reset) {
cfg_slider_set(hDlg, IDC_THREAD_PRIORITY_SLIDER, IDC_THREAD_PRIORITY_TEXT, settings->thread_priority, 1, 7, dlg_priority_strings);
dlg_input_set_d(hDlg, IDC_FADE_TIME, settings->fade_time);
dlg_input_set_d(hDlg, IDC_FADE_DELAY, settings->fade_delay);
dlg_input_set_d(hDlg, IDC_LOOP_COUNT, settings->loop_count);
dlg_check_set(hDlg, IDC_LOOP_FOREVER, settings->loop_forever);
dlg_check_set(hDlg, IDC_IGNORE_LOOP, settings->ignore_loop);
dlg_check_set(hDlg, IDC_LOOP_NORMALLY, (!settings->loop_forever && !settings->ignore_loop));
dlg_check_set(hDlg, IDC_DISABLE_SUBSONGS, settings->disable_subsongs);
dlg_input_set_i(hDlg, IDC_DOWNMIX_CHANNELS, settings->downmix_channels);
dlg_check_set(hDlg, IDC_TAGFILE_DISABLE, settings->tagfile_disable);
dlg_check_set(hDlg, IDC_FORCE_TITLE, settings->force_title);
dlg_check_set(hDlg, IDC_EXTS_UNKNOWN_ON, settings->exts_unknown_on);
dlg_check_set(hDlg, IDC_EXTS_COMMON_ON, settings->exts_common_on);
cfg_combo_set(hDlg, IDC_GAIN_TYPE, settings->gain_type, dlg_replaygain_strings, (reset ? 0 : 3));
cfg_combo_set(hDlg, IDC_CLIP_TYPE, settings->clip_type, dlg_replaygain_strings, (reset ? 0 : 3));
}
/* config dialog handler */
INT_PTR CALLBACK configDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
static int priority;
/* globals to get them a bit controlled */
winamp_settings_t* settings_ext = &settings;
In_Module* input_module_ext = &input_module;
switch (uMsg) {
case WM_CLOSE: /* hide dialog */
EndDialog(hDlg,TRUE);
return TRUE;
case WM_INITDIALOG: /* open dialog: load form with current settings */
priority = settings_ext->thread_priority;
dlg_save_form(hDlg, settings_ext, 0);
break;
case WM_COMMAND: /* button presses */
switch (GET_WM_COMMAND_ID(wParam, lParam)) {
case IDOK: { /* read and verify new values, save and close */
int ok;
settings_ext->thread_priority = priority;
ok = dlg_load_form(hDlg, settings_ext);
if (!ok) break; /* this leaves values changed though */
save_config(input_module_ext, settings_ext);
EndDialog(hDlg,TRUE);
break;
}
case IDCANCEL: /* cancel dialog */
EndDialog(hDlg,TRUE);
break;
case IDC_DEFAULT_BUTTON: { /* reset values */
priority = defaults.thread_priority;
dlg_save_form(hDlg, &defaults, 1);
/* we don't save settings here as user can still cancel the dialog */
break;
}
default:
return FALSE;
}
return FALSE;
case WM_HSCROLL: /* priority scroll */
if ((struct HWND__*)lParam == GetDlgItem(hDlg, IDC_THREAD_PRIORITY_SLIDER)) {
if (LOWORD(wParam) == TB_THUMBPOSITION || LOWORD(wParam) == TB_THUMBTRACK) {
priority = HIWORD(wParam)-1;
}
else {
priority = SendMessage(GetDlgItem(hDlg,IDC_THREAD_PRIORITY_SLIDER),TBM_GETPOS,0,0)-1;
}
SetDlgItemText(hDlg, IDC_THREAD_PRIORITY_TEXT, dlg_priority_strings[priority]);
}
break;
default:
return FALSE;
}
return TRUE;
}

155
winamp/in_streamfile.c Normal file
View File

@ -0,0 +1,155 @@
/**
* streamfile for Winamp
*/
#include <stdio.h>
#include <io.h>
#include "in_vgmstream.h"
/* ************************************* */
/* IN_UNICODE */
/* ************************************* */
/* opens a utf16 (unicode) path */
static FILE* wa_fopen(const in_char *wpath) {
#ifdef UNICODE_INPUT_PLUGIN
return _wfopen(wpath,L"rb");
#else
return fopen(wpath,"rb");
#endif
}
/* dupes a utf16 (unicode) file */
static FILE* wa_fdopen(int fd) {
#ifdef UNICODE_INPUT_PLUGIN
return _wfdopen(fd,L"rb");
#else
return fdopen(fd,"rb");
#endif
}
/* ************************************* */
/* IN_STREAMFILE */
/* ************************************* */
/* a STREAMFILE that operates via STDIOSTREAMFILE but handles Winamp's unicode (in_char) paths */
typedef struct {
STREAMFILE sf;
STREAMFILE *stdiosf;
FILE *infile_ref; /* pointer to the infile in stdiosf (partially handled by stdiosf) */
} WINAMP_STREAMFILE;
static STREAMFILE *open_winamp_streamfile_by_file(FILE *infile, const char * path);
//static STREAMFILE *open_winamp_streamfile_by_ipath(const in_char *wpath);
static size_t wasf_read(WINAMP_STREAMFILE* sf, uint8_t* dest, off_t offset, size_t length) {
return sf->stdiosf->read(sf->stdiosf, dest, offset, length);
}
static off_t wasf_get_size(WINAMP_STREAMFILE* sf) {
return sf->stdiosf->get_size(sf->stdiosf);
}
static off_t wasf_get_offset(WINAMP_STREAMFILE* sf) {
return sf->stdiosf->get_offset(sf->stdiosf);
}
static void wasf_get_name(WINAMP_STREAMFILE* sf, char* buffer, size_t length) {
sf->stdiosf->get_name(sf->stdiosf, buffer, length);
}
static STREAMFILE *wasf_open(WINAMP_STREAMFILE* sf, const char* const filename, size_t buffersize) {
in_char wpath[PATH_LIMIT];
char name[PATH_LIMIT];
if (!filename)
return NULL;
#if !defined (__ANDROID__) && !defined (_MSC_VER)
/* When enabling this for MSVC it'll seemingly work, but there are issues possibly related to underlying
* IO buffers when using dup(), noticeable by re-opening the same streamfile with small buffer sizes
* (reads garbage). This reportedly causes issues in Android too */
sf->stdiosf->get_name(sf->stdiosf, name, PATH_LIMIT);
/* if same name, duplicate the file descriptor we already have open */ //unsure if all this is needed
if (sf->infile_ref && !strcmp(name,filename)) {
int new_fd;
FILE *new_file;
if (((new_fd = dup(fileno(sf->infile_ref))) >= 0) && (new_file = wa_fdopen(new_fd))) {
STREAMFILE *new_sf = open_winamp_streamfile_by_file(new_file, filename);
if (new_sf)
return new_sf;
fclose(new_file);
}
if (new_fd >= 0 && !new_file)
close(new_fd); /* fdopen may fail when opening too many files */
/* on failure just close and try the default path (which will probably fail a second time) */
}
#endif
/* STREAMFILEs carry char/UTF8 names, convert to wchar for Winamp */
wa_char_to_ichar(wpath, PATH_LIMIT, filename);
return open_winamp_streamfile_by_ipath(wpath);
}
static void wasf_close(WINAMP_STREAMFILE* sf) {
/* closes infile_ref + frees in the internal STDIOSTREAMFILE (fclose for wchar is not needed) */
sf->stdiosf->close(sf->stdiosf);
free(sf); /* and the current struct */
}
static STREAMFILE *open_winamp_streamfile_by_file(FILE* file, const char* path) {
WINAMP_STREAMFILE* this_sf = NULL;
STREAMFILE* stdiosf = NULL;
this_sf = calloc(1,sizeof(WINAMP_STREAMFILE));
if (!this_sf) goto fail;
stdiosf = open_stdio_streamfile_by_file(file, path);
if (!stdiosf) goto fail;
this_sf->sf.read = (void*)wasf_read;
this_sf->sf.get_size = (void*)wasf_get_size;
this_sf->sf.get_offset = (void*)wasf_get_offset;
this_sf->sf.get_name = (void*)wasf_get_name;
this_sf->sf.open = (void*)wasf_open;
this_sf->sf.close = (void*)wasf_close;
this_sf->stdiosf = stdiosf;
this_sf->infile_ref = file;
return &this_sf->sf; /* pointer to STREAMFILE start = rest of the custom data follows */
fail:
close_streamfile(stdiosf);
free(this_sf);
return NULL;
}
STREAMFILE* open_winamp_streamfile_by_ipath(const in_char* wpath) {
FILE* infile = NULL;
STREAMFILE* sf;
char path[PATH_LIMIT];
/* convert to UTF-8 if needed for internal use */
wa_ichar_to_char(path,PATH_LIMIT, wpath);
/* open a FILE from a Winamp (possibly UTF-16) path */
infile = wa_fopen(wpath);
if (!infile) {
/* allow non-existing files in some cases */
if (!vgmstream_is_virtual_filename(path))
return NULL;
}
sf = open_winamp_streamfile_by_file(infile,path);
if (!sf) {
if (infile) fclose(infile);
}
return sf;
}

View File

@ -1,33 +1,7 @@
/** /**
* vgmstream for Winamp * vgmstream for Winamp
*/ */
#include "in_vgmstream.h"
/* Normally Winamp opens unicode files by their DOS 8.3 name. #define this to use wchar_t filenames,
* which must be opened with _wfopen in a WINAMP_STREAMFILE (needed for dual files like .pos).
* Only for Winamp paths, other parts would need #define UNICODE for Windows. */
#ifdef VGM_WINAMP_UNICODE
#define UNICODE_INPUT_PLUGIN
#endif
#ifdef _MSC_VER
#define _CRT_SECURE_NO_DEPRECATE
#endif
#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
#include <stdio.h>
#include <io.h>
#include <string.h>
#include <ctype.h>
#include "../src/vgmstream.h"
#include "../src/plugins.h"
#include "sdk/in2.h"
#include "sdk/wa_ipc.h"
#include "sdk/ipc_pe.h"
#include "resource.h"
#ifndef VERSION #ifndef VERSION
#include "version.h" #include "version.h"
@ -39,7 +13,9 @@
#define PLUGIN_DESCRIPTION "vgmstream plugin " VERSION " " __DATE__ #define PLUGIN_DESCRIPTION "vgmstream plugin " VERSION " " __DATE__
/* ************************************* */ /* ***************************************** */
/* IN_STATE */
/* ***************************************** */
#define EXT_BUFFER_SIZE 200 #define EXT_BUFFER_SIZE 200
@ -52,34 +28,6 @@ DWORD WINAPI __stdcall decode(void *arg);
/* fixed list to simplify but could also malloc/free on init/close */ /* fixed list to simplify but could also malloc/free on init/close */
char working_extension_list[EXTENSION_LIST_SIZE] = {0}; char working_extension_list[EXTENSION_LIST_SIZE] = {0};
typedef enum {
REPLAYGAIN_NONE,
REPLAYGAIN_ALBUM,
REPLAYGAIN_TRACK
} replay_gain_type_t;
/* loaded settings */
typedef struct {
int thread_priority;
double fade_time;
double fade_delay;
double loop_count;
int ignore_loop;
int loop_forever;
int disable_subsongs;
int downmix_channels;
int tagfile_disable;
int force_title;
int exts_unknown_on;
int exts_common_on;
replay_gain_type_t gain_type;
replay_gain_type_t clip_type;
int is_xmplay;
} winamp_settings_t;
/* current play state */ /* current play state */
typedef struct { typedef struct {
@ -110,223 +58,9 @@ winamp_state_t state;
short sample_buffer[SAMPLE_BUFFER_SIZE*2 * VGMSTREAM_MAX_CHANNELS]; //todo maybe should be dynamic short sample_buffer[SAMPLE_BUFFER_SIZE*2 * VGMSTREAM_MAX_CHANNELS]; //todo maybe should be dynamic
/* ************************************* */ /* ***************************************** */
/* IN_UNICODE */ /* IN_VGMSTREAM UTILS */
/* ************************************* */ /* ***************************************** */
//todo safe ops
//todo there must be a better way to handle unicode...
#ifdef UNICODE_INPUT_PLUGIN
#define wa_strcmp wcscmp
#define wa_strcpy wcscpy
#define wa_strncpy wcsncpy
#define wa_strcat wcscat
#define wa_strlen wcslen
#define wa_strchr wcschr
#define wa_sscanf swscanf
#define wa_snprintf _snwprintf
#define wa_strrchr wcsrchr
#define wa_fileinfo fileinfoW
#define wa_IPC_PE_INSERTFILENAME IPC_PE_INSERTFILENAMEW
#define wa_L(x) L ##x
#else
#define wa_strcmp strcmp
#define wa_strcpy strcpy
#define wa_strncpy strncpy
#define wa_strcat strcat
#define wa_strlen strlen
#define wa_strchr strchr
#define wa_sscanf sscanf
#define wa_snprintf snprintf
#define wa_strrchr strrchr
#define wa_fileinfo fileinfo
#define wa_IPC_PE_INSERTFILENAME IPC_PE_INSERTFILENAME
#define wa_L(x) x
#endif
/* converts from utf16 to utf8 (if unicode is on) */
static void wa_ichar_to_char(char *dst, size_t dstsize, const in_char *wsrc) {
#ifdef UNICODE_INPUT_PLUGIN
/* converto to UTF8 codepage, default separate bytes, source wstr, wstr length */
//int size_needed = WideCharToMultiByte(CP_UTF8,0, src,-1, NULL,0, NULL, NULL);
WideCharToMultiByte(CP_UTF8,0, wsrc,-1, dst,dstsize, NULL, NULL);
#else
strncpy(dst, wsrc, dstsize);
dst[dstsize - 1] = '\0';
#endif
}
/* converts from utf8 to utf16 (if unicode is on) */
static void wa_char_to_ichar(in_char *wdst, size_t wdstsize, const char *src) {
#ifdef UNICODE_INPUT_PLUGIN
//int size_needed = MultiByteToWideChar(CP_UTF8,0, src,-1, NULL,0);
MultiByteToWideChar(CP_UTF8,0, src,-1, wdst,wdstsize);
#else
strncpy(wdst, src, wdstsize);
wdst[wdstsize - 1] = '\0';
#endif
}
/* copies from utf16 to utf16 (if unicode is active) */
static void wa_wchar_to_ichar(in_char *wdst, size_t wdstsize, const wchar_t *src) {
#ifdef UNICODE_INPUT_PLUGIN
wcscpy(wdst,src);
#else
strcpy(wdst,src); //todo ???
#endif
}
/* copies from utf16 to utf16 */
static void wa_char_to_wchar(wchar_t *wdst, size_t wdstsize, const char *src) {
#ifdef UNICODE_INPUT_PLUGIN
MultiByteToWideChar(CP_UTF8,0, src,-1, wdst,wdstsize);
#else
strcpy(wdst,src); //todo ???
#endif
}
/* opens a utf16 (unicode) path */
static FILE* wa_fopen(const in_char *wpath) {
#ifdef UNICODE_INPUT_PLUGIN
return _wfopen(wpath,L"rb");
#else
return fopen(wpath,"rb");
#endif
}
/* dupes a utf16 (unicode) file */
static FILE* wa_fdopen(int fd) {
#ifdef UNICODE_INPUT_PLUGIN
return _wfdopen(fd,L"rb");
#else
return fdopen(fd,"rb");
#endif
}
/* ************************************* */
/* IN_STREAMFILE */
/* ************************************* */
/* a STREAMFILE that operates via STDIOSTREAMFILE but handles Winamp's unicode (in_char) paths */
typedef struct {
STREAMFILE sf;
STREAMFILE *stdiosf;
FILE *infile_ref; /* pointer to the infile in stdiosf (partially handled by stdiosf) */
} WINAMP_STREAMFILE;
static STREAMFILE *open_winamp_streamfile_by_file(FILE *infile, const char * path);
static STREAMFILE *open_winamp_streamfile_by_ipath(const in_char *wpath);
static size_t wasf_read(WINAMP_STREAMFILE* sf, uint8_t* dest, off_t offset, size_t length) {
return sf->stdiosf->read(sf->stdiosf, dest, offset, length);
}
static off_t wasf_get_size(WINAMP_STREAMFILE* sf) {
return sf->stdiosf->get_size(sf->stdiosf);
}
static off_t wasf_get_offset(WINAMP_STREAMFILE* sf) {
return sf->stdiosf->get_offset(sf->stdiosf);
}
static void wasf_get_name(WINAMP_STREAMFILE* sf, char* buffer, size_t length) {
sf->stdiosf->get_name(sf->stdiosf, buffer, length);
}
static STREAMFILE *wasf_open(WINAMP_STREAMFILE* sf, const char* const filename, size_t buffersize) {
in_char wpath[PATH_LIMIT];
char name[PATH_LIMIT];
if (!filename)
return NULL;
#if !defined (__ANDROID__) && !defined (_MSC_VER)
/* When enabling this for MSVC it'll seemingly work, but there are issues possibly related to underlying
* IO buffers when using dup(), noticeable by re-opening the same streamfile with small buffer sizes
* (reads garbage). This reportedly causes issues in Android too */
sf->stdiosf->get_name(sf->stdiosf, name, PATH_LIMIT);
/* if same name, duplicate the file descriptor we already have open */ //unsure if all this is needed
if (sf->infile_ref && !strcmp(name,filename)) {
int new_fd;
FILE *new_file;
if (((new_fd = dup(fileno(sf->infile_ref))) >= 0) && (new_file = wa_fdopen(new_fd))) {
STREAMFILE *new_sf = open_winamp_streamfile_by_file(new_file, filename);
if (new_sf)
return new_sf;
fclose(new_file);
}
if (new_fd >= 0 && !new_file)
close(new_fd); /* fdopen may fail when opening too many files */
/* on failure just close and try the default path (which will probably fail a second time) */
}
#endif
/* STREAMFILEs carry char/UTF8 names, convert to wchar for Winamp */
wa_char_to_ichar(wpath, PATH_LIMIT, filename);
return open_winamp_streamfile_by_ipath(wpath);
}
static void wasf_close(WINAMP_STREAMFILE* sf) {
/* closes infile_ref + frees in the internal STDIOSTREAMFILE (fclose for wchar is not needed) */
sf->stdiosf->close(sf->stdiosf);
free(sf); /* and the current struct */
}
static STREAMFILE *open_winamp_streamfile_by_file(FILE* file, const char* path) {
WINAMP_STREAMFILE* this_sf = NULL;
STREAMFILE* stdiosf = NULL;
this_sf = calloc(1,sizeof(WINAMP_STREAMFILE));
if (!this_sf) goto fail;
stdiosf = open_stdio_streamfile_by_file(file, path);
if (!stdiosf) goto fail;
this_sf->sf.read = (void*)wasf_read;
this_sf->sf.get_size = (void*)wasf_get_size;
this_sf->sf.get_offset = (void*)wasf_get_offset;
this_sf->sf.get_name = (void*)wasf_get_name;
this_sf->sf.open = (void*)wasf_open;
this_sf->sf.close = (void*)wasf_close;
this_sf->stdiosf = stdiosf;
this_sf->infile_ref = file;
return &this_sf->sf; /* pointer to STREAMFILE start = rest of the custom data follows */
fail:
close_streamfile(stdiosf);
free(this_sf);
return NULL;
}
static STREAMFILE* open_winamp_streamfile_by_ipath(const in_char* wpath) {
FILE* infile = NULL;
STREAMFILE* sf;
char path[PATH_LIMIT];
/* convert to UTF-8 if needed for internal use */
wa_ichar_to_char(path,PATH_LIMIT, wpath);
/* open a FILE from a Winamp (possibly UTF-16) path */
infile = wa_fopen(wpath);
if (!infile) {
/* allow non-existing files in some cases */
if (!vgmstream_is_virtual_filename(path))
return NULL;
}
sf = open_winamp_streamfile_by_file(infile,path);
if (!sf) {
if (infile) fclose(infile);
}
return sf;
}
/* opens vgmstream for winamp */ /* opens vgmstream for winamp */
static VGMSTREAM* init_vgmstream_winamp(const in_char* fn, int stream_index) { static VGMSTREAM* init_vgmstream_winamp(const in_char* fn, int stream_index) {
@ -345,411 +79,15 @@ static VGMSTREAM* init_vgmstream_winamp(const in_char* fn, int stream_index) {
return vgmstream; return vgmstream;
} }
/* ************************************* */
/* IN_CONFIG */
/* ************************************* */
//todo snprintf
/* Windows unicode, separate from Winamp's unicode flag */
#ifdef UNICODE
#define cfg_strncpy wcsncpy
#define cfg_strncat wcsncat
#define cfg_sprintf _swprintf
#define cfg_sscanf swscanf
#define cfg_strlen wcslen
#define cfg_strrchr wcsrchr
#else
#define cfg_strncpy strncpy
#define cfg_strncat strncat
#define cfg_sprintf sprintf
#define cfg_sscanf sscanf
#define cfg_strlen strlen
#define cfg_strrchr strrchr
#endif
/* converts from utf8 to utf16 (if unicode is active) */
static void cfg_char_to_wchar(TCHAR *wdst, size_t wdstsize, const char *src) {
#ifdef UNICODE
//int size_needed = MultiByteToWideChar(CP_UTF8,0, src,-1, NULL,0);
MultiByteToWideChar(CP_UTF8,0, src,-1, wdst,wdstsize);
#else
strcpy(wdst,src);
#endif
}
/* config */
#define CONFIG_APP_NAME TEXT("vgmstream plugin")
#define CONFIG_INI_NAME TEXT("plugin.ini")
#define INI_FADE_TIME TEXT("fade_seconds")
#define INI_FADE_DELAY TEXT("fade_delay")
#define INI_LOOP_COUNT TEXT("loop_count")
#define INI_THREAD_PRIORITY TEXT("thread_priority")
#define INI_LOOP_FOREVER TEXT("loop_forever")
#define INI_IGNORE_LOOP TEXT("ignore_loop")
#define INI_DISABLE_SUBSONGS TEXT("disable_subsongs")
#define INI_DOWNMIX_CHANNELS TEXT("downmix_channels")
#define INI_TAGFILE_DISABLE TEXT("tagfile_disable")
#define INI_FORCE_TITLE TEXT("force_title")
#define INI_EXTS_UNKNOWN_ON TEXT("exts_unknown_on")
#define INI_EXTS_COMMON_ON TEXT("exts_common_on")
#define INI_GAIN_TYPE TEXT("gain_type")
#define INI_CLIP_TYPE TEXT("clip_type")
TCHAR *dlg_priority_strings[] = {
TEXT("Idle"),
TEXT("Lowest"),
TEXT("Below Normal"),
TEXT("Normal"),
TEXT("Above Normal"),
TEXT("Highest (not recommended)"),
TEXT("Time Critical (not recommended)")
};
TCHAR *dlg_replaygain_strings[] = {
TEXT("None"),
TEXT("Album"),
TEXT("Peak")
};
int priority_values[] = {
THREAD_PRIORITY_IDLE,
THREAD_PRIORITY_LOWEST,
THREAD_PRIORITY_BELOW_NORMAL,
THREAD_PRIORITY_NORMAL,
THREAD_PRIORITY_ABOVE_NORMAL,
THREAD_PRIORITY_HIGHEST,
THREAD_PRIORITY_TIME_CRITICAL
};
// todo finish UNICODE (requires IPC_GETINIDIRECTORYW from later SDKs to read the ini path properly)
/* Winamp INI reader */
static void ini_get_filename(TCHAR *inifile) {
if (IsWindow(input_module.hMainWindow) && SendMessage(input_module.hMainWindow, WM_WA_IPC,0,IPC_GETVERSION) >= 0x5000) {
/* newer Winamp with per-user settings */
TCHAR *ini_dir = (TCHAR *)SendMessage(input_module.hMainWindow, WM_WA_IPC, 0, IPC_GETINIDIRECTORY);
cfg_strncpy(inifile, ini_dir, PATH_LIMIT);
cfg_strncat(inifile, TEXT("\\Plugins\\"), PATH_LIMIT);
/* can't be certain that \Plugins already exists in the user dir */
CreateDirectory(inifile,NULL);
cfg_strncat(inifile, CONFIG_INI_NAME, PATH_LIMIT);
}
else {
/* older winamp with single settings */
TCHAR *lastSlash;
GetModuleFileName(NULL, inifile, PATH_LIMIT);
lastSlash = cfg_strrchr(inifile, TEXT('\\'));
*(lastSlash + 1) = 0;
/* XMPlay doesn't have a "plugins" subfolder */
if (settings.is_xmplay)
cfg_strncat(inifile, CONFIG_INI_NAME,PATH_LIMIT);
else
cfg_strncat(inifile, TEXT("Plugins\\") CONFIG_INI_NAME,PATH_LIMIT);
/* Maybe should query IPC_GETINIDIRECTORY and use that, not sure what ancient Winamps need.
* There must be some proper way to handle dirs since other Winamp plugins save config in
* XMPlay correctly (this feels like archaeology, try later) */
}
}
static void ini_get_d(const char *inifile, const char *entry, double defval, double *p_val) {
TCHAR buf[256];
TCHAR defbuf[256];
int consumed, res;
cfg_sprintf(defbuf, TEXT("%.2lf"), defval);
GetPrivateProfileString(CONFIG_APP_NAME, entry, defbuf, buf, 256, inifile);
res = cfg_sscanf(buf, TEXT("%lf%n"), p_val, &consumed);
if (res < 1 || consumed != cfg_strlen(buf) || *p_val < 0) {
*p_val = defval;
}
}
static void ini_get_i(const char *inifile, const char *entry, int defval, int *p_val, int min, int max) {
*p_val = GetPrivateProfileInt(CONFIG_APP_NAME, entry, defval, inifile);
if (*p_val < min || *p_val > max) {
*p_val = defval;
}
}
static void ini_get_b(const char *inifile, const char *entry, int defval, int *p_val) {
ini_get_i(inifile, entry, defval, p_val, 0, 1);
}
static void ini_set_d(const char *inifile, const char *entry, double val) {
TCHAR buf[256];
cfg_sprintf(buf, TEXT("%.2lf"), val);
WritePrivateProfileString(CONFIG_APP_NAME, entry, buf, inifile);
}
static void ini_set_i(const char *inifile, const char *entry, int val) {
TCHAR buf[32];
cfg_sprintf(buf, TEXT("%d"), val);
WritePrivateProfileString(CONFIG_APP_NAME, entry, buf, inifile);
}
static void ini_set_b(const char *inifile, const char *entry, int val) {
ini_set_i(inifile, entry, val);
}
static void load_defaults(winamp_settings_t* defaults) {
defaults->thread_priority = 3;
defaults->fade_time = 10.0;
defaults->fade_delay = 0.0;
defaults->loop_count = 2.0;
defaults->loop_forever = 0;
defaults->ignore_loop = 0;
defaults->disable_subsongs = 0;
defaults->downmix_channels = 0;
defaults->tagfile_disable = 0;
defaults->force_title = 0;
defaults->exts_unknown_on = 0;
defaults->exts_common_on = 0;
defaults->gain_type = 1;
defaults->clip_type = 2;
}
static void load_config(winamp_settings_t* settings, winamp_settings_t* defaults) {
TCHAR inifile[PATH_LIMIT];
ini_get_filename(inifile);
ini_get_i(inifile, INI_THREAD_PRIORITY, defaults->thread_priority, &settings->thread_priority, 0, 6);
ini_get_d(inifile, INI_FADE_TIME, defaults->fade_time, &settings->fade_time);
ini_get_d(inifile, INI_FADE_DELAY, defaults->fade_delay, &settings->fade_delay);
ini_get_d(inifile, INI_LOOP_COUNT, defaults->loop_count, &settings->loop_count);
ini_get_b(inifile, INI_LOOP_FOREVER, defaults->loop_forever, &settings->loop_forever);
ini_get_b(inifile, INI_IGNORE_LOOP, defaults->ignore_loop, &settings->ignore_loop);
ini_get_b(inifile, INI_DISABLE_SUBSONGS, defaults->disable_subsongs, &settings->disable_subsongs);
ini_get_i(inifile, INI_DOWNMIX_CHANNELS, defaults->downmix_channels, &settings->downmix_channels, 0, 64);
ini_get_b(inifile, INI_TAGFILE_DISABLE, defaults->tagfile_disable, &settings->tagfile_disable);
ini_get_b(inifile, INI_FORCE_TITLE, defaults->force_title, &settings->force_title);
ini_get_b(inifile, INI_EXTS_UNKNOWN_ON, defaults->exts_unknown_on, &settings->exts_unknown_on);
ini_get_b(inifile, INI_EXTS_COMMON_ON, defaults->exts_common_on, &settings->exts_common_on);
ini_get_i(inifile, INI_GAIN_TYPE, defaults->gain_type, (int*)&settings->gain_type, 0, 3);
ini_get_i(inifile, INI_CLIP_TYPE, defaults->clip_type, (int*)&settings->clip_type, 0, 3);
if (settings->loop_forever && settings->ignore_loop)
settings->ignore_loop = 0;
}
static void save_config(winamp_settings_t* settings) {
TCHAR inifile[PATH_LIMIT];
ini_get_filename(inifile);
ini_set_i(inifile, INI_THREAD_PRIORITY, settings->thread_priority);
ini_set_d(inifile, INI_FADE_TIME, settings->fade_time);
ini_set_d(inifile, INI_FADE_DELAY, settings->fade_delay);
ini_set_d(inifile, INI_LOOP_COUNT, settings->loop_count);
ini_set_b(inifile, INI_LOOP_FOREVER, settings->loop_forever);
ini_set_b(inifile, INI_IGNORE_LOOP, settings->ignore_loop);
ini_set_b(inifile, INI_DISABLE_SUBSONGS, settings->disable_subsongs);
ini_set_i(inifile, INI_DOWNMIX_CHANNELS, settings->downmix_channels);
ini_set_b(inifile, INI_TAGFILE_DISABLE, settings->tagfile_disable);
ini_set_b(inifile, INI_FORCE_TITLE, settings->force_title);
ini_set_b(inifile, INI_EXTS_UNKNOWN_ON, settings->exts_unknown_on);
ini_set_b(inifile, INI_EXTS_COMMON_ON, settings->exts_common_on);
ini_set_i(inifile, INI_GAIN_TYPE, settings->gain_type);
ini_set_i(inifile, INI_CLIP_TYPE, settings->clip_type);
}
static void dlg_input_set_d(HWND hDlg, int idc, double val) {
TCHAR buf[256];
cfg_sprintf(buf, TEXT("%.2lf"), val);
SetDlgItemText(hDlg, idc, buf);
}
static void dlg_input_set_i(HWND hDlg, int idc, int val) {
TCHAR buf[32];
cfg_sprintf(buf, TEXT("%d"), val);
SetDlgItemText(hDlg, idc, buf);
}
static void dlg_check_set(HWND hDlg, int idc, int val) {
CheckDlgButton(hDlg, idc, val ? BST_CHECKED : BST_UNCHECKED);
}
static void cfg_combo_set(HWND hDlg, int idc, int val, TCHAR **list, int list_size) {
int i;
HANDLE hCombo = GetDlgItem(hDlg, idc);
for (i = 0; i < list_size; i++) {
SendMessage(hCombo, CB_ADDSTRING, 0, (LPARAM)list[i]);
}
SendMessage(hCombo, CB_SETCURSEL, val, 0);
}
static void cfg_slider_set(HWND hDlg, int idc1, int idc2, int val, int min, int max, TCHAR **list) {
HANDLE hSlider = GetDlgItem(hDlg, idc1);
SendMessage(hSlider, TBM_SETRANGE, (WPARAM)TRUE, (LPARAM)MAKELONG(min, max));
SendMessage(hSlider, TBM_SETPOS, (WPARAM)TRUE, (LPARAM)val+1);
SetDlgItemText(hDlg, idc2, list[val]);
}
static void dlg_input_get_d(HWND hDlg, int idc, double *p_val, LPCTSTR error, int *p_err) {
TCHAR buf[256];
int res, consumed;
double defval = *p_val;
GetDlgItemText(hDlg, idc, buf, 256);
res = cfg_sscanf(buf, TEXT("%lf%n"), p_val, &consumed);
if (res < 1 || consumed != cfg_strlen(buf) || *p_val < 0) {
MessageBox(hDlg, error, NULL, MB_OK|MB_ICONERROR);
*p_val = defval;
*p_err = 1;
}
}
static void dlg_input_get_i(HWND hDlg, int idc, int *p_val, LPCTSTR error, int *p_err) {
TCHAR buf[32];
int res, consumed;
int defval = *p_val;
GetDlgItemText(hDlg, idc, buf, 32);
res = cfg_sscanf(buf, TEXT("%d%n"), p_val, &consumed);
if (res < 1 || consumed != cfg_strlen(buf) || *p_val < 0) {
MessageBox(hDlg, error, NULL, MB_OK|MB_ICONERROR);
*p_val = defval;
*p_err = 1;
}
}
static void dlg_check_get(HWND hDlg, int idc, int *p_val) {
*p_val = (IsDlgButtonChecked(hDlg, idc) == BST_CHECKED);
}
static void dlg_combo_get(HWND hDlg, int idc, int *p_val) {
*p_val = SendMessage(GetDlgItem(hDlg, idc), CB_GETCURSEL, 0, 0);
}
static int dlg_load_form(HWND hDlg, winamp_settings_t* settings) {
int err = 0;
dlg_input_get_d(hDlg, IDC_FADE_TIME, &settings->fade_time, TEXT("Fade Length must be a positive number"), &err);
dlg_input_get_d(hDlg, IDC_FADE_DELAY, &settings->fade_delay, TEXT("Fade Delay must be a positive number"), &err);
dlg_input_get_d(hDlg, IDC_LOOP_COUNT, &settings->loop_count, TEXT("Loop Count must be a positive number"), &err);
dlg_check_get(hDlg, IDC_LOOP_FOREVER, &settings->loop_forever);
dlg_check_get(hDlg, IDC_IGNORE_LOOP, &settings->ignore_loop);
dlg_check_get(hDlg, IDC_DISABLE_SUBSONGS, &settings->disable_subsongs);
dlg_input_get_i(hDlg, IDC_DOWNMIX_CHANNELS, &settings->downmix_channels, TEXT("Downmix must be a positive integer number"), &err);
dlg_check_get(hDlg, IDC_TAGFILE_DISABLE, &settings->tagfile_disable);
dlg_check_get(hDlg, IDC_FORCE_TITLE, &settings->force_title);
dlg_check_get(hDlg, IDC_EXTS_UNKNOWN_ON, &settings->exts_unknown_on);
dlg_check_get(hDlg, IDC_EXTS_COMMON_ON, &settings->exts_common_on);
dlg_combo_get(hDlg, IDC_GAIN_TYPE, (int*)&settings->gain_type);
dlg_combo_get(hDlg, IDC_CLIP_TYPE, (int*)&settings->clip_type);
return err ? 0 : 1;
}
static void dlg_save_form(HWND hDlg, winamp_settings_t* settings, int reset) {
cfg_slider_set(hDlg, IDC_THREAD_PRIORITY_SLIDER, IDC_THREAD_PRIORITY_TEXT, settings->thread_priority, 1, 7, dlg_priority_strings);
dlg_input_set_d(hDlg, IDC_FADE_TIME, settings->fade_time);
dlg_input_set_d(hDlg, IDC_FADE_DELAY, settings->fade_delay);
dlg_input_set_d(hDlg, IDC_LOOP_COUNT, settings->loop_count);
dlg_check_set(hDlg, IDC_LOOP_FOREVER, settings->loop_forever);
dlg_check_set(hDlg, IDC_IGNORE_LOOP, settings->ignore_loop);
dlg_check_set(hDlg, IDC_LOOP_NORMALLY, (!settings->loop_forever && !settings->ignore_loop));
dlg_check_set(hDlg, IDC_DISABLE_SUBSONGS, settings->disable_subsongs);
dlg_input_set_i(hDlg, IDC_DOWNMIX_CHANNELS, settings->downmix_channels);
dlg_check_set(hDlg, IDC_TAGFILE_DISABLE, settings->tagfile_disable);
dlg_check_set(hDlg, IDC_FORCE_TITLE, settings->force_title);
dlg_check_set(hDlg, IDC_EXTS_UNKNOWN_ON, settings->exts_unknown_on);
dlg_check_set(hDlg, IDC_EXTS_COMMON_ON, settings->exts_common_on);
cfg_combo_set(hDlg, IDC_GAIN_TYPE, settings->gain_type, dlg_replaygain_strings, (reset ? 0 : 3));
cfg_combo_set(hDlg, IDC_CLIP_TYPE, settings->clip_type, dlg_replaygain_strings, (reset ? 0 : 3));
}
/* config dialog handler */
INT_PTR CALLBACK configDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
static int priority;
switch (uMsg) {
case WM_CLOSE: /* hide dialog */
EndDialog(hDlg,TRUE);
return TRUE;
case WM_INITDIALOG: /* open dialog: load form with current settings */
priority = settings.thread_priority;
dlg_save_form(hDlg, &settings, 0);
break;
case WM_COMMAND: /* button presses */
switch (GET_WM_COMMAND_ID(wParam, lParam)) {
case IDOK: { /* read and verify new values, save and close */
int ok;
settings.thread_priority = priority;
ok = dlg_load_form(hDlg, &settings);
if (!ok) break; /* this leaves values changed though */
save_config(&settings);
EndDialog(hDlg,TRUE);
break;
}
case IDCANCEL: /* cancel dialog */
EndDialog(hDlg,TRUE);
break;
case IDC_DEFAULT_BUTTON: { /* reset values */
priority = defaults.thread_priority;
dlg_save_form(hDlg, &defaults, 1);
/* we don't save settings here as user can still cancel the dialog */
break;
}
default:
return FALSE;
}
return FALSE;
case WM_HSCROLL: /* priority scroll */
if ((struct HWND__*)lParam == GetDlgItem(hDlg, IDC_THREAD_PRIORITY_SLIDER)) {
if (LOWORD(wParam) == TB_THUMBPOSITION || LOWORD(wParam) == TB_THUMBTRACK) {
priority = HIWORD(wParam)-1;
}
else {
priority = SendMessage(GetDlgItem(hDlg,IDC_THREAD_PRIORITY_SLIDER),TBM_GETPOS,0,0)-1;
}
SetDlgItemText(hDlg, IDC_THREAD_PRIORITY_TEXT, dlg_priority_strings[priority]);
}
break;
default:
return FALSE;
}
return TRUE;
}
/* ***************************************** */
/* IN_VGMSTREAM UTILS */
/* ***************************************** */
/* makes a modified filename, suitable to pass parameters around */ /* makes a modified filename, suitable to pass parameters around */
static void make_fn_subsong(in_char * dst, int dst_size, const in_char * filename, int stream_index) { static void make_fn_subsong(in_char* dst, int dst_size, const in_char* filename, int stream_index) {
/* Follows "(file)(config)(ext)". Winamp needs to "see" (ext) to validate, and file goes first so relative /* Follows "(file)(config)(ext)". Winamp needs to "see" (ext) to validate, and file goes first so relative
* files work in M3Us (path is added). Protocols a la "vgmstream://(config)(file)" work but don't get full paths. */ * files work in M3Us (path is added). Protocols a la "vgmstream://(config)(file)" work but don't get full paths. */
wa_snprintf(dst,dst_size, wa_L("%s|$s=%i|.vgmstream"), filename, stream_index); wa_snprintf(dst,dst_size, wa_L("%s|$s=%i|.vgmstream"), filename, stream_index);
} }
/* unpacks the subsongs by adding entries to the playlist */ /* unpacks the subsongs by adding entries to the playlist */
static int split_subsongs(const in_char * filename, int stream_index, VGMSTREAM *vgmstream) { static int split_subsongs(const in_char* filename, int stream_index, VGMSTREAM *vgmstream) {
int i, playlist_index; int i, playlist_index;
HWND hPlaylistWindow; HWND hPlaylistWindow;
@ -796,8 +134,8 @@ static int split_subsongs(const in_char * filename, int stream_index, VGMSTREAM
} }
/* parses a modified filename ('fakename') extracting tags parameters (NULL tag for first = filename) */ /* parses a modified filename ('fakename') extracting tags parameters (NULL tag for first = filename) */
static int parse_fn_string(const in_char * fn, const in_char * tag, in_char * dst, int dst_size) { static int parse_fn_string(const in_char* fn, const in_char* tag, in_char* dst, int dst_size) {
const in_char *end = wa_strchr(fn,'|'); const in_char* end = wa_strchr(fn,'|');
if (tag==NULL) { if (tag==NULL) {
wa_strcpy(dst,fn); wa_strcpy(dst,fn);
@ -809,8 +147,8 @@ static int parse_fn_string(const in_char * fn, const in_char * tag, in_char * ds
dst[0] = '\0'; dst[0] = '\0';
return 0; return 0;
} }
static int parse_fn_int(const in_char * fn, const in_char * tag, int * num) { static int parse_fn_int(const in_char* fn, const in_char* tag, int* num) {
const in_char * start = wa_strchr(fn,'|'); const in_char* start = wa_strchr(fn,'|');
if (start > 0) { if (start > 0) {
wa_sscanf(start+1, wa_L("$s=%i "), num); wa_sscanf(start+1, wa_L("$s=%i "), num);
@ -834,7 +172,7 @@ static int is_xmplay() {
} }
/* Adds ext to Winamp's extension list */ /* Adds ext to Winamp's extension list */
static void add_extension(char *dst, int dst_len, const char *ext) { static void add_extension(char* dst, int dst_len, const char* ext) {
char buf[EXT_BUFFER_SIZE]; char buf[EXT_BUFFER_SIZE];
char ext_upp[EXT_BUFFER_SIZE]; char ext_upp[EXT_BUFFER_SIZE];
int ext_len, written; int ext_len, written;
@ -876,17 +214,17 @@ static void add_extension(char *dst, int dst_len, const char *ext) {
* Each extension must be in this format: "extension\0Description\0" * Each extension must be in this format: "extension\0Description\0"
* The list is used to accept extensions by default when IsOurFile returns 0, and to register file types. * The list is used to accept extensions by default when IsOurFile returns 0, and to register file types.
* It could be ignored/empty and just detect in IsOurFile instead. */ * It could be ignored/empty and just detect in IsOurFile instead. */
static void build_extension_list(char *winamp_list, int winamp_list_size) { static void build_extension_list(char* winamp_list, int winamp_list_size) {
const char ** ext_list; const char** ext_list;
size_t ext_list_len; size_t ext_list_len;
int i; int i;
winamp_list[0]='\0'; winamp_list[0] = '\0';
winamp_list[1]='\0'; winamp_list[1] = '\0';
ext_list = vgmstream_get_formats(&ext_list_len); ext_list = vgmstream_get_formats(&ext_list_len);
for (i=0; i < ext_list_len; i++) { for (i = 0; i < ext_list_len; i++) {
add_extension(winamp_list, winamp_list_size, ext_list[i]); add_extension(winamp_list, winamp_list_size, ext_list[i]);
} }
} }
@ -930,9 +268,9 @@ static void apply_config(VGMSTREAM* vgmstream, winamp_settings_t* settings) {
vgmstream_apply_config(vgmstream, &vcfg); vgmstream_apply_config(vgmstream, &vcfg);
} }
static int winampGetExtendedFileInfo_common(in_char* filename, char *metadata, char* ret, int retlen); static int winampGetExtendedFileInfo_common(in_char* filename, char* metadata, char* ret, int retlen);
static double get_album_gain_volume(const in_char *fn) { static double get_album_gain_volume(const in_char* fn) {
char replaygain[64]; char replaygain[64];
double gain = 0.0; double gain = 0.0;
int had_replaygain = 0; int had_replaygain = 0;
@ -941,7 +279,7 @@ static double get_album_gain_volume(const in_char *fn) {
replaygain[0] = '\0'; /* reset each time to make sure we read actual tags */ replaygain[0] = '\0'; /* reset each time to make sure we read actual tags */
if (settings.gain_type == REPLAYGAIN_ALBUM if (settings.gain_type == REPLAYGAIN_ALBUM
&& winampGetExtendedFileInfo_common((in_char *)fn, "replaygain_album_gain", replaygain, sizeof(replaygain)) && winampGetExtendedFileInfo_common((in_char*)fn, "replaygain_album_gain", replaygain, sizeof(replaygain))
&& replaygain[0] != '\0') { && replaygain[0] != '\0') {
gain = atof(replaygain); gain = atof(replaygain);
had_replaygain = 1; had_replaygain = 1;
@ -949,7 +287,7 @@ static double get_album_gain_volume(const in_char *fn) {
replaygain[0] = '\0'; replaygain[0] = '\0';
if (!had_replaygain if (!had_replaygain
&& winampGetExtendedFileInfo_common((in_char *)fn, "replaygain_track_gain", replaygain, sizeof(replaygain)) && winampGetExtendedFileInfo_common((in_char*)fn, "replaygain_track_gain", replaygain, sizeof(replaygain))
&& replaygain[0] != '\0') { && replaygain[0] != '\0') {
gain = atof(replaygain); gain = atof(replaygain);
had_replaygain = 1; had_replaygain = 1;
@ -961,12 +299,12 @@ static double get_album_gain_volume(const in_char *fn) {
replaygain[0] = '\0'; replaygain[0] = '\0';
if (settings.clip_type == REPLAYGAIN_ALBUM if (settings.clip_type == REPLAYGAIN_ALBUM
&& winampGetExtendedFileInfo_common((in_char *)fn, "replaygain_album_peak", replaygain, sizeof(replaygain)) && winampGetExtendedFileInfo_common((in_char*)fn, "replaygain_album_peak", replaygain, sizeof(replaygain))
&& replaygain[0] != '\0') { && replaygain[0] != '\0') {
peak = atof(replaygain); peak = atof(replaygain);
} }
else if (settings.clip_type != REPLAYGAIN_NONE else if (settings.clip_type != REPLAYGAIN_NONE
&& winampGetExtendedFileInfo_common((in_char *)fn, "replaygain_track_peak", replaygain, sizeof(replaygain)) && winampGetExtendedFileInfo_common((in_char*)fn, "replaygain_track_peak", replaygain, sizeof(replaygain))
&& replaygain[0] != '\0') { && replaygain[0] != '\0') {
peak = atof(replaygain); peak = atof(replaygain);
} }
@ -983,7 +321,7 @@ static double get_album_gain_volume(const in_char *fn) {
/* about dialog */ /* about dialog */
void winamp_About(HWND hwndParent) { void winamp_About(HWND hwndParent) {
const char *ABOUT_TEXT = const char* ABOUT_TEXT =
PLUGIN_DESCRIPTION "\n" PLUGIN_DESCRIPTION "\n"
"by hcs, FastElbja, manakoAT, bxaimc, snakemeat, soneek, kode54, bnnm and many others\n" "by hcs, FastElbja, manakoAT, bxaimc, snakemeat, soneek, kode54, bnnm and many others\n"
"\n" "\n"
@ -1008,7 +346,7 @@ void winamp_Init() {
/* get ini config */ /* get ini config */
load_defaults(&defaults); load_defaults(&defaults);
load_config(&settings, &defaults); load_config(&input_module, &settings, &defaults);
/* XMPlay with in_vgmstream doesn't support most IPC_x messages so no playlist manipulation */ /* XMPlay with in_vgmstream doesn't support most IPC_x messages so no playlist manipulation */
if (settings.is_xmplay) { if (settings.is_xmplay) {

186
winamp/in_vgmstream.h Normal file
View File

@ -0,0 +1,186 @@
#ifndef _IN_VGMSTREAM_
#define _IN_VGMSTREAM_
/* Normally Winamp opens unicode files by their DOS 8.3 name. #define this to use wchar_t filenames,
* which must be opened with _wfopen in a WINAMP_STREAMFILE (needed for dual files like .pos).
* Only for Winamp paths, other parts would need #define UNICODE for Windows. */
#ifdef VGM_WINAMP_UNICODE
#define UNICODE_INPUT_PLUGIN
#endif
#ifdef _MSC_VER
#define _CRT_SECURE_NO_DEPRECATE
#endif
#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
#include <string.h>
#include <ctype.h>
#include "../src/vgmstream.h"
#include "../src/plugins.h"
#include "sdk/in2.h"
#include "sdk/wa_ipc.h"
#include "sdk/ipc_pe.h"
#include "resource.h"
/* ************************************* */
/* IN_CONFIG */
/* ************************************* */
extern In_Module input_module;
extern int priority_values[7];
typedef enum {
REPLAYGAIN_NONE,
REPLAYGAIN_ALBUM,
REPLAYGAIN_TRACK
} replay_gain_type_t;
/* loaded settings */
typedef struct {
int thread_priority;
double fade_time;
double fade_delay;
double loop_count;
int ignore_loop;
int loop_forever;
int disable_subsongs;
int downmix_channels;
int tagfile_disable;
int force_title;
int exts_unknown_on;
int exts_common_on;
replay_gain_type_t gain_type;
replay_gain_type_t clip_type;
int is_xmplay;
} winamp_settings_t;
extern winamp_settings_t defaults;
extern winamp_settings_t settings;
/* in_config.c */
void load_defaults(winamp_settings_t* defaults);
void load_config(In_Module* input_module, winamp_settings_t* settings, winamp_settings_t* defaults);
INT_PTR CALLBACK configDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
/* ************************************* */
/* IN_UNICODE */
/* ************************************* */
//todo safe ops
//todo there must be a better way to handle unicode...
#ifdef UNICODE_INPUT_PLUGIN
#define wa_strcmp wcscmp
#define wa_strcpy wcscpy
#define wa_strncpy wcsncpy
#define wa_strcat wcscat
#define wa_strlen wcslen
#define wa_strchr wcschr
#define wa_sscanf swscanf
#define wa_snprintf _snwprintf
#define wa_strrchr wcsrchr
#define wa_fileinfo fileinfoW
#define wa_IPC_PE_INSERTFILENAME IPC_PE_INSERTFILENAMEW
#define wa_L(x) L ##x
#else
#define wa_strcmp strcmp
#define wa_strcpy strcpy
#define wa_strncpy strncpy
#define wa_strcat strcat
#define wa_strlen strlen
#define wa_strchr strchr
#define wa_sscanf sscanf
#define wa_snprintf snprintf
#define wa_strrchr strrchr
#define wa_fileinfo fileinfo
#define wa_IPC_PE_INSERTFILENAME IPC_PE_INSERTFILENAME
#define wa_L(x) x
#endif
/* converts from utf16 to utf8 (if unicode is on) */
static inline void wa_ichar_to_char(char *dst, size_t dstsize, const in_char *wsrc) {
#ifdef UNICODE_INPUT_PLUGIN
/* converto to UTF8 codepage, default separate bytes, source wstr, wstr length */
//int size_needed = WideCharToMultiByte(CP_UTF8,0, src,-1, NULL,0, NULL, NULL);
WideCharToMultiByte(CP_UTF8,0, wsrc,-1, dst,dstsize, NULL, NULL);
#else
strncpy(dst, wsrc, dstsize);
dst[dstsize - 1] = '\0';
#endif
}
/* converts from utf8 to utf16 (if unicode is on) */
static inline void wa_char_to_ichar(in_char *wdst, size_t wdstsize, const char *src) {
#ifdef UNICODE_INPUT_PLUGIN
//int size_needed = MultiByteToWideChar(CP_UTF8,0, src,-1, NULL,0);
MultiByteToWideChar(CP_UTF8,0, src,-1, wdst,wdstsize);
#else
strncpy(wdst, src, wdstsize);
wdst[wdstsize - 1] = '\0';
#endif
}
/* copies from utf16 to utf16 (if unicode is active) */
static inline void wa_wchar_to_ichar(in_char *wdst, size_t wdstsize, const wchar_t *src) {
#ifdef UNICODE_INPUT_PLUGIN
wcscpy(wdst,src);
#else
strcpy(wdst,src); //todo ???
#endif
}
/* copies from utf16 to utf16 */
static inline void wa_char_to_wchar(wchar_t *wdst, size_t wdstsize, const char *src) {
#ifdef UNICODE_INPUT_PLUGIN
MultiByteToWideChar(CP_UTF8,0, src,-1, wdst,wdstsize);
#else
strcpy(wdst,src); //todo ???
#endif
}
//todo snprintf
/* Windows unicode, separate from Winamp's unicode flag */
#ifdef UNICODE
#define cfg_strncpy wcsncpy
#define cfg_strncat wcsncat
#define cfg_sprintf _swprintf
#define cfg_sscanf swscanf
#define cfg_strlen wcslen
#define cfg_strrchr wcsrchr
#else
#define cfg_strncpy strncpy
#define cfg_strncat strncat
#define cfg_sprintf sprintf
#define cfg_sscanf sscanf
#define cfg_strlen strlen
#define cfg_strrchr strrchr
#endif
/* converts from utf8 to utf16 (if unicode is active) */
static inline void cfg_char_to_wchar(TCHAR *wdst, size_t wdstsize, const char *src) {
#ifdef UNICODE
//int size_needed = MultiByteToWideChar(CP_UTF8,0, src,-1, NULL,0);
MultiByteToWideChar(CP_UTF8,0, src,-1, wdst,wdstsize);
#else
strcpy(wdst,src);
#endif
}
/* ************************************* */
/* IN_STREAMFILE */
/* ************************************* */
/* in_streamfile.c */
STREAMFILE* open_winamp_streamfile_by_ipath(const in_char* wpath);
#endif /*_IN_VGMSTREAM_*/

View File

@ -1,206 +1,218 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<VisualStudioProject <VisualStudioProject
ProjectType="Visual C++" ProjectType="Visual C++"
Version="9.00" Version="9.00"
Name="in_vgmstream" Name="in_vgmstream"
ProjectGUID="{42D86561-8CE4-40F5-86CE-58C986B77502}" ProjectGUID="{42D86561-8CE4-40F5-86CE-58C986B77502}"
RootNamespace="in_vgmstream" RootNamespace="in_vgmstream"
Keyword="Win32Proj" Keyword="Win32Proj"
TargetFrameworkVersion="131072" TargetFrameworkVersion="131072"
> >
<Platforms> <Platforms>
<Platform <Platform
Name="Win32" Name="Win32"
/> />
</Platforms> </Platforms>
<ToolFiles> <ToolFiles>
</ToolFiles> </ToolFiles>
<Configurations> <Configurations>
<Configuration <Configuration
Name="Debug|Win32" Name="Debug|Win32"
OutputDirectory="Debug" OutputDirectory="Debug"
IntermediateDirectory="Debug" IntermediateDirectory="Debug"
ConfigurationType="2" ConfigurationType="2"
> >
<Tool <Tool
Name="VCPreBuildEventTool" Name="VCPreBuildEventTool"
/> />
<Tool <Tool
Name="VCCustomBuildTool" Name="VCCustomBuildTool"
/> />
<Tool <Tool
Name="VCXMLDataGeneratorTool" Name="VCXMLDataGeneratorTool"
/> />
<Tool <Tool
Name="VCWebServiceProxyGeneratorTool" Name="VCWebServiceProxyGeneratorTool"
/> />
<Tool <Tool
Name="VCMIDLTool" Name="VCMIDLTool"
/> />
<Tool <Tool
Name="VCCLCompilerTool" Name="VCCLCompilerTool"
Optimization="0" Optimization="0"
AdditionalIncludeDirectories="../ext_includes" AdditionalIncludeDirectories="../ext_includes"
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;IN_VGMSTREAM_EXPORTS;" PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;IN_VGMSTREAM_EXPORTS;"
MinimalRebuild="true" MinimalRebuild="true"
BasicRuntimeChecks="3" BasicRuntimeChecks="3"
RuntimeLibrary="3" RuntimeLibrary="3"
UsePrecompiledHeader="0" UsePrecompiledHeader="0"
WarningLevel="3" WarningLevel="3"
Detect64BitPortabilityProblems="true" Detect64BitPortabilityProblems="true"
DebugInformationFormat="4" DebugInformationFormat="4"
/> />
<Tool <Tool
Name="VCManagedResourceCompilerTool" Name="VCManagedResourceCompilerTool"
/> />
<Tool <Tool
Name="VCResourceCompilerTool" Name="VCResourceCompilerTool"
/> />
<Tool <Tool
Name="VCPreLinkEventTool" Name="VCPreLinkEventTool"
/> />
<Tool <Tool
Name="VCLinkerTool" Name="VCLinkerTool"
AdditionalDependencies="../ext_libs/libvorbis.lib ../ext_libs/libmpg123-0.lib ../ext_libs/libg719_decode.lib" AdditionalDependencies="../ext_libs/libvorbis.lib ../ext_libs/libmpg123-0.lib ../ext_libs/libg719_decode.lib"
LinkIncremental="2" LinkIncremental="2"
GenerateDebugInformation="true" GenerateDebugInformation="true"
SubSystem="2" SubSystem="2"
RandomizedBaseAddress="1" RandomizedBaseAddress="1"
DataExecutionPrevention="0" DataExecutionPrevention="0"
TargetMachine="1" TargetMachine="1"
/> />
<Tool <Tool
Name="VCALinkTool" Name="VCALinkTool"
/> />
<Tool <Tool
Name="VCManifestTool" Name="VCManifestTool"
/> />
<Tool <Tool
Name="VCXDCMakeTool" Name="VCXDCMakeTool"
/> />
<Tool <Tool
Name="VCBscMakeTool" Name="VCBscMakeTool"
/> />
<Tool <Tool
Name="VCFxCopTool" Name="VCFxCopTool"
/> />
<Tool <Tool
Name="VCAppVerifierTool" Name="VCAppVerifierTool"
/> />
<Tool <Tool
Name="VCPostBuildEventTool" Name="VCPostBuildEventTool"
/> />
</Configuration> </Configuration>
<Configuration <Configuration
Name="Release|Win32" Name="Release|Win32"
OutputDirectory="Release" OutputDirectory="Release"
IntermediateDirectory="Release" IntermediateDirectory="Release"
ConfigurationType="2" ConfigurationType="2"
> >
<Tool <Tool
Name="VCPreBuildEventTool" Name="VCPreBuildEventTool"
/> />
<Tool <Tool
Name="VCCustomBuildTool" Name="VCCustomBuildTool"
/> />
<Tool <Tool
Name="VCXMLDataGeneratorTool" Name="VCXMLDataGeneratorTool"
/> />
<Tool <Tool
Name="VCWebServiceProxyGeneratorTool" Name="VCWebServiceProxyGeneratorTool"
/> />
<Tool <Tool
Name="VCMIDLTool" Name="VCMIDLTool"
/> />
<Tool <Tool
Name="VCCLCompilerTool" Name="VCCLCompilerTool"
AdditionalIncludeDirectories="../ext_includes" AdditionalIncludeDirectories="../ext_includes"
PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;IN_VGMSTREAM_EXPORTS;VGM_USE_VORBIS;VGM_USE_MPEG;VGM_USE_G7221" PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;IN_VGMSTREAM_EXPORTS;VGM_USE_VORBIS;VGM_USE_MPEG;VGM_USE_G7221"
RuntimeLibrary="2" RuntimeLibrary="2"
UsePrecompiledHeader="0" UsePrecompiledHeader="0"
WarningLevel="3" WarningLevel="3"
Detect64BitPortabilityProblems="true" Detect64BitPortabilityProblems="true"
DebugInformationFormat="3" DebugInformationFormat="3"
/> />
<Tool <Tool
Name="VCManagedResourceCompilerTool" Name="VCManagedResourceCompilerTool"
/> />
<Tool <Tool
Name="VCResourceCompilerTool" Name="VCResourceCompilerTool"
/> />
<Tool <Tool
Name="VCPreLinkEventTool" Name="VCPreLinkEventTool"
/> />
<Tool <Tool
Name="VCLinkerTool" Name="VCLinkerTool"
AdditionalDependencies="../ext_libs/libvorbis.lib ../ext_libs/libmpg123-0.lib ../ext_libs/libg719_decode.lib" AdditionalDependencies="../ext_libs/libvorbis.lib ../ext_libs/libmpg123-0.lib ../ext_libs/libg719_decode.lib"
LinkIncremental="2" LinkIncremental="2"
ModuleDefinitionFile="" ModuleDefinitionFile=""
GenerateDebugInformation="true" GenerateDebugInformation="true"
SubSystem="2" SubSystem="2"
OptimizeReferences="2" OptimizeReferences="2"
EnableCOMDATFolding="2" EnableCOMDATFolding="2"
RandomizedBaseAddress="1" RandomizedBaseAddress="1"
DataExecutionPrevention="0" DataExecutionPrevention="0"
TargetMachine="1" TargetMachine="1"
/> />
<Tool <Tool
Name="VCALinkTool" Name="VCALinkTool"
/> />
<Tool <Tool
Name="VCManifestTool" Name="VCManifestTool"
/> />
<Tool <Tool
Name="VCXDCMakeTool" Name="VCXDCMakeTool"
/> />
<Tool <Tool
Name="VCBscMakeTool" Name="VCBscMakeTool"
/> />
<Tool <Tool
Name="VCFxCopTool" Name="VCFxCopTool"
/> />
<Tool <Tool
Name="VCAppVerifierTool" Name="VCAppVerifierTool"
/> />
<Tool <Tool
Name="VCPostBuildEventTool" Name="VCPostBuildEventTool"
/> />
</Configuration> </Configuration>
</Configurations> </Configurations>
<References> <References>
</References> </References>
<Files> <Files>
<Filter <Filter
Name="Header Files" Name="Header Files"
Filter="h;hpp;hxx;hm;inl;inc;xsd" Filter="h;hpp;hxx;hm;inl;inc;xsd"
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
> >
<File <File
RelativePath=".\resource.h" RelativePath=".\in_vgmstream.h"
> >
</File> </File>
</Filter> <File
<Filter RelativePath=".\resource.h"
Name="Resource Files" >
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx" </File>
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}" </Filter>
> <Filter
<File Name="Resource Files"
RelativePath=".\resource.rc" Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"
> UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
</File> >
</Filter> <File
<Filter RelativePath=".\resource.rc"
Name="Source Files" >
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx" </File>
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" </Filter>
> <Filter
<File Name="Source Files"
RelativePath=".\in_vgmstream.c" Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
> UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
</File> >
</Filter> <File
</Files> RelativePath=".\in_config.c"
<Globals> >
</Globals> </File>
</VisualStudioProject> <File
RelativePath=".\in_streamfile.c"
>
</File>
<File
RelativePath=".\in_vgmstream.c"
>
</File>
</Filter>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@ -126,12 +126,15 @@
</PreBuildEvent> </PreBuildEvent>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="in_vgmstream.h" />
<ClInclude Include="resource.h" /> <ClInclude Include="resource.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ResourceCompile Include="resource.rc" /> <ResourceCompile Include="resource.rc" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="in_config.c" />
<ClCompile Include="in_streamfile.c" />
<ClCompile Include="in_vgmstream.c" /> <ClCompile Include="in_vgmstream.c" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -1,32 +1,41 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup> <ItemGroup>
<Filter Include="Header Files"> <Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter> </Filter>
<Filter Include="Resource Files"> <Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx</Extensions> <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx</Extensions>
</Filter> </Filter>
<Filter Include="Source Files"> <Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter> </Filter>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="resource.h"> <ClInclude Include="in_vgmstream.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
</ItemGroup> <ClInclude Include="resource.h">
<ItemGroup> <Filter>Header Files</Filter>
<ResourceCompile Include="resource.rc"> </ClInclude>
<Filter>Resource Files</Filter> </ItemGroup>
</ResourceCompile> <ItemGroup>
</ItemGroup> <ResourceCompile Include="resource.rc">
<ItemGroup> <Filter>Resource Files</Filter>
<ClCompile Include="in_vgmstream.c"> </ResourceCompile>
<Filter>Source Files</Filter> </ItemGroup>
</ClCompile> <ItemGroup>
</ItemGroup> <ClCompile Include="in_config.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="in_streamfile.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="in_vgmstream.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project> </Project>