mirror of
https://github.com/libretro/FBNeo.git
synced 2024-11-23 08:59:39 +00:00
Merge branch 'finalburnneo-master'
This commit is contained in:
commit
c4adb97a41
@ -102,7 +102,7 @@ drvsrc = d_akkaarrh.o d_arcadecl.o d_atarig1.o d_badlands.o d_batman.o d_blstro
|
||||
d_spectrum.o
|
||||
|
||||
depobj = burn.o burn_bitmap.o burn_gun.o burn_led.o burn_shift.o burn_memory.o burn_pal.o burn_sound.o burn_sound_c.o cheat.o debug_track.o hiscore.o \
|
||||
load.o tilemap_generic.o tiles_generic.o timer.o vector.o \
|
||||
load.o burn_sha1.o tilemap_generic.o tiles_generic.o timer.o vector.o \
|
||||
\
|
||||
6821pia.o 6840ptm.o 8255ppi.o 8257dma.o c169.o atariic.o atarijsa.o atarimo.o atarirle.o atarivad.o avgdvg.o bsmt2000.o decobsmt.o ds2404.o dtimer.o earom.o eeprom.o epic12.o gaelco_crypt.o i4x00.o intelfsh.o \
|
||||
joyprocess.o nb1414m4.o nb1414m4_8bit.o nmk004.o nmk112.o k1ge.o kaneko_hit.o kaneko_tmap.o mathbox.o mb87078.o mermaid.o midcsd.o midsat.o midsg.o midcvsd.o midssio.o midtcs.o \
|
||||
|
@ -464,6 +464,11 @@ extern bool bDoIpsPatch;
|
||||
|
||||
void IpsApplyPatches(UINT8* base, char* rom_name, UINT32 rom_crc, bool readonly = false);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// MISC Helper / utility functions, etc
|
||||
int BurnComputeSHA1(const UINT8 *buffer, int buffer_size, char *hash_str);
|
||||
//int BurnComputeSHA1(const char *filename, char *hash_str);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Flags used with the Burndriver structure
|
||||
|
||||
|
166
src/burn/burn_sha1.cpp
Normal file
166
src/burn/burn_sha1.cpp
Normal file
@ -0,0 +1,166 @@
|
||||
#include "burnint.h"
|
||||
#include <stdint.h>
|
||||
|
||||
#define SHA1_BLOCK_SIZE 64
|
||||
#define SHA1_HASH_SIZE 20
|
||||
|
||||
struct sha1_state {
|
||||
UINT32 state[5];
|
||||
UINT64 bitcount;
|
||||
UINT8 buffer[SHA1_BLOCK_SIZE];
|
||||
};
|
||||
|
||||
#define ROTLEFT(a, b) (((a) << (b)) | ((a) >> (32 - (b))))
|
||||
#define F0(b, c, d) ((b & c) | (~b & d))
|
||||
#define F1(b, c, d) (b ^ c ^ d)
|
||||
#define F2(b, c, d) ((b & c) | (b & d) | (c & d))
|
||||
#define F3(b, c, d) (b ^ c ^ d)
|
||||
|
||||
void SHA1_Transform(sha1_state *ctx, const UINT8 *data) {
|
||||
UINT32 a, b, c, d, e, i, t;
|
||||
UINT32 m[80];
|
||||
|
||||
for (i = 0; i < 16; i++) {
|
||||
m[i] = (data[i * 4] << 24) | (data[i * 4 + 1] << 16) |
|
||||
(data[i * 4 + 2] << 8) | (data[i * 4 + 3]);
|
||||
}
|
||||
for (i = 16; i < 80; i++) {
|
||||
m[i] = ROTLEFT(m[i - 3] ^ m[i - 8] ^ m[i - 14] ^ m[i - 16], 1);
|
||||
}
|
||||
|
||||
a = ctx->state[0];
|
||||
b = ctx->state[1];
|
||||
c = ctx->state[2];
|
||||
d = ctx->state[3];
|
||||
e = ctx->state[4];
|
||||
|
||||
for (i = 0; i < 80; i++) {
|
||||
if (i < 20) {
|
||||
t = ROTLEFT(a, 5) + F0(b, c, d) + e + m[i] + 0x5A827999;
|
||||
} else if (i < 40) {
|
||||
t = ROTLEFT(a, 5) + F1(b, c, d) + e + m[i] + 0x6ED9EBA1;
|
||||
} else if (i < 60) {
|
||||
t = ROTLEFT(a, 5) + F2(b, c, d) + e + m[i] + 0x8F1BBCDC;
|
||||
} else {
|
||||
t = ROTLEFT(a, 5) + F3(b, c, d) + e + m[i] + 0xCA62C1D6;
|
||||
}
|
||||
e = d;
|
||||
d = c;
|
||||
c = ROTLEFT(b, 30);
|
||||
b = a;
|
||||
a = t;
|
||||
}
|
||||
|
||||
ctx->state[0] += a;
|
||||
ctx->state[1] += b;
|
||||
ctx->state[2] += c;
|
||||
ctx->state[3] += d;
|
||||
ctx->state[4] += e;
|
||||
}
|
||||
|
||||
void SHA1_Init(sha1_state *ctx) {
|
||||
ctx->state[0] = 0x67452301;
|
||||
ctx->state[1] = 0xEFCDAB89;
|
||||
ctx->state[2] = 0x98BADCFE;
|
||||
ctx->state[3] = 0x10325476;
|
||||
ctx->state[4] = 0xC3D2E1F0;
|
||||
ctx->bitcount = 0;
|
||||
memset(ctx->buffer, 0, SHA1_BLOCK_SIZE);
|
||||
}
|
||||
|
||||
void SHA1_Update(sha1_state *ctx, const UINT8 *data, INT32 len) {
|
||||
INT32 i;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
ctx->buffer[ctx->bitcount / 8 % SHA1_BLOCK_SIZE] = data[i];
|
||||
ctx->bitcount += 8;
|
||||
if ((ctx->bitcount / 8 % SHA1_BLOCK_SIZE) == 0) {
|
||||
SHA1_Transform(ctx, ctx->buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SHA1_Final(UINT8 *hash, sha1_state *ctx) {
|
||||
INT32 i = ctx->bitcount / 8 % SHA1_BLOCK_SIZE;
|
||||
|
||||
ctx->buffer[i++] = 0x80;
|
||||
if (i > SHA1_BLOCK_SIZE - 8) {
|
||||
while (i < SHA1_BLOCK_SIZE) {
|
||||
ctx->buffer[i++] = 0;
|
||||
}
|
||||
SHA1_Transform(ctx, ctx->buffer);
|
||||
i = 0;
|
||||
}
|
||||
while (i < SHA1_BLOCK_SIZE - 8) {
|
||||
ctx->buffer[i++] = 0;
|
||||
}
|
||||
|
||||
for (int j = 0; j < 8; j++) {
|
||||
ctx->buffer[SHA1_BLOCK_SIZE - 1 - j] = ctx->bitcount >> (j * 8);
|
||||
}
|
||||
SHA1_Transform(ctx, ctx->buffer);
|
||||
|
||||
for (i = 0; i < 5; i++) {
|
||||
hash[i * 4 + 0] = (ctx->state[i] >> 24) & 0xff;
|
||||
hash[i * 4 + 1] = (ctx->state[i] >> 16) & 0xff;
|
||||
hash[i * 4 + 2] = (ctx->state[i] >> 8) & 0xff;
|
||||
hash[i * 4 + 3] = (ctx->state[i]) & 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
int BurnComputeSHA1(const UINT8 *buffer, int buffer_size, char *hash_str) {
|
||||
sha1_state ctx;
|
||||
UINT8 hash[SHA1_HASH_SIZE];
|
||||
|
||||
SHA1_Init(&ctx);
|
||||
SHA1_Update(&ctx, buffer, buffer_size);
|
||||
SHA1_Final(hash, &ctx);
|
||||
|
||||
for (int i = 0; i < SHA1_HASH_SIZE; i++) {
|
||||
char temp[128];
|
||||
sprintf(temp, "%02x", hash[i]);
|
||||
hash_str[i*2 + 0] = temp[0];
|
||||
hash_str[i*2 + 1] = temp[1];
|
||||
}
|
||||
|
||||
hash_str[SHA1_HASH_SIZE*2] = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int BurnComputeSHA1(const char *filename, char *hash_str) {
|
||||
sha1_state ctx;
|
||||
UINT8 hash[SHA1_HASH_SIZE];
|
||||
const int filebuf_size = 1024 * 1024;
|
||||
UINT8 *buffer;
|
||||
|
||||
FILE *file = fopen(filename, "rb");
|
||||
if (!file) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
buffer = (UINT8*)malloc(filebuf_size);
|
||||
if (!buffer) return 2;
|
||||
|
||||
SHA1_Init(&ctx);
|
||||
INT32 bytes_read;
|
||||
while ((bytes_read = fread(buffer, 1, filebuf_size, file)) > 0) {
|
||||
SHA1_Update(&ctx, buffer, bytes_read);
|
||||
}
|
||||
fclose(file);
|
||||
|
||||
free(buffer);
|
||||
|
||||
SHA1_Final(hash, &ctx);
|
||||
|
||||
for (int i = 0; i < SHA1_HASH_SIZE; i++) {
|
||||
char temp[128];
|
||||
sprintf(temp, "%02x", hash[i]);
|
||||
hash_str[i*2 + 0] = temp[0];
|
||||
hash_str[i*2 + 1] = temp[1];
|
||||
}
|
||||
|
||||
hash_str[SHA1_HASH_SIZE*2] = 0;
|
||||
|
||||
return 0;
|
||||
}
|
@ -5,7 +5,8 @@
|
||||
|
||||
#define CHEAT_MAXCPU 8 // enough?
|
||||
|
||||
#define HW_NES ( ((BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK) == HARDWARE_NES) || ((BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK) == HARDWARE_FDS) )
|
||||
// any system that uses Game Genie/Pro Action Replay codes can be defined as HW_NES...
|
||||
#define HW_NES ( ((BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK) == HARDWARE_SNES) || ((BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK) == HARDWARE_NES) || ((BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK) == HARDWARE_FDS) )
|
||||
|
||||
void (*nes_add_cheat)(char *) = NULL;
|
||||
void (*nes_remove_cheat)(char *) = NULL;
|
||||
|
@ -47830,6 +47830,26 @@ struct BurnDriver BurnDrvmd_btomatog = {
|
||||
&bMegadriveRecalcPalette, 0x100, 320, 224, 4, 3
|
||||
};
|
||||
|
||||
// Bishoujo Super Street Fighter II: Glamor Queen (Hack, v1.6)
|
||||
// https://romhackplaza.org/romhacks/bishoujo-super-street-fighter-ii-glamor-queen-genesis/
|
||||
|
||||
static struct BurnRomInfo md_bssf2gqRomDesc[] = {
|
||||
{ "Bishoujo Super Street Fighter II - Glamor Queen v1.6 (2024)(Yoni Arousement).bin", 0x500000, 0x87a6c750, BRF_PRG | SEGA_MD_ROM_LOAD16_WORD_SWAP | SEGA_MD_ROM_OFFS_000000 },
|
||||
};
|
||||
|
||||
STD_ROM_PICK(md_bssf2gq)
|
||||
STD_ROM_FN(md_bssf2gq)
|
||||
|
||||
struct BurnDriver BurnDrvmd_bssf2gq = {
|
||||
"md_bssf2gq", "md_ssf2", NULL, NULL, "2024",
|
||||
"Bishoujo Super Street Fighter II: Glamor Queen (Hack, v1.6)\0", NULL, "hack (Yoni Arousement)", "Sega Megadrive",
|
||||
NULL, NULL, NULL, NULL,
|
||||
BDF_GAME_WORKING | BDF_16BIT_ONLY | BDF_CLONE, 2, HARDWARE_SEGA_MEGADRIVE | HARDWARE_SEGA_MEGADRIVE_PCB_SSF2, GBF_VSFIGHT, 0,
|
||||
MegadriveGetZipName, md_bssf2gqRomInfo, md_bssf2gqRomName, NULL, NULL, NULL, NULL, MegadriveInputInfo, MegadriveDIPInfo,
|
||||
MegadriveInit, MegadriveExit, MegadriveFrame, MegadriveDraw, MegadriveScan,
|
||||
&bMegadriveRecalcPalette, 0x100, 320, 224, 4, 3
|
||||
};
|
||||
|
||||
// Cadash (Hack, Arcade Colors)
|
||||
// http://www.romhacking.net/hacks/3905/
|
||||
static struct BurnRomInfo md_cadashacRomDesc[] = {
|
||||
@ -52547,18 +52567,18 @@ struct BurnDriver BurnDrvmd_mkae = {
|
||||
&bMegadriveRecalcPalette, 0x100, 320, 224, 4, 3
|
||||
};
|
||||
|
||||
// Mortal Kombat Arcade Edition (Hack)
|
||||
// https://www.romhacking.net/reviews/9714/
|
||||
// Mortal Kombat Arcade Edition (Hack, v2.1)
|
||||
// https://romhackplaza.org/romhacks/mortal-kombat-arcade-edition-enhanced-genesis-2/
|
||||
static struct BurnRomInfo md_mkaeeRomDesc[] = {
|
||||
{ "Mortal Kombat Arcade Edition Enhanced v1.5 (2022)(Rael G.C.).bin", 4194304, 0xb884890b, BRF_PRG | SEGA_MD_ROM_LOAD16_WORD_SWAP | SEGA_MD_ROM_OFFS_000000 },
|
||||
{ "Mortal Kombat Arcade Edition Enhanced v2.1 (2024)(Rael G.C.).bin", 4194304, 0x486979c5, BRF_PRG | SEGA_MD_ROM_LOAD16_WORD_SWAP | SEGA_MD_ROM_OFFS_000000 },
|
||||
};
|
||||
|
||||
STD_ROM_PICK(md_mkaee)
|
||||
STD_ROM_FN(md_mkaee)
|
||||
|
||||
struct BurnDriver BurnDrvmd_mkaee = {
|
||||
"md_mkaee", "md_mk", NULL, NULL, "2022",
|
||||
"Mortal Kombat Arcade Edition Enhanced (Hack, v1.5)\0", NULL, "Rael G.C.", "Sega Megadrive",
|
||||
"md_mkaee", "md_mk", NULL, NULL, "2024",
|
||||
"Mortal Kombat Arcade Edition Enhanced (Hack, v2.1)\0", NULL, "Rael G.C.", "Sega Megadrive",
|
||||
NULL, NULL, NULL, NULL,
|
||||
BDF_GAME_WORKING | BDF_16BIT_ONLY | BDF_CLONE | BDF_HACK, 2, HARDWARE_SEGA_MEGADRIVE, GBF_VSFIGHT, 0,
|
||||
MegadriveGetZipName, md_mkaeeRomInfo, md_mkaeeRomName, NULL, NULL, NULL, NULL, MegadriveInputInfo, MegadriveDIPInfo,
|
||||
|
@ -14132,7 +14132,7 @@ struct BurnDriver BurnDrvMSX_gundamk = {
|
||||
// Mokari Makka? Bochibochi Denna! (Japan)
|
||||
|
||||
static struct BurnRomInfo MSX_mokarimaRomDesc[] = {
|
||||
{ "Mokari Makka? Bochibochi Denna! (Japan)(1986)(Leben Pro).rom", 0x08000, 0xd40f481d, BRF_PRG | BRF_ESS },
|
||||
{ "Mokari Makka Bochibochi Denna! (Japan)(1986)(Leben Pro).rom", 0x08000, 0xd40f481d, BRF_PRG | BRF_ESS },
|
||||
};
|
||||
|
||||
STDROMPICKEXT(MSX_mokarima, MSX_mokarima, msx_msx)
|
||||
|
@ -10975,7 +10975,7 @@ static INT32 gg_decode(char *gg_code, UINT16 &address, UINT8 &value, INT32 &comp
|
||||
return 0;
|
||||
}
|
||||
|
||||
const INT32 cheat_MAX = 0x100;
|
||||
static const INT32 cheat_MAX = 0x100;
|
||||
static INT32 cheats_active = 0;
|
||||
|
||||
struct cheat_struct {
|
||||
@ -10987,7 +10987,7 @@ struct cheat_struct {
|
||||
|
||||
static cheat_struct cheats[cheat_MAX];
|
||||
|
||||
void nes_add_cheat(char *code) // 6/8 character game genie codes allowed
|
||||
static void nes_add_cheat(char *code) // 6/8 character game genie codes allowed
|
||||
{
|
||||
UINT16 address;
|
||||
UINT8 value;
|
||||
@ -11009,7 +11009,7 @@ void nes_add_cheat(char *code) // 6/8 character game genie codes allowed
|
||||
}
|
||||
}
|
||||
|
||||
void nes_remove_cheat(char *code)
|
||||
static void nes_remove_cheat(char *code)
|
||||
{
|
||||
cheat_struct cheat_temp[cheat_MAX];
|
||||
INT32 temp_num = 0;
|
||||
@ -11192,6 +11192,12 @@ static INT32 NESInit()
|
||||
if (fds_load(rom, ri.nLen, ri.nCrc)) return 1;
|
||||
} else {
|
||||
if (BurnLoadRom(rom, 0, 1)) return 1;
|
||||
|
||||
// sha1 hash
|
||||
char hash_potato[128] = { 0, };
|
||||
BurnComputeSHA1(rom + 0x10, ri.nLen - 0x10, hash_potato);
|
||||
bprintf(0, _T("sha1 hash: [%S]\n"), hash_potato);
|
||||
|
||||
if (cartridge_load(rom, ri.nLen, ri.nCrc)) return 1;
|
||||
}
|
||||
|
||||
@ -28056,10 +28062,10 @@ struct BurnDriver BurnDrvnes_marioadventure = {
|
||||
SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_WIDTH, SCREEN_HEIGHT
|
||||
};
|
||||
|
||||
// Mario Adventure 3 (Hack, v1.9.8)
|
||||
// Mario Adventure 3 (Hack, v1.9.9)
|
||||
// https://marioadventure3.com/
|
||||
static struct BurnRomInfo nes_marioadventure3RomDesc[] = {
|
||||
{ "Mario Adventure 3 v1.9.8 (2024)(ScarlettVixen).nes", 786448, 0x494115ef, BRF_ESS | BRF_PRG },
|
||||
{ "Mario Adventure 3 v1.9.9 (2024)(ScarlettVixen).nes", 786448, 0x0bf1b61c, BRF_ESS | BRF_PRG },
|
||||
};
|
||||
|
||||
STD_ROM_PICK(nes_marioadventure3)
|
||||
@ -28067,7 +28073,7 @@ STD_ROM_FN(nes_marioadventure3)
|
||||
|
||||
struct BurnDriver BurnDrvnes_marioadventure3 = {
|
||||
"nes_marioadventure3", "nes_smb3", NULL, NULL, "2024",
|
||||
"Mario Adventure 3 (Hack, v1.9.8)\0", NULL, "ScarlettVixen", "Miscellaneous",
|
||||
"Mario Adventure 3 (Hack, v1.9.9)\0", NULL, "ScarlettVixen", "Miscellaneous",
|
||||
NULL, NULL, NULL, NULL,
|
||||
BDF_GAME_WORKING | BDF_CLONE | BDF_HACK, 1, HARDWARE_NES, GBF_PLATFORM, 0,
|
||||
NESGetZipName, nes_marioadventure3RomInfo, nes_marioadventure3RomName, NULL, NULL, NULL, NULL, NESInputInfo, NESDIPInfo,
|
||||
@ -28800,9 +28806,9 @@ struct BurnDriver BurnDrvnes_multidudee = {
|
||||
SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_WIDTH, SCREEN_HEIGHT
|
||||
};
|
||||
|
||||
// Murder in the Maze (HB)
|
||||
// Murder in the Maze (HB, v1.01)
|
||||
static struct BurnRomInfo nes_murdmazeRomDesc[] = {
|
||||
{ "Murder in the Maze (2024)(T1LT).nes", 524304, 0xbaac5e32, BRF_ESS | BRF_PRG },
|
||||
{ "Murder in the Maze v1.01 (2024)(T1LT).nes", 524304, 0xbaac5e32, BRF_ESS | BRF_PRG },
|
||||
};
|
||||
|
||||
STD_ROM_PICK(nes_murdmaze)
|
||||
@ -28810,7 +28816,7 @@ STD_ROM_FN(nes_murdmaze)
|
||||
|
||||
struct BurnDriver BurnDrvnes_murdmaze = {
|
||||
"nes_murdmaze", NULL, NULL, NULL, "2024",
|
||||
"Murder in the Maze (HB)\0", NULL, "T1LT", "Miscellaneous",
|
||||
"Murder in the Maze (HB, v1.01)\0", NULL, "T1LT", "Miscellaneous",
|
||||
NULL, NULL, NULL, NULL,
|
||||
BDF_GAME_WORKING | BDF_HOMEBREW, 1, HARDWARE_NES, GBF_ADV | GBF_MINIGAMES, 0,
|
||||
NESGetZipName, nes_murdmazeRomInfo, nes_murdmazeRomName, NULL, NULL, NULL, NULL, NESInputInfo, NESDIPInfo,
|
||||
|
@ -525,7 +525,7 @@ static INT32 DrvCommonInit(INT32 game) // 0 = gng, 1 = gnga, 2 = diamrun
|
||||
{
|
||||
BurnAllocMemIndex();
|
||||
|
||||
BurnSetRefreshRate(59.59);
|
||||
BurnSetRefreshRate(59.64);
|
||||
|
||||
{
|
||||
if (game == 0 || game == 1) {
|
||||
@ -755,7 +755,7 @@ static INT32 DrvFrame()
|
||||
}
|
||||
|
||||
INT32 nInterleave = 256;
|
||||
INT32 nCyclesTotal[2] = { (INT32)(1500000 / 59.59), (INT32)(3000000 / 59.59) };
|
||||
INT32 nCyclesTotal[2] = { (INT32)(1500000 / 59.637405), (INT32)(3000000 / 59.637405) };
|
||||
INT32 nCyclesDone[2] = { nExtraCycles, 0 };
|
||||
|
||||
M6809Open(0);
|
||||
|
@ -2211,7 +2211,7 @@ static void snkwave_exit()
|
||||
|
||||
static void snkwave_update_waveform(UINT32 offset, UINT8 data)
|
||||
{
|
||||
snkwave_waveform[offset * 2] = ((data & 0x38) >> 3) << (12-CLOCK_SHIFT);
|
||||
snkwave_waveform[offset * 2] = ((data & 0x70) >> 4) << (12-CLOCK_SHIFT);
|
||||
snkwave_waveform[offset * 2 + 1] = ((data & 0x07) >> 0) << (12-CLOCK_SHIFT);
|
||||
snkwave_waveform[SNKWAVE_WAVEFORM_LENGTH-2 - offset * 2] = ~snkwave_waveform[offset * 2 + 1];
|
||||
snkwave_waveform[SNKWAVE_WAVEFORM_LENGTH-1 - offset * 2] = ~snkwave_waveform[offset * 2];
|
||||
@ -2221,13 +2221,11 @@ static void snkwave_w(UINT32 offset, UINT8 data)
|
||||
{
|
||||
stream.update();
|
||||
|
||||
data &= 0x3f; // all registers are 6-bit
|
||||
|
||||
if (offset == 0)
|
||||
snkwave_frequency = (snkwave_frequency & 0x03f) | (data << 6);
|
||||
else if (offset == 1)
|
||||
snkwave_frequency = (snkwave_frequency & 0xfc0) | data;
|
||||
else if (offset <= 5)
|
||||
if (offset == 0) // F1, high 6 bits
|
||||
snkwave_frequency = (snkwave_frequency & 0x03f) | ((data & 0xfc) << 4);
|
||||
else if (offset == 1) // F2, low 6 bits
|
||||
snkwave_frequency = (snkwave_frequency & 0xfc0) | (data & 0x3f);
|
||||
else if (offset <= 5) // W3 thru W6, low 3 bits of each nybble
|
||||
snkwave_update_waveform(offset - 2, data);
|
||||
}
|
||||
|
||||
|
@ -4164,7 +4164,7 @@ static void draw_screen(INT32 which)
|
||||
|
||||
BurnTransferClear(0);
|
||||
|
||||
if (!system32_displayenable[0]) {
|
||||
if (!system32_displayenable[which]) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -181,11 +181,13 @@ static struct BurnDIPInfo SNESMouseP1MouseP2GamepadDIPList[] =
|
||||
{0x01, 0xff, 0xff, 0x01, NULL },
|
||||
};
|
||||
|
||||
#if 0
|
||||
static struct BurnDIPInfo SNESMouseP1GamepadP2MouseDIPList[] =
|
||||
{
|
||||
{0x00, 0xff, 0xff, 0x00, NULL },
|
||||
{0x01, 0xff, 0xff, 0x02, NULL },
|
||||
};
|
||||
#endif
|
||||
|
||||
static struct BurnDIPInfo SNESMouseP1MouseP2MouseDIPList[] =
|
||||
{
|
||||
@ -195,7 +197,7 @@ static struct BurnDIPInfo SNESMouseP1MouseP2MouseDIPList[] =
|
||||
|
||||
STDDIPINFOEXT(SNESMouse, SNESMouseP1GamepadP2Gamepad, SNESMouseBase) // zoop
|
||||
STDDIPINFOEXT(SNESMouseP1, SNESMouseP1MouseP2Gamepad, SNESMouseBase) // zoop
|
||||
STDDIPINFOEXT(SNESMouseP2, SNESMouseP1GamepadP2Mouse, SNESMouseBase) // zoop
|
||||
//STDDIPINFOEXT(SNESMouseP2, SNESMouseP1GamepadP2Mouse, SNESMouseBase) // zoop
|
||||
STDDIPINFOEXT(SNESMouseP1P2, SNESMouseP1MouseP2Mouse, SNESMouseBase) // zoop
|
||||
|
||||
static UINT32 CheckControllerPlug()
|
||||
@ -2727,10 +2729,10 @@ struct BurnDriver BurnDrvsnes_Bahalagoonte = {
|
||||
512, 448, 4, 3
|
||||
};
|
||||
|
||||
// Bahamut Lagoon (Hack, Spanish v1.03a)
|
||||
// Bahamut Lagoon (Hack, Spanish v1.03c)
|
||||
// https://crackowia.com/bahamuts.html
|
||||
static struct BurnRomInfo snes_BahalagoontsRomDesc[] = {
|
||||
{ "Bahamut Lagoon T-Spa v1.03a (2022)(Rod Merida).sfc", 8388608, 0x04a290fa, BRF_ESS | BRF_PRG },
|
||||
{ "Bahamut Lagoon T-Spa v1.03c (2022)(Rod Merida).sfc", 8388608, 0xf24350ec, BRF_ESS | BRF_PRG },
|
||||
};
|
||||
|
||||
STD_ROM_PICK(snes_Bahalagoonts)
|
||||
@ -2738,7 +2740,7 @@ STD_ROM_FN(snes_Bahalagoonts)
|
||||
|
||||
struct BurnDriver BurnDrvsnes_Bahalagoonts = {
|
||||
"snes_bahalagoonts", "snes_bahalagoonte", NULL, NULL, "2022",
|
||||
"Bahamut Lagoon (Hack, Spanish v1.03a)\0", NULL, "Rod Merida", "Nintendo",
|
||||
"Bahamut Lagoon (Hack, Spanish v1.03c)\0", NULL, "Rod Merida", "Nintendo",
|
||||
NULL, NULL, NULL, NULL,
|
||||
BDF_GAME_WORKING | BDF_CLONE | BDF_HACK, 1, HARDWARE_SNES, GBF_RPG | GBF_STRATEGY, 0,
|
||||
SNESGetZipName, snes_BahalagoontsRomInfo, snes_BahalagoontsRomName, NULL, NULL, NULL, NULL, SNESInputInfo, SNESDIPInfo,
|
||||
@ -6473,7 +6475,7 @@ struct BurnDriver BurnDrvsnes_Dezaemonj = {
|
||||
// Dezaemon (Hack, English)
|
||||
// https://www.romhacking.net/translations/3749/
|
||||
static struct BurnRomInfo snes_DezaemonteRomDesc[] = {
|
||||
{ "Dezaemon T-Eng (2018)(Aeon Genesis)..sfc", 524288, 0x32f9c958, BRF_ESS | BRF_PRG },
|
||||
{ "Dezaemon T-Eng (2018)(Aeon Genesis).sfc", 524288, 0x32f9c958, BRF_ESS | BRF_PRG },
|
||||
};
|
||||
|
||||
STD_ROM_PICK(snes_Dezaemonte)
|
||||
@ -29676,25 +29678,6 @@ struct BurnDriver BurnDrvsnes_2048 = {
|
||||
512, 448, 4, 3
|
||||
};
|
||||
|
||||
// 4096 (HB)
|
||||
|
||||
static struct BurnRomInfo snes_4096RomDesc[] = {
|
||||
{ "4096 (2015)(e-nost).sfc", 262144, 0xdd073c4b, BRF_ESS | BRF_PRG },
|
||||
};
|
||||
|
||||
STD_ROM_PICK(snes_4096)
|
||||
STD_ROM_FN(snes_4096)
|
||||
|
||||
struct BurnDriver BurnDrvsnes_4096 = {
|
||||
"snes_4096", NULL, NULL, NULL, "2015",
|
||||
"4096 (HB)\0", NULL, "e-nost", "Nintendo",
|
||||
NULL, NULL, NULL, NULL,
|
||||
BDF_GAME_WORKING | BDF_HOMEBREW, 1, HARDWARE_SNES, GBF_PUZZLE, 0,
|
||||
SNESGetZipName, snes_4096RomInfo, snes_4096RomName, NULL, NULL, NULL, NULL, SNESInputInfo, SNESDIPInfo,
|
||||
DrvInit, DrvExit, DrvFrame, DrvDraw, DrvScan, &DrvRecalc, 0x8000,
|
||||
512, 448, 4, 3
|
||||
};
|
||||
|
||||
// Alien Cat 2 Enhanced Edition (HB, v1.1)
|
||||
|
||||
static struct BurnRomInfo snes_Aliencat2RomDesc[] = {
|
||||
@ -30152,10 +30135,10 @@ struct BurnDriver BurnDrvsnes_Ff5rte = {
|
||||
512, 448, 4, 3
|
||||
};
|
||||
|
||||
// Final Fantasy VI Reimagined (Hack)
|
||||
// Final Fantasy VI Reimagined (Hack) - 2024-10-30
|
||||
// https://www.ff6hacking.com/forums/thread-4416.html
|
||||
static struct BurnRomInfo snes_Ff6rmdRomDesc[] = {
|
||||
{ "Final Fantasy VI Reimagined (2024)(DrakeyC).sfc", 4194304, 0xb1148ddf, BRF_ESS | BRF_PRG },
|
||||
{ "Final Fantasy VI Reimagined (2024)(DrakeyC).sfc", 4194304, 0x60aff03d, BRF_ESS | BRF_PRG },
|
||||
};
|
||||
|
||||
STD_ROM_PICK(snes_Ff6rmd)
|
||||
@ -30513,10 +30496,10 @@ struct BurnDriver BurnDrvsnes_Jimpoweree = {
|
||||
512, 448, 4, 3
|
||||
};
|
||||
|
||||
// Legend of Zelda, The - 18 Hours Past (Hack, v1.11)
|
||||
// Legend of Zelda, The - 18 Hours Past (Hack, v1.12)
|
||||
// https://www.romhacking.net/hacks/7732/
|
||||
static struct BurnRomInfo snes_Legendofzelda18hpRomDesc[] = {
|
||||
{ "Legend of Zelda, The - 18 Hours Past v1.11 (2024)(Letterbomb).sfc", 2097152, 0x45d6950f, BRF_ESS | BRF_PRG },
|
||||
{ "Legend of Zelda, The - 18 Hours Past v1.12 (2024)(Letterbomb).sfc", 2097152, 0xc2dc7b83, BRF_ESS | BRF_PRG },
|
||||
};
|
||||
|
||||
STD_ROM_PICK(snes_Legendofzelda18hp)
|
||||
@ -30524,7 +30507,7 @@ STD_ROM_FN(snes_Legendofzelda18hp)
|
||||
|
||||
struct BurnDriver BurnDrvsnes_Legendofzelda18hp = {
|
||||
"snes_legendofzelda18hp", "snes_legendofzelda", NULL, NULL, "2024",
|
||||
"Legend of Zelda, The - 18 Hours Past (Hack, v1.11)\0", NULL, "Letterbomb", "Nintendo",
|
||||
"Legend of Zelda, The - 18 Hours Past (Hack, v1.12)\0", NULL, "Letterbomb", "Nintendo",
|
||||
NULL, NULL, NULL, NULL,
|
||||
BDF_GAME_WORKING | BDF_CLONE | BDF_HACK, 1, HARDWARE_SNES, GBF_ACTION | GBF_ADV, 0,
|
||||
SNESGetZipName, snes_Legendofzelda18hpRomInfo, snes_Legendofzelda18hpRomName, NULL, NULL, NULL, NULL, SNESInputInfo, SNESDIPInfo,
|
||||
@ -31007,10 +30990,10 @@ struct BurnDriver BurnDrvsnes_Racedrivinsa1o = {
|
||||
512, 448, 4, 3
|
||||
};
|
||||
|
||||
// Ranma 1/2 - Chougi Ranbu Hen (Japan)
|
||||
// Ranma 1/2 - Chougi Ranbu Hen (Hack, Framerate Imp. v1.4)
|
||||
// https://romhackplaza.org/romhacks/ranma-1-2-chougi-ranbu-hen-big-framerate-improvement-snes/
|
||||
static struct BurnRomInfo snes_Ranmahb2jfrRomDesc[] = {
|
||||
{ "Ranma 1-2 - Chougi Ranbu Hen frameratehack v1.3 (2024)(Upsilandre).sfc", 4194304, 0x71aaff40, BRF_ESS | BRF_PRG },
|
||||
{ "Ranma 1-2 - Chougi Ranbu Hen frameratehack v1.4 (2024)(Upsilandre).sfc", 4194304, 0x1705415f, BRF_ESS | BRF_PRG },
|
||||
};
|
||||
|
||||
STD_ROM_PICK(snes_Ranmahb2jfr)
|
||||
@ -31018,7 +31001,7 @@ STD_ROM_FN(snes_Ranmahb2jfr)
|
||||
|
||||
struct BurnDriver BurnDrvsnes_Ranmahb2jfr = {
|
||||
"snes_ranmahb2jfr", "snes_ranmahb2te", NULL, NULL, "2024",
|
||||
"Ranma 1/2 - Chougi Ranbu Hen (Hack, Framerate Imp. v1.3)\0", NULL, "Upsilandre", "Nintendo",
|
||||
"Ranma 1/2 - Chougi Ranbu Hen (Hack, Framerate Imp. v1.4)\0", NULL, "Upsilandre", "Nintendo",
|
||||
NULL, NULL, NULL, NULL,
|
||||
BDF_GAME_WORKING | BDF_CLONE | BDF_HACK, 2, HARDWARE_SNES, GBF_VSFIGHT, 0,
|
||||
SNESGetZipName, snes_Ranmahb2jfrRomInfo, snes_Ranmahb2jfrRomName, NULL, NULL, NULL, NULL, SNESInputInfo, SNESDIPInfo,
|
||||
@ -31026,11 +31009,11 @@ struct BurnDriver BurnDrvsnes_Ranmahb2jfr = {
|
||||
512, 448, 4, 3
|
||||
};
|
||||
|
||||
// Ranma 1/2 - Hard Battle II - Super Move Hustle (Hack, English v1.10 + Framerate Imp. v1.3)
|
||||
// Ranma 1/2 - Hard Battle II - Super Move Hustle (Hack, English v1.10 + Framerate Imp. v1.4)
|
||||
// https://www.romhacking.net/translations/3516/
|
||||
// https://romhackplaza.org/romhacks/ranma-1-2-chougi-ranbu-hen-big-framerate-improvement-snes/
|
||||
static struct BurnRomInfo snes_Ranmahb2tefrRomDesc[] = {
|
||||
{ "Ranma 1-2 - Hard Battle II - Super Move Hustle T-Eng + frameratehack v1.3 (2024)(Upsilandre).sfc", 4194304, 0x4401b68c, BRF_ESS | BRF_PRG },
|
||||
{ "Ranma 1-2 - Hard Battle II - Super Move Hustle T-Eng + frameratehack v1.4 (2024)(Upsilandre).sfc", 4194304, 0x22ae0893, BRF_ESS | BRF_PRG },
|
||||
};
|
||||
|
||||
STD_ROM_PICK(snes_Ranmahb2tefr)
|
||||
@ -31038,7 +31021,7 @@ STD_ROM_FN(snes_Ranmahb2tefr)
|
||||
|
||||
struct BurnDriver BurnDrvsnes_Ranmahb2tefr = {
|
||||
"snes_ranmahb2tefr", "snes_ranmahb2te", NULL, NULL, "2024",
|
||||
"Ranma 1/2 - Hard Battle II - Super Move Hustle (Hack, English v1.10 + Framerate Imp. v1.3)\0", NULL, "Dynamic-Designs - Upsilandre", "Nintendo",
|
||||
"Ranma 1/2 - Hard Battle II - Super Move Hustle (Hack, English v1.10 + Framerate Imp. v1.4)\0", NULL, "Dynamic-Designs - Upsilandre", "Nintendo",
|
||||
NULL, NULL, NULL, NULL,
|
||||
BDF_GAME_WORKING | BDF_CLONE | BDF_HACK, 2, HARDWARE_SNES, GBF_VSFIGHT, 0,
|
||||
SNESGetZipName, snes_Ranmahb2tefrRomInfo, snes_Ranmahb2tefrRomName, NULL, NULL, NULL, NULL, SNESInputInfo, SNESDIPInfo,
|
||||
@ -31217,25 +31200,6 @@ struct BurnDriver BurnDrvsnes_Spacegulls = {
|
||||
512, 448, 4, 3
|
||||
};
|
||||
|
||||
// Star Keeper (HB)
|
||||
|
||||
static struct BurnRomInfo snes_StarkeeperRomDesc[] = {
|
||||
{ "Star Keeper (2018)(87Arts).sfc", 524288, 0xd925182f, BRF_ESS | BRF_PRG },
|
||||
};
|
||||
|
||||
STD_ROM_PICK(snes_Starkeeper)
|
||||
STD_ROM_FN(snes_Starkeeper)
|
||||
|
||||
struct BurnDriver BurnDrvsnes_Starkeeper = {
|
||||
"snes_starkeeper", NULL, NULL, NULL, "2018",
|
||||
"Star Keeper (HB)\0", NULL, "87Arts", "Nintendo",
|
||||
NULL, NULL, NULL, NULL,
|
||||
BDF_GAME_WORKING | BDF_HOMEBREW, 1, HARDWARE_SNES, GBF_HORSHOOT, 0,
|
||||
SNESGetZipName, snes_StarkeeperRomInfo, snes_StarkeeperRomName, NULL, NULL, NULL, NULL, SNESInputInfo, SNESDIPInfo,
|
||||
DrvInit, DrvExit, DrvFrame, DrvDraw, DrvScan, &DrvRecalc, 0x8000,
|
||||
512, 448, 4, 3
|
||||
};
|
||||
|
||||
// Steel Talons - Fastrom fix (Hack)
|
||||
// https://www.patreon.com/posts/steel-talons-fix-79800713
|
||||
static struct BurnRomInfo snes_SteeltalonsffRomDesc[] = {
|
||||
@ -31559,25 +31523,6 @@ struct BurnDriver BurnDrvsnes_Supsudoku = {
|
||||
512, 448, 4, 3
|
||||
};
|
||||
|
||||
// Super Xoxol vs. Pigdog (HB)
|
||||
|
||||
static struct BurnRomInfo snes_SupxoxolRomDesc[] = {
|
||||
{ "Super Xoxol vs. Pigdog (2023)(Sergey Cyka).sfc", 1048576, 0xed786ffe, BRF_ESS | BRF_PRG },
|
||||
};
|
||||
|
||||
STD_ROM_PICK(snes_Supxoxol)
|
||||
STD_ROM_FN(snes_Supxoxol)
|
||||
|
||||
struct BurnDriver BurnDrvsnes_Supxoxol = {
|
||||
"snes_supxoxol", NULL, NULL, NULL, "2023",
|
||||
"Super Xoxol vs. Pigdog (HB)\0", NULL, "Sergey Cyka", "Nintendo",
|
||||
NULL, NULL, NULL, NULL,
|
||||
BDF_GAME_WORKING | BDF_HOMEBREW, 2, HARDWARE_SNES, GBF_SCRFIGHT, 0,
|
||||
SNESGetZipName, snes_SupxoxolRomInfo, snes_SupxoxolRomName, NULL, NULL, NULL, NULL, SNESInputInfo, SNESDIPInfo,
|
||||
DrvInit, DrvExit, DrvFrame, DrvDraw, DrvScan, &DrvRecalc, 0x8000,
|
||||
512, 448, 4, 3
|
||||
};
|
||||
|
||||
// Sure Instinct Mini-Edition (HB, v1.0.2)
|
||||
|
||||
static struct BurnRomInfo snes_SureinstinctRomDesc[] = {
|
||||
|
@ -27,6 +27,7 @@ static uint8_t snes_rread(Snes* snes, uint32_t adr); // wrapped by read, to set
|
||||
static int snes_getAccessTime(Snes* snes, uint32_t adr);
|
||||
static void build_accesstime(Snes* snes);
|
||||
static void free_accesstime();
|
||||
static void snes_init_fbn_cheat_subsys();
|
||||
|
||||
static uint8_t *access_time;
|
||||
|
||||
@ -41,6 +42,8 @@ Snes* snes_init(void) {
|
||||
snes->input2 = input_init(snes, 2);
|
||||
snes->palTiming = false;
|
||||
|
||||
snes_init_fbn_cheat_subsys();
|
||||
|
||||
return snes;
|
||||
}
|
||||
|
||||
@ -649,9 +652,126 @@ static void free_accesstime() {
|
||||
BurnFree(access_time);
|
||||
}
|
||||
|
||||
// game genie/par cheat system
|
||||
static UINT8 gg_nibble(UINT8 g)
|
||||
{
|
||||
const UINT8 gg_str[] = "DF4709156BC8A23E";
|
||||
|
||||
for (UINT8 i = 0; i < 0x10; i++) {
|
||||
if (g == gg_str[i]) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static INT32 gg_decode(char *gg_code, UINT32 &address, UINT8 &value)
|
||||
{
|
||||
UINT32 type = strlen(gg_code);
|
||||
UINT32 temp = 0;
|
||||
|
||||
if (type != 8 && type != 9) {
|
||||
// bad code!
|
||||
return 1;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case 8: { // Pro Action Replay (PAR)
|
||||
temp = (UINT32)strtoul(gg_code, NULL, 16);
|
||||
address = (temp >> 8) & 0xffffff;
|
||||
value = temp & 0xff;
|
||||
break;
|
||||
}
|
||||
case 9: { // Game Genie
|
||||
for (int i = 0; i < 9; i++) {
|
||||
if (i != 4) temp = (temp << 4) | gg_nibble(gg_code[i]);
|
||||
}
|
||||
|
||||
value = (temp >> 24) & 0xff;
|
||||
address = (temp >> 10) & 0xf;
|
||||
address = (address << 4) | ((temp >> 2) & 0xf);
|
||||
address = (address << 4) | ((temp >> 20) & 0xf);
|
||||
address = (address << 4) | ((temp << 2) & 0xc) | ((temp >> 14) & 0x3);
|
||||
address = (address << 4) | ((temp >> 16) & 0xf);
|
||||
address = (address << 4) | ((temp >> 6) & 0xf);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const INT32 cheat_MAX = 0x100;
|
||||
static INT32 cheats_active = 0;
|
||||
|
||||
struct cheat_struct {
|
||||
char code[0x10]; // gamegenie, par code
|
||||
UINT32 address;
|
||||
UINT8 value;
|
||||
};
|
||||
|
||||
static cheat_struct cheats[cheat_MAX];
|
||||
|
||||
static void snes_add_cheat(char *code)
|
||||
{
|
||||
UINT32 address;
|
||||
UINT8 value;
|
||||
|
||||
if (!gg_decode(code, address, value) && cheats_active < (cheat_MAX-1)) {
|
||||
strncpy(cheats[cheats_active].code, code, 9);
|
||||
cheats[cheats_active].code[9] = '\0';
|
||||
cheats[cheats_active].address = address;
|
||||
cheats[cheats_active].value = value;
|
||||
bprintf(0, _T("cheat #%d (%S) added. (%x, %x)\n"), cheats_active, cheats[cheats_active].code, address, value);
|
||||
cheats_active++;
|
||||
} else {
|
||||
if (cheats_active < (cheat_MAX-1)) {
|
||||
bprintf(0, _T("snes cheat engine: bad GameGenie code %S\n"), code);
|
||||
} else {
|
||||
bprintf(0, _T("snes cheat engine: too many active!\n"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void snes_remove_cheat(char *code)
|
||||
{
|
||||
cheat_struct cheat_temp[cheat_MAX];
|
||||
INT32 temp_num = 0;
|
||||
|
||||
for (INT32 i = 0; i < cheats_active; i++) {
|
||||
if (strcmp(code, cheats[i].code) != 0) {
|
||||
memcpy(&cheat_temp[temp_num], &cheats[i], sizeof(cheat_struct));
|
||||
temp_num++;
|
||||
} else {
|
||||
bprintf(0, _T("cheat %S disabled.\n"), cheats[i].code);
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(cheats, cheat_temp, sizeof(cheats));
|
||||
cheats_active = temp_num;
|
||||
}
|
||||
|
||||
static void snes_init_fbn_cheat_subsys()
|
||||
{
|
||||
cheats_active = 0;
|
||||
nes_init_cheat_functions(snes_add_cheat, snes_remove_cheat);
|
||||
}
|
||||
|
||||
static inline UINT8 cheat_check(UINT32 address, UINT8 value)
|
||||
{
|
||||
for (INT32 i = 0; i < cheats_active; i++) {
|
||||
if (cheats[i].address == address) {
|
||||
//bprintf(0, _T("."));
|
||||
return cheats[i].value;
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
uint8_t snes_read(Snes* snes, uint32_t adr) {
|
||||
snes->adrBus = adr;
|
||||
uint8_t val = snes_rread(snes, adr);
|
||||
const uint8_t val = cheat_check(adr, snes_rread(snes, adr));
|
||||
snes->openBus = val;
|
||||
return val;
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
#include "burner.h"
|
||||
|
||||
#define HW_NES ( ((BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK) == HARDWARE_NES) || ((BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK) == HARDWARE_FDS) )
|
||||
#define HW_NES ( ((BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK) == HARDWARE_SNES) || ((BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK) == HARDWARE_NES) || ((BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK) == HARDWARE_FDS) )
|
||||
|
||||
static bool SkipComma(TCHAR** s)
|
||||
{
|
||||
@ -237,7 +237,7 @@ static INT32 ConfigParseFile(TCHAR* pszFilename)
|
||||
for (INT32 z = 0; z < strlen(t); z++) {
|
||||
#endif
|
||||
char c = toupper((char)*s);
|
||||
if (c >= 'A' && c <= 'Z' && newlen < 10)
|
||||
if ( ((c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || (c == '-' || c == ':')) && newlen < 10)
|
||||
pCurrentCheat->pOption[n]->AddressInfo[nCurrentAddress].szGenieCode[newlen++] = c;
|
||||
s++;
|
||||
if (*s == _T(',')) break;
|
||||
|
Loading…
Reference in New Issue
Block a user