mirror of https://github.com/bsnes-emu/bsnes.git
Update to bsnes v037 release.
This release adds support for the SNES mouse, Super Scope and Justifier peripherals. It also simplifies cartridge loading and refines the user interface. Lastly, GZ and ZIP archives can now contain non-ANSI characters (Chinese, Japanese, Russian, ...) This support existed in the last release for all uncompressed files. Together, this means only JMA support on Windows lacks support for loading non-ANSI filenames. This is due to the library itself (really, it's more Windows' fault), and licensing issues prevent me from patching libjma as I did with zlib (bsnes is not GPL compatible.) I'm planning to work with Nach to fix this in a future release. About the cartridge loading changes ... the emulator now determines what kind of cartridge is being loaded (eg normal, BS-X BIOS, Sufami Turbo cart, etc) by looking inside the file itself. If it detects a cart type that requires more than one ROM image to load, it will present you with the appropriate specialized load menu automatically. Aside from being more intuitive, this method also allows loading of BS-X and Sufami Turbo games from the command-line or via file association. Changelog: - added mouse support to DirectInput and SDL input drivers - up to 96 buttons per controller; 8 buttons per mouse (5 per mouse on Linux) can be mapped now - added SNES mouse support (does not support speed setting yet) - added Super Scope support - added Justifier support (supports both Justifiers) - input management system almost completely rewritten to support new controllers - "Load Special" menu removed, all cart loading merged to "Load Cartridge ..." option - replaced "Power Cycle" and "Unload Cartridge" with "Power" -> "On" / "Off" - when video exceeds screen size and is scaled down, aspect ratio is now maintained [Ver Greeneyes] - zlib modified to support non-ANSI characters - cheat code count was limited to 1,024 codes before; it now supports unlimited codes per game - added sort by description setting for cheat code list - polished listbox control interaction (disable buttons when nothing selected, etc) - cleaned up OBC-1 chip emulation (code is functionally identical to v036) - added option to toggle fullscreen mode to settings menu - added advanced mode options to toggle base unit (none, Satellaview) and system region (Auto-detect, NTSC, PAL)
This commit is contained in:
parent
20be19f876
commit
a9bff19b5b
14
readme.txt
14
readme.txt
|
@ -1,5 +1,5 @@
|
||||||
bsnes
|
bsnes
|
||||||
Version: 0.036
|
Version: 0.037
|
||||||
Author: byuu
|
Author: byuu
|
||||||
|
|
||||||
========
|
========
|
||||||
|
@ -99,18 +99,10 @@ SETA RISC CPU used by Quick-move Shogi Match with Nidan Rank-holder Morita 2
|
||||||
Super Gameboy
|
Super Gameboy
|
||||||
Cartridge passthrough used for playing Gameboy games
|
Cartridge passthrough used for playing Gameboy games
|
||||||
|
|
||||||
==========================
|
|
||||||
Unsupported Controller(s):
|
|
||||||
==========================
|
|
||||||
|
|
||||||
Mouse
|
|
||||||
Super Scope
|
|
||||||
Justifier
|
|
||||||
|
|
||||||
=============
|
=============
|
||||||
Contributors:
|
Contributors:
|
||||||
=============
|
=============
|
||||||
|
|
||||||
Andreas Naive, anomie, blargg, DMV27, FitzRoy, GIGO, Jonas Quinn, kode54, krom,
|
Andreas Naive, anomie, blargg, DMV27, FitzRoy, GIGO, Jonas Quinn, kode54, krom,
|
||||||
mudlord, Nach, neviksti, Overload, RedDwarf, Richard Bannister, tetsuo55, TRAC,
|
Matthew Callis, mudlord, Nach, neviksti, Overload, RedDwarf, Richard Bannister,
|
||||||
zones
|
tetsuo55, TRAC, zones
|
||||||
|
|
10
src/base.h
10
src/base.h
|
@ -1,4 +1,4 @@
|
||||||
#define BSNES_VERSION "0.036"
|
#define BSNES_VERSION "0.037"
|
||||||
#define BSNES_TITLE "bsnes v" BSNES_VERSION
|
#define BSNES_TITLE "bsnes v" BSNES_VERSION
|
||||||
|
|
||||||
#define BUSCORE sBus
|
#define BUSCORE sBus
|
||||||
|
@ -24,6 +24,7 @@
|
||||||
#include <nall/bit.hpp>
|
#include <nall/bit.hpp>
|
||||||
#include <nall/config.hpp>
|
#include <nall/config.hpp>
|
||||||
#include <nall/detect.hpp>
|
#include <nall/detect.hpp>
|
||||||
|
#include <nall/endian.hpp>
|
||||||
#include <nall/file.hpp>
|
#include <nall/file.hpp>
|
||||||
#include <nall/function.hpp>
|
#include <nall/function.hpp>
|
||||||
#include <nall/modulo.hpp>
|
#include <nall/modulo.hpp>
|
||||||
|
@ -31,6 +32,7 @@
|
||||||
#include <nall/sort.hpp>
|
#include <nall/sort.hpp>
|
||||||
#include <nall/stdint.hpp>
|
#include <nall/stdint.hpp>
|
||||||
#include <nall/string.hpp>
|
#include <nall/string.hpp>
|
||||||
|
#include <nall/utility.hpp>
|
||||||
#include <nall/vector.hpp>
|
#include <nall/vector.hpp>
|
||||||
using namespace nall;
|
using namespace nall;
|
||||||
|
|
||||||
|
@ -42,9 +44,3 @@ void alert(const char*, ...);
|
||||||
void dprintf(const char*, ...);
|
void dprintf(const char*, ...);
|
||||||
|
|
||||||
#include "interface.h"
|
#include "interface.h"
|
||||||
|
|
||||||
//helper: disable access to FILE, when possible (GZIP / JMA require it)
|
|
||||||
//reason: Windows fopen() does not support UTF-8 filenames; use nall::file instead.
|
|
||||||
#if !defined(GZIP_SUPPORT) && !defined(JMA_SUPPORT)
|
|
||||||
#define FILE FILE_deprecated
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -3,7 +3,8 @@
|
||||||
|
|
||||||
#include <nall/crc32.hpp>
|
#include <nall/crc32.hpp>
|
||||||
#include <nall/ups.hpp>
|
#include <nall/ups.hpp>
|
||||||
|
|
||||||
|
#include "cart_load.cpp"
|
||||||
#include "cart_normal.cpp"
|
#include "cart_normal.cpp"
|
||||||
#include "cart_bsx.cpp"
|
#include "cart_bsx.cpp"
|
||||||
#include "cart_bsc.cpp"
|
#include "cart_bsc.cpp"
|
||||||
|
@ -21,12 +22,13 @@ namespace memory {
|
||||||
|
|
||||||
Cartridge cartridge;
|
Cartridge cartridge;
|
||||||
|
|
||||||
|
const char* Cartridge::name() { return info.filename; }
|
||||||
|
Cartridge::CartridgeMode Cartridge::mode() { return info.mode; }
|
||||||
Cartridge::MemoryMapper Cartridge::mapper() { return info.mapper; }
|
Cartridge::MemoryMapper Cartridge::mapper() { return info.mapper; }
|
||||||
Cartridge::Region Cartridge::region() { return info.region; }
|
Cartridge::Region Cartridge::region() { return info.region; }
|
||||||
|
|
||||||
bool Cartridge::loaded() { return cart.loaded; }
|
bool Cartridge::loaded() { return cart.loaded; }
|
||||||
|
|
||||||
void Cartridge::load_begin(CartridgeType cart_type) {
|
void Cartridge::load_begin(CartridgeMode mode) {
|
||||||
cart.rom = cart.ram = cart.rtc = 0;
|
cart.rom = cart.ram = cart.rtc = 0;
|
||||||
bs.ram = 0;
|
bs.ram = 0;
|
||||||
stA.rom = stA.ram = 0;
|
stA.rom = stA.ram = 0;
|
||||||
|
@ -37,38 +39,11 @@ void Cartridge::load_begin(CartridgeType cart_type) {
|
||||||
stA.rom_size = stA.ram_size = 0;
|
stA.rom_size = stA.ram_size = 0;
|
||||||
stB.rom_size = stB.ram_size = 0;
|
stB.rom_size = stB.ram_size = 0;
|
||||||
|
|
||||||
info.type = cart_type;
|
info.mode = mode;
|
||||||
info.patched = false;
|
info.patched = false;
|
||||||
|
|
||||||
info.bsxbase = false;
|
|
||||||
info.bsxcart = false;
|
info.bsxcart = false;
|
||||||
info.bsxflash = false;
|
info.bsxflash = false;
|
||||||
info.st = false;
|
|
||||||
|
|
||||||
info.superfx = false;
|
|
||||||
info.sa1 = false;
|
|
||||||
info.srtc = false;
|
|
||||||
info.sdd1 = false;
|
|
||||||
info.spc7110 = false;
|
|
||||||
info.spc7110rtc = false;
|
|
||||||
info.cx4 = false;
|
|
||||||
info.dsp1 = false;
|
|
||||||
info.dsp2 = false;
|
|
||||||
info.dsp3 = false;
|
|
||||||
info.dsp4 = false;
|
|
||||||
info.obc1 = false;
|
|
||||||
info.st010 = false;
|
|
||||||
info.st011 = false;
|
|
||||||
info.st018 = false;
|
|
||||||
|
|
||||||
info.dsp1_mapper = DSP1Unmapped;
|
|
||||||
|
|
||||||
info.header_index = 0xffc0;
|
|
||||||
info.mapper = LoROM;
|
|
||||||
info.region = NTSC;
|
|
||||||
|
|
||||||
info.rom_size = 0;
|
|
||||||
info.ram_size = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cartridge::load_end() {
|
void Cartridge::load_end() {
|
||||||
|
@ -103,11 +78,11 @@ bool Cartridge::unload() {
|
||||||
|
|
||||||
bus.unload_cart();
|
bus.unload_cart();
|
||||||
|
|
||||||
switch(info.type) {
|
switch(info.mode) {
|
||||||
case CartridgeNormal: unload_cart_normal(); break;
|
case ModeNormal: unload_cart_normal(); break;
|
||||||
case CartridgeBSX: unload_cart_bsx(); break;
|
case ModeBSX: unload_cart_bsx(); break;
|
||||||
case CartridgeBSC: unload_cart_bsc(); break;
|
case ModeBSC: unload_cart_bsc(); break;
|
||||||
case CartridgeSufamiTurbo: unload_cart_st(); break;
|
case ModeSufamiTurbo: unload_cart_st(); break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(cart.rom) { delete[] cart.rom; cart.rom = 0; }
|
if(cart.rom) { delete[] cart.rom; cart.rom = 0; }
|
||||||
|
@ -119,9 +94,6 @@ bool Cartridge::unload() {
|
||||||
if(stB.rom) { delete[] stB.rom; stB.rom = 0; }
|
if(stB.rom) { delete[] stB.rom; stB.rom = 0; }
|
||||||
if(stB.ram) { delete[] stB.ram; stB.ram = 0; }
|
if(stB.ram) { delete[] stB.ram; stB.ram = 0; }
|
||||||
|
|
||||||
char fn[PATH_MAX];
|
|
||||||
strcpy(fn, cart.fn);
|
|
||||||
modify_extension(fn, "cht");
|
|
||||||
if(cheat.count() > 0 || file::exists(get_cheat_filename(cart.fn, "cht"))) {
|
if(cheat.count() > 0 || file::exists(get_cheat_filename(cart.fn, "cht"))) {
|
||||||
cheat.save(cheatfn);
|
cheat.save(cheatfn);
|
||||||
cheat.clear();
|
cheat.clear();
|
||||||
|
@ -138,3 +110,58 @@ Cartridge::Cartridge() {
|
||||||
Cartridge::~Cartridge() {
|
Cartridge::~Cartridge() {
|
||||||
if(cart.loaded == true) unload();
|
if(cart.loaded == true) unload();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
void Cartridge::cartinfo_t::reset() {
|
||||||
|
type = TypeUnknown;
|
||||||
|
mapper = LoROM;
|
||||||
|
dsp1_mapper = DSP1Unmapped;
|
||||||
|
region = NTSC;
|
||||||
|
|
||||||
|
rom_size = 0;
|
||||||
|
ram_size = 0;
|
||||||
|
|
||||||
|
bsxslot = false;
|
||||||
|
superfx = false;
|
||||||
|
sa1 = false;
|
||||||
|
srtc = false;
|
||||||
|
sdd1 = false;
|
||||||
|
spc7110 = false;
|
||||||
|
spc7110rtc = false;
|
||||||
|
cx4 = false;
|
||||||
|
dsp1 = false;
|
||||||
|
dsp2 = false;
|
||||||
|
dsp3 = false;
|
||||||
|
dsp4 = false;
|
||||||
|
obc1 = false;
|
||||||
|
st010 = false;
|
||||||
|
st011 = false;
|
||||||
|
st018 = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//apply cart-specific settings to current cartridge mode settings
|
||||||
|
Cartridge::info_t& Cartridge::info_t::operator=(const Cartridge::cartinfo_t &source) {
|
||||||
|
mapper = source.mapper;
|
||||||
|
dsp1_mapper = source.dsp1_mapper;
|
||||||
|
region = source.region;
|
||||||
|
|
||||||
|
bsxslot = source.bsxslot;
|
||||||
|
superfx = source.superfx;
|
||||||
|
sa1 = source.sa1;
|
||||||
|
srtc = source.srtc;
|
||||||
|
sdd1 = source.sdd1;
|
||||||
|
spc7110 = source.spc7110;
|
||||||
|
spc7110rtc = source.spc7110rtc;
|
||||||
|
cx4 = source.cx4;
|
||||||
|
dsp1 = source.dsp1;
|
||||||
|
dsp2 = source.dsp2;
|
||||||
|
dsp3 = source.dsp3;
|
||||||
|
dsp4 = source.dsp4;
|
||||||
|
obc1 = source.obc1;
|
||||||
|
st010 = source.st010;
|
||||||
|
st011 = source.st011;
|
||||||
|
st018 = source.st018;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
102
src/cart/cart.h
102
src/cart/cart.h
|
@ -1,10 +1,20 @@
|
||||||
class Cartridge {
|
class Cartridge {
|
||||||
public:
|
public:
|
||||||
|
enum CartridgeMode {
|
||||||
|
ModeNormal,
|
||||||
|
ModeBSC,
|
||||||
|
ModeBSX,
|
||||||
|
ModeSufamiTurbo,
|
||||||
|
};
|
||||||
|
|
||||||
enum CartridgeType {
|
enum CartridgeType {
|
||||||
CartridgeNormal,
|
TypeNormal,
|
||||||
CartridgeBSX,
|
TypeBSC,
|
||||||
CartridgeBSC,
|
TypeBSXBIOS,
|
||||||
CartridgeSufamiTurbo,
|
TypeBSX,
|
||||||
|
TypeSufamiTurboBIOS,
|
||||||
|
TypeSufamiTurbo,
|
||||||
|
TypeUnknown,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum HeaderField {
|
enum HeaderField {
|
||||||
|
@ -32,9 +42,9 @@ public:
|
||||||
ExLoROM,
|
ExLoROM,
|
||||||
ExHiROM,
|
ExHiROM,
|
||||||
SPC7110ROM,
|
SPC7110ROM,
|
||||||
BSXROM,
|
|
||||||
BSCLoROM,
|
BSCLoROM,
|
||||||
BSCHiROM,
|
BSCHiROM,
|
||||||
|
BSXROM,
|
||||||
STROM,
|
STROM,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -45,6 +55,11 @@ public:
|
||||||
DSP1HiROM,
|
DSP1HiROM,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const char* name();
|
||||||
|
CartridgeMode mode();
|
||||||
|
MemoryMapper mapper();
|
||||||
|
Region region();
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
bool loaded;
|
bool loaded;
|
||||||
char fn[PATH_MAX];
|
char fn[PATH_MAX];
|
||||||
|
@ -64,26 +79,20 @@ public:
|
||||||
uint rom_size, ram_size;
|
uint rom_size, ram_size;
|
||||||
} stA, stB;
|
} stA, stB;
|
||||||
|
|
||||||
struct {
|
struct cartinfo_t {
|
||||||
CartridgeType type;
|
CartridgeType type;
|
||||||
|
|
||||||
uint32 crc32;
|
|
||||||
char filename[PATH_MAX * 4];
|
|
||||||
bool patched;
|
|
||||||
|
|
||||||
Region region;
|
|
||||||
MemoryMapper mapper;
|
MemoryMapper mapper;
|
||||||
uint rom_size;
|
DSP1MemoryMapper dsp1_mapper;
|
||||||
uint ram_size;
|
Region region;
|
||||||
|
|
||||||
bool bsxbase;
|
unsigned rom_size;
|
||||||
bool bsxcart;
|
unsigned ram_size;
|
||||||
bool bsxflash;
|
|
||||||
bool st;
|
bool bsxslot;
|
||||||
bool superfx;
|
bool superfx;
|
||||||
bool sa1;
|
bool sa1;
|
||||||
bool srtc;
|
bool srtc;
|
||||||
bool sdd1;
|
bool sdd1;
|
||||||
bool spc7110;
|
bool spc7110;
|
||||||
bool spc7110rtc;
|
bool spc7110rtc;
|
||||||
bool cx4;
|
bool cx4;
|
||||||
|
@ -96,17 +105,53 @@ public:
|
||||||
bool st011;
|
bool st011;
|
||||||
bool st018;
|
bool st018;
|
||||||
|
|
||||||
DSP1MemoryMapper dsp1_mapper;
|
void reset();
|
||||||
|
};
|
||||||
|
|
||||||
uint header_index;
|
struct info_t {
|
||||||
|
char filename[PATH_MAX * 4];
|
||||||
|
bool patched;
|
||||||
|
|
||||||
|
CartridgeMode mode;
|
||||||
|
MemoryMapper mapper;
|
||||||
|
DSP1MemoryMapper dsp1_mapper;
|
||||||
|
Region region;
|
||||||
|
|
||||||
|
bool bsxcart; //is BS-X cart inserted?
|
||||||
|
bool bsxflash; //is BS-X flash cart inserted into BS-X cart?
|
||||||
|
|
||||||
|
bool bsxslot;
|
||||||
|
bool superfx;
|
||||||
|
bool sa1;
|
||||||
|
bool srtc;
|
||||||
|
bool sdd1;
|
||||||
|
bool spc7110;
|
||||||
|
bool spc7110rtc;
|
||||||
|
bool cx4;
|
||||||
|
bool dsp1;
|
||||||
|
bool dsp2;
|
||||||
|
bool dsp3;
|
||||||
|
bool dsp4;
|
||||||
|
bool obc1;
|
||||||
|
bool st010;
|
||||||
|
bool st011;
|
||||||
|
bool st018;
|
||||||
|
|
||||||
|
info_t& operator=(const cartinfo_t&);
|
||||||
} info;
|
} info;
|
||||||
|
|
||||||
MemoryMapper mapper();
|
struct {
|
||||||
Region region();
|
char fn[PATH_MAX];
|
||||||
|
uint8_t *data;
|
||||||
|
unsigned size;
|
||||||
|
} image;
|
||||||
|
bool load_image(const char*);
|
||||||
|
bool inspect_image(cartinfo_t &cartinfo, const char *filename);
|
||||||
|
bool load_ram(const char *filename, uint8_t *&data, unsigned size, uint8_t init);
|
||||||
|
|
||||||
void load_cart_normal(const char*);
|
void load_cart_normal(const char*);
|
||||||
void load_cart_bsx(const char*, const char*);
|
|
||||||
void load_cart_bsc(const char*, const char*);
|
void load_cart_bsc(const char*, const char*);
|
||||||
|
void load_cart_bsx(const char*, const char*);
|
||||||
void load_cart_st(const char*, const char*, const char*);
|
void load_cart_st(const char*, const char*, const char*);
|
||||||
|
|
||||||
void unload_cart_normal();
|
void unload_cart_normal();
|
||||||
|
@ -115,14 +160,13 @@ public:
|
||||||
void unload_cart_st();
|
void unload_cart_st();
|
||||||
|
|
||||||
bool loaded();
|
bool loaded();
|
||||||
void load_begin(CartridgeType);
|
void load_begin(CartridgeMode);
|
||||||
void load_end();
|
void load_end();
|
||||||
bool unload();
|
bool unload();
|
||||||
|
|
||||||
unsigned score_header(unsigned);
|
void read_header(cartinfo_t &info, const uint8_t *data, unsigned size);
|
||||||
void find_header();
|
unsigned find_header(const uint8_t *data, unsigned size);
|
||||||
void read_header();
|
unsigned score_header(const uint8_t *data, unsigned size, unsigned addr);
|
||||||
void read_extended_header();
|
|
||||||
|
|
||||||
enum CompressionMode {
|
enum CompressionMode {
|
||||||
CompressionNone, //always load without compression
|
CompressionNone, //always load without compression
|
||||||
|
|
|
@ -1,57 +1,36 @@
|
||||||
#ifdef CART_CPP
|
#ifdef CART_CPP
|
||||||
|
|
||||||
void Cartridge::load_cart_bsc(const char *base, const char *slot) {
|
void Cartridge::load_cart_bsc(const char *base, const char *slot) {
|
||||||
if(!base || !*base) return;
|
uint8_t *data;
|
||||||
|
|
||||||
strcpy(cart.fn, base);
|
|
||||||
strcpy(bs.fn, slot ? slot : "");
|
|
||||||
load_begin(CartridgeBSC);
|
|
||||||
|
|
||||||
uint8_t *data = 0;
|
|
||||||
unsigned size;
|
unsigned size;
|
||||||
load_file(cart.fn, data, size, CompressionAuto);
|
strcpy(cart.fn, base);
|
||||||
cart.rom = data, cart.rom_size = size;
|
strcpy(bs.fn, slot);
|
||||||
|
|
||||||
if(load_file(get_patch_filename(cart.fn, "ups"), data, size, CompressionInspect) == true) {
|
load_begin(ModeBSC);
|
||||||
apply_patch(data, size, cart.rom, cart.rom_size);
|
if(load_image(base) == false) return;
|
||||||
delete[] data;
|
|
||||||
|
cartinfo_t cartinfo;
|
||||||
|
read_header(cartinfo, cart.rom = image.data, cart.rom_size = image.size);
|
||||||
|
info = cartinfo;
|
||||||
|
|
||||||
|
if(load_image(slot) == true) {
|
||||||
|
info.bsxflash = true;
|
||||||
|
bs.ram = image.data;
|
||||||
|
bs.ram_size = image.size;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(*bs.fn) {
|
if(cartinfo.ram_size > 0) {
|
||||||
if(load_file(bs.fn, data, size, CompressionAuto) == true) {
|
load_ram(get_save_filename(base, "srm"), cart.ram, cart.ram_size = cartinfo.ram_size, 0xff);
|
||||||
info.bsxflash = true;
|
|
||||||
bs.ram = data, bs.ram_size = size;
|
|
||||||
if(load_file(get_patch_filename(bs.fn, "ups"), data, size, CompressionInspect) == true) {
|
|
||||||
apply_patch(data, size, bs.ram, bs.ram_size);
|
|
||||||
delete[] data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
find_header();
|
|
||||||
read_header();
|
|
||||||
|
|
||||||
info.mapper = cartridge.info.header_index == 0x7fc0 ? BSCLoROM : BSCHiROM;
|
|
||||||
info.region = NTSC;
|
|
||||||
|
|
||||||
if(info.ram_size > 0) {
|
|
||||||
cart.ram = new uint8_t[cart.ram_size = info.ram_size];
|
|
||||||
memset(cart.ram, 0xff, cart.ram_size);
|
|
||||||
|
|
||||||
if(load_file(get_save_filename(cart.fn, "srm"), data, size, CompressionNone) == true) {
|
|
||||||
memcpy(cart.ram, data, min(size, cart.ram_size));
|
|
||||||
delete[] data;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
load_end();
|
load_end();
|
||||||
|
|
||||||
//set base filename
|
//set base filename
|
||||||
strcpy(info.filename, cart.fn);
|
strcpy(info.filename, base);
|
||||||
get_base_filename(info.filename);
|
get_base_filename(info.filename);
|
||||||
if(*bs.fn) {
|
if(*slot) {
|
||||||
char filenameBS[PATH_MAX];
|
char filenameBS[PATH_MAX];
|
||||||
strcpy(filenameBS, bs.fn);
|
strcpy(filenameBS, slot);
|
||||||
get_base_filename(filenameBS);
|
get_base_filename(filenameBS);
|
||||||
strcat(info.filename, " + ");
|
strcat(info.filename, " + ");
|
||||||
strcat(info.filename, filenameBS);
|
strcat(info.filename, filenameBS);
|
||||||
|
|
|
@ -1,27 +1,20 @@
|
||||||
#ifdef CART_CPP
|
#ifdef CART_CPP
|
||||||
|
|
||||||
void Cartridge::load_cart_bsx(const char *base, const char *slot) {
|
void Cartridge::load_cart_bsx(const char *base, const char *slot) {
|
||||||
if(!base || !*base) return;
|
uint8_t *data;
|
||||||
|
|
||||||
strcpy(cart.fn, base);
|
|
||||||
strcpy(bs.fn, slot ? slot : "");
|
|
||||||
|
|
||||||
load_begin(CartridgeBSX);
|
|
||||||
info.bsxbase = true;
|
|
||||||
info.bsxcart = true;
|
|
||||||
info.mapper = BSXROM;
|
|
||||||
info.region = NTSC;
|
|
||||||
|
|
||||||
uint8_t *data = 0;
|
|
||||||
unsigned size;
|
unsigned size;
|
||||||
load_file(cart.fn, data, size, CompressionAuto);
|
strcpy(cart.fn, base);
|
||||||
cart.rom = data, cart.rom_size = size;
|
strcpy(bs.fn, slot);
|
||||||
cart.ram = 0, cart.ram_size = 0;
|
|
||||||
|
|
||||||
if(load_file(get_patch_filename(cart.fn, "ups"), data, size, CompressionInspect) == true) {
|
load_begin(ModeBSX);
|
||||||
apply_patch(data, size, cart.rom, cart.rom_size);
|
if(load_image(base) == false) return;
|
||||||
delete[] data;
|
info.bsxcart = true;
|
||||||
}
|
|
||||||
|
cartinfo_t cartinfo;
|
||||||
|
read_header(cartinfo, cart.rom = image.data, cart.rom_size = image.size);
|
||||||
|
info = cartinfo;
|
||||||
|
cart.ram = 0;
|
||||||
|
cart.ram_size = 0;
|
||||||
|
|
||||||
memset(bsxcart.sram.handle (), 0x00, bsxcart.sram.size ());
|
memset(bsxcart.sram.handle (), 0x00, bsxcart.sram.size ());
|
||||||
memset(bsxcart.psram.handle(), 0x00, bsxcart.psram.size());
|
memset(bsxcart.psram.handle(), 0x00, bsxcart.psram.size());
|
||||||
|
@ -36,20 +29,15 @@ void Cartridge::load_cart_bsx(const char *base, const char *slot) {
|
||||||
delete[] data;
|
delete[] data;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(*bs.fn) {
|
if(load_image(slot)) {
|
||||||
if(load_file(bs.fn, data, size, CompressionAuto) == true) {
|
info.bsxflash = true;
|
||||||
info.bsxflash = true;
|
bs.ram = image.data;
|
||||||
bs.ram = data, bs.ram_size = size;
|
bs.ram_size = image.size;
|
||||||
if(load_file(get_patch_filename(bs.fn, "ups"), data, size, CompressionInspect) == true) {
|
|
||||||
apply_patch(data, size, bs.ram, bs.ram_size);
|
|
||||||
delete[] data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
load_end();
|
load_end();
|
||||||
|
|
||||||
strcpy(info.filename, !*bs.fn ? cart.fn : bs.fn);
|
strcpy(info.filename, !*slot ? base : slot);
|
||||||
get_base_filename(info.filename);
|
get_base_filename(info.filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,30 +1,70 @@
|
||||||
#ifdef CART_CPP
|
#ifdef CART_CPP
|
||||||
|
|
||||||
void Cartridge::read_header() {
|
void Cartridge::read_header(cartinfo_t &info, const uint8_t *data, unsigned size) {
|
||||||
uint8 *rom = cart.rom;
|
info.reset();
|
||||||
uint index = info.header_index;
|
unsigned index = find_header(data, size);
|
||||||
uint8 mapper = rom[index + MAPPER];
|
|
||||||
uint8 rom_type = rom[index + ROM_TYPE];
|
|
||||||
uint8 rom_size = rom[index + ROM_SIZE];
|
|
||||||
uint8 company = rom[index + COMPANY];
|
|
||||||
uint8 region = rom[index + REGION] & 0x7f;
|
|
||||||
|
|
||||||
//detect presence of BS-X flash cartridge connector (reads extended header information)
|
//detect BS-X flash carts
|
||||||
bool has_bsxflash = false;
|
if(data[index + 0x13] == 0x00 || data[index + 0x13] == 0xff) {
|
||||||
if(rom[index - 14] == 'Z') {
|
if(data[index + 0x14] == 0x00) {
|
||||||
if(rom[index - 11] == 'J') {
|
const uint8_t n15 = data[index + 0x15];
|
||||||
uint8 n13 = rom[index - 13];
|
if(n15 == 0x00 || n15 == 0x80 || n15 == 0x84 || n15 == 0x9c || n15 == 0xbc || n15 == 0xfc) {
|
||||||
if((n13 >= 'A' && n13 <= 'Z') || (n13 >= '0' && n13 <= '9')) {
|
if(data[index + 0x1a] == 0x33 || data[index + 0x1a] == 0xff) {
|
||||||
if(company == 0x33 || (rom[index - 10] == 0x00 && rom[index - 4] == 0x00)) {
|
info.type = TypeBSX;
|
||||||
has_bsxflash = true;
|
info.mapper = BSXROM;
|
||||||
|
info.region = NTSC; //BS-X only released in Japan
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(has_bsxflash == true) {
|
//detect Sufami Turbo carts
|
||||||
info.mapper = index == 0x7fc0 ? BSCLoROM : BSCHiROM;
|
if(!memcmp(data, "BANDAI SFC-ADX", 14)) {
|
||||||
} else if(index == 0x7fc0 && cart.rom_size >= 0x401000) {
|
if(!memcmp(data + 16, "SFC-ADX BACKUP", 14)) {
|
||||||
|
info.type = TypeSufamiTurboBIOS;
|
||||||
|
} else {
|
||||||
|
info.type = TypeSufamiTurbo;
|
||||||
|
}
|
||||||
|
info.mapper = STROM;
|
||||||
|
info.region = NTSC; //Sufami Turbo only released in Japan
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//standard cart
|
||||||
|
uint8 mapper = data[index + MAPPER];
|
||||||
|
uint8 rom_type = data[index + ROM_TYPE];
|
||||||
|
uint8 rom_size = data[index + ROM_SIZE];
|
||||||
|
uint8 company = data[index + COMPANY];
|
||||||
|
uint8 region = data[index + REGION] & 0x7f;
|
||||||
|
|
||||||
|
//detect presence of BS-X flash cartridge connector (reads extended header information)
|
||||||
|
if(data[index - 14] == 'Z') {
|
||||||
|
if(data[index - 11] == 'J') {
|
||||||
|
uint8 n13 = data[index - 13];
|
||||||
|
if((n13 >= 'A' && n13 <= 'Z') || (n13 >= '0' && n13 <= '9')) {
|
||||||
|
if(company == 0x33 || (data[index - 10] == 0x00 && data[index - 4] == 0x00)) {
|
||||||
|
info.bsxslot = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(info.bsxslot == true) {
|
||||||
|
if(!memcmp(data + index, "Satellaview BS-X ", 21)) {
|
||||||
|
//BS-X base cart
|
||||||
|
info.type = TypeBSXBIOS;
|
||||||
|
info.mapper = BSXROM;
|
||||||
|
} else {
|
||||||
|
info.type = TypeBSC;
|
||||||
|
info.mapper = (index == 0x7fc0 ? BSCLoROM : BSCHiROM);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
info.type = TypeNormal;
|
||||||
|
|
||||||
|
if(index == 0x7fc0 && size >= 0x401000) {
|
||||||
info.mapper = ExLoROM;
|
info.mapper = ExLoROM;
|
||||||
} else if(index == 0x7fc0 && mapper == 0x32) {
|
} else if(index == 0x7fc0 && mapper == 0x32) {
|
||||||
info.mapper = ExLoROM;
|
info.mapper = ExLoROM;
|
||||||
|
@ -75,7 +115,7 @@ void Cartridge::read_header() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if(info.dsp1 == true) {
|
if(info.dsp1 == true) {
|
||||||
if((mapper & 0x2f) == 0x20 && cart.rom_size <= 0x100000) {
|
if((mapper & 0x2f) == 0x20 && size <= 0x100000) {
|
||||||
info.dsp1_mapper = DSP1LoROM1MB;
|
info.dsp1_mapper = DSP1LoROM1MB;
|
||||||
} else if((mapper & 0x2f) == 0x20) {
|
} else if((mapper & 0x2f) == 0x20) {
|
||||||
info.dsp1_mapper = DSP1LoROM2MB;
|
info.dsp1_mapper = DSP1LoROM2MB;
|
||||||
|
@ -112,8 +152,8 @@ void Cartridge::read_header() {
|
||||||
info.st018 = true;
|
info.st018 = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(rom[info.header_index + RAM_SIZE] & 7) {
|
if(data[index + RAM_SIZE] & 7) {
|
||||||
info.ram_size = 1024 << (rom[info.header_index + RAM_SIZE] & 7);
|
info.ram_size = 1024 << (data[index + RAM_SIZE] & 7);
|
||||||
} else {
|
} else {
|
||||||
info.ram_size = 0;
|
info.ram_size = 0;
|
||||||
}
|
}
|
||||||
|
@ -122,17 +162,31 @@ void Cartridge::read_header() {
|
||||||
info.region = (region <= 1 || region >= 13) ? NTSC : PAL;
|
info.region = (region <= 1 || region >= 13) ? NTSC : PAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned Cartridge::score_header(unsigned addr) {
|
unsigned Cartridge::find_header(const uint8_t *data, unsigned size) {
|
||||||
if(cart.rom_size < addr + 64) return 0; //image too small to contain header at this location?
|
unsigned score_lo = score_header(data, size, 0x007fc0);
|
||||||
uint8 *rom = cart.rom;
|
unsigned score_hi = score_header(data, size, 0x00ffc0);
|
||||||
|
unsigned score_ex = score_header(data, size, 0x40ffc0);
|
||||||
|
if(score_ex) score_ex += 4; //favor ExHiROM on images > 32mbits
|
||||||
|
|
||||||
|
if(score_lo >= score_hi && score_lo >= score_ex) {
|
||||||
|
return 0x007fc0;
|
||||||
|
} else if(score_hi >= score_ex) {
|
||||||
|
return 0x00ffc0;
|
||||||
|
} else {
|
||||||
|
return 0x40ffc0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned Cartridge::score_header(const uint8_t *data, unsigned size, unsigned addr) {
|
||||||
|
if(size < addr + 64) return 0; //image too small to contain header at this location?
|
||||||
int score = 0;
|
int score = 0;
|
||||||
|
|
||||||
uint16 resetvector = rom[addr + RESETV] | (rom[addr + RESETV + 1] << 8);
|
uint16 resetvector = data[addr + RESETV] | (data[addr + RESETV + 1] << 8);
|
||||||
uint16 checksum = rom[addr + CKSUM] | (rom[addr + CKSUM + 1] << 8);
|
uint16 checksum = data[addr + CKSUM] | (data[addr + CKSUM + 1] << 8);
|
||||||
uint16 ichecksum = rom[addr + ICKSUM] | (rom[addr + ICKSUM + 1] << 8);
|
uint16 ichecksum = data[addr + ICKSUM] | (data[addr + ICKSUM + 1] << 8);
|
||||||
|
|
||||||
uint8 resetop = rom[(addr & ~0x7fff) | (resetvector & 0x7fff)]; //first opcode executed upon reset
|
uint8 resetop = data[(addr & ~0x7fff) | (resetvector & 0x7fff)]; //first opcode executed upon reset
|
||||||
uint8 mapper = rom[addr + MAPPER] & ~0x10; //mask off irrelevent FastROM-capable bit
|
uint8 mapper = data[addr + MAPPER] & ~0x10; //mask off irrelevent FastROM-capable bit
|
||||||
|
|
||||||
//$00:[000-7fff] contains uninitialized RAM and MMIO.
|
//$00:[000-7fff] contains uninitialized RAM and MMIO.
|
||||||
//reset vector must point to ROM at $00:[8000-ffff] to be considered valid.
|
//reset vector must point to ROM at $00:[8000-ffff] to be considered valid.
|
||||||
|
@ -194,29 +248,14 @@ unsigned Cartridge::score_header(unsigned addr) {
|
||||||
if(addr == 0x007fc0 && mapper == 0x22) score += 2; //0x22 is usually ExLoROM
|
if(addr == 0x007fc0 && mapper == 0x22) score += 2; //0x22 is usually ExLoROM
|
||||||
if(addr == 0x40ffc0 && mapper == 0x25) score += 2; //0x25 is usually ExHiROM
|
if(addr == 0x40ffc0 && mapper == 0x25) score += 2; //0x25 is usually ExHiROM
|
||||||
|
|
||||||
if(rom[addr + COMPANY] == 0x33) score += 2; //0x33 indicates extended header
|
if(data[addr + COMPANY] == 0x33) score += 2; //0x33 indicates extended header
|
||||||
if(rom[addr + ROM_TYPE] < 0x08) score++;
|
if(data[addr + ROM_TYPE] < 0x08) score++;
|
||||||
if(rom[addr + ROM_SIZE] < 0x10) score++;
|
if(data[addr + ROM_SIZE] < 0x10) score++;
|
||||||
if(rom[addr + RAM_SIZE] < 0x08) score++;
|
if(data[addr + RAM_SIZE] < 0x08) score++;
|
||||||
if(rom[addr + REGION] < 14) score++;
|
if(data[addr + REGION] < 14) score++;
|
||||||
|
|
||||||
if(score < 0) score = 0;
|
if(score < 0) score = 0;
|
||||||
return score;
|
return score;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cartridge::find_header() {
|
|
||||||
unsigned score_lo = score_header(0x007fc0);
|
|
||||||
unsigned score_hi = score_header(0x00ffc0);
|
|
||||||
unsigned score_ex = score_header(0x40ffc0);
|
|
||||||
if(score_ex) score_ex += 4; //favor ExHiROM on images > 32mbits
|
|
||||||
|
|
||||||
if(score_lo >= score_hi && score_lo >= score_ex) {
|
|
||||||
info.header_index = 0x007fc0;
|
|
||||||
} else if(score_hi >= score_ex) {
|
|
||||||
info.header_index = 0x00ffc0;
|
|
||||||
} else {
|
|
||||||
info.header_index = 0x40ffc0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif //ifdef CART_CPP
|
#endif //ifdef CART_CPP
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
#ifdef CART_CPP
|
||||||
|
|
||||||
|
bool Cartridge::load_image(const char *filename) {
|
||||||
|
if(!filename || !*filename) return false;
|
||||||
|
|
||||||
|
uint8_t *data;
|
||||||
|
unsigned size;
|
||||||
|
if(!load_file(filename, data, size, CompressionAuto)) return false;
|
||||||
|
|
||||||
|
if((size & 0x7fff) != 512) {
|
||||||
|
image.data = data;
|
||||||
|
image.size = size;
|
||||||
|
} else {
|
||||||
|
//remove 512-byte header
|
||||||
|
image.data = new uint8_t[image.size = size - 512];
|
||||||
|
memcpy(image.data, data + 512, image.size);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(load_file(get_patch_filename(filename, "ups"), data, size, CompressionInspect) == true) {
|
||||||
|
apply_patch(data, size, image.data, image.size);
|
||||||
|
delete[] data;
|
||||||
|
info.patched = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Cartridge::inspect_image(cartinfo_t &cartinfo, const char *filename) {
|
||||||
|
cartinfo.reset();
|
||||||
|
if(!load_image(filename)) return false;
|
||||||
|
|
||||||
|
read_header(cartinfo, image.data, image.size);
|
||||||
|
delete[] image.data;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Cartridge::load_ram(const char *filename, uint8_t *&data, unsigned size, uint8_t init) {
|
||||||
|
data = new uint8_t[size];
|
||||||
|
memset(data, init, size);
|
||||||
|
|
||||||
|
uint8_t *savedata;
|
||||||
|
unsigned savesize;
|
||||||
|
if(load_file(filename, savedata, savesize, CompressionNone) == false) return false;
|
||||||
|
|
||||||
|
memcpy(data, savedata, min(size, savesize));
|
||||||
|
delete[] savedata;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //ifdef CART_CPP
|
|
@ -1,58 +1,29 @@
|
||||||
#ifdef CART_CPP
|
#ifdef CART_CPP
|
||||||
|
|
||||||
void Cartridge::load_cart_normal(const char *filename) {
|
void Cartridge::load_cart_normal(const char *base) {
|
||||||
if(!filename || !*filename) return;
|
uint8_t *data;
|
||||||
|
|
||||||
uint8_t *data = 0;
|
|
||||||
unsigned size;
|
unsigned size;
|
||||||
if(load_file(filename, data, size, CompressionAuto) == false) return;
|
strcpy(cart.fn, base);
|
||||||
strcpy(cart.fn, filename);
|
|
||||||
|
|
||||||
load_begin(CartridgeNormal);
|
load_begin(ModeNormal);
|
||||||
|
if(load_image(base) == false) return;
|
||||||
|
|
||||||
//load ROM data, ignore 512-byte header if detected
|
cartinfo_t cartinfo;
|
||||||
if((size & 0x7fff) != 512) {
|
read_header(cartinfo, cart.rom = image.data, cart.rom_size = image.size);
|
||||||
cart.rom = new uint8_t[cart.rom_size = size];
|
info = cartinfo;
|
||||||
memcpy(cart.rom, data, size);
|
|
||||||
} else {
|
|
||||||
cart.rom = new uint8_t[cart.rom_size = size - 512];
|
|
||||||
memcpy(cart.rom, data + 512, size - 512);
|
|
||||||
}
|
|
||||||
delete[] data;
|
|
||||||
|
|
||||||
if(load_file(get_patch_filename(cart.fn, "ups"), data, size, CompressionInspect) == true) {
|
if(cartinfo.ram_size > 0) {
|
||||||
apply_patch(data, size, cart.rom, cart.rom_size);
|
load_ram(get_save_filename(base, "srm"), cart.ram, cart.ram_size = cartinfo.ram_size, 0xff);
|
||||||
delete[] data;
|
|
||||||
info.patched = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
info.crc32 = crc32_calculate(cart.rom, cart.rom_size);
|
if(cartinfo.srtc || cartinfo.spc7110rtc) {
|
||||||
|
load_ram(get_save_filename(base, "rtc"), cart.rtc, cart.rtc_size = 20, 0x00);
|
||||||
find_header();
|
|
||||||
read_header();
|
|
||||||
|
|
||||||
if(info.ram_size > 0) {
|
|
||||||
cart.ram = new uint8_t[cart.ram_size = info.ram_size];
|
|
||||||
memset(cart.ram, 0xff, cart.ram_size);
|
|
||||||
|
|
||||||
if(load_file(get_save_filename(cart.fn, "srm"), data, size, CompressionNone) == true) {
|
|
||||||
memcpy(cart.ram, data, min(size, cart.ram_size));
|
|
||||||
delete[] data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(info.srtc || info.spc7110rtc) {
|
|
||||||
cart.rtc = new(zeromemory) uint8_t[cart.rtc_size = 20];
|
|
||||||
if(load_file(get_save_filename(cart.fn, "rtc"), data, size, CompressionNone) == true) {
|
|
||||||
memcpy(cart.rtc, data, min(size, cart.rtc_size));
|
|
||||||
delete[] data;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
load_end();
|
load_end();
|
||||||
|
|
||||||
//set base filename
|
//set base filename
|
||||||
strcpy(info.filename, cart.fn);
|
strcpy(info.filename, base);
|
||||||
get_base_filename(info.filename);
|
get_base_filename(info.filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,86 +1,52 @@
|
||||||
#ifdef CART_CPP
|
#ifdef CART_CPP
|
||||||
|
|
||||||
void Cartridge::load_cart_st(const char *base, const char *slotA, const char *slotB) {
|
void Cartridge::load_cart_st(const char *base, const char *slotA, const char *slotB) {
|
||||||
if(!base || !*base) return;
|
uint8_t *data;
|
||||||
|
|
||||||
strcpy(cart.fn, base);
|
|
||||||
strcpy(stA.fn, slotA ? slotA : "");
|
|
||||||
strcpy(stB.fn, slotB ? slotB : "");
|
|
||||||
|
|
||||||
load_begin(CartridgeSufamiTurbo);
|
|
||||||
info.st = true;
|
|
||||||
info.mapper = STROM;
|
|
||||||
info.region = NTSC;
|
|
||||||
|
|
||||||
uint8_t *data = 0;
|
|
||||||
unsigned size;
|
unsigned size;
|
||||||
if(load_file(cart.fn, data, size, CompressionAuto) == true) {
|
strcpy(cart.fn, base);
|
||||||
cart.rom = new(zeromemory) uint8_t[cart.rom_size = 0x040000];
|
strcpy(stA.fn, slotA);
|
||||||
memcpy(cart.rom, data, min(size, cart.rom_size));
|
strcpy(stB.fn, slotB);
|
||||||
delete[] data;
|
|
||||||
if(load_file(get_patch_filename(cart.fn, "ups"), data, size, CompressionInspect) == true) {
|
load_begin(ModeSufamiTurbo);
|
||||||
apply_patch(data, size, cart.rom, cart.rom_size);
|
if(load_image(base) == false) return;
|
||||||
delete[] data;
|
|
||||||
}
|
cartinfo_t cartinfo;
|
||||||
|
read_header(cartinfo, cart.rom = image.data, cart.rom_size = image.size);
|
||||||
|
info = cartinfo;
|
||||||
|
|
||||||
|
if(load_image(slotA)) {
|
||||||
|
stA.rom = new(zeromemory) uint8_t[stA.rom_size = 0x100000];
|
||||||
|
memcpy(stA.rom, image.data, min(image.size, stA.rom_size));
|
||||||
|
delete[] image.data;
|
||||||
|
|
||||||
|
load_ram(get_save_filename(slotA, "srm"), stA.ram, stA.ram_size = 0x020000, 0xff);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(*stA.fn) {
|
if(load_image(slotB)) {
|
||||||
if(load_file(stA.fn, data, size, CompressionAuto) == true) {
|
stB.rom = new(zeromemory) uint8_t[stB.rom_size = 0x100000];
|
||||||
stA.rom = new(zeromemory) uint8_t[stA.rom_size = 0x100000];
|
memcpy(stB.rom, image.data, min(image.size, stB.rom_size));
|
||||||
memcpy(stA.rom, data, min(size, stA.rom_size));
|
delete[] image.data;
|
||||||
delete[] data;
|
|
||||||
if(load_file(get_patch_filename(stA.fn, "ups"), data, size, CompressionInspect) == true) {
|
|
||||||
apply_patch(data, size, stA.rom, stA.rom_size);
|
|
||||||
delete[] data;
|
|
||||||
}
|
|
||||||
|
|
||||||
stA.ram = new uint8_t[stA.ram_size = 0x020000];
|
load_ram(get_save_filename(slotB, "srm"), stB.ram, stB.ram_size = 0x020000, 0xff);
|
||||||
memset(stA.ram, 0xff, stA.ram_size);
|
|
||||||
|
|
||||||
if(load_file(get_save_filename(stA.fn, "srm"), data, size, CompressionNone) == true) {
|
|
||||||
memcpy(stA.ram, data, min(size, 0x020000U));
|
|
||||||
delete[] data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(*stB.fn) {
|
|
||||||
if(load_file(stB.fn, data, size, CompressionAuto) == true) {
|
|
||||||
stB.rom = new(zeromemory) uint8_t[stB.rom_size = 0x100000];
|
|
||||||
memcpy(stB.rom, data, min(size, stB.rom_size));
|
|
||||||
delete[] data;
|
|
||||||
if(load_file(get_patch_filename(stB.fn, "ups"), data, size, CompressionInspect) == true) {
|
|
||||||
apply_patch(data, size, stB.rom, stB.rom_size);
|
|
||||||
delete[] data;
|
|
||||||
}
|
|
||||||
|
|
||||||
stB.ram = new uint8_t[stB.ram_size = 0x020000];
|
|
||||||
memset(stB.ram, 0xff, stB.ram_size);
|
|
||||||
|
|
||||||
if(load_file(get_save_filename(stB.fn, "srm"), data, size, CompressionNone) == true) {
|
|
||||||
memcpy(stB.ram, data, min(size, 0x020000U));
|
|
||||||
delete[] data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
load_end();
|
load_end();
|
||||||
|
|
||||||
//set base filename
|
//set base filename
|
||||||
if(!*stA.fn && !*stB.fn) {
|
if(!*slotA && !*slotB) {
|
||||||
strcpy(info.filename, cart.fn);
|
strcpy(info.filename, cart.fn);
|
||||||
get_base_filename(info.filename);
|
get_base_filename(info.filename);
|
||||||
} else if(*stA.fn && !*stB.fn) {
|
} else if(*slotA && !*slotB) {
|
||||||
strcpy(info.filename, stA.fn);
|
strcpy(info.filename, slotA);
|
||||||
get_base_filename(info.filename);
|
get_base_filename(info.filename);
|
||||||
} else if(!*stA.fn && *stB.fn) {
|
} else if(!*slotA && *slotB) {
|
||||||
strcpy(info.filename, stB.fn);
|
strcpy(info.filename, slotB);
|
||||||
get_base_filename(info.filename);
|
get_base_filename(info.filename);
|
||||||
} else {
|
} else {
|
||||||
char filenameA[PATH_MAX], filenameB[PATH_MAX];
|
char filenameA[PATH_MAX], filenameB[PATH_MAX];
|
||||||
strcpy(filenameA, stA.fn);
|
strcpy(filenameA, slotA);
|
||||||
get_base_filename(filenameA);
|
get_base_filename(filenameA);
|
||||||
strcpy(filenameB, stB.fn);
|
strcpy(filenameB, slotB);
|
||||||
get_base_filename(filenameB);
|
get_base_filename(filenameB);
|
||||||
strcpy(info.filename, filenameA);
|
strcpy(info.filename, filenameA);
|
||||||
strcat(info.filename, " + ");
|
strcat(info.filename, " + ");
|
||||||
|
|
|
@ -1,30 +1,53 @@
|
||||||
#include "../base.h"
|
#include "../base.h"
|
||||||
#include "../reader/filereader.h"
|
|
||||||
|
|
||||||
Cheat cheat;
|
Cheat cheat;
|
||||||
|
|
||||||
|
Cheat::cheat_t& Cheat::cheat_t::operator=(const Cheat::cheat_t& source) {
|
||||||
|
enabled = source.enabled;
|
||||||
|
addr = source.addr;
|
||||||
|
data = source.data;
|
||||||
|
code = source.code;
|
||||||
|
desc = source.desc;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
//used to sort cheat code list by description
|
||||||
|
bool Cheat::cheat_t::operator<(const Cheat::cheat_t& source) {
|
||||||
|
return strcmp(desc, source.desc) < 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*****
|
/*****
|
||||||
* string <> binary code translation routines
|
* string <> binary code translation routines
|
||||||
* decode() "7e1234:56" -> 0x7e123456
|
* decode() "7e1234:56" -> 0x7e123456
|
||||||
* encode() 0x7e123456 -> "7e1234:56"
|
* encode() 0x7e123456 -> "7e1234:56"
|
||||||
*****/
|
*****/
|
||||||
|
|
||||||
bool Cheat::decode(char *str, uint32 &addr, uint8 &data, uint8 &type) {
|
bool Cheat::decode(const char *str, unsigned &addr, uint8 &data, type_t &type) {
|
||||||
string t, part;
|
string t = str;
|
||||||
strcpy(t, str);
|
strlower(t);
|
||||||
strlower(t());
|
|
||||||
if(strlen(t) == 8 || (strlen(t) == 9 && t()[6] == ':')) {
|
#define ischr(n) ((n >= '0' && n <= '9') || (n >= 'a' && n <= 'f'))
|
||||||
|
|
||||||
|
if(strlen(t) == 8 || (strlen(t) == 9 && t[6] == ':')) {
|
||||||
|
//strip ':'
|
||||||
|
if(strlen(t) == 9 && t[6] == ':') t = string() << substr(t, 0, 6) << substr(t, 7);
|
||||||
|
//validate input
|
||||||
|
for(unsigned i = 0; i < 8; i++) if(!ischr(t[i])) return false;
|
||||||
|
|
||||||
type = ProActionReplay;
|
type = ProActionReplay;
|
||||||
replace(t, ":", "");
|
unsigned r = strhex((const char*)t);
|
||||||
uint32 r = strhex((const char*)t);
|
|
||||||
addr = r >> 8;
|
addr = r >> 8;
|
||||||
data = r & 0xff;
|
data = r & 0xff;
|
||||||
return true;
|
return true;
|
||||||
} else if(strlen(t) == 9 && t()[4] == '-') {
|
} else if(strlen(t) == 9 && t[4] == '-') {
|
||||||
|
//strip '-'
|
||||||
|
t = string() << substr(t, 0, 4) << substr(t, 5);
|
||||||
|
//validate input
|
||||||
|
for(unsigned i = 0; i < 8; i++) if(!ischr(t[i])) return false;
|
||||||
|
|
||||||
type = GameGenie;
|
type = GameGenie;
|
||||||
replace(t, "-", "");
|
|
||||||
strtr(t, "df4709156bc8a23e", "0123456789abcdef");
|
strtr(t, "df4709156bc8a23e", "0123456789abcdef");
|
||||||
uint32 r = strhex((const char*)t);
|
unsigned r = strhex((const char*)t);
|
||||||
//8421 8421 8421 8421 8421 8421
|
//8421 8421 8421 8421 8421 8421
|
||||||
//abcd efgh ijkl mnop qrst uvwx
|
//abcd efgh ijkl mnop qrst uvwx
|
||||||
//ijkl qrst opab cduv wxef ghmn
|
//ijkl qrst opab cduv wxef ghmn
|
||||||
|
@ -42,16 +65,20 @@ bool Cheat::decode(char *str, uint32 &addr, uint8 &data, uint8 &type) {
|
||||||
(!!(r & 0x000080) << 1) | (!!(r & 0x000040) << 0);
|
(!!(r & 0x000080) << 1) | (!!(r & 0x000040) << 0);
|
||||||
data = r >> 24;
|
data = r >> 24;
|
||||||
return true;
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Cheat::encode(char *str, uint32 addr, uint8 data, uint8 type) {
|
bool Cheat::encode(string &str, unsigned addr, uint8 data, type_t type) {
|
||||||
|
char t[16];
|
||||||
|
|
||||||
if(type == ProActionReplay) {
|
if(type == ProActionReplay) {
|
||||||
sprintf(str, "%0.6x:%0.2x", addr, data);
|
sprintf(t, "%0.6x:%0.2x", addr, data);
|
||||||
|
str = t;
|
||||||
return true;
|
return true;
|
||||||
} else if(type == GameGenie) {
|
} else if(type == GameGenie) {
|
||||||
uint32 r = addr;
|
unsigned r = addr;
|
||||||
addr = (!!(r & 0x008000) << 23) | (!!(r & 0x004000) << 22) |
|
addr = (!!(r & 0x008000) << 23) | (!!(r & 0x004000) << 22) |
|
||||||
(!!(r & 0x002000) << 21) | (!!(r & 0x001000) << 20) |
|
(!!(r & 0x002000) << 21) | (!!(r & 0x001000) << 20) |
|
||||||
(!!(r & 0x000080) << 19) | (!!(r & 0x000040) << 18) |
|
(!!(r & 0x000080) << 19) | (!!(r & 0x000040) << 18) |
|
||||||
|
@ -64,11 +91,13 @@ bool Cheat::encode(char *str, uint32 addr, uint8 data, uint8 type) {
|
||||||
(!!(r & 0x080000) << 5) | (!!(r & 0x040000) << 4) |
|
(!!(r & 0x080000) << 5) | (!!(r & 0x040000) << 4) |
|
||||||
(!!(r & 0x020000) << 3) | (!!(r & 0x010000) << 2) |
|
(!!(r & 0x020000) << 3) | (!!(r & 0x010000) << 2) |
|
||||||
(!!(r & 0x000800) << 1) | (!!(r & 0x000400) << 0);
|
(!!(r & 0x000800) << 1) | (!!(r & 0x000400) << 0);
|
||||||
sprintf(str, "%0.2x%0.2x-%0.4x", data, addr >> 16, addr & 0xffff);
|
sprintf(t, "%0.2x%0.2x-%0.4x", data, addr >> 16, addr & 0xffff);
|
||||||
strtr(str, "0123456789abcdef", "df4709156bc8a23e");
|
strtr(t, "0123456789abcdef", "df4709156bc8a23e");
|
||||||
|
str = t;
|
||||||
return true;
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*****
|
/*****
|
||||||
|
@ -78,21 +107,21 @@ bool Cheat::encode(char *str, uint32 addr, uint8 data, uint8 type) {
|
||||||
* clear() disable specified address, mirror accordingly
|
* clear() disable specified address, mirror accordingly
|
||||||
*****/
|
*****/
|
||||||
|
|
||||||
uint Cheat::mirror_address(uint addr) {
|
unsigned Cheat::mirror_address(unsigned addr) const {
|
||||||
if((addr & 0x40e000) != 0x0000) return addr;
|
if((addr & 0x40e000) != 0x0000) return addr;
|
||||||
//8k WRAM mirror
|
//8k WRAM mirror
|
||||||
//$[00-3f|80-bf]:[0000-1fff] -> $7e:[0000-1fff]
|
//$[00-3f|80-bf]:[0000-1fff] -> $7e:[0000-1fff]
|
||||||
return (0x7e0000 + (addr & 0x1fff));
|
return (0x7e0000 + (addr & 0x1fff));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cheat::set(uint32 addr) {
|
void Cheat::set(unsigned addr) {
|
||||||
addr = mirror_address(addr);
|
addr = mirror_address(addr);
|
||||||
|
|
||||||
mask[addr >> 3] |= 1 << (addr & 7);
|
mask[addr >> 3] |= 1 << (addr & 7);
|
||||||
if((addr & 0xffe000) == 0x7e0000) {
|
if((addr & 0xffe000) == 0x7e0000) {
|
||||||
//mirror $7e:[0000-1fff] to $[00-3f|80-bf]:[0000-1fff]
|
//mirror $7e:[0000-1fff] to $[00-3f|80-bf]:[0000-1fff]
|
||||||
uint mirror;
|
unsigned mirror;
|
||||||
for(int x = 0; x <= 0x3f; x++) {
|
for(unsigned x = 0; x <= 0x3f; x++) {
|
||||||
mirror = ((0x00 + x) << 16) + (addr & 0x1fff);
|
mirror = ((0x00 + x) << 16) + (addr & 0x1fff);
|
||||||
mask[mirror >> 3] |= 1 << (mirror & 7);
|
mask[mirror >> 3] |= 1 << (mirror & 7);
|
||||||
mirror = ((0x80 + x) << 16) + (addr & 0x1fff);
|
mirror = ((0x80 + x) << 16) + (addr & 0x1fff);
|
||||||
|
@ -101,20 +130,20 @@ void Cheat::set(uint32 addr) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cheat::clear(uint32 addr) {
|
void Cheat::clear(unsigned addr) {
|
||||||
addr = mirror_address(addr);
|
addr = mirror_address(addr);
|
||||||
|
|
||||||
//is there more than one cheat code using the same address
|
//if there is more than one cheat code using the same address,
|
||||||
//(and likely a different override value) that is enabled?
|
//(eg with a different override value) then do not clear code
|
||||||
//if so, do not clear code lookup table entry for this address.
|
//lookup table entry.
|
||||||
uint8 r;
|
uint8 r;
|
||||||
if(read(addr, r) == true)return;
|
if(read(addr, r) == true) return;
|
||||||
|
|
||||||
mask[addr >> 3] &= ~(1 << (addr & 7));
|
mask[addr >> 3] &= ~(1 << (addr & 7));
|
||||||
if((addr & 0xffe000) == 0x7e0000) {
|
if((addr & 0xffe000) == 0x7e0000) {
|
||||||
//mirror $7e:[0000-1fff] to $[00-3f|80-bf]:[0000-1fff]
|
//mirror $7e:[0000-1fff] to $[00-3f|80-bf]:[0000-1fff]
|
||||||
uint mirror;
|
unsigned mirror;
|
||||||
for(int x = 0; x <= 0x3f; x++) {
|
for(unsigned x = 0; x <= 0x3f; x++) {
|
||||||
mirror = ((0x00 + x) << 16) + (addr & 0x1fff);
|
mirror = ((0x00 + x) << 16) + (addr & 0x1fff);
|
||||||
mask[mirror >> 3] &= ~(1 << (mirror & 7));
|
mask[mirror >> 3] &= ~(1 << (mirror & 7));
|
||||||
mirror = ((0x80 + x) << 16) + (addr & 0x1fff);
|
mirror = ((0x80 + x) << 16) + (addr & 0x1fff);
|
||||||
|
@ -130,12 +159,12 @@ void Cheat::clear(uint32 addr) {
|
||||||
* when true, cheat code substitution value is stored in data.
|
* when true, cheat code substitution value is stored in data.
|
||||||
*****/
|
*****/
|
||||||
|
|
||||||
bool Cheat::read(uint32 addr, uint8 &data) {
|
bool Cheat::read(unsigned addr, uint8 &data) const {
|
||||||
addr = mirror_address(addr);
|
addr = mirror_address(addr);
|
||||||
for(int i = 0; i < cheat_count; i++) {
|
for(unsigned i = 0; i < code.size(); i++) {
|
||||||
if(enabled(i) == false) continue;
|
if(enabled(i) == false) continue;
|
||||||
if(addr == mirror_address(index[i].addr)) {
|
if(addr == mirror_address(code[i].addr)) {
|
||||||
data = index[i].data;
|
data = code[i].data;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -150,96 +179,74 @@ bool Cheat::read(uint32 addr, uint8 &data) {
|
||||||
*****/
|
*****/
|
||||||
|
|
||||||
void Cheat::update_cheat_status() {
|
void Cheat::update_cheat_status() {
|
||||||
for(unsigned i = 0; i < cheat_count; i++) {
|
for(unsigned i = 0; i < code.size(); i++) {
|
||||||
if(index[i].enabled) {
|
if(code[i].enabled) {
|
||||||
cheat_enabled = true;
|
cheat_system_enabled = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cheat_enabled = false;
|
cheat_system_enabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*****
|
/*****
|
||||||
* cheat list manipulation routines
|
* cheat list manipulation routines
|
||||||
*****/
|
*****/
|
||||||
|
|
||||||
bool Cheat::add(bool enable, char *code, char *desc) {
|
bool Cheat::add(bool enable, const char *code_, const char *desc_) {
|
||||||
if(cheat_count >= CheatLimit) return false;
|
unsigned addr;
|
||||||
|
uint8 data;
|
||||||
|
type_t type;
|
||||||
|
if(decode(code_, addr, data, type) == false) return false;
|
||||||
|
|
||||||
uint32 addr, len;
|
unsigned n = code.size();
|
||||||
uint8 data, type;
|
code[n].enabled = enable;
|
||||||
if(decode(code, addr, data, type) == false) return false;
|
code[n].addr = addr;
|
||||||
|
code[n].data = data;
|
||||||
index[cheat_count].enabled = enable;
|
code[n].code = code_;
|
||||||
index[cheat_count].addr = addr;
|
code[n].desc = desc_;
|
||||||
index[cheat_count].data = data;
|
|
||||||
len = strlen(code);
|
|
||||||
len = len > 16 ? 16 : len;
|
|
||||||
memcpy(index[cheat_count].code, code, len);
|
|
||||||
index[cheat_count].code[len] = 0;
|
|
||||||
len = strlen(desc);
|
|
||||||
len = len > 128 ? 128 : len;
|
|
||||||
memcpy(index[cheat_count].desc, desc, len);
|
|
||||||
index[cheat_count].desc[len] = 0;
|
|
||||||
cheat_count++;
|
|
||||||
(enable) ? set(addr) : clear(addr);
|
(enable) ? set(addr) : clear(addr);
|
||||||
|
|
||||||
update_cheat_status();
|
update_cheat_status();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Cheat::edit(uint32 n, bool enable, char *code, char *desc) {
|
bool Cheat::edit(unsigned n, bool enable, const char *code_, const char *desc_) {
|
||||||
if(n >= cheat_count) return false;
|
unsigned addr;
|
||||||
|
uint8 data;
|
||||||
uint32 addr, len;
|
type_t type;
|
||||||
uint8 data, type;
|
if(decode(code_, addr, data, type) == false) return false;
|
||||||
if(decode(code, addr, data, type) == false) return false;
|
|
||||||
|
|
||||||
//disable current code and clear from code lookup table
|
//disable current code and clear from code lookup table
|
||||||
index[n].enabled = false;
|
code[n].enabled = false;
|
||||||
clear(index[n].addr);
|
clear(code[n].addr);
|
||||||
|
|
||||||
//update code and enable in code lookup table
|
//update code and enable in code lookup table
|
||||||
index[n].enabled = enable;
|
code[n].enabled = enable;
|
||||||
index[n].addr = addr;
|
code[n].addr = addr;
|
||||||
index[n].data = data;
|
code[n].data = data;
|
||||||
len = strlen(code);
|
code[n].code = code_;
|
||||||
len = len > 16 ? 16 : len;
|
code[n].desc = desc_;
|
||||||
memcpy(index[n].code, code, len);
|
|
||||||
index[n].code[len] = 0;
|
|
||||||
len = strlen(desc);
|
|
||||||
len = len > 128 ? 128 : len;
|
|
||||||
memcpy(index[n].desc, desc, len);
|
|
||||||
index[n].desc[len] = 0;
|
|
||||||
set(addr);
|
set(addr);
|
||||||
|
|
||||||
update_cheat_status();
|
update_cheat_status();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Cheat::remove(uint32 n) {
|
bool Cheat::remove(unsigned n) {
|
||||||
if(n >= cheat_count) return false;
|
unsigned size = code.size();
|
||||||
|
if(n >= size) return false; //also verifies size cannot be < 1
|
||||||
|
|
||||||
for(unsigned i = n; i < cheat_count; i++) {
|
for(unsigned i = n; i < size - 1; i++) code[i] = code[i + 1];
|
||||||
index[i].enabled = index[i + 1].enabled;
|
code.resize(size - 1);
|
||||||
index[i].addr = index[i + 1].addr;
|
|
||||||
index[i].data = index[i + 1].data;
|
|
||||||
strcpy(index[i].desc, index[i + 1].desc);
|
|
||||||
}
|
|
||||||
|
|
||||||
cheat_count--;
|
|
||||||
|
|
||||||
update_cheat_status();
|
update_cheat_status();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Cheat::get(uint32 n, bool &enable, uint32 &addr, uint8 &data, char *code, char *desc) {
|
bool Cheat::get(unsigned n, cheat_t &cheat) const {
|
||||||
if(n >= cheat_count) return false;
|
if(n >= code.size()) return false;
|
||||||
enable = index[n].enabled;
|
|
||||||
addr = index[n].addr;
|
cheat = code[n];
|
||||||
data = index[n].data;
|
|
||||||
strcpy(code, index[n].code);
|
|
||||||
strcpy(desc, index[n].desc);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,22 +254,23 @@ bool Cheat::get(uint32 n, bool &enable, uint32 &addr, uint8 &data, char *code, c
|
||||||
* code status modifier routines
|
* code status modifier routines
|
||||||
*****/
|
*****/
|
||||||
|
|
||||||
bool Cheat::enabled(uint32 n) {
|
bool Cheat::enabled(unsigned n) const {
|
||||||
if(n >= cheat_count) return false;
|
return (n < code.size()) ? code[n].enabled : false;
|
||||||
return index[n].enabled;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cheat::enable(uint32 n) {
|
void Cheat::enable(unsigned n) {
|
||||||
if(n >= cheat_count) return;
|
if(n >= code.size()) return;
|
||||||
index[n].enabled = true;
|
|
||||||
set(index[n].addr);
|
code[n].enabled = true;
|
||||||
|
set(code[n].addr);
|
||||||
update_cheat_status();
|
update_cheat_status();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cheat::disable(uint32 n) {
|
void Cheat::disable(unsigned n) {
|
||||||
if(n >= cheat_count) return;
|
if(n >= code.size()) return;
|
||||||
index[n].enabled = false;
|
|
||||||
clear(index[n].addr);
|
code[n].enabled = false;
|
||||||
|
clear(code[n].addr);
|
||||||
update_cheat_status();
|
update_cheat_status();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,40 +296,39 @@ bool Cheat::load(const char *fn) {
|
||||||
split(part, ",", line[i]);
|
split(part, ",", line[i]);
|
||||||
if(::count(part) != 3) continue;
|
if(::count(part) != 3) continue;
|
||||||
trim(part[2], "\"");
|
trim(part[2], "\"");
|
||||||
add(part[1] == "enabled", part[0](), part[2]());
|
add(part[1] == "enabled", part[0], part[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Cheat::save(const char *fn) {
|
bool Cheat::save(const char *fn) const {
|
||||||
file fp;
|
file fp;
|
||||||
if(!fp.open(fn, file::mode_write)) return false;
|
if(!fp.open(fn, file::mode_write)) return false;
|
||||||
for(unsigned i = 0; i < cheat_count; i++) {
|
for(unsigned i = 0; i < code.size(); i++) {
|
||||||
fp.print(string()
|
fp.print(string()
|
||||||
<< index[i].code << " = "
|
<< code[i].code << " = "
|
||||||
<< (index[i].enabled ? "enabled" : "disabled") << ", \""
|
<< (code[i].enabled ? "enabled" : "disabled") << ", "
|
||||||
<< index[i].desc << "\"\r\n");
|
<< "\"" << code[i].desc << "\""
|
||||||
|
<< "\r\n");
|
||||||
}
|
}
|
||||||
fp.close();
|
fp.close();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*****
|
void Cheat::sort() {
|
||||||
* initialization routines
|
if(code.size() <= 1) return; //nothing to sort?
|
||||||
*****/
|
cheat_t *buffer = new cheat_t[code.size()];
|
||||||
|
for(unsigned i = 0; i < code.size(); i++) buffer[i] = code[i];
|
||||||
|
nall::sort(buffer, code.size());
|
||||||
|
for(unsigned i = 0; i < code.size(); i++) code[i] = buffer[i];
|
||||||
|
delete[] buffer;
|
||||||
|
}
|
||||||
|
|
||||||
void Cheat::clear() {
|
void Cheat::clear() {
|
||||||
cheat_enabled = false;
|
cheat_system_enabled = false;
|
||||||
cheat_count = 0;
|
|
||||||
memset(mask, 0, 0x200000);
|
memset(mask, 0, 0x200000);
|
||||||
for(unsigned i = 0; i <= CheatLimit; i++) {
|
code.reset();
|
||||||
index[i].enabled = false;
|
|
||||||
index[i].addr = 0x000000;
|
|
||||||
index[i].data = 0x00;
|
|
||||||
strcpy(index[i].code, "");
|
|
||||||
strcpy(index[i].desc, "");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Cheat::Cheat() {
|
Cheat::Cheat() {
|
||||||
|
|
|
@ -1,51 +1,55 @@
|
||||||
class Cheat {
|
class Cheat {
|
||||||
public:
|
public:
|
||||||
enum { CheatLimit = 1024 };
|
enum type_t {
|
||||||
|
ProActionReplay,
|
||||||
enum Type {
|
GameGenie,
|
||||||
ProActionReplay,
|
|
||||||
GameGenie,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CheatIndex {
|
struct cheat_t {
|
||||||
bool enabled;
|
bool enabled;
|
||||||
uint32 addr;
|
unsigned addr;
|
||||||
uint8 data;
|
uint8 data;
|
||||||
char code[ 16 + 1];
|
string code;
|
||||||
char desc[128 + 1];
|
string desc;
|
||||||
} index[CheatLimit + 1];
|
|
||||||
|
|
||||||
bool cheat_enabled;
|
|
||||||
uint32 cheat_count;
|
|
||||||
uint8 mask[0x200000];
|
|
||||||
|
|
||||||
inline bool enabled() { return cheat_enabled; }
|
cheat_t& operator=(const cheat_t&);
|
||||||
inline uint count() { return cheat_count; }
|
bool operator<(const cheat_t&);
|
||||||
inline bool exists(uint32 addr) { return bool(mask[addr >> 3] & 1 << (addr & 7)); }
|
};
|
||||||
|
|
||||||
bool decode(char *str, uint32 &addr, uint8 &data, uint8 &type);
|
static bool decode(const char *str, unsigned &addr, uint8 &data, type_t &type);
|
||||||
bool encode(char *str, uint32 addr, uint8 data, uint8 type);
|
static bool encode(string &str, unsigned addr, uint8 data, type_t type);
|
||||||
|
|
||||||
bool read(uint32 addr, uint8 &data);
|
inline bool enabled() const { return cheat_system_enabled; }
|
||||||
|
inline unsigned count() const { return code.size(); }
|
||||||
|
inline bool exists(unsigned addr) const { return bool(mask[addr >> 3] & 1 << (addr & 7)); }
|
||||||
|
|
||||||
|
bool read(unsigned addr, uint8 &data) const;
|
||||||
|
|
||||||
|
bool add(bool enable, const char *code, const char *desc);
|
||||||
|
bool edit(unsigned n, bool enable, const char *code, const char *desc);
|
||||||
|
bool get(unsigned n, cheat_t &cheat) const;
|
||||||
|
bool remove(unsigned n);
|
||||||
|
bool enabled(unsigned n) const;
|
||||||
|
void enable(unsigned n);
|
||||||
|
void disable(unsigned n);
|
||||||
|
|
||||||
void update_cheat_status();
|
|
||||||
bool add(bool enable, char *code, char *desc);
|
|
||||||
bool edit(uint32 n, bool enable, char *code, char *desc);
|
|
||||||
bool get(uint32 n, bool &enable, uint32 &addr, uint8 &data, char *code, char *desc);
|
|
||||||
bool remove(uint32 n);
|
|
||||||
bool enabled(uint32 n);
|
|
||||||
void enable(uint32 n);
|
|
||||||
void disable(uint32 n);
|
|
||||||
bool load(const char *fn);
|
bool load(const char *fn);
|
||||||
bool save(const char *fn);
|
bool save(const char *fn) const;
|
||||||
|
|
||||||
|
void sort();
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
Cheat();
|
Cheat();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint mirror_address(uint addr);
|
bool cheat_system_enabled;
|
||||||
void set(uint32 addr);
|
uint8 mask[0x200000];
|
||||||
void clear(uint32 addr);
|
vector<cheat_t> code;
|
||||||
|
|
||||||
|
void update_cheat_status();
|
||||||
|
unsigned mirror_address(unsigned addr) const;
|
||||||
|
void set(unsigned addr);
|
||||||
|
void clear(unsigned addr);
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Cheat cheat;
|
extern Cheat cheat;
|
||||||
|
|
|
@ -8,80 +8,61 @@ void OBC1::power() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void OBC1::reset() {
|
void OBC1::reset() {
|
||||||
for(uint i = 0x0000; i <= 0x1fff; i++) ram_write(i, 0xff);
|
for(unsigned i = 0x0000; i <= 0x1fff; i++) ram_write(i, 0xff);
|
||||||
|
|
||||||
status.baseptr = (ram_read(0x1ff5) & 1) ? 0x1800 : 0x1c00;
|
status.baseptr = (ram_read(0x1ff5) & 1) ? 0x1800 : 0x1c00;
|
||||||
status.address = (ram_read(0x1ff6) & 0x7f);
|
status.address = (ram_read(0x1ff6) & 0x7f);
|
||||||
status.shift = (ram_read(0x1ff6) & 3) << 1;
|
status.shift = (ram_read(0x1ff6) & 3) << 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 OBC1::read(uint addr) {
|
uint8 OBC1::read(unsigned addr) {
|
||||||
addr &= 0x1fff;
|
addr &= 0x1fff;
|
||||||
if((addr & 0x1ff8) != 0x1ff0) return ram_read(addr);
|
if((addr & 0x1ff8) != 0x1ff0) return ram_read(addr);
|
||||||
|
|
||||||
switch(addr) {
|
switch(addr) { default: //never used, avoids compiler warning
|
||||||
case 0x1ff0:
|
case 0x1ff0: return ram_read(status.baseptr + (status.address << 2) + 0);
|
||||||
return ram_read(status.baseptr + (status.address << 2) + 0);
|
case 0x1ff1: return ram_read(status.baseptr + (status.address << 2) + 1);
|
||||||
case 0x1ff1:
|
case 0x1ff2: return ram_read(status.baseptr + (status.address << 2) + 2);
|
||||||
return ram_read(status.baseptr + (status.address << 2) + 1);
|
case 0x1ff3: return ram_read(status.baseptr + (status.address << 2) + 3);
|
||||||
case 0x1ff2:
|
case 0x1ff4: return ram_read(status.baseptr + (status.address >> 2) + 0x200);
|
||||||
return ram_read(status.baseptr + (status.address << 2) + 2);
|
case 0x1ff5: case 0x1ff6: case 0x1ff7: return ram_read(addr);
|
||||||
case 0x1ff3:
|
|
||||||
return ram_read(status.baseptr + (status.address << 2) + 3);
|
|
||||||
case 0x1ff4:
|
|
||||||
return ram_read(status.baseptr + (status.address >> 2) + 0x200);
|
|
||||||
case 0x1ff5:
|
|
||||||
case 0x1ff6:
|
|
||||||
case 0x1ff7:
|
|
||||||
return ram_read(addr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0x00; //never used, avoids compiler warning
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void OBC1::write(uint addr, uint8 data) {
|
void OBC1::write(unsigned addr, uint8 data) {
|
||||||
addr &= 0x1fff;
|
addr &= 0x1fff;
|
||||||
if((addr & 0x1ff8) != 0x1ff0) return ram_write(addr, data);
|
if((addr & 0x1ff8) != 0x1ff0) return ram_write(addr, data);
|
||||||
|
|
||||||
switch(addr) {
|
switch(addr) {
|
||||||
case 0x1ff0:
|
case 0x1ff0: ram_write(status.baseptr + (status.address << 2) + 0, data); break;
|
||||||
ram_write(status.baseptr + (status.address << 2) + 0, data);
|
case 0x1ff1: ram_write(status.baseptr + (status.address << 2) + 1, data); break;
|
||||||
break;
|
case 0x1ff2: ram_write(status.baseptr + (status.address << 2) + 2, data); break;
|
||||||
case 0x1ff1:
|
case 0x1ff3: ram_write(status.baseptr + (status.address << 2) + 3, data); break;
|
||||||
ram_write(status.baseptr + (status.address << 2) + 1, data);
|
case 0x1ff4: {
|
||||||
break;
|
uint8 temp = ram_read(status.baseptr + (status.address >> 2) + 0x200);
|
||||||
case 0x1ff2:
|
temp = (temp & ~(3 << status.shift)) | ((data & 3) << status.shift);
|
||||||
ram_write(status.baseptr + (status.address << 2) + 2, data);
|
ram_write(status.baseptr + (status.address >> 2) + 0x200, temp);
|
||||||
break;
|
} break;
|
||||||
case 0x1ff3:
|
case 0x1ff5: {
|
||||||
ram_write(status.baseptr + (status.address << 2) + 3, data);
|
status.baseptr = (data & 1) ? 0x1800 : 0x1c00;
|
||||||
break;
|
ram_write(addr, data);
|
||||||
case 0x1ff4: {
|
} break;
|
||||||
uint8 temp;
|
case 0x1ff6: {
|
||||||
temp = ram_read(status.baseptr + (status.address >> 2) + 0x200);
|
status.address = (data & 0x7f);
|
||||||
temp = (temp & ~(3 << status.shift)) | ((data & 3) << status.shift);
|
status.shift = (data & 3) << 1;
|
||||||
ram_write(status.baseptr + (status.address >> 2) + 0x200, temp);
|
ram_write(addr, data);
|
||||||
} break;
|
} break;
|
||||||
case 0x1ff5:
|
case 0x1ff7: {
|
||||||
status.baseptr = (data & 1) ? 0x1800 : 0x1c00;
|
ram_write(addr, data);
|
||||||
ram_write(addr, data);
|
} break;
|
||||||
break;
|
|
||||||
case 0x1ff6:
|
|
||||||
status.address = (data & 0x7f);
|
|
||||||
status.shift = (data & 3) << 1;
|
|
||||||
ram_write(addr, data);
|
|
||||||
break;
|
|
||||||
case 0x1ff7:
|
|
||||||
ram_write(addr, data);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 OBC1::ram_read(uint addr) {
|
uint8 OBC1::ram_read(unsigned addr) {
|
||||||
return memory::cartram.read(addr & 0x1fff);
|
return memory::cartram.read(addr & 0x1fff);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OBC1::ram_write(uint addr, uint8 data) {
|
void OBC1::ram_write(unsigned addr, uint8 data) {
|
||||||
memory::cartram.write(addr & 0x1fff, data);
|
memory::cartram.write(addr & 0x1fff, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,15 +5,15 @@ public:
|
||||||
void power();
|
void power();
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
uint8 read(uint addr);
|
uint8 read(unsigned addr);
|
||||||
void write(uint addr, uint8 data);
|
void write(unsigned addr, uint8 data);
|
||||||
|
|
||||||
OBC1();
|
OBC1();
|
||||||
~OBC1();
|
~OBC1();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint8 ram_read(uint addr);
|
uint8 ram_read(unsigned addr);
|
||||||
void ram_write(uint addr, uint8 data);
|
void ram_write(unsigned addr, uint8 data);
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
uint16 address;
|
uint16 address;
|
||||||
|
|
|
@ -139,7 +139,7 @@ unsigned SRTC::weekday(unsigned year, unsigned month, unsigned day) {
|
||||||
return (sum + 1) % 7; //1900-01-01 was a Monday
|
return (sum + 1) % 7; //1900-01-01 was a Monday
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 SRTC::mmio_read(uint addr) {
|
uint8 SRTC::mmio_read(unsigned addr) {
|
||||||
addr &= 0xffff;
|
addr &= 0xffff;
|
||||||
|
|
||||||
if(addr == 0x2800) {
|
if(addr == 0x2800) {
|
||||||
|
@ -160,7 +160,7 @@ uint8 SRTC::mmio_read(uint addr) {
|
||||||
return cpu.regs.mdr;
|
return cpu.regs.mdr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SRTC::mmio_write(uint addr, uint8 data) {
|
void SRTC::mmio_write(unsigned addr, uint8 data) {
|
||||||
addr &= 0xffff;
|
addr &= 0xffff;
|
||||||
|
|
||||||
if(addr == 0x2801) {
|
if(addr == 0x2801) {
|
||||||
|
|
|
@ -8,8 +8,8 @@ public:
|
||||||
void power();
|
void power();
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
uint8 mmio_read (uint addr);
|
uint8 mmio_read (unsigned addr);
|
||||||
void mmio_write(uint addr, uint8 data);
|
void mmio_write(unsigned addr, uint8 data);
|
||||||
|
|
||||||
SRTC();
|
SRTC();
|
||||||
|
|
||||||
|
|
|
@ -3,22 +3,22 @@ namespace config {
|
||||||
configuration& config() {
|
configuration& config() {
|
||||||
static configuration config;
|
static configuration config;
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
integral_setting File::autodetect_type(config(), "file.autodetect_type",
|
integral_setting File::autodetect_type(config(), "file.autodetect_type",
|
||||||
"Auto-detect file type by inspecting file header, rather than by file extension.\n"
|
"Auto-detect file type by inspecting file header, rather than by file extension.\n"
|
||||||
"In other words, if a .zip file is renamed to .smc, it will still be correctly\n"
|
"In other words, if a .zip file is renamed to .smc, it will still be correctly\n"
|
||||||
"identified as a .zip file. However, there is an infinitesimal (1:~500,000,000)\n"
|
"identified as a .zip file. However, there is an infinitesimal (1:~500,000,000)\n"
|
||||||
"chance of a false detection when loading an uncompressed image file, if this\n"
|
"chance of a false detection when loading an uncompressed image file, if this\n"
|
||||||
"option is enabled.",
|
"option is enabled.",
|
||||||
integral_setting::boolean, false);
|
integral_setting::boolean, false);
|
||||||
|
|
||||||
integral_setting File::bypass_patch_crc32(config(), "file.bypass_patch_crc32",
|
integral_setting File::bypass_patch_crc32(config(), "file.bypass_patch_crc32",
|
||||||
"UPS patches contain CRC32s to validate that a patch was applied successfully.\n"
|
"UPS patches contain CRC32s to validate that a patch was applied successfully.\n"
|
||||||
"By default, if this validation fails, said patch will not be applied.\n"
|
"By default, if this validation fails, said patch will not be applied.\n"
|
||||||
"Setting this option to true will bypass the validation,\n"
|
"Setting this option to true will bypass the validation,\n"
|
||||||
"which may or may not result in a working image.\n"
|
"which may or may not result in a working image.\n"
|
||||||
"Enabling this option is strongly discouraged.",
|
"Enabling this option is strongly discouraged.",
|
||||||
integral_setting::boolean, false);
|
integral_setting::boolean, false);
|
||||||
|
|
||||||
string file_updatepath(const char *req_file, const char *req_path) {
|
string file_updatepath(const char *req_file, const char *req_path) {
|
||||||
|
@ -48,13 +48,13 @@ string_setting Path::base("path.base", "Path that bsnes resides in", "");
|
||||||
string_setting Path::user("path.user", "Path to user folder", "");
|
string_setting Path::user("path.user", "Path to user folder", "");
|
||||||
|
|
||||||
string_setting Path::rom(config(), "path.rom",
|
string_setting Path::rom(config(), "path.rom",
|
||||||
"Default path to look for ROM files in (\"\" = use default directory)", "");
|
"Default path to look for ROM files in (\"\" = use default directory)", "");
|
||||||
string_setting Path::patch(config(), "path.patch",
|
string_setting Path::patch(config(), "path.patch",
|
||||||
"Default path for all UPS patch files (\"\" = use current directory)", "");
|
"Default path for all UPS patch files (\"\" = use current directory)", "");
|
||||||
string_setting Path::save(config(), "path.save",
|
string_setting Path::save(config(), "path.save",
|
||||||
"Default path for all save RAM files (\"\" = use current directory)", "");
|
"Default path for all save RAM files (\"\" = use current directory)", "");
|
||||||
string_setting Path::cheat(config(), "path.cheat",
|
string_setting Path::cheat(config(), "path.cheat",
|
||||||
"Default path for all cheat files (\"\" = use current directory)", "");
|
"Default path for all cheat files (\"\" = use current directory)", "");
|
||||||
string_setting Path::bsx(config(), "path.bsx", "", "");
|
string_setting Path::bsx(config(), "path.bsx", "", "");
|
||||||
string_setting Path::st(config(), "path.st", "", "");
|
string_setting Path::st(config(), "path.st", "", "");
|
||||||
|
|
||||||
|
@ -62,6 +62,17 @@ integral_setting SNES::controller_port1(config(), "snes.controller_port1",
|
||||||
"Controller attached to SNES port 1", integral_setting::decimal, ::SNES::Input::DeviceJoypad);
|
"Controller attached to SNES port 1", integral_setting::decimal, ::SNES::Input::DeviceJoypad);
|
||||||
integral_setting SNES::controller_port2(config(), "snes.controller_port2",
|
integral_setting SNES::controller_port2(config(), "snes.controller_port2",
|
||||||
"Controller attached to SNES port 2", integral_setting::decimal, ::SNES::Input::DeviceJoypad);
|
"Controller attached to SNES port 2", integral_setting::decimal, ::SNES::Input::DeviceJoypad);
|
||||||
|
integral_setting SNES::expansion_port(config(), "snes.expansion_port",
|
||||||
|
"Device attached to SNES expansion port\n"
|
||||||
|
"0 = None\n"
|
||||||
|
"1 = Satellaview BS-X\n"
|
||||||
|
"", integral_setting::decimal, ::SNES::ExpansionBSX);
|
||||||
|
integral_setting SNES::region(config(), "snes.region",
|
||||||
|
"SNES regional model\n"
|
||||||
|
"0 = Auto-detect based on cartridge\n"
|
||||||
|
"1 = NTSC\n"
|
||||||
|
"2 = PAL\n"
|
||||||
|
"", integral_setting::decimal, ::SNES::Autodetect);
|
||||||
|
|
||||||
integral_setting CPU::ntsc_clock_rate(config(), "cpu.ntsc_clock_rate",
|
integral_setting CPU::ntsc_clock_rate(config(), "cpu.ntsc_clock_rate",
|
||||||
"NTSC S-CPU clock rate (in hz)", integral_setting::decimal, 21477272);
|
"NTSC S-CPU clock rate (in hz)", integral_setting::decimal, 21477272);
|
||||||
|
@ -82,9 +93,9 @@ integral_setting CPU::wram_init_value(config(), "cpu.wram_init_value",
|
||||||
integral_setting::hex, 0x55);
|
integral_setting::hex, 0x55);
|
||||||
|
|
||||||
integral_setting SMP::ntsc_clock_rate(config(), "smp.ntsc_clock_rate",
|
integral_setting SMP::ntsc_clock_rate(config(), "smp.ntsc_clock_rate",
|
||||||
"NTSC S-SMP clock rate (in hz)", integral_setting::decimal, 32040 * 768);
|
"NTSC S-SMP clock rate (in hz)", integral_setting::decimal, 32041 * 768);
|
||||||
integral_setting SMP::pal_clock_rate(config(), "smp.pal_clock_rate",
|
integral_setting SMP::pal_clock_rate(config(), "smp.pal_clock_rate",
|
||||||
"PAL S-SMP clock rate (in hz)", integral_setting::decimal, 32040 * 768);
|
"PAL S-SMP clock rate (in hz)", integral_setting::decimal, 32041 * 768);
|
||||||
|
|
||||||
integral_setting PPU::Hack::render_scanline_position(config(), "ppu.hack.render_scanline_position",
|
integral_setting PPU::Hack::render_scanline_position(config(), "ppu.hack.render_scanline_position",
|
||||||
"Approximate HCLOCK position to render at for scanline-based renderers",
|
"Approximate HCLOCK position to render at for scanline-based renderers",
|
||||||
|
@ -124,4 +135,4 @@ integral_setting PPU::oam_pri1_enable("ppu.oam_pri1_enable", "Enable OAM Priorit
|
||||||
integral_setting PPU::oam_pri2_enable("ppu.oam_pri2_enable", "Enable OAM Priority 2", integral_setting::boolean, true);
|
integral_setting PPU::oam_pri2_enable("ppu.oam_pri2_enable", "Enable OAM Priority 2", integral_setting::boolean, true);
|
||||||
integral_setting PPU::oam_pri3_enable("ppu.oam_pri3_enable", "Enable OAM Priority 3", integral_setting::boolean, true);
|
integral_setting PPU::oam_pri3_enable("ppu.oam_pri3_enable", "Enable OAM Priority 3", integral_setting::boolean, true);
|
||||||
|
|
||||||
} //namespace config
|
} //namespace config
|
||||||
|
|
|
@ -17,6 +17,8 @@ extern struct Path {
|
||||||
extern struct SNES {
|
extern struct SNES {
|
||||||
static integral_setting controller_port1;
|
static integral_setting controller_port1;
|
||||||
static integral_setting controller_port2;
|
static integral_setting controller_port2;
|
||||||
|
static integral_setting expansion_port;
|
||||||
|
static integral_setting region;
|
||||||
} snes;
|
} snes;
|
||||||
|
|
||||||
extern struct CPU {
|
extern struct CPU {
|
||||||
|
|
|
@ -1,255 +1,256 @@
|
||||||
#ifdef SCPU_CPP
|
#ifdef SCPU_CPP
|
||||||
|
|
||||||
#include "irq.cpp"
|
#include "irq.cpp"
|
||||||
#include "joypad.cpp"
|
#include "joypad.cpp"
|
||||||
|
|
||||||
uint16 sCPU::vcounter() { return status.vcounter; }
|
unsigned sCPU::dma_counter() {
|
||||||
uint16 sCPU::hcounter() { return status.hcounter; }
|
return (status.dma_counter + status.hcounter) & 7;
|
||||||
uint sCPU::dma_counter() { return (status.dma_counter + status.hcounter) & 7; }
|
}
|
||||||
|
|
||||||
/*****
|
/*****
|
||||||
* One PPU dot = 4 CPU clocks
|
* One PPU dot = 4 CPU clocks
|
||||||
*
|
*
|
||||||
* PPU dots 323 and 327 are 6 CPU clocks long.
|
* PPU dots 323 and 327 are 6 CPU clocks long.
|
||||||
* This does not apply to NTSC non-interlace scanline 240 on odd fields. This is
|
* This does not apply to NTSC non-interlace scanline 240 on odd fields. This is
|
||||||
* because the PPU skips one dot to alter the color burst phase of the video signal.
|
* because the PPU skips one dot to alter the color burst phase of the video signal.
|
||||||
*
|
*
|
||||||
* Dot 323 range = { 1292, 1294, 1296 }
|
* Dot 323 range = { 1292, 1294, 1296 }
|
||||||
* Dot 327 range = { 1310, 1312, 1314 }
|
* Dot 327 range = { 1310, 1312, 1314 }
|
||||||
*****/
|
*****/
|
||||||
|
|
||||||
#define ntsc_color_burst_phase_shift_scanline() ( \
|
#define ntsc_color_burst_phase_shift_scanline() ( \
|
||||||
snes.region() == SNES::NTSC && status.vcounter == 240 && \
|
snes.region() == SNES::NTSC && status.vcounter == 240 && \
|
||||||
ppu.interlace() == false && ppu.field() == 1 \
|
ppu.interlace() == false && ppu.field() == 1 \
|
||||||
)
|
)
|
||||||
|
|
||||||
uint16 sCPU::hdot() {
|
uint16 sCPU::hdot() {
|
||||||
if(ntsc_color_burst_phase_shift_scanline() == true) return (status.hcounter >> 2);
|
if(ntsc_color_burst_phase_shift_scanline() == true) return (status.hcounter >> 2);
|
||||||
return (status.hcounter - ((status.hcounter > 1292) << 1) - ((status.hcounter > 1310) << 1)) >> 2;
|
return (status.hcounter - ((status.hcounter > 1292) << 1) - ((status.hcounter > 1310) << 1)) >> 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
void sCPU::add_clocks(uint clocks) {
|
void sCPU::add_clocks(unsigned clocks) {
|
||||||
if(status.dram_refreshed == false) {
|
if(status.dram_refreshed == false) {
|
||||||
if(status.hcounter + clocks >= status.dram_refresh_position) {
|
if(status.hcounter + clocks >= status.dram_refresh_position) {
|
||||||
status.dram_refreshed = true;
|
status.dram_refreshed = true;
|
||||||
clocks += 40;
|
clocks += 40;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
counter.sub(counter.irq_delay, clocks);
|
counter.sub(counter.irq_delay, clocks);
|
||||||
scheduler.addclocks_cpu(clocks);
|
scheduler.addclocks_cpu(clocks);
|
||||||
|
|
||||||
clocks >>= 1;
|
clocks >>= 1;
|
||||||
while(clocks--) {
|
while(clocks--) {
|
||||||
history.enqueue(status.vcounter, status.hcounter);
|
history.enqueue(status.vcounter, status.hcounter);
|
||||||
status.hcounter += 2;
|
status.hcounter += 2;
|
||||||
if(status.hcounter >= status.line_clocks) scanline();
|
if(status.hcounter >= status.line_clocks) scanline();
|
||||||
poll_interrupts();
|
poll_interrupts();
|
||||||
}
|
snes.input.tick();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
void sCPU::scanline() {
|
|
||||||
status.hcounter = 0;
|
void sCPU::scanline() {
|
||||||
status.dma_counter = (status.dma_counter + status.line_clocks) & 7;
|
status.hcounter = 0;
|
||||||
if(++status.vcounter >= status.field_lines) frame();
|
status.dma_counter = (status.dma_counter + status.line_clocks) & 7;
|
||||||
status.line_clocks = (ntsc_color_burst_phase_shift_scanline() == false) ? 1364 : 1360;
|
if(++status.vcounter >= status.field_lines) frame();
|
||||||
|
status.line_clocks = (ntsc_color_burst_phase_shift_scanline() == false) ? 1364 : 1360;
|
||||||
//dram refresh occurs once every scanline
|
|
||||||
status.dram_refreshed = false;
|
//dram refresh occurs once every scanline
|
||||||
if(cpu_version == 2) status.dram_refresh_position = 530 + 8 - dma_counter();
|
status.dram_refreshed = false;
|
||||||
|
if(cpu_version == 2) status.dram_refresh_position = 530 + 8 - dma_counter();
|
||||||
//hdma triggers once every visible scanline
|
|
||||||
status.line_rendered = false;
|
//hdma triggers once every visible scanline
|
||||||
status.hdma_triggered = (status.vcounter <= (ppu.overscan() == false ? 224 : 239)) ? false : true;
|
status.line_rendered = false;
|
||||||
|
status.hdma_triggered = (status.vcounter <= (ppu.overscan() == false ? 224 : 239)) ? false : true;
|
||||||
ppu.scanline();
|
|
||||||
snes.scanline();
|
ppu.scanline();
|
||||||
|
snes.scanline();
|
||||||
update_interrupts();
|
|
||||||
|
update_interrupts();
|
||||||
if(status.auto_joypad_poll == true && status.vcounter == (ppu.overscan() == false ? 227 : 242)) {
|
|
||||||
snes.input.poll();
|
if(status.auto_joypad_poll == true && status.vcounter == (ppu.overscan() == false ? 227 : 242)) {
|
||||||
run_auto_joypad_poll();
|
snes.input.poll();
|
||||||
}
|
run_auto_joypad_poll();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void sCPU::frame() {
|
void sCPU::frame() {
|
||||||
ppu.frame();
|
ppu.frame();
|
||||||
snes.frame();
|
snes.frame();
|
||||||
|
|
||||||
status.vcounter = 0;
|
status.vcounter = 0;
|
||||||
status.field_lines = (snes.region() == SNES::NTSC ? 525 : 625) >> 1;
|
status.field_lines = (snes.region() == SNES::NTSC ? 525 : 625) >> 1;
|
||||||
//interlaced even fields have one extra scanline (263+262=525 NTSC, 313+312=625 PAL)
|
//interlaced even fields have one extra scanline (263+262=525 NTSC, 313+312=625 PAL)
|
||||||
if(ppu.interlace() == true && ppu.field() == 0) status.field_lines++;
|
if(ppu.interlace() == true && ppu.field() == 0) status.field_lines++;
|
||||||
|
|
||||||
status.hdmainit_triggered = false;
|
status.hdmainit_triggered = false;
|
||||||
if(cpu_version == 1) {
|
if(cpu_version == 1) {
|
||||||
status.hdmainit_trigger_position = 12 + 8 - dma_counter();
|
status.hdmainit_trigger_position = 12 + 8 - dma_counter();
|
||||||
} else {
|
} else {
|
||||||
status.hdmainit_trigger_position = 12 + dma_counter();
|
status.hdmainit_trigger_position = 12 + dma_counter();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*****
|
/*****
|
||||||
* precycle_edge()
|
* precycle_edge()
|
||||||
*
|
*
|
||||||
* Used for H/DMA bus synchronization
|
* Used for H/DMA bus synchronization
|
||||||
*****/
|
*****/
|
||||||
void sCPU::precycle_edge() {
|
void sCPU::precycle_edge() {
|
||||||
if(status.dma_state == DMA_CPUsync) {
|
if(status.dma_state == DMA_CPUsync) {
|
||||||
add_clocks(status.clock_count - (status.dma_clocks % status.clock_count));
|
add_clocks(status.clock_count - (status.dma_clocks % status.clock_count));
|
||||||
status.dma_state = DMA_Inactive;
|
status.dma_state = DMA_Inactive;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*****
|
/*****
|
||||||
* cycle_edge()
|
* cycle_edge()
|
||||||
*
|
*
|
||||||
* Used to test for H/DMA, which can trigger on the edge of every opcode cycle.
|
* Used to test for H/DMA, which can trigger on the edge of every opcode cycle.
|
||||||
*****/
|
*****/
|
||||||
void sCPU::cycle_edge() {
|
void sCPU::cycle_edge() {
|
||||||
if(status.line_rendered == false) {
|
if(status.line_rendered == false) {
|
||||||
if(status.hcounter >= status.line_render_position) {
|
if(status.hcounter >= status.line_render_position) {
|
||||||
status.line_rendered = true;
|
status.line_rendered = true;
|
||||||
ppu.render_scanline();
|
ppu.render_scanline();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(status.hdmainit_triggered == false) {
|
if(status.hdmainit_triggered == false) {
|
||||||
if(status.hcounter >= status.hdmainit_trigger_position || status.vcounter) {
|
if(status.hcounter >= status.hdmainit_trigger_position || status.vcounter) {
|
||||||
status.hdmainit_triggered = true;
|
status.hdmainit_triggered = true;
|
||||||
hdma_init_reset();
|
hdma_init_reset();
|
||||||
if(hdma_enabled_channels()) {
|
if(hdma_enabled_channels()) {
|
||||||
status.hdma_pending = true;
|
status.hdma_pending = true;
|
||||||
status.hdma_mode = 0;
|
status.hdma_mode = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(status.hdma_triggered == false) {
|
if(status.hdma_triggered == false) {
|
||||||
if(status.hcounter >= 1104) {
|
if(status.hcounter >= 1104) {
|
||||||
status.hdma_triggered = true;
|
status.hdma_triggered = true;
|
||||||
if(hdma_active_channels()) {
|
if(hdma_active_channels()) {
|
||||||
status.hdma_pending = true;
|
status.hdma_pending = true;
|
||||||
status.hdma_mode = 1;
|
status.hdma_mode = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//H/DMA pending && DMA inactive?
|
//H/DMA pending && DMA inactive?
|
||||||
//.. Run one full CPU cycle
|
//.. Run one full CPU cycle
|
||||||
//.. HDMA pending && HDMA enabled ? DMA sync + HDMA run
|
//.. HDMA pending && HDMA enabled ? DMA sync + HDMA run
|
||||||
//.. DMA pending && DMA enabled ? DMA sync + DMA run
|
//.. DMA pending && DMA enabled ? DMA sync + DMA run
|
||||||
//.... HDMA during DMA && HDMA enabled ? DMA sync + HDMA run
|
//.... HDMA during DMA && HDMA enabled ? DMA sync + HDMA run
|
||||||
//.. Run one bus CPU cycle
|
//.. Run one bus CPU cycle
|
||||||
//.. CPU sync
|
//.. CPU sync
|
||||||
|
|
||||||
if(status.dma_state == DMA_Run) {
|
if(status.dma_state == DMA_Run) {
|
||||||
if(status.hdma_pending) {
|
if(status.hdma_pending) {
|
||||||
status.hdma_pending = false;
|
status.hdma_pending = false;
|
||||||
if(hdma_enabled_channels()) {
|
if(hdma_enabled_channels()) {
|
||||||
dma_add_clocks(8 - dma_counter()); //DMA sync
|
dma_add_clocks(8 - dma_counter()); //DMA sync
|
||||||
status.hdma_mode == 0 ? hdma_init() : hdma_run();
|
status.hdma_mode == 0 ? hdma_init() : hdma_run();
|
||||||
if(!dma_enabled_channels()) status.dma_state = DMA_CPUsync;
|
if(!dma_enabled_channels()) status.dma_state = DMA_CPUsync;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(status.dma_pending) {
|
if(status.dma_pending) {
|
||||||
status.dma_pending = false;
|
status.dma_pending = false;
|
||||||
if(dma_enabled_channels()) {
|
if(dma_enabled_channels()) {
|
||||||
dma_add_clocks(8 - dma_counter()); //DMA sync
|
dma_add_clocks(8 - dma_counter()); //DMA sync
|
||||||
dma_run();
|
dma_run();
|
||||||
status.dma_state = DMA_CPUsync;
|
status.dma_state = DMA_CPUsync;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(status.dma_state == DMA_Inactive) {
|
if(status.dma_state == DMA_Inactive) {
|
||||||
if(status.dma_pending || status.hdma_pending) {
|
if(status.dma_pending || status.hdma_pending) {
|
||||||
status.dma_clocks = 0;
|
status.dma_clocks = 0;
|
||||||
status.dma_state = DMA_Run;
|
status.dma_state = DMA_Run;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*****
|
/*****
|
||||||
* last_cycle()
|
* last_cycle()
|
||||||
*
|
*
|
||||||
* Used to test for NMI/IRQ, which can trigger on the edge of every opcode.
|
* Used to test for NMI/IRQ, which can trigger on the edge of every opcode.
|
||||||
* Test one cycle early to simulate two-stage pipeline of x816 CPU.
|
* Test one cycle early to simulate two-stage pipeline of x816 CPU.
|
||||||
*
|
*
|
||||||
* status.irq_delay is used to simulate hardware delay before interrupts can
|
* status.irq_delay is used to simulate hardware delay before interrupts can
|
||||||
* trigger during certain events (immediately after DMA, writes to $4200, etc)
|
* trigger during certain events (immediately after DMA, writes to $4200, etc)
|
||||||
*****/
|
*****/
|
||||||
void sCPU::last_cycle() {
|
void sCPU::last_cycle() {
|
||||||
if(counter.irq_delay) return;
|
if(counter.irq_delay) return;
|
||||||
|
|
||||||
status.nmi_pending |= nmi_test();
|
status.nmi_pending |= nmi_test();
|
||||||
status.irq_pending |= irq_test();
|
status.irq_pending |= irq_test();
|
||||||
|
|
||||||
event.irq = (status.nmi_pending || status.irq_pending);
|
event.irq = (status.nmi_pending || status.irq_pending);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sCPU::timing_power() {
|
void sCPU::timing_power() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void sCPU::timing_reset() {
|
void sCPU::timing_reset() {
|
||||||
counter.nmi_hold = 0;
|
counter.nmi_hold = 0;
|
||||||
counter.irq_hold = 0;
|
counter.irq_hold = 0;
|
||||||
|
|
||||||
counter.nmi_fire = 0;
|
counter.nmi_fire = 0;
|
||||||
counter.irq_fire = 0;
|
counter.irq_fire = 0;
|
||||||
counter.irq_delay = 0;
|
counter.irq_delay = 0;
|
||||||
counter.hw_math = 0;
|
counter.hw_math = 0;
|
||||||
|
|
||||||
status.clock_count = 0;
|
status.clock_count = 0;
|
||||||
|
|
||||||
status.vcounter = 0;
|
status.vcounter = 0;
|
||||||
status.hcounter = 0;
|
status.hcounter = 0;
|
||||||
|
|
||||||
status.field_lines = (snes.region() == SNES::NTSC ? 525 : 625) >> 1;
|
status.field_lines = (snes.region() == SNES::NTSC ? 525 : 625) >> 1;
|
||||||
status.line_clocks = 1364;
|
status.line_clocks = 1364;
|
||||||
|
|
||||||
status.line_rendered = false;
|
status.line_rendered = false;
|
||||||
status.line_render_position = min(1112U, (unsigned)config::ppu.hack.render_scanline_position);
|
status.line_render_position = min(1112U, (unsigned)config::ppu.hack.render_scanline_position);
|
||||||
|
|
||||||
status.dram_refreshed = false;
|
status.dram_refreshed = false;
|
||||||
status.dram_refresh_position = (cpu_version == 1) ? 530 : 538;
|
status.dram_refresh_position = (cpu_version == 1) ? 530 : 538;
|
||||||
|
|
||||||
status.hdmainit_triggered = false;
|
status.hdmainit_triggered = false;
|
||||||
status.hdmainit_trigger_position = 0;
|
status.hdmainit_trigger_position = 0;
|
||||||
|
|
||||||
status.hdma_triggered = false;
|
status.hdma_triggered = false;
|
||||||
|
|
||||||
status.irq_delay = 0;
|
status.irq_delay = 0;
|
||||||
|
|
||||||
status.nmi_valid = false;
|
status.nmi_valid = false;
|
||||||
status.nmi_line = false;
|
status.nmi_line = false;
|
||||||
status.nmi_transition = false;
|
status.nmi_transition = false;
|
||||||
status.nmi_pending = false;
|
status.nmi_pending = false;
|
||||||
|
|
||||||
status.irq_valid = false;
|
status.irq_valid = false;
|
||||||
status.irq_line = false;
|
status.irq_line = false;
|
||||||
status.irq_transition = false;
|
status.irq_transition = false;
|
||||||
status.irq_pending = false;
|
status.irq_pending = false;
|
||||||
|
|
||||||
update_interrupts();
|
update_interrupts();
|
||||||
|
|
||||||
status.dma_counter = 0;
|
status.dma_counter = 0;
|
||||||
status.dma_clocks = 0;
|
status.dma_clocks = 0;
|
||||||
status.dma_pending = false;
|
status.dma_pending = false;
|
||||||
status.hdma_pending = false;
|
status.hdma_pending = false;
|
||||||
status.hdma_mode = 0;
|
status.hdma_mode = 0;
|
||||||
status.dma_state = DMA_Inactive;
|
status.dma_state = DMA_Inactive;
|
||||||
|
|
||||||
history.reset();
|
history.reset();
|
||||||
|
|
||||||
//initial latch values for $213c/$213d
|
//initial latch values for $213c/$213d
|
||||||
//[x]0035 : [y]0000 (53.0 -> 212) [lda $2137]
|
//[x]0035 : [y]0000 (53.0 -> 212) [lda $2137]
|
||||||
//[x]0038 : [y]0000 (56.5 -> 226) [nop : lda $2137]
|
//[x]0038 : [y]0000 (56.5 -> 226) [nop : lda $2137]
|
||||||
add_clocks(186);
|
add_clocks(186);
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef ntsc_color_burst_phase_shift_scanline
|
#undef ntsc_color_burst_phase_shift_scanline
|
||||||
|
|
||||||
#endif //ifdef SCPU_CPP
|
#endif //ifdef SCPU_CPP
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
uint16 vcounter();
|
alwaysinline uint16 vcounter() { return status.vcounter; }
|
||||||
uint16 hcounter();
|
alwaysinline uint16 hcounter() { return status.hcounter; }
|
||||||
uint16 hdot();
|
uint16 hdot();
|
||||||
uint dma_counter();
|
unsigned dma_counter();
|
||||||
|
|
||||||
void add_clocks(uint clocks);
|
void add_clocks(unsigned clocks);
|
||||||
void scanline();
|
void scanline();
|
||||||
void frame();
|
void frame();
|
||||||
|
|
||||||
|
@ -14,30 +14,30 @@
|
||||||
|
|
||||||
void timing_power();
|
void timing_power();
|
||||||
void timing_reset();
|
void timing_reset();
|
||||||
|
|
||||||
//timeshifting -- needed by NMI and IRQ timing
|
//timeshifting -- needed by NMI and IRQ timing
|
||||||
struct History {
|
struct History {
|
||||||
struct Time {
|
struct Time {
|
||||||
uint16 vcounter;
|
uint16 vcounter;
|
||||||
uint16 hcounter;
|
uint16 hcounter;
|
||||||
} time[32];
|
} time[32];
|
||||||
unsigned index;
|
unsigned index;
|
||||||
alwaysinline void enqueue(uint16 vcounter, uint16 hcounter) {
|
alwaysinline void enqueue(uint16 vcounter, uint16 hcounter) {
|
||||||
Time &t = time[index++];
|
Time &t = time[index++];
|
||||||
index &= 31;
|
index &= 31;
|
||||||
t.vcounter = vcounter;
|
t.vcounter = vcounter;
|
||||||
t.hcounter = hcounter;
|
t.hcounter = hcounter;
|
||||||
}
|
}
|
||||||
alwaysinline void query(unsigned offset, uint16 &vcounter, uint16 &hcounter) {
|
alwaysinline void query(unsigned offset, uint16 &vcounter, uint16 &hcounter) {
|
||||||
Time &t = time[(index - (offset >> 1)) & 31];
|
Time &t = time[(index - (offset >> 1)) & 31];
|
||||||
vcounter = t.vcounter;
|
vcounter = t.vcounter;
|
||||||
hcounter = t.hcounter;
|
hcounter = t.hcounter;
|
||||||
}
|
}
|
||||||
void reset() {
|
void reset() {
|
||||||
index = 0;
|
index = 0;
|
||||||
for(unsigned i = 0; i < 32; i++) time[i].vcounter = time[i].hcounter = 0;
|
for(unsigned i = 0; i < 32; i++) time[i].vcounter = time[i].hcounter = 0;
|
||||||
}
|
}
|
||||||
History() { reset(); }
|
History() { reset(); }
|
||||||
} history;
|
} history;
|
||||||
|
|
||||||
//irq.cpp
|
//irq.cpp
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
bbase : version 0.15 ~byuu (2008-09-14)
|
bbase : version 0.17 ~byuu (2008-10-19)
|
||||||
license: public domain
|
license: public domain
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ typedef uint8_t uint8;
|
||||||
typedef uint16_t uint16;
|
typedef uint16_t uint16;
|
||||||
typedef uint32_t uint32;
|
typedef uint32_t uint32;
|
||||||
typedef uint64_t uint64;
|
typedef uint64_t uint64;
|
||||||
typedef unsigned int uint;
|
typedef unsigned uint;
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
using std::min;
|
using std::min;
|
||||||
|
@ -76,85 +76,4 @@ using std::max;
|
||||||
#define alwaysinline inline
|
#define alwaysinline inline
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*****
|
|
||||||
* OS localization
|
|
||||||
*****/
|
|
||||||
|
|
||||||
#if defined(_MSC_VER) || defined(__MINGW32__)
|
|
||||||
static char* realpath(const char *file_name, char *resolved_name) {
|
|
||||||
wchar_t filename[PATH_MAX] = L"";
|
|
||||||
_wfullpath(filename, utf16(file_name), PATH_MAX);
|
|
||||||
strcpy(resolved_name, utf8(filename));
|
|
||||||
return resolved_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
static char* userpath(char *output) {
|
|
||||||
wchar_t path[PATH_MAX] = L"."; //failsafe
|
|
||||||
SHGetFolderPathW(0, CSIDL_APPDATA | CSIDL_FLAG_CREATE, 0, 0, path);
|
|
||||||
strcpy(output, utf8(path));
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
#define mkdir(path) _wmkdir(utf16(path))
|
|
||||||
#else
|
|
||||||
static char* userpath(char *output) {
|
|
||||||
strcpy(output, "."); //failsafe
|
|
||||||
struct passwd *userinfo = getpwuid(getuid());
|
|
||||||
if(userinfo) { strcpy(output, userinfo->pw_dir); }
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
#define mkdir(path) (mkdir)(path, 0755);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
template<int min, int max, typename T> inline T minmax(const T x) {
|
|
||||||
return (x < (T)min) ? (T)min : (x > (T)max) ? (T)max : x;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*****
|
|
||||||
* endian wrappers
|
|
||||||
*****/
|
|
||||||
|
|
||||||
#ifndef ARCH_MSB
|
|
||||||
//little-endian: uint8[] { 0x01, 0x02, 0x03, 0x04 } == 0x04030201
|
|
||||||
#define order_lsb2(a,b) a,b
|
|
||||||
#define order_lsb3(a,b,c) a,b,c
|
|
||||||
#define order_lsb4(a,b,c,d) a,b,c,d
|
|
||||||
#define order_lsb5(a,b,c,d,e) a,b,c,d,e
|
|
||||||
#define order_lsb6(a,b,c,d,e,f) a,b,c,d,e,f
|
|
||||||
#define order_lsb7(a,b,c,d,e,f,g) a,b,c,d,e,f,g
|
|
||||||
#define order_lsb8(a,b,c,d,e,f,g,h) a,b,c,d,e,f,g,h
|
|
||||||
#define order_msb2(a,b) b,a
|
|
||||||
#define order_msb3(a,b,c) c,b,a
|
|
||||||
#define order_msb4(a,b,c,d) d,c,b,a
|
|
||||||
#define order_msb5(a,b,c,d,e) e,d,c,b,a
|
|
||||||
#define order_msb6(a,b,c,d,e,f) f,e,d,c,b,a
|
|
||||||
#define order_msb7(a,b,c,d,e,f,g) g,f,e,d,c,b,a
|
|
||||||
#define order_msb8(a,b,c,d,e,f,g,h) h,g,f,e,d,c,b,a
|
|
||||||
#else
|
|
||||||
//big-endian: uint8[] { 0x01, 0x02, 0x03, 0x04 } == 0x01020304
|
|
||||||
#define order_lsb2(a,b) b,a
|
|
||||||
#define order_lsb3(a,b,c) c,b,a
|
|
||||||
#define order_lsb4(a,b,c,d) d,c,b,a
|
|
||||||
#define order_lsb5(a,b,c,d,e) e,d,c,b,a
|
|
||||||
#define order_lsb6(a,b,c,d,e,f) f,e,d,c,b,a
|
|
||||||
#define order_lsb7(a,b,c,d,e,f,g) g,f,e,d,c,b,a
|
|
||||||
#define order_lsb8(a,b,c,d,e,f,g,h) h,g,f,e,d,c,b,a
|
|
||||||
#define order_msb2(a,b) a,b
|
|
||||||
#define order_msb3(a,b,c) a,b,c
|
|
||||||
#define order_msb4(a,b,c,d) a,b,c,d
|
|
||||||
#define order_msb5(a,b,c,d,e) a,b,c,d,e
|
|
||||||
#define order_msb6(a,b,c,d,e,f) a,b,c,d,e,f
|
|
||||||
#define order_msb7(a,b,c,d,e,f,g) a,b,c,d,e,f,g
|
|
||||||
#define order_msb8(a,b,c,d,e,f,g,h) a,b,c,d,e,f,g,h
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*****
|
|
||||||
* libc extensions
|
|
||||||
*****/
|
|
||||||
|
|
||||||
//pseudo-random number generator
|
|
||||||
static unsigned prng() {
|
|
||||||
static unsigned n = 0;
|
|
||||||
return n = (n >> 1) ^ (((n & 1) - 1) & 0xedb88320);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif //ifndef BBASE_H
|
#endif //ifndef BBASE_H
|
||||||
|
|
|
@ -13,6 +13,7 @@ void pButton::create(unsigned style, unsigned width, unsigned height, const char
|
||||||
void pButton::set_text(const char *text) {
|
void pButton::set_text(const char *text) {
|
||||||
if(!button) return;
|
if(!button) return;
|
||||||
gtk_button_set_label(GTK_BUTTON(button), text ? text : "");
|
gtk_button_set_label(GTK_BUTTON(button), text ? text : "");
|
||||||
|
set_default_font(button);
|
||||||
}
|
}
|
||||||
|
|
||||||
pButton::pButton(Button &self_) : pFormControl(self_), self(self_) {
|
pButton::pButton(Button &self_) : pFormControl(self_), self(self_) {
|
||||||
|
|
|
@ -14,6 +14,20 @@ void hiro_pcanvas_expose(pCanvas *p) {
|
||||||
GDK_RGB_DITHER_NONE, (guchar*)p->rbuffer, p->bpitch);
|
GDK_RGB_DITHER_NONE, (guchar*)p->rbuffer, p->bpitch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gboolean hiro_pcanvas_button_press(GtkWidget *widget, GdkEventButton *event, pCanvas *p) {
|
||||||
|
if(p->self.on_input && event->button < mouse::buttons) {
|
||||||
|
p->self.on_input(event_t(event_t::Input, (mouse::button + event->button) + (1 << 16), &p->self));
|
||||||
|
}
|
||||||
|
return false; //do not propogate the event to other handlers
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean hiro_pcanvas_button_release(GtkWidget *widget, GdkEventButton *event, pCanvas *p) {
|
||||||
|
if(p->self.on_input && event->button < mouse::buttons) {
|
||||||
|
p->self.on_input(event_t(event_t::Input, (mouse::button + event->button) + (0 << 16), &p->self));
|
||||||
|
}
|
||||||
|
return false; //do not propogate the event to other handlers
|
||||||
|
}
|
||||||
|
|
||||||
void pCanvas::create(unsigned style, unsigned width, unsigned height) {
|
void pCanvas::create(unsigned style, unsigned width, unsigned height) {
|
||||||
canvas = gtk_drawing_area_new();
|
canvas = gtk_drawing_area_new();
|
||||||
resize(width, height);
|
resize(width, height);
|
||||||
|
@ -21,8 +35,11 @@ void pCanvas::create(unsigned style, unsigned width, unsigned height) {
|
||||||
color.pixel = color.red = color.green = color.blue = 0;
|
color.pixel = color.red = color.green = color.blue = 0;
|
||||||
gtk_widget_modify_bg(canvas, GTK_STATE_NORMAL, &color);
|
gtk_widget_modify_bg(canvas, GTK_STATE_NORMAL, &color);
|
||||||
gtk_widget_set_double_buffered(canvas, false);
|
gtk_widget_set_double_buffered(canvas, false);
|
||||||
|
gtk_widget_add_events(canvas, GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
|
||||||
gtk_widget_show(canvas);
|
gtk_widget_show(canvas);
|
||||||
g_signal_connect_swapped(G_OBJECT(canvas), "expose_event", G_CALLBACK(hiro_pcanvas_expose), (gpointer)this);
|
g_signal_connect_swapped(G_OBJECT(canvas), "expose_event", G_CALLBACK(hiro_pcanvas_expose), (gpointer)this);
|
||||||
|
g_signal_connect(G_OBJECT(canvas), "button_press_event", G_CALLBACK(hiro_pcanvas_button_press), (gpointer)this);
|
||||||
|
g_signal_connect(G_OBJECT(canvas), "button_release_event", G_CALLBACK(hiro_pcanvas_button_release), (gpointer)this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pCanvas::redraw() {
|
void pCanvas::redraw() {
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
|
void hiro_peditbox_change(pEditbox *p) {
|
||||||
|
if(p->self.on_change) {
|
||||||
|
p->self.on_change(event_t(event_t::Change, 0, &p->self));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void pEditbox::create(unsigned style, unsigned width, unsigned height, const char *text) {
|
void pEditbox::create(unsigned style, unsigned width, unsigned height, const char *text) {
|
||||||
multiline = bool(style & Editbox::Multiline);
|
multiline = bool(style & Editbox::Multiline);
|
||||||
|
|
||||||
|
@ -7,6 +13,7 @@ void pEditbox::create(unsigned style, unsigned width, unsigned height, const cha
|
||||||
gtk_entry_set_text(GTK_ENTRY(editbox), text ? text : "");
|
gtk_entry_set_text(GTK_ENTRY(editbox), text ? text : "");
|
||||||
gtk_widget_set_size_request(editbox, width, height);
|
gtk_widget_set_size_request(editbox, width, height);
|
||||||
gtk_widget_show(editbox);
|
gtk_widget_show(editbox);
|
||||||
|
g_signal_connect_swapped(G_OBJECT(editbox), "changed", G_CALLBACK(hiro_peditbox_change), (gpointer)this);
|
||||||
} else {
|
} else {
|
||||||
GtkPolicyType hscroll = (style & Editbox::HorizontalScrollAlways) ? GTK_POLICY_ALWAYS :
|
GtkPolicyType hscroll = (style & Editbox::HorizontalScrollAlways) ? GTK_POLICY_ALWAYS :
|
||||||
(style & Editbox::HorizontalScrollNever) ? GTK_POLICY_NEVER :
|
(style & Editbox::HorizontalScrollNever) ? GTK_POLICY_NEVER :
|
||||||
|
@ -25,6 +32,7 @@ void pEditbox::create(unsigned style, unsigned width, unsigned height, const cha
|
||||||
gtk_text_buffer_set_text(buffer, text ? text : "", -1);
|
gtk_text_buffer_set_text(buffer, text ? text : "", -1);
|
||||||
gtk_widget_show(editbox);
|
gtk_widget_show(editbox);
|
||||||
gtk_widget_show(scrollbox);
|
gtk_widget_show(scrollbox);
|
||||||
|
g_signal_connect_swapped(G_OBJECT(buffer), "changed", G_CALLBACK(hiro_peditbox_change), (gpointer)this);
|
||||||
}
|
}
|
||||||
|
|
||||||
set_default_font(editbox);
|
set_default_font(editbox);
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include "hiro.h"
|
#include "hiro.hpp"
|
||||||
|
#include "port.cpp"
|
||||||
|
|
||||||
#include <nall/algorithm.hpp>
|
#include <nall/algorithm.hpp>
|
||||||
using nall::min;
|
using nall::min;
|
||||||
|
@ -40,16 +41,6 @@ static void set_default_font(GtkWidget *widget) {
|
||||||
#include "slider.cpp"
|
#include "slider.cpp"
|
||||||
|
|
||||||
void pHiro::init() {
|
void pHiro::init() {
|
||||||
//simulate passing argc, argv to gtk_init()
|
|
||||||
int argc = 1;
|
|
||||||
char **argv;
|
|
||||||
argv = (char**)malloc(1 * sizeof(char*));
|
|
||||||
argv[0] = (char*)malloc(64 * sizeof(char));
|
|
||||||
strcpy(argv[0], "./hiro");
|
|
||||||
gtk_init(&argc, &argv);
|
|
||||||
free(argv[0]);
|
|
||||||
free(argv);
|
|
||||||
|
|
||||||
is_composited = false;
|
is_composited = false;
|
||||||
*default_path = 0;
|
*default_path = 0;
|
||||||
screen = gdk_screen_get_default();
|
screen = gdk_screen_get_default();
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
#ifndef HIRO_GTK_H
|
#ifndef HIRO_GTK_H
|
||||||
#define HIRO_GTK_H
|
#define HIRO_GTK_H
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <pwd.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#include <gtk/gtk.h>
|
#include <gtk/gtk.h>
|
||||||
#include <gdk/gdkx.h>
|
#include <gdk/gdkx.h>
|
||||||
#include <cairo.h>
|
#include <cairo.h>
|
||||||
|
@ -8,28 +12,30 @@
|
||||||
#include <X11/extensions/dpms.h>
|
#include <X11/extensions/dpms.h>
|
||||||
#include <X11/extensions/XTest.h>
|
#include <X11/extensions/XTest.h>
|
||||||
|
|
||||||
|
extern int hiromain(int argc, const char *const argv[]);
|
||||||
|
|
||||||
namespace libhiro {
|
namespace libhiro {
|
||||||
|
|
||||||
#include "widget.h"
|
#include "widget.hpp"
|
||||||
#include "window.h"
|
#include "window.hpp"
|
||||||
#include "menucontrol.h"
|
#include "menucontrol.hpp"
|
||||||
#include "menugroup.h"
|
#include "menugroup.hpp"
|
||||||
#include "menuitem.h"
|
#include "menuitem.hpp"
|
||||||
#include "menucheckitem.h"
|
#include "menucheckitem.hpp"
|
||||||
#include "menuradioitem.h"
|
#include "menuradioitem.hpp"
|
||||||
#include "menuseparator.h"
|
#include "menuseparator.hpp"
|
||||||
#include "formcontrol.h"
|
#include "formcontrol.hpp"
|
||||||
#include "frame.h"
|
#include "frame.hpp"
|
||||||
#include "canvas.h"
|
#include "canvas.hpp"
|
||||||
#include "label.h"
|
#include "label.hpp"
|
||||||
#include "button.h"
|
#include "button.hpp"
|
||||||
#include "checkbox.h"
|
#include "checkbox.hpp"
|
||||||
#include "radiobox.h"
|
#include "radiobox.hpp"
|
||||||
#include "editbox.h"
|
#include "editbox.hpp"
|
||||||
#include "listbox.h"
|
#include "listbox.hpp"
|
||||||
#include "combobox.h"
|
#include "combobox.hpp"
|
||||||
#include "progressbar.h"
|
#include "progressbar.hpp"
|
||||||
#include "slider.h"
|
#include "slider.hpp"
|
||||||
|
|
||||||
class pHiro {
|
class pHiro {
|
||||||
public:
|
public:
|
|
@ -0,0 +1,17 @@
|
||||||
|
char* userpath(char *output) {
|
||||||
|
struct passwd *userinfo = getpwuid(getuid());
|
||||||
|
if(userinfo) { strcpy(output, userinfo->pw_dir); }
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
int mkdir(const char *path) {
|
||||||
|
return mkdir(path, 0755);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
gtk_init(&argc, &argv);
|
||||||
|
libhiro::hiro().init();
|
||||||
|
int result = hiromain(argc, argv);
|
||||||
|
libhiro::hiro().term();
|
||||||
|
return result;
|
||||||
|
}
|
|
@ -25,12 +25,20 @@ static gboolean hiro_pwindow_expose(pWindow *p) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static gint hiro_pwindow_keydown(GtkWidget *w, GdkEventKey *key, pWindow *p) {
|
static gint hiro_pwindow_keydown(GtkWidget *w, GdkEventKey *key, pWindow *p) {
|
||||||
if(p && p->self.on_keydown) p->self.on_keydown(event_t(event_t::KeyDown, phiro().translate_key(key->keyval), &p->self));
|
if(p && p->self.on_input) {
|
||||||
|
p->self.on_input(event_t(event_t::Input,
|
||||||
|
phiro().translate_key(key->keyval) + (1 << 16),
|
||||||
|
&p->self));
|
||||||
|
}
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static gint hiro_pwindow_keyup(GtkWidget *w, GdkEventKey *key, pWindow *p) {
|
static gint hiro_pwindow_keyup(GtkWidget *w, GdkEventKey *key, pWindow *p) {
|
||||||
if(p && p->self.on_keyup) p->self.on_keyup(event_t(event_t::KeyUp, phiro().translate_key(key->keyval), &p->self));
|
if(p && p->self.on_input) {
|
||||||
|
p->self.on_input(event_t(event_t::Input,
|
||||||
|
phiro().translate_key(key->keyval) + (0 << 16),
|
||||||
|
&p->self));
|
||||||
|
}
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#include "hiro.h"
|
#include "hiro.hpp"
|
||||||
using namespace nall;
|
using namespace nall;
|
||||||
|
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
hiro
|
hiro
|
||||||
version: 0.006 (2008-08-12)
|
version: 0.007b (2008-10-26)
|
||||||
author: byuu
|
author: byuu
|
||||||
license: public domain
|
license: public domain
|
||||||
*/
|
*/
|
||||||
|
@ -16,6 +16,10 @@
|
||||||
#include <nall/string.hpp>
|
#include <nall/string.hpp>
|
||||||
#include <nall/utility.hpp>
|
#include <nall/utility.hpp>
|
||||||
|
|
||||||
|
extern char* realpath(const char*, char*);
|
||||||
|
extern char* userpath(char*);
|
||||||
|
int mkdir(const char*);
|
||||||
|
|
||||||
namespace libhiro {
|
namespace libhiro {
|
||||||
|
|
||||||
class pHiro;
|
class pHiro;
|
||||||
|
@ -92,8 +96,7 @@ struct event_t {
|
||||||
enum type_t {
|
enum type_t {
|
||||||
Close,
|
Close,
|
||||||
Block,
|
Block,
|
||||||
KeyDown,
|
Input,
|
||||||
KeyUp,
|
|
||||||
Change,
|
Change,
|
||||||
Tick,
|
Tick,
|
||||||
Activate,
|
Activate,
|
||||||
|
@ -225,8 +228,7 @@ public:
|
||||||
|
|
||||||
nall::function<uintptr_t (event_t)> on_close;
|
nall::function<uintptr_t (event_t)> on_close;
|
||||||
nall::function<uintptr_t (event_t)> on_block;
|
nall::function<uintptr_t (event_t)> on_block;
|
||||||
nall::function<uintptr_t (event_t)> on_keydown;
|
nall::function<uintptr_t (event_t)> on_input;
|
||||||
nall::function<uintptr_t (event_t)> on_keyup;
|
|
||||||
|
|
||||||
Window();
|
Window();
|
||||||
|
|
||||||
|
@ -352,6 +354,8 @@ public:
|
||||||
|
|
||||||
Canvas();
|
Canvas();
|
||||||
|
|
||||||
|
nall::function<uintptr_t (event_t)> on_input;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
pFriends;
|
pFriends;
|
||||||
pCanvas &p;
|
pCanvas &p;
|
||||||
|
@ -435,6 +439,8 @@ public:
|
||||||
unsigned get_text(char *text, unsigned length = -1U);
|
unsigned get_text(char *text, unsigned length = -1U);
|
||||||
void set_text(const char *text = "");
|
void set_text(const char *text = "");
|
||||||
|
|
||||||
|
nall::function<uintptr_t (event_t)> on_change;
|
||||||
|
|
||||||
Editbox();
|
Editbox();
|
||||||
|
|
||||||
private:
|
private:
|
|
@ -1,12 +1,5 @@
|
||||||
#include "hiro.h"
|
#include "hiro.hpp"
|
||||||
|
#include "port.cpp"
|
||||||
#include <nall/algorithm.hpp>
|
|
||||||
using nall::min;
|
|
||||||
using nall::max;
|
|
||||||
|
|
||||||
#include <nall/utf8.hpp>
|
|
||||||
using nall::utf8;
|
|
||||||
using nall::utf16;
|
|
||||||
|
|
||||||
namespace libhiro {
|
namespace libhiro {
|
||||||
|
|
||||||
|
@ -66,10 +59,14 @@ bool pHiro::run() {
|
||||||
MSG msg;
|
MSG msg;
|
||||||
if(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
|
if(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
|
||||||
//TODO: IsDialogMessage() does not clear keyboard buffer, but is required for tab key to work ...
|
//TODO: IsDialogMessage() does not clear keyboard buffer, but is required for tab key to work ...
|
||||||
//if(!IsDialogMessage(GetParent(msg.hwnd) ? GetParent(msg.hwnd) : msg.hwnd, &msg)) {
|
#if defined(HIRO_WIN_TABSTOP)
|
||||||
|
if(!IsDialogMessage(GetParent(msg.hwnd) ? GetParent(msg.hwnd) : msg.hwnd, &msg)) {
|
||||||
|
#endif
|
||||||
TranslateMessage(&msg);
|
TranslateMessage(&msg);
|
||||||
DispatchMessage(&msg);
|
DispatchMessage(&msg);
|
||||||
//}
|
#if defined(HIRO_WIN_TABSTOP)
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
return pending();
|
return pending();
|
||||||
}
|
}
|
||||||
|
@ -147,7 +144,7 @@ bool pHiro::file_open(Window *focus, char *filename, const char *path, const cha
|
||||||
ofn.lpstrInitialDir = wdir;
|
ofn.lpstrInitialDir = wdir;
|
||||||
ofn.lpstrFile = wfilename;
|
ofn.lpstrFile = wfilename;
|
||||||
ofn.nMaxFile = MAX_PATH;
|
ofn.nMaxFile = MAX_PATH;
|
||||||
ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST;
|
ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
|
||||||
ofn.lpstrDefExt = L"";
|
ofn.lpstrDefExt = L"";
|
||||||
|
|
||||||
bool result = GetOpenFileName(&ofn);
|
bool result = GetOpenFileName(&ofn);
|
||||||
|
@ -195,7 +192,7 @@ bool pHiro::file_save(Window *focus, char *filename, const char *path, const cha
|
||||||
ofn.lpstrInitialDir = wdir;
|
ofn.lpstrInitialDir = wdir;
|
||||||
ofn.lpstrFile = wfilename;
|
ofn.lpstrFile = wfilename;
|
||||||
ofn.nMaxFile = MAX_PATH;
|
ofn.nMaxFile = MAX_PATH;
|
||||||
ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST;
|
ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
|
||||||
ofn.lpstrDefExt = L"";
|
ofn.lpstrDefExt = L"";
|
||||||
|
|
||||||
bool result = GetSaveFileName(&ofn);
|
bool result = GetSaveFileName(&ofn);
|
||||||
|
@ -290,13 +287,29 @@ LRESULT pHiro::wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
|
||||||
case WM_KEYDOWN: {
|
case WM_KEYDOWN: {
|
||||||
if(!p || p->self.type != Widget::WindowType) break;
|
if(!p || p->self.type != Widget::WindowType) break;
|
||||||
Window &w = ((pWindow*)p)->self;
|
Window &w = ((pWindow*)p)->self;
|
||||||
if(w.on_keydown) w.on_keydown(event_t(event_t::KeyDown, translate_key(wparam), &w));
|
if(w.on_input) w.on_input(event_t(event_t::Input, translate_key(wparam) + (1 << 16), &w));
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case WM_KEYUP: {
|
case WM_KEYUP: {
|
||||||
if(!p || p->self.type != Widget::WindowType) break;
|
if(!p || p->self.type != Widget::WindowType) break;
|
||||||
Window &w = ((pWindow*)p)->self;
|
Window &w = ((pWindow*)p)->self;
|
||||||
if(w.on_keyup) w.on_keyup(event_t(event_t::KeyUp, translate_key(wparam), &w));
|
if(w.on_input) w.on_input(event_t(event_t::Input, translate_key(wparam) + (0 << 16), &w));
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case WM_LBUTTONDOWN:
|
||||||
|
case WM_RBUTTONDOWN: {
|
||||||
|
if(!p || p->self.type != Widget::CanvasType) break;
|
||||||
|
Canvas &canvas = ((pCanvas*)p)->self;
|
||||||
|
uintptr_t param = (msg == WM_LBUTTONDOWN ? mouse::button + 0 : mouse::button + 1) + (1 << 16);
|
||||||
|
if(canvas.on_input) canvas.on_input(event_t(event_t::Input, param, &canvas));
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case WM_LBUTTONUP:
|
||||||
|
case WM_RBUTTONUP: {
|
||||||
|
if(!p || p->self.type != Widget::CanvasType) break;
|
||||||
|
Canvas &canvas = ((pCanvas*)p)->self;
|
||||||
|
uintptr_t param = (msg == WM_LBUTTONUP ? mouse::button + 0 : mouse::button + 1) + (0 << 16);
|
||||||
|
if(canvas.on_input) canvas.on_input(event_t(event_t::Input, param, &canvas));
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case WM_ERASEBKGND: {
|
case WM_ERASEBKGND: {
|
||||||
|
@ -327,32 +340,45 @@ LRESULT pHiro::wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
|
||||||
MenuItem &w = (MenuItem&)*widget;
|
MenuItem &w = (MenuItem&)*widget;
|
||||||
if(w.on_tick) w.on_tick(event_t(event_t::Tick, 0, &w));
|
if(w.on_tick) w.on_tick(event_t(event_t::Tick, 0, &w));
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case Widget::MenuCheckItemType: {
|
case Widget::MenuCheckItemType: {
|
||||||
MenuCheckItem &w = (MenuCheckItem&)*widget;
|
MenuCheckItem &w = (MenuCheckItem&)*widget;
|
||||||
w.check(!w.checked()); //invert check state
|
w.check(!w.checked()); //invert check state
|
||||||
if(w.on_tick) w.on_tick(event_t(event_t::Tick, w.checked(), &w));
|
if(w.on_tick) w.on_tick(event_t(event_t::Tick, w.checked(), &w));
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case Widget::MenuRadioItemType: {
|
case Widget::MenuRadioItemType: {
|
||||||
MenuRadioItem &w = (MenuRadioItem&)*widget;
|
MenuRadioItem &w = (MenuRadioItem&)*widget;
|
||||||
bool checked = w.checked();
|
bool checked = w.checked();
|
||||||
w.check();
|
w.check();
|
||||||
if(!checked && w.on_tick) w.on_tick(event_t(event_t::Tick, w.checked(), &w));
|
if(!checked && w.on_tick) w.on_tick(event_t(event_t::Tick, w.checked(), &w));
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case Widget::ButtonType: {
|
case Widget::ButtonType: {
|
||||||
Button &w = (Button&)*widget;
|
Button &w = (Button&)*widget;
|
||||||
if(w.on_tick) w.on_tick(event_t(event_t::Tick, 0, &w));
|
if(w.on_tick) w.on_tick(event_t(event_t::Tick, 0, &w));
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case Widget::CheckboxType: {
|
case Widget::CheckboxType: {
|
||||||
Checkbox &w = (Checkbox&)*widget;
|
Checkbox &w = (Checkbox&)*widget;
|
||||||
w.check(!w.checked()); //invert check state
|
w.check(!w.checked()); //invert check state
|
||||||
if(w.on_tick) w.on_tick(event_t(event_t::Tick, w.checked(), &w));
|
if(w.on_tick) w.on_tick(event_t(event_t::Tick, w.checked(), &w));
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case Widget::RadioboxType: {
|
case Widget::RadioboxType: {
|
||||||
Radiobox &w = (Radiobox&)*widget;
|
Radiobox &w = (Radiobox&)*widget;
|
||||||
bool checked = w.checked();
|
bool checked = w.checked();
|
||||||
w.check();
|
w.check();
|
||||||
if(!checked && w.on_tick) w.on_tick(event_t(event_t::Tick, w.checked(), &w));
|
if(!checked && w.on_tick) w.on_tick(event_t(event_t::Tick, w.checked(), &w));
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case Widget::EditboxType: {
|
||||||
|
Editbox &editbox = (Editbox&)*widget;
|
||||||
|
if(HIWORD(wparam) == EN_CHANGE) {
|
||||||
|
if(editbox.on_change) editbox.on_change(event_t(event_t::Change, 0, &editbox));
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
case Widget::ComboboxType: {
|
case Widget::ComboboxType: {
|
||||||
Combobox &combobox = (Combobox&)*widget;
|
Combobox &combobox = (Combobox&)*widget;
|
||||||
if(HIWORD(wparam) == CBN_SELCHANGE) {
|
if(HIWORD(wparam) == CBN_SELCHANGE) {
|
||||||
|
@ -384,13 +410,28 @@ LRESULT pHiro::wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
|
||||||
switch(widget->type) {
|
switch(widget->type) {
|
||||||
case Widget::ListboxType: {
|
case Widget::ListboxType: {
|
||||||
Listbox &listbox = (Listbox&)*widget;
|
Listbox &listbox = (Listbox&)*widget;
|
||||||
if(((LPNMHDR)lparam)->code == LVN_ITEMCHANGED
|
LPNMHDR nmhdr = (LPNMHDR)lparam;
|
||||||
&& ((LPNMLISTVIEW)lparam)->uChanged & LVIF_STATE
|
LPNMLISTVIEW nmlistview = (LPNMLISTVIEW)lparam;
|
||||||
&& ListView_GetItemState(listbox.p.hwnd, ((LPNMLISTVIEW)lparam)->iItem, LVIS_FOCUSED)
|
|
||||||
&& ListView_GetItemState(listbox.p.hwnd, ((LPNMLISTVIEW)lparam)->iItem, LVIS_SELECTED)
|
if(nmhdr->code == LVN_ITEMCHANGED && (nmlistview->uChanged & LVIF_STATE)) {
|
||||||
) {
|
//LVN_ITEMCHANGED is sent whenever an item gains or loses either focus or selection;
|
||||||
if(listbox.on_change) listbox.on_change(event_t(event_t::Change, listbox.get_selection(), &listbox));
|
//it does not send a special message to indicate that no items are focused or selected.
|
||||||
} else if(((LPNMHDR)lparam)->code == LVN_ITEMACTIVATE) {
|
//it will send two messages when a different item gains selection -- the first to remove
|
||||||
|
//focus from the old item, the second to set selection to the new item.
|
||||||
|
//hiro sends only one message whenever an item changed (eg gained selection),
|
||||||
|
//including for deselection of all items. below code adapts win32 model to hiro model.
|
||||||
|
//(focused means an item has a dotted outline box around it, but is not highlighted.)
|
||||||
|
//(selected means an item is highlighted.)
|
||||||
|
if((nmlistview->uOldState & LVIS_FOCUSED) && !(nmlistview->uNewState & LVIS_FOCUSED)) {
|
||||||
|
listbox.p.lostfocus = true;
|
||||||
|
} else {
|
||||||
|
if((!(nmlistview->uOldState & LVIS_SELECTED) && (nmlistview->uNewState & LVIS_SELECTED))
|
||||||
|
|| (listbox.p.lostfocus == false && listbox.get_selection() == -1)) {
|
||||||
|
if(listbox.on_change) listbox.on_change(event_t(event_t::Change, listbox.get_selection(), &listbox));
|
||||||
|
}
|
||||||
|
listbox.p.lostfocus = false;
|
||||||
|
}
|
||||||
|
} else if(nmhdr->code == LVN_ITEMACTIVATE) {
|
||||||
if(listbox.on_activate) listbox.on_activate(event_t(event_t::Activate, listbox.get_selection(), &listbox));
|
if(listbox.on_activate) listbox.on_activate(event_t(event_t::Activate, listbox.get_selection(), &listbox));
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
|
@ -5,39 +5,53 @@
|
||||||
#undef _WIN32_WINNT
|
#undef _WIN32_WINNT
|
||||||
#undef _WIN32_IE
|
#undef _WIN32_IE
|
||||||
#undef NOMINMAX
|
#undef NOMINMAX
|
||||||
|
#undef _NO_OLDNAMES
|
||||||
|
|
||||||
#define WINVER 0x0501
|
#define WINVER 0x0501
|
||||||
#define _WIN32_WINNT 0x0501
|
#define _WIN32_WINNT 0x0501
|
||||||
#define _WIN32_IE 0x0600
|
#define _WIN32_IE 0x0600
|
||||||
#define NOMINMAX
|
#define NOMINMAX
|
||||||
|
#define _NO_OLDNAMES
|
||||||
|
|
||||||
#define UNICODE
|
#define UNICODE
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <commctrl.h>
|
#include <commctrl.h>
|
||||||
|
#include <io.h>
|
||||||
|
#include <direct.h>
|
||||||
#include <shlobj.h>
|
#include <shlobj.h>
|
||||||
|
|
||||||
|
#include <nall/algorithm.hpp>
|
||||||
|
using nall::min;
|
||||||
|
using nall::max;
|
||||||
|
|
||||||
|
#include <nall/utf8.hpp>
|
||||||
|
using nall::utf8;
|
||||||
|
using nall::utf16;
|
||||||
|
|
||||||
|
extern int hiromain(int argc, const char *const argv[]);
|
||||||
|
|
||||||
namespace libhiro {
|
namespace libhiro {
|
||||||
|
|
||||||
#include "widget.h"
|
#include "widget.hpp"
|
||||||
#include "window.h"
|
#include "window.hpp"
|
||||||
#include "menucontrol.h"
|
#include "menucontrol.hpp"
|
||||||
#include "menugroup.h"
|
#include "menugroup.hpp"
|
||||||
#include "menuitem.h"
|
#include "menuitem.hpp"
|
||||||
#include "menucheckitem.h"
|
#include "menucheckitem.hpp"
|
||||||
#include "menuradioitem.h"
|
#include "menuradioitem.hpp"
|
||||||
#include "menuseparator.h"
|
#include "menuseparator.hpp"
|
||||||
#include "formcontrol.h"
|
#include "formcontrol.hpp"
|
||||||
#include "frame.h"
|
#include "frame.hpp"
|
||||||
#include "canvas.h"
|
#include "canvas.hpp"
|
||||||
#include "label.h"
|
#include "label.hpp"
|
||||||
#include "button.h"
|
#include "button.hpp"
|
||||||
#include "checkbox.h"
|
#include "checkbox.hpp"
|
||||||
#include "radiobox.h"
|
#include "radiobox.hpp"
|
||||||
#include "editbox.h"
|
#include "editbox.hpp"
|
||||||
#include "listbox.h"
|
#include "listbox.hpp"
|
||||||
#include "combobox.h"
|
#include "combobox.hpp"
|
||||||
#include "progressbar.h"
|
#include "progressbar.hpp"
|
||||||
#include "slider.h"
|
#include "slider.hpp"
|
||||||
|
|
||||||
class pHiro {
|
class pHiro {
|
||||||
public:
|
public:
|
|
@ -94,4 +94,5 @@ void pListbox::reset() {
|
||||||
|
|
||||||
pListbox::pListbox(Listbox &self_) : pFormControl(self_), self(self_) {
|
pListbox::pListbox(Listbox &self_) : pFormControl(self_), self(self_) {
|
||||||
column_count = 0;
|
column_count = 0;
|
||||||
|
lostfocus = false; //used for message parsing
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,4 +14,5 @@ public:
|
||||||
|
|
||||||
/* internal */
|
/* internal */
|
||||||
unsigned column_count;
|
unsigned column_count;
|
||||||
|
bool lostfocus;
|
||||||
};
|
};
|
|
@ -19,5 +19,22 @@ bool pMenuCheckItem::checked() {
|
||||||
return info.fState & MFS_CHECKED;
|
return info.fState & MFS_CHECKED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void pMenuCheckItem::enable(bool state) {
|
||||||
|
EnableMenuItem(parent, instance, MF_BYCOMMAND | (state ? MF_ENABLED : MF_GRAYED));
|
||||||
|
}
|
||||||
|
|
||||||
|
void pMenuCheckItem::disable() {
|
||||||
|
enable(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool pMenuCheckItem::enabled() {
|
||||||
|
MENUITEMINFO info;
|
||||||
|
memset(&info, 0, sizeof info);
|
||||||
|
info.cbSize = sizeof info;
|
||||||
|
info.fMask = MIIM_STATE;
|
||||||
|
GetMenuItemInfo(parent, instance, false, &info);
|
||||||
|
return info.fState & MFS_ENABLED;
|
||||||
|
}
|
||||||
|
|
||||||
pMenuCheckItem::pMenuCheckItem(MenuCheckItem &self_) : pMenuControl(self_), self(self_) {
|
pMenuCheckItem::pMenuCheckItem(MenuCheckItem &self_) : pMenuControl(self_), self(self_) {
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,10 @@ public:
|
||||||
void uncheck();
|
void uncheck();
|
||||||
bool checked();
|
bool checked();
|
||||||
|
|
||||||
|
void enable(bool = true);
|
||||||
|
void disable();
|
||||||
|
bool enabled();
|
||||||
|
|
||||||
MenuCheckItem &self;
|
MenuCheckItem &self;
|
||||||
pMenuCheckItem(MenuCheckItem&);
|
pMenuCheckItem(MenuCheckItem&);
|
||||||
};
|
};
|
|
@ -1,18 +1,11 @@
|
||||||
void pMenuControl::enable(bool state) {
|
void pMenuControl::enable(bool) {
|
||||||
EnableMenuItem(parent, instance, MF_BYCOMMAND | (state ? MF_ENABLED : MF_GRAYED));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void pMenuControl::disable() {
|
void pMenuControl::disable() {
|
||||||
enable(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool pMenuControl::enabled() {
|
bool pMenuControl::enabled() {
|
||||||
MENUITEMINFO info;
|
return true;
|
||||||
memset(&info, 0, sizeof info);
|
|
||||||
info.cbSize = sizeof info;
|
|
||||||
info.fMask = MIIM_STATE;
|
|
||||||
GetMenuItemInfo(parent, instance, false, &info);
|
|
||||||
return info.fState & MFS_ENABLED;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pMenuControl::pMenuControl(MenuControl &self_) : pWidget(self_), self(self_) {
|
pMenuControl::pMenuControl(MenuControl &self_) : pWidget(self_), self(self_) {
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
class pMenuControl : public pWidget {
|
class pMenuControl : public pWidget {
|
||||||
public:
|
public:
|
||||||
void enable(bool = true);
|
virtual void enable(bool = true);
|
||||||
void disable();
|
virtual void disable();
|
||||||
bool enabled();
|
virtual bool enabled();
|
||||||
|
|
||||||
MenuControl &self;
|
MenuControl &self;
|
||||||
pMenuControl(MenuControl&);
|
pMenuControl(MenuControl&);
|
|
@ -26,6 +26,23 @@ void pMenuGroup::attach(MenuControl &menucontrol) {
|
||||||
menucontrol.p.parent = group;
|
menucontrol.p.parent = group;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void pMenuGroup::enable(bool state) {
|
||||||
|
EnableMenuItem(parent, (unsigned)group, MF_BYCOMMAND | (state ? MF_ENABLED : MF_GRAYED));
|
||||||
|
}
|
||||||
|
|
||||||
|
void pMenuGroup::disable() {
|
||||||
|
enable(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool pMenuGroup::enabled() {
|
||||||
|
MENUITEMINFO info;
|
||||||
|
memset(&info, 0, sizeof info);
|
||||||
|
info.cbSize = sizeof info;
|
||||||
|
info.fMask = MIIM_STATE;
|
||||||
|
GetMenuItemInfo(parent, (unsigned)group, false, &info);
|
||||||
|
return info.fState & MFS_ENABLED;
|
||||||
|
}
|
||||||
|
|
||||||
pMenuGroup::pMenuGroup(MenuGroup &self_) : pMenuControl(self_), self(self_) {
|
pMenuGroup::pMenuGroup(MenuGroup &self_) : pMenuControl(self_), self(self_) {
|
||||||
group = 0;
|
group = 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,10 @@ public:
|
||||||
void create(const char *text);
|
void create(const char *text);
|
||||||
void attach(MenuControl &menucontrol);
|
void attach(MenuControl &menucontrol);
|
||||||
|
|
||||||
|
void enable(bool = true);
|
||||||
|
void disable();
|
||||||
|
bool enabled();
|
||||||
|
|
||||||
pMenuGroup(MenuGroup&);
|
pMenuGroup(MenuGroup&);
|
||||||
|
|
||||||
/* internal */
|
/* internal */
|
|
@ -2,5 +2,22 @@ void pMenuItem::create(const char *text_) {
|
||||||
text = strdup(text_);
|
text = strdup(text_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void pMenuItem::enable(bool state) {
|
||||||
|
EnableMenuItem(parent, instance, MF_BYCOMMAND | (state ? MF_ENABLED : MF_GRAYED));
|
||||||
|
}
|
||||||
|
|
||||||
|
void pMenuItem::disable() {
|
||||||
|
enable(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool pMenuItem::enabled() {
|
||||||
|
MENUITEMINFO info;
|
||||||
|
memset(&info, 0, sizeof info);
|
||||||
|
info.cbSize = sizeof info;
|
||||||
|
info.fMask = MIIM_STATE;
|
||||||
|
GetMenuItemInfo(parent, instance, false, &info);
|
||||||
|
return info.fState & MFS_ENABLED;
|
||||||
|
}
|
||||||
|
|
||||||
pMenuItem::pMenuItem(MenuItem &self_) : pMenuControl(self_), self(self_) {
|
pMenuItem::pMenuItem(MenuItem &self_) : pMenuControl(self_), self(self_) {
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,10 @@ class pMenuItem : public pMenuControl {
|
||||||
public:
|
public:
|
||||||
void create(const char *text = "");
|
void create(const char *text = "");
|
||||||
|
|
||||||
|
void enable(bool = true);
|
||||||
|
void disable();
|
||||||
|
bool enabled();
|
||||||
|
|
||||||
MenuItem &self;
|
MenuItem &self;
|
||||||
pMenuItem(MenuItem&);
|
pMenuItem(MenuItem&);
|
||||||
};
|
};
|
|
@ -19,6 +19,23 @@ bool pMenuRadioItem::checked() {
|
||||||
return info.fState & MFS_CHECKED;
|
return info.fState & MFS_CHECKED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void pMenuRadioItem::enable(bool state) {
|
||||||
|
EnableMenuItem(parent, instance, MF_BYCOMMAND | (state ? MF_ENABLED : MF_GRAYED));
|
||||||
|
}
|
||||||
|
|
||||||
|
void pMenuRadioItem::disable() {
|
||||||
|
enable(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool pMenuRadioItem::enabled() {
|
||||||
|
MENUITEMINFO info;
|
||||||
|
memset(&info, 0, sizeof info);
|
||||||
|
info.cbSize = sizeof info;
|
||||||
|
info.fMask = MIIM_STATE;
|
||||||
|
GetMenuItemInfo(parent, instance, false, &info);
|
||||||
|
return info.fState & MFS_ENABLED;
|
||||||
|
}
|
||||||
|
|
||||||
pMenuRadioItem::pMenuRadioItem(MenuRadioItem &self_) : pMenuControl(self_), self(self_) {
|
pMenuRadioItem::pMenuRadioItem(MenuRadioItem &self_) : pMenuControl(self_), self(self_) {
|
||||||
create_checked = false;
|
create_checked = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,10 @@ public:
|
||||||
void check();
|
void check();
|
||||||
bool checked();
|
bool checked();
|
||||||
|
|
||||||
|
void enable(bool = true);
|
||||||
|
void disable();
|
||||||
|
bool enabled();
|
||||||
|
|
||||||
MenuRadioItem &self;
|
MenuRadioItem &self;
|
||||||
pMenuRadioItem(MenuRadioItem&);
|
pMenuRadioItem(MenuRadioItem&);
|
||||||
|
|
|
@ -1,5 +1,22 @@
|
||||||
void pMenuSeparator::create() {
|
void pMenuSeparator::create() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void pMenuSeparator::enable(bool state) {
|
||||||
|
EnableMenuItem(parent, instance, MF_BYCOMMAND | (state ? MF_ENABLED : MF_GRAYED));
|
||||||
|
}
|
||||||
|
|
||||||
|
void pMenuSeparator::disable() {
|
||||||
|
enable(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool pMenuSeparator::enabled() {
|
||||||
|
MENUITEMINFO info;
|
||||||
|
memset(&info, 0, sizeof info);
|
||||||
|
info.cbSize = sizeof info;
|
||||||
|
info.fMask = MIIM_STATE;
|
||||||
|
GetMenuItemInfo(parent, instance, false, &info);
|
||||||
|
return info.fState & MFS_ENABLED;
|
||||||
|
}
|
||||||
|
|
||||||
pMenuSeparator::pMenuSeparator(MenuSeparator &self_) : pMenuControl(self_), self(self_) {
|
pMenuSeparator::pMenuSeparator(MenuSeparator &self_) : pMenuControl(self_), self(self_) {
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,5 +3,9 @@ public:
|
||||||
MenuSeparator &self;
|
MenuSeparator &self;
|
||||||
void create();
|
void create();
|
||||||
|
|
||||||
|
void enable(bool = true);
|
||||||
|
void disable();
|
||||||
|
bool enabled();
|
||||||
|
|
||||||
pMenuSeparator(MenuSeparator&);
|
pMenuSeparator(MenuSeparator&);
|
||||||
};
|
};
|
|
@ -0,0 +1,40 @@
|
||||||
|
char* realpath(const char *file_name, char *resolved_name) {
|
||||||
|
wchar_t filename[_MAX_PATH] = L"";
|
||||||
|
_wfullpath(filename, utf16(file_name), _MAX_PATH);
|
||||||
|
strcpy(resolved_name, utf8(filename));
|
||||||
|
return resolved_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* userpath(char *output) {
|
||||||
|
wchar_t path[_MAX_PATH] = L"";
|
||||||
|
SHGetFolderPathW(0, CSIDL_APPDATA | CSIDL_FLAG_CREATE, 0, 0, path);
|
||||||
|
strcpy(output, utf8(path));
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
int mkdir(const char *path) {
|
||||||
|
return _wmkdir(utf16(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
int __stdcall WinMain(HINSTANCE, HINSTANCE, LPSTR, int) {
|
||||||
|
//argv[] is in 7-bit ANSI format; Unicode characters are converted to '?'s.
|
||||||
|
//this needs to be converted to UTF-8, eg for realpath(argv[0]) to work.
|
||||||
|
int argc;
|
||||||
|
wchar_t **wargv = CommandLineToArgvW(GetCommandLineW(), &argc);
|
||||||
|
char **argv = new char*[argc];
|
||||||
|
for(unsigned i = 0; i < argc; i++) {
|
||||||
|
argv[i] = new char[_MAX_PATH];
|
||||||
|
strcpy(argv[i], utf8(wargv[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
libhiro::hiro().init();
|
||||||
|
int result = hiromain(argc, argv);
|
||||||
|
libhiro::hiro().term();
|
||||||
|
|
||||||
|
for(unsigned i = 0; i < argc; i++) {
|
||||||
|
delete[] argv[i];
|
||||||
|
}
|
||||||
|
delete[] argv;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
#include "libfilter.h"
|
#include "libfilter.hpp"
|
||||||
using nall::min;
|
using nall::min;
|
||||||
using nall::max;
|
using nall::max;
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,12 @@ T max(const T& t, const U& u) {
|
||||||
return t > u ? t : u;
|
return t > u ? t : u;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//pseudo-random number generator
|
||||||
|
inline unsigned prng() {
|
||||||
|
static unsigned n = 0;
|
||||||
|
return n = (n >> 1) ^ (((n & 1) - 1) & 0xedb88320);
|
||||||
|
}
|
||||||
|
|
||||||
} //namespace nall
|
} //namespace nall
|
||||||
|
|
||||||
#endif //ifndef NALL_ALGORITHM_HPP
|
#endif //ifndef NALL_ALGORITHM_HPP
|
||||||
|
|
|
@ -58,8 +58,8 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void resize(unsigned size) {
|
void resize(unsigned size) {
|
||||||
reserve(findsize(size));
|
if(size > poolsize) reserve(findsize(size));
|
||||||
buffersize = size;
|
if(size > buffersize) buffersize = size;
|
||||||
}
|
}
|
||||||
|
|
||||||
array() {
|
array() {
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
#ifndef NALL_ENDIAN_HPP
|
||||||
|
#define NALL_ENDIAN_HPP
|
||||||
|
|
||||||
|
#if !defined(ARCH_MSB)
|
||||||
|
//little-endian: uint8_t[] { 0x01, 0x02, 0x03, 0x04 } == 0x04030201
|
||||||
|
#define order_lsb2(a,b) a,b
|
||||||
|
#define order_lsb3(a,b,c) a,b,c
|
||||||
|
#define order_lsb4(a,b,c,d) a,b,c,d
|
||||||
|
#define order_lsb5(a,b,c,d,e) a,b,c,d,e
|
||||||
|
#define order_lsb6(a,b,c,d,e,f) a,b,c,d,e,f
|
||||||
|
#define order_lsb7(a,b,c,d,e,f,g) a,b,c,d,e,f,g
|
||||||
|
#define order_lsb8(a,b,c,d,e,f,g,h) a,b,c,d,e,f,g,h
|
||||||
|
#define order_msb2(a,b) b,a
|
||||||
|
#define order_msb3(a,b,c) c,b,a
|
||||||
|
#define order_msb4(a,b,c,d) d,c,b,a
|
||||||
|
#define order_msb5(a,b,c,d,e) e,d,c,b,a
|
||||||
|
#define order_msb6(a,b,c,d,e,f) f,e,d,c,b,a
|
||||||
|
#define order_msb7(a,b,c,d,e,f,g) g,f,e,d,c,b,a
|
||||||
|
#define order_msb8(a,b,c,d,e,f,g,h) h,g,f,e,d,c,b,a
|
||||||
|
#else
|
||||||
|
//big-endian: uint8_t[] { 0x01, 0x02, 0x03, 0x04 } == 0x01020304
|
||||||
|
#define order_lsb2(a,b) b,a
|
||||||
|
#define order_lsb3(a,b,c) c,b,a
|
||||||
|
#define order_lsb4(a,b,c,d) d,c,b,a
|
||||||
|
#define order_lsb5(a,b,c,d,e) e,d,c,b,a
|
||||||
|
#define order_lsb6(a,b,c,d,e,f) f,e,d,c,b,a
|
||||||
|
#define order_lsb7(a,b,c,d,e,f,g) g,f,e,d,c,b,a
|
||||||
|
#define order_lsb8(a,b,c,d,e,f,g,h) h,g,f,e,d,c,b,a
|
||||||
|
#define order_msb2(a,b) a,b
|
||||||
|
#define order_msb3(a,b,c) a,b,c
|
||||||
|
#define order_msb4(a,b,c,d) a,b,c,d
|
||||||
|
#define order_msb5(a,b,c,d,e) a,b,c,d,e
|
||||||
|
#define order_msb6(a,b,c,d,e,f) a,b,c,d,e,f
|
||||||
|
#define order_msb7(a,b,c,d,e,f,g) a,b,c,d,e,f,g
|
||||||
|
#define order_msb8(a,b,c,d,e,f,g,h) a,b,c,d,e,f,g,h
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif //ifndef NALL_ENDIAN_HPP
|
|
@ -5,7 +5,6 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include <nall/static.hpp>
|
|
||||||
#include <nall/stdint.hpp>
|
#include <nall/stdint.hpp>
|
||||||
|
|
||||||
namespace nall {
|
namespace nall {
|
||||||
|
@ -31,41 +30,54 @@ struct keyboard {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct mouse {
|
||||||
|
enum { buttons = 8 };
|
||||||
|
|
||||||
|
enum {
|
||||||
|
none = keyboard::limit,
|
||||||
|
x, y, z,
|
||||||
|
button,
|
||||||
|
limit = button + buttons,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
template<int number = -1> struct joypad {
|
template<int number = -1> struct joypad {
|
||||||
|
enum { axes = 8 };
|
||||||
|
enum { buttons = 96 };
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
none = joypad<number - 1>::limit,
|
none = joypad<number - 1>::limit,
|
||||||
up, down, left, right,
|
up, down, left, right,
|
||||||
button_00, button_01, button_02, button_03,
|
axis,
|
||||||
button_04, button_05, button_06, button_07,
|
button = axis + axes,
|
||||||
button_08, button_09, button_10, button_11,
|
limit = button + buttons,
|
||||||
button_12, button_13, button_14, button_15,
|
|
||||||
limit,
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
template<> struct joypad<-1> {
|
template<> struct joypad<-1> {
|
||||||
|
enum { count = 16 };
|
||||||
|
enum { axes = 8 };
|
||||||
|
enum { buttons = 96 };
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
none,
|
none,
|
||||||
up, down, left, right,
|
up, down, left, right,
|
||||||
button_00, button_01, button_02, button_03,
|
axis,
|
||||||
button_04, button_05, button_06, button_07,
|
button = axis + axes,
|
||||||
button_08, button_09, button_10, button_11,
|
length = button + buttons - none, //number of syms per joypad
|
||||||
button_12, button_13, button_14, button_15,
|
limit = mouse::limit,
|
||||||
length = button_15 - none + 1, //number of syms per joypad
|
|
||||||
limit = keyboard::limit, //start joypad syms immediately after keyboard syms
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static uint16_t index(int joypad_number, int joypad_enum) {
|
static uint16_t index(unsigned joypad_number, unsigned joypad_enum) {
|
||||||
if(joypad_number < 0 || joypad_number > 15) return keyboard::none;
|
if(joypad_number >= count) return keyboard::none;
|
||||||
return limit + joypad_number * length + joypad_enum;
|
return limit + joypad_number * length + joypad_enum;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
enum { input_limit = joypad<15>::limit };
|
enum { input_limit = joypad<joypad<>::count - 1>::limit };
|
||||||
|
|
||||||
static static_assert<keyboard::limit < 256> keyboard_length_assert; //error if keyboard syms spill into joypad syms
|
static const char sym_table[][64] = {
|
||||||
|
//keyboard
|
||||||
static const char keyboard_table[][64] = {
|
|
||||||
"none",
|
"none",
|
||||||
"escape", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "f10", "f11", "f12",
|
"escape", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "f10", "f11", "f12",
|
||||||
"print_screen", "scroll_lock", "pause", "tilde",
|
"print_screen", "scroll_lock", "pause", "tilde",
|
||||||
|
@ -81,24 +93,39 @@ static const char keyboard_table[][64] = {
|
||||||
"up", "down", "left", "right",
|
"up", "down", "left", "right",
|
||||||
"tab", "return", "spacebar",
|
"tab", "return", "spacebar",
|
||||||
"lctrl", "rctrl", "lalt", "ralt", "lshift", "rshift", "lsuper", "rsuper", "menu",
|
"lctrl", "rctrl", "lalt", "ralt", "lshift", "rshift", "lsuper", "rsuper", "menu",
|
||||||
"limit",
|
"keyboard.limit",
|
||||||
|
|
||||||
|
//mouse
|
||||||
|
"mouse.x", "mouse.y", "mouse.z",
|
||||||
|
"mouse.button00", "mouse.button01", "mouse.button02", "mouse.button03",
|
||||||
|
"mouse.button04", "mouse.button05", "mouse.button06", "mouse.button07",
|
||||||
|
"mouse.limit",
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char* input_find(uint16_t key) {
|
static const char* input_find(uint16_t key) {
|
||||||
if(key < keyboard::limit) return keyboard_table[key];
|
if(key < mouse::limit) return sym_table[key];
|
||||||
|
|
||||||
static char buffer[64];
|
static char buffer[64];
|
||||||
for(uint16_t j = 0; j < 16; j++) {
|
for(unsigned j = 0; j < 16; j++) {
|
||||||
if(key == joypad<>::index(j, joypad<>::up)) { sprintf(buffer, "joypad%0.2d.up", j); return buffer; }
|
if(key == joypad<>::index(j, joypad<>::up)) { sprintf(buffer, "joypad%0.2d.up", j); return buffer; }
|
||||||
if(key == joypad<>::index(j, joypad<>::down)) { sprintf(buffer, "joypad%0.2d.down", j); return buffer; }
|
if(key == joypad<>::index(j, joypad<>::down)) { sprintf(buffer, "joypad%0.2d.down", j); return buffer; }
|
||||||
if(key == joypad<>::index(j, joypad<>::left)) { sprintf(buffer, "joypad%0.2d.left", j); return buffer; }
|
if(key == joypad<>::index(j, joypad<>::left)) { sprintf(buffer, "joypad%0.2d.left", j); return buffer; }
|
||||||
if(key == joypad<>::index(j, joypad<>::right)) { sprintf(buffer, "joypad%0.2d.right", j); return buffer; }
|
if(key == joypad<>::index(j, joypad<>::right)) { sprintf(buffer, "joypad%0.2d.right", j); return buffer; }
|
||||||
if(key >= joypad<>::index(j, joypad<>::button_00)
|
|
||||||
&& key <= joypad<>::index(j, joypad<>::button_15)) {
|
if(key >= joypad<>::index(j, joypad<>::axis + 0)
|
||||||
sprintf(buffer, "joypad%0.2d.button_%0.2d", j, key - joypad<>::index(j, joypad<>::button_00));
|
&& key < joypad<>::index(j, joypad<>::axis + joypad<>::axes)) {
|
||||||
|
sprintf(buffer, "joypad%0.2d.axis%0.2d", j, key - joypad<>::index(j, joypad<>::axis));
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(key >= joypad<>::index(j, joypad<>::button + 0)
|
||||||
|
&& key < joypad<>::index(j, joypad<>::button + joypad<>::buttons)) {
|
||||||
|
sprintf(buffer, "joypad%0.2d.button%0.2d", j, key - joypad<>::index(j, joypad<>::button));
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return keyboard_table[0]; //"none"
|
|
||||||
|
return "none";
|
||||||
}
|
}
|
||||||
|
|
||||||
static char* input_find(char *out, uint16_t key) {
|
static char* input_find(char *out, uint16_t key) {
|
||||||
|
@ -107,8 +134,8 @@ static char* input_find(char *out, uint16_t key) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint16_t input_find(const char *key) {
|
static uint16_t input_find(const char *key) {
|
||||||
for(uint16_t i = 0; i < keyboard::limit; i++) {
|
for(unsigned i = 0; i < mouse::limit; i++) {
|
||||||
if(!strcmp(keyboard_table[i], key)) return i;
|
if(!strcmp(sym_table[i], key)) return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(memcmp(key, "joypad", 6)) return keyboard::none;
|
if(memcmp(key, "joypad", 6)) return keyboard::none;
|
||||||
|
@ -116,17 +143,30 @@ static uint16_t input_find(const char *key) {
|
||||||
if(!*key || !*(key + 1)) return keyboard::none;
|
if(!*key || !*(key + 1)) return keyboard::none;
|
||||||
uint8_t j = (*key - '0') * 10 + (*(key + 1) - '0');
|
uint8_t j = (*key - '0') * 10 + (*(key + 1) - '0');
|
||||||
if(j > 15) return keyboard::none;
|
if(j > 15) return keyboard::none;
|
||||||
|
|
||||||
key += 2;
|
key += 2;
|
||||||
if(!strcmp(key, ".up")) return joypad<>::index(j, joypad<>::up);
|
if(!strcmp(key, ".up")) return joypad<>::index(j, joypad<>::up);
|
||||||
if(!strcmp(key, ".down")) return joypad<>::index(j, joypad<>::down);
|
if(!strcmp(key, ".down")) return joypad<>::index(j, joypad<>::down);
|
||||||
if(!strcmp(key, ".left")) return joypad<>::index(j, joypad<>::left);
|
if(!strcmp(key, ".left")) return joypad<>::index(j, joypad<>::left);
|
||||||
if(!strcmp(key, ".right")) return joypad<>::index(j, joypad<>::right);
|
if(!strcmp(key, ".right")) return joypad<>::index(j, joypad<>::right);
|
||||||
if(memcmp(key, ".button_", 8)) return keyboard::none;
|
|
||||||
key += 8;
|
if(!memcmp(key, ".axis", 5)) {
|
||||||
if(!*key || !*(key + 1)) return keyboard::none;
|
key += 5;
|
||||||
uint8_t button = (*key - '0') * 10 + (*(key + 1) - '0');
|
if(!*key || !*(key + 1)) return keyboard::none;
|
||||||
if(button > 15) return keyboard::none;
|
uint8_t axis = (*key - '0') * 10 + (*(key + 1) - '0');
|
||||||
return joypad<>::index(j, joypad<>::button_00 + button);
|
if(axis >= joypad<>::axes) return keyboard::none;
|
||||||
|
return joypad<>::index(j, joypad<>::axis + axis);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!memcmp(key, ".button", 7)) {
|
||||||
|
key += 7;
|
||||||
|
if(!*key || !*(key + 1)) return keyboard::none;
|
||||||
|
uint8_t button = (*key - '0') * 10 + (*(key + 1) - '0');
|
||||||
|
if(button >= joypad<>::buttons) return keyboard::none;
|
||||||
|
return joypad<>::index(j, joypad<>::button + button);
|
||||||
|
}
|
||||||
|
|
||||||
|
return keyboard::none;
|
||||||
}
|
}
|
||||||
|
|
||||||
} //namespace nall
|
} //namespace nall
|
||||||
|
|
|
@ -1,40 +1,62 @@
|
||||||
#ifndef NALL_SORT_HPP
|
#ifndef NALL_SORT_HPP
|
||||||
#define NALL_SORT_HPP
|
#define NALL_SORT_HPP
|
||||||
|
|
||||||
|
//class: merge sort
|
||||||
|
//average: O(n log n)
|
||||||
|
//worst: O(n log n)
|
||||||
|
//memory: O(n)
|
||||||
|
//stack: O(log n)
|
||||||
|
//stable?: yes
|
||||||
|
|
||||||
|
//notes:
|
||||||
|
//there are two primary reasons for choosing merge sort
|
||||||
|
//over the (usually) faster quick sort*:
|
||||||
|
//1: it is a stable sort.
|
||||||
|
//2: it lacks O(n^2) worst-case overhead.
|
||||||
|
//(* which is also O(n log n) in the average case.)
|
||||||
|
|
||||||
namespace nall {
|
namespace nall {
|
||||||
|
|
||||||
template<typename T> inline void swap(T &x, T &y) {
|
template<typename T>
|
||||||
T temp = x;
|
void sort(T list[], unsigned length) {
|
||||||
x = y;
|
if(length <= 1) return; //nothing to sort
|
||||||
y = temp;
|
|
||||||
}
|
//use insertion sort to quickly sort smaller blocks
|
||||||
|
if(length < 64) {
|
||||||
template<typename T>
|
for(unsigned i = 0; i < length; i++) {
|
||||||
void sort(T list[], unsigned length) {
|
unsigned min = i;
|
||||||
for(unsigned d = 0; d < length; d++) {
|
for(unsigned j = i + 1; j < length; j++) {
|
||||||
unsigned min = d;
|
if(list[j] < list[min]) min = j;
|
||||||
for(unsigned s = d + 1; s < length; s++) {
|
}
|
||||||
if(list[s] < list[min]) { min = s; }
|
if(min != i) swap(list[i], list[min]);
|
||||||
}
|
}
|
||||||
if(min != d) {
|
return;
|
||||||
swap(list[d], list[min]);
|
}
|
||||||
}
|
|
||||||
}
|
//split list in half and recursively sort both
|
||||||
}
|
unsigned middle = length / 2;
|
||||||
|
sort(list, middle);
|
||||||
template<typename T, typename Comparator>
|
sort(list + middle, length - middle);
|
||||||
void sort(T list[], unsigned length, Comparator comparator) {
|
|
||||||
for(unsigned d = 0; d < length; d++) {
|
//left and right are sorted here; perform merge sort
|
||||||
unsigned min = d;
|
T *buffer = new T[length];
|
||||||
for(unsigned s = d + 1; s < length; s++) {
|
unsigned offset = 0;
|
||||||
if(comparator(list[s], list[min]) == true) { min = s; }
|
unsigned left = 0;
|
||||||
}
|
unsigned right = middle;
|
||||||
if(min != d) {
|
while(left < middle && right < length) {
|
||||||
swap(list[d], list[min]);
|
if(list[left] < list[right]) {
|
||||||
}
|
buffer[offset++] = list[left++];
|
||||||
}
|
} else {
|
||||||
|
buffer[offset++] = list[right++];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while(left < middle) buffer[offset++] = list[left++];
|
||||||
|
while(right < length) buffer[offset++] = list[right++];
|
||||||
|
|
||||||
|
for(unsigned i = 0; i < length; i++) list[i] = buffer[i];
|
||||||
|
delete[] buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
} //namespace nall
|
} //namespace nall
|
||||||
|
|
||||||
#endif //ifndef NALL_SORT_HPP
|
#endif //ifndef NALL_SORT_HPP
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
#ifdef NALL_STRING_CPP
|
#ifdef NALL_STRING_CPP
|
||||||
|
|
||||||
static int eval_integer(const char *&s) {
|
static int eval_integer(const char *&s) {
|
||||||
if(!*s) throw "nall::bad_eval_integer";
|
if(!*s) throw "unrecognized_integer";
|
||||||
int value = 0, x = *s, y = *(s + 1);
|
int value = 0, x = *s, y = *(s + 1);
|
||||||
|
|
||||||
//hexadecimal
|
//hexadecimal
|
||||||
if(x == '0' && (y == 'X' || y == 'x')) {
|
if(x == '0' && (y == 'X' || y == 'x')) {
|
||||||
s += 2;
|
s += 2;
|
||||||
for(;;) {
|
while(true) {
|
||||||
if(*s >= '0' && *s <= '9') { value = value * 16 + (*s++ - '0'); continue; }
|
if(*s >= '0' && *s <= '9') { value = value * 16 + (*s++ - '0'); continue; }
|
||||||
if(*s >= 'A' && *s <= 'F') { value = value * 16 + (*s++ - 'A' + 10); continue; }
|
if(*s >= 'A' && *s <= 'F') { value = value * 16 + (*s++ - 'A' + 10); continue; }
|
||||||
if(*s >= 'a' && *s <= 'f') { value = value * 16 + (*s++ - 'a' + 10); continue; }
|
if(*s >= 'a' && *s <= 'f') { value = value * 16 + (*s++ - 'a' + 10); continue; }
|
||||||
|
@ -18,7 +18,7 @@ static int eval_integer(const char *&s) {
|
||||||
//binary
|
//binary
|
||||||
if(x == '0' && (y == 'B' || y == 'b')) {
|
if(x == '0' && (y == 'B' || y == 'b')) {
|
||||||
s += 2;
|
s += 2;
|
||||||
for(;;) {
|
while(true) {
|
||||||
if(*s == '0' || *s == '1') { value = value * 2 + (*s++ - '0'); continue; }
|
if(*s == '0' || *s == '1') { value = value * 2 + (*s++ - '0'); continue; }
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ static int eval_integer(const char *&s) {
|
||||||
//octal (or decimal '0')
|
//octal (or decimal '0')
|
||||||
if(x == '0') {
|
if(x == '0') {
|
||||||
s += 1;
|
s += 1;
|
||||||
for(;;) {
|
while(true) {
|
||||||
if(*s >= '0' && *s <= '7') { value = value * 8 + (*s++ - '0'); continue; }
|
if(*s >= '0' && *s <= '7') { value = value * 8 + (*s++ - '0'); continue; }
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
@ -35,35 +35,45 @@ static int eval_integer(const char *&s) {
|
||||||
|
|
||||||
//decimal
|
//decimal
|
||||||
if(x >= '0' && x <= '9') {
|
if(x >= '0' && x <= '9') {
|
||||||
for(;;) {
|
while(true) {
|
||||||
if(*s >= '0' && *s <= '9') { value = value * 10 + (*s++ - '0'); continue; }
|
if(*s >= '0' && *s <= '9') { value = value * 10 + (*s++ - '0'); continue; }
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw "nall::bad_eval_integer";
|
//char
|
||||||
|
if(x == '\'' && y != '\'') {
|
||||||
|
s += 1;
|
||||||
|
while(true) {
|
||||||
|
value = value * 256 + *s++;
|
||||||
|
if(*s == '\'') { s += 1; return value; }
|
||||||
|
if(!*s) throw "mismatched_char";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw "unrecognized_integer";
|
||||||
}
|
}
|
||||||
|
|
||||||
static int eval(const char *&s, int depth = 0) {
|
static int eval(const char *&s, int depth = 0) {
|
||||||
if(!*s) throw "nall::bad_eval";
|
|
||||||
while(*s == ' ' || *s == '\t') s++; //trim whitespace
|
while(*s == ' ' || *s == '\t') s++; //trim whitespace
|
||||||
|
if(!*s) throw "unrecognized_token";
|
||||||
int value = 0, x = *s, y = *(s + 1);
|
int value = 0, x = *s, y = *(s + 1);
|
||||||
|
|
||||||
if(*s == '(') {
|
if(*s == '(') {
|
||||||
value = eval(++s, 1);
|
value = eval(++s, 1);
|
||||||
if(*s++ != ')') throw "nall::bad_eval";
|
if(*s++ != ')') throw "mismatched_group";
|
||||||
}
|
}
|
||||||
|
|
||||||
else if(x == '!') value = !eval(++s, 14);
|
else if(x == '!') value = !eval(++s, 13);
|
||||||
else if(x == '~') value = ~eval(++s, 14);
|
else if(x == '~') value = ~eval(++s, 13);
|
||||||
else if(x == '+') value = +eval(++s, 14);
|
else if(x == '+') value = +eval(++s, 13);
|
||||||
else if(x == '-') value = -eval(++s, 14);
|
else if(x == '-') value = -eval(++s, 13);
|
||||||
|
|
||||||
else if(x >= '0' && x <= '9') value = eval_integer(s);
|
else if((x >= '0' && x <= '9') || x == '\'') value = eval_integer(s);
|
||||||
|
|
||||||
else throw "nall::bad_eval";
|
else throw "unrecognized_token";
|
||||||
|
|
||||||
for(;;) {
|
while(true) {
|
||||||
while(*s == ' ' || *s == '\t') s++; //trim whitespace
|
while(*s == ' ' || *s == '\t') s++; //trim whitespace
|
||||||
if(!*s) break;
|
if(!*s) break;
|
||||||
x = *s, y = *(s + 1);
|
x = *s, y = *(s + 1);
|
||||||
|
@ -109,18 +119,18 @@ static int eval(const char *&s, int depth = 0) {
|
||||||
if(depth >= 3) break;
|
if(depth >= 3) break;
|
||||||
if(x == '|' && y == '|') { value = eval(++++s, 3) || value; continue; }
|
if(x == '|' && y == '|') { value = eval(++++s, 3) || value; continue; }
|
||||||
|
|
||||||
if(depth >= 2) break;
|
|
||||||
if(x == '?') {
|
if(x == '?') {
|
||||||
int lhs = eval(++s, 2);
|
int lhs = eval(++s, 2);
|
||||||
if(*s != ':') throw "nall::bad_eval";
|
if(*s != ':') throw "mismatched_ternary";
|
||||||
int rhs = eval(++s, 2);
|
int rhs = eval(++s, 2);
|
||||||
value = value ? lhs : rhs;
|
value = value ? lhs : rhs;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if(depth >= 2) break;
|
||||||
|
|
||||||
if(depth > 0 && x == ')') break;
|
if(depth > 0 && x == ')') break;
|
||||||
|
|
||||||
throw "nall::bad_eval";
|
throw "unrecognized_token";
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
|
|
|
@ -3,6 +3,12 @@
|
||||||
|
|
||||||
namespace nall {
|
namespace nall {
|
||||||
|
|
||||||
|
template<typename T> inline void swap(T &x, T &y) {
|
||||||
|
T temp = x;
|
||||||
|
x = y;
|
||||||
|
y = temp;
|
||||||
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
struct base_from_member {
|
struct base_from_member {
|
||||||
T value;
|
T value;
|
||||||
|
|
|
@ -89,7 +89,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void reserve(unsigned size) {
|
void reserve(unsigned size) {
|
||||||
if(size == poolsize)return;
|
if(size == poolsize) return;
|
||||||
|
|
||||||
if(size < poolsize) {
|
if(size < poolsize) {
|
||||||
for(unsigned i = size; i < objectsize; i++) { pool[i].~T(); }
|
for(unsigned i = size; i < objectsize; i++) { pool[i].~T(); }
|
||||||
|
@ -100,9 +100,9 @@ public:
|
||||||
poolsize = size;
|
poolsize = size;
|
||||||
}
|
}
|
||||||
|
|
||||||
void resize(int size) {
|
void resize(unsigned size) {
|
||||||
if(size == objectsize)return;
|
if(size == objectsize) return;
|
||||||
if(size > poolsize)reserve(size);
|
if(size > poolsize) reserve(size);
|
||||||
|
|
||||||
if(size < objectsize) {
|
if(size < objectsize) {
|
||||||
for(unsigned i = size; i < objectsize; i++) { pool[i].~T(); }
|
for(unsigned i = size; i < objectsize; i++) { pool[i].~T(); }
|
||||||
|
@ -122,14 +122,24 @@ public:
|
||||||
~linear_vector() { reset(); }
|
~linear_vector() { reset(); }
|
||||||
|
|
||||||
inline operator T&() {
|
inline operator T&() {
|
||||||
if(objectsize == 0)resize(1);
|
if(objectsize == 0) resize(1);
|
||||||
if(objectsize == 0)throw "vector[] out of bounds";
|
if(objectsize == 0) throw "vector[] out of bounds";
|
||||||
return pool[0];
|
return pool[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
inline T &operator[](int index) {
|
inline operator const T&() const {
|
||||||
if(index >= objectsize)resize(index + 1);
|
if(objectsize == 0) throw "vector[] out of bounds";
|
||||||
if(index >= objectsize)throw "vector[] out of bounds";
|
return pool[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
inline T& operator[](int index) {
|
||||||
|
if(index >= objectsize) resize(index + 1);
|
||||||
|
if(index >= objectsize) throw "vector[] out of bounds";
|
||||||
|
return pool[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const T& operator[](int index) const {
|
||||||
|
if(index >= objectsize) throw "vector[] out of bounds";
|
||||||
return pool[index];
|
return pool[index];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -144,7 +154,7 @@ public:
|
||||||
unsigned capacity() const { return poolsize; }
|
unsigned capacity() const { return poolsize; }
|
||||||
|
|
||||||
void reset() {
|
void reset() {
|
||||||
for(unsigned i = 0; i < objectsize; i++) { if(pool[i])delete pool[i]; }
|
for(unsigned i = 0; i < objectsize; i++) { if(pool[i]) delete pool[i]; }
|
||||||
|
|
||||||
if(pool) {
|
if(pool) {
|
||||||
free(pool);
|
free(pool);
|
||||||
|
@ -156,10 +166,10 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void reserve(unsigned size) {
|
void reserve(unsigned size) {
|
||||||
if(size == poolsize)return;
|
if(size == poolsize) return;
|
||||||
|
|
||||||
if(size < poolsize) {
|
if(size < poolsize) {
|
||||||
for(unsigned i = size; i < objectsize; i++) { if(pool[i])delete pool[i]; }
|
for(unsigned i = size; i < objectsize; i++) { if(pool[i]) delete pool[i]; }
|
||||||
objectsize = size;
|
objectsize = size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,12 +180,12 @@ public:
|
||||||
poolsize = size;
|
poolsize = size;
|
||||||
}
|
}
|
||||||
|
|
||||||
void resize(int size) {
|
void resize(unsigned size) {
|
||||||
if(size == objectsize)return;
|
if(size == objectsize) return;
|
||||||
if(size > poolsize)reserve(size);
|
if(size > poolsize) reserve(size);
|
||||||
|
|
||||||
if(size < objectsize) {
|
if(size < objectsize) {
|
||||||
for(unsigned i = size; i < objectsize; i++) { if(pool[i])delete pool[i]; }
|
for(unsigned i = size; i < objectsize; i++) { if(pool[i]) delete pool[i]; }
|
||||||
}
|
}
|
||||||
|
|
||||||
objectsize = size;
|
objectsize = size;
|
||||||
|
@ -190,16 +200,26 @@ public:
|
||||||
~ptr_vector() { reset(); }
|
~ptr_vector() { reset(); }
|
||||||
|
|
||||||
inline operator T&() {
|
inline operator T&() {
|
||||||
if(objectsize == 0)resize(1);
|
if(objectsize == 0) resize(1);
|
||||||
if(objectsize == 0)throw "vector[] out of bounds";
|
if(objectsize == 0) throw "vector[] out of bounds";
|
||||||
if(!pool[0])pool[0] = new T;
|
if(!pool[0]) pool[0] = new T;
|
||||||
return *pool[0];
|
return *pool[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
inline T &operator[](int index) {
|
inline operator const T&() const {
|
||||||
if(index >= objectsize)resize(index + 1);
|
if(objectsize == 0 || !pool[0]) throw "vector[] out of bounds";
|
||||||
if(index >= objectsize)throw "vector[] out of bounds";
|
return *pool[0];
|
||||||
if(!pool[index])pool[index] = new T;
|
}
|
||||||
|
|
||||||
|
inline T& operator[](int index) {
|
||||||
|
if(index >= objectsize) resize(index + 1);
|
||||||
|
if(index >= objectsize) throw "vector[] out of bounds";
|
||||||
|
if(!pool[index]) pool[index] = new T;
|
||||||
|
return *pool[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const T& operator[](int index) const {
|
||||||
|
if(index >= objectsize || !pool[index]) throw "vector[] out of bounds";
|
||||||
return *pool[index];
|
return *pool[index];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
#include <alsa/asoundlib.h>
|
#include <alsa/asoundlib.h>
|
||||||
|
|
||||||
#include <ruby/ruby.h>
|
|
||||||
|
|
||||||
namespace ruby {
|
namespace ruby {
|
||||||
|
|
||||||
#include "alsa.h"
|
#include "alsa.hpp"
|
||||||
|
|
||||||
class pAudioALSA {
|
class pAudioALSA {
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
#include <ao/ao.h>
|
#include <ao/ao.h>
|
||||||
|
|
||||||
#include <ruby/ruby.h>
|
|
||||||
|
|
||||||
namespace ruby {
|
namespace ruby {
|
||||||
|
|
||||||
#include "ao.h"
|
#include "ao.hpp"
|
||||||
|
|
||||||
class pAudioAO {
|
class pAudioAO {
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <dsound.h>
|
#include <dsound.h>
|
||||||
|
|
||||||
#include <ruby/ruby.h>
|
|
||||||
|
|
||||||
namespace ruby {
|
namespace ruby {
|
||||||
|
|
||||||
#include "directsound.h"
|
#include "directsound.hpp"
|
||||||
|
|
||||||
class pAudioDS {
|
class pAudioDS {
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
#include <AL/al.h>
|
#include <AL/al.h>
|
||||||
#include <AL/alc.h>
|
#include <AL/alc.h>
|
||||||
|
|
||||||
#include <ruby/ruby.h>
|
|
||||||
|
|
||||||
namespace ruby {
|
namespace ruby {
|
||||||
|
|
||||||
#include "openal.h"
|
#include "openal.hpp"
|
||||||
|
|
||||||
class pAudioOpenAL {
|
class pAudioOpenAL {
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -18,11 +18,9 @@
|
||||||
#define SNDCTL_DSP_POLICY _IOW('P', 45, int)
|
#define SNDCTL_DSP_POLICY _IOW('P', 45, int)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <ruby/ruby.h>
|
|
||||||
|
|
||||||
namespace ruby {
|
namespace ruby {
|
||||||
|
|
||||||
#include "oss.h"
|
#include "oss.hpp"
|
||||||
|
|
||||||
class pAudioOSS {
|
class pAudioOSS {
|
||||||
public:
|
public:
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue