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:
byuu 2008-10-26 19:59:04 +00:00
parent 20be19f876
commit a9bff19b5b
172 changed files with 4696 additions and 2935 deletions

View File

@ -1,5 +1,5 @@
bsnes
Version: 0.036
Version: 0.037
Author: byuu
========
@ -99,18 +99,10 @@ SETA RISC CPU used by Quick-move Shogi Match with Nidan Rank-holder Morita 2
Super Gameboy
Cartridge passthrough used for playing Gameboy games
==========================
Unsupported Controller(s):
==========================
Mouse
Super Scope
Justifier
=============
Contributors:
=============
Andreas Naive, anomie, blargg, DMV27, FitzRoy, GIGO, Jonas Quinn, kode54, krom,
mudlord, Nach, neviksti, Overload, RedDwarf, Richard Bannister, tetsuo55, TRAC,
zones
Matthew Callis, mudlord, Nach, neviksti, Overload, RedDwarf, Richard Bannister,
tetsuo55, TRAC, zones

View File

@ -1,4 +1,4 @@
#define BSNES_VERSION "0.036"
#define BSNES_VERSION "0.037"
#define BSNES_TITLE "bsnes v" BSNES_VERSION
#define BUSCORE sBus
@ -24,6 +24,7 @@
#include <nall/bit.hpp>
#include <nall/config.hpp>
#include <nall/detect.hpp>
#include <nall/endian.hpp>
#include <nall/file.hpp>
#include <nall/function.hpp>
#include <nall/modulo.hpp>
@ -31,6 +32,7 @@
#include <nall/sort.hpp>
#include <nall/stdint.hpp>
#include <nall/string.hpp>
#include <nall/utility.hpp>
#include <nall/vector.hpp>
using namespace nall;
@ -42,9 +44,3 @@ void alert(const char*, ...);
void dprintf(const char*, ...);
#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

View File

@ -3,7 +3,8 @@
#include <nall/crc32.hpp>
#include <nall/ups.hpp>
#include "cart_load.cpp"
#include "cart_normal.cpp"
#include "cart_bsx.cpp"
#include "cart_bsc.cpp"
@ -21,12 +22,13 @@ namespace memory {
Cartridge cartridge;
const char* Cartridge::name() { return info.filename; }
Cartridge::CartridgeMode Cartridge::mode() { return info.mode; }
Cartridge::MemoryMapper Cartridge::mapper() { return info.mapper; }
Cartridge::Region Cartridge::region() { return info.region; }
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;
bs.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;
stB.rom_size = stB.ram_size = 0;
info.type = cart_type;
info.mode = mode;
info.patched = false;
info.bsxbase = false;
info.bsxcart = 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() {
@ -103,11 +78,11 @@ bool Cartridge::unload() {
bus.unload_cart();
switch(info.type) {
case CartridgeNormal: unload_cart_normal(); break;
case CartridgeBSX: unload_cart_bsx(); break;
case CartridgeBSC: unload_cart_bsc(); break;
case CartridgeSufamiTurbo: unload_cart_st(); break;
switch(info.mode) {
case ModeNormal: unload_cart_normal(); break;
case ModeBSX: unload_cart_bsx(); break;
case ModeBSC: unload_cart_bsc(); break;
case ModeSufamiTurbo: unload_cart_st(); break;
}
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.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"))) {
cheat.save(cheatfn);
cheat.clear();
@ -138,3 +110,58 @@ Cartridge::Cartridge() {
Cartridge::~Cartridge() {
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;
}

View File

@ -1,10 +1,20 @@
class Cartridge {
public:
enum CartridgeMode {
ModeNormal,
ModeBSC,
ModeBSX,
ModeSufamiTurbo,
};
enum CartridgeType {
CartridgeNormal,
CartridgeBSX,
CartridgeBSC,
CartridgeSufamiTurbo,
TypeNormal,
TypeBSC,
TypeBSXBIOS,
TypeBSX,
TypeSufamiTurboBIOS,
TypeSufamiTurbo,
TypeUnknown,
};
enum HeaderField {
@ -32,9 +42,9 @@ public:
ExLoROM,
ExHiROM,
SPC7110ROM,
BSXROM,
BSCLoROM,
BSCHiROM,
BSXROM,
STROM,
};
@ -45,6 +55,11 @@ public:
DSP1HiROM,
};
const char* name();
CartridgeMode mode();
MemoryMapper mapper();
Region region();
struct {
bool loaded;
char fn[PATH_MAX];
@ -64,26 +79,20 @@ public:
uint rom_size, ram_size;
} stA, stB;
struct {
struct cartinfo_t {
CartridgeType type;
uint32 crc32;
char filename[PATH_MAX * 4];
bool patched;
Region region;
MemoryMapper mapper;
uint rom_size;
uint ram_size;
DSP1MemoryMapper dsp1_mapper;
Region region;
bool bsxbase;
bool bsxcart;
bool bsxflash;
bool st;
unsigned rom_size;
unsigned ram_size;
bool bsxslot;
bool superfx;
bool sa1;
bool srtc;
bool sdd1;
bool sdd1;
bool spc7110;
bool spc7110rtc;
bool cx4;
@ -96,17 +105,53 @@ public:
bool st011;
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;
MemoryMapper mapper();
Region region();
struct {
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_bsx(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 unload_cart_normal();
@ -115,14 +160,13 @@ public:
void unload_cart_st();
bool loaded();
void load_begin(CartridgeType);
void load_begin(CartridgeMode);
void load_end();
bool unload();
unsigned score_header(unsigned);
void find_header();
void read_header();
void read_extended_header();
void read_header(cartinfo_t &info, const uint8_t *data, unsigned size);
unsigned find_header(const uint8_t *data, unsigned size);
unsigned score_header(const uint8_t *data, unsigned size, unsigned addr);
enum CompressionMode {
CompressionNone, //always load without compression

View File

@ -1,57 +1,36 @@
#ifdef CART_CPP
void Cartridge::load_cart_bsc(const char *base, const char *slot) {
if(!base || !*base) return;
strcpy(cart.fn, base);
strcpy(bs.fn, slot ? slot : "");
load_begin(CartridgeBSC);
uint8_t *data = 0;
uint8_t *data;
unsigned size;
load_file(cart.fn, data, size, CompressionAuto);
cart.rom = data, cart.rom_size = size;
strcpy(cart.fn, base);
strcpy(bs.fn, slot);
if(load_file(get_patch_filename(cart.fn, "ups"), data, size, CompressionInspect) == true) {
apply_patch(data, size, cart.rom, cart.rom_size);
delete[] data;
load_begin(ModeBSC);
if(load_image(base) == false) return;
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(load_file(bs.fn, data, size, CompressionAuto) == true) {
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;
}
if(cartinfo.ram_size > 0) {
load_ram(get_save_filename(base, "srm"), cart.ram, cart.ram_size = cartinfo.ram_size, 0xff);
}
load_end();
//set base filename
strcpy(info.filename, cart.fn);
strcpy(info.filename, base);
get_base_filename(info.filename);
if(*bs.fn) {
if(*slot) {
char filenameBS[PATH_MAX];
strcpy(filenameBS, bs.fn);
strcpy(filenameBS, slot);
get_base_filename(filenameBS);
strcat(info.filename, " + ");
strcat(info.filename, filenameBS);

View File

@ -1,27 +1,20 @@
#ifdef CART_CPP
void Cartridge::load_cart_bsx(const char *base, const char *slot) {
if(!base || !*base) return;
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;
uint8_t *data;
unsigned size;
load_file(cart.fn, data, size, CompressionAuto);
cart.rom = data, cart.rom_size = size;
cart.ram = 0, cart.ram_size = 0;
strcpy(cart.fn, base);
strcpy(bs.fn, slot);
if(load_file(get_patch_filename(cart.fn, "ups"), data, size, CompressionInspect) == true) {
apply_patch(data, size, cart.rom, cart.rom_size);
delete[] data;
}
load_begin(ModeBSX);
if(load_image(base) == false) return;
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.psram.handle(), 0x00, bsxcart.psram.size());
@ -36,20 +29,15 @@ void Cartridge::load_cart_bsx(const char *base, const char *slot) {
delete[] data;
}
if(*bs.fn) {
if(load_file(bs.fn, data, size, CompressionAuto) == true) {
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;
}
}
if(load_image(slot)) {
info.bsxflash = true;
bs.ram = image.data;
bs.ram_size = image.size;
}
load_end();
strcpy(info.filename, !*bs.fn ? cart.fn : bs.fn);
strcpy(info.filename, !*slot ? base : slot);
get_base_filename(info.filename);
}

View File

@ -1,30 +1,70 @@
#ifdef CART_CPP
void Cartridge::read_header() {
uint8 *rom = cart.rom;
uint index = info.header_index;
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;
void Cartridge::read_header(cartinfo_t &info, const uint8_t *data, unsigned size) {
info.reset();
unsigned index = find_header(data, size);
//detect presence of BS-X flash cartridge connector (reads extended header information)
bool has_bsxflash = false;
if(rom[index - 14] == 'Z') {
if(rom[index - 11] == 'J') {
uint8 n13 = rom[index - 13];
if((n13 >= 'A' && n13 <= 'Z') || (n13 >= '0' && n13 <= '9')) {
if(company == 0x33 || (rom[index - 10] == 0x00 && rom[index - 4] == 0x00)) {
has_bsxflash = true;
//detect BS-X flash carts
if(data[index + 0x13] == 0x00 || data[index + 0x13] == 0xff) {
if(data[index + 0x14] == 0x00) {
const uint8_t n15 = data[index + 0x15];
if(n15 == 0x00 || n15 == 0x80 || n15 == 0x84 || n15 == 0x9c || n15 == 0xbc || n15 == 0xfc) {
if(data[index + 0x1a] == 0x33 || data[index + 0x1a] == 0xff) {
info.type = TypeBSX;
info.mapper = BSXROM;
info.region = NTSC; //BS-X only released in Japan
return;
}
}
}
}
if(has_bsxflash == true) {
info.mapper = index == 0x7fc0 ? BSCLoROM : BSCHiROM;
} else if(index == 0x7fc0 && cart.rom_size >= 0x401000) {
//detect Sufami Turbo carts
if(!memcmp(data, "BANDAI SFC-ADX", 14)) {
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;
} else if(index == 0x7fc0 && mapper == 0x32) {
info.mapper = ExLoROM;
@ -75,7 +115,7 @@ void Cartridge::read_header() {
}
if(info.dsp1 == true) {
if((mapper & 0x2f) == 0x20 && cart.rom_size <= 0x100000) {
if((mapper & 0x2f) == 0x20 && size <= 0x100000) {
info.dsp1_mapper = DSP1LoROM1MB;
} else if((mapper & 0x2f) == 0x20) {
info.dsp1_mapper = DSP1LoROM2MB;
@ -112,8 +152,8 @@ void Cartridge::read_header() {
info.st018 = true;
}
if(rom[info.header_index + RAM_SIZE] & 7) {
info.ram_size = 1024 << (rom[info.header_index + RAM_SIZE] & 7);
if(data[index + RAM_SIZE] & 7) {
info.ram_size = 1024 << (data[index + RAM_SIZE] & 7);
} else {
info.ram_size = 0;
}
@ -122,17 +162,31 @@ void Cartridge::read_header() {
info.region = (region <= 1 || region >= 13) ? NTSC : PAL;
}
unsigned Cartridge::score_header(unsigned addr) {
if(cart.rom_size < addr + 64) return 0; //image too small to contain header at this location?
uint8 *rom = cart.rom;
unsigned Cartridge::find_header(const uint8_t *data, unsigned size) {
unsigned score_lo = score_header(data, size, 0x007fc0);
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;
uint16 resetvector = rom[addr + RESETV] | (rom[addr + RESETV + 1] << 8);
uint16 checksum = rom[addr + CKSUM] | (rom[addr + CKSUM + 1] << 8);
uint16 ichecksum = rom[addr + ICKSUM] | (rom[addr + ICKSUM + 1] << 8);
uint16 resetvector = data[addr + RESETV] | (data[addr + RESETV + 1] << 8);
uint16 checksum = data[addr + CKSUM] | (data[addr + CKSUM + 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 mapper = rom[addr + MAPPER] & ~0x10; //mask off irrelevent FastROM-capable bit
uint8 resetop = data[(addr & ~0x7fff) | (resetvector & 0x7fff)]; //first opcode executed upon reset
uint8 mapper = data[addr + MAPPER] & ~0x10; //mask off irrelevent FastROM-capable bit
//$00:[000-7fff] contains uninitialized RAM and MMIO.
//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 == 0x40ffc0 && mapper == 0x25) score += 2; //0x25 is usually ExHiROM
if(rom[addr + COMPANY] == 0x33) score += 2; //0x33 indicates extended header
if(rom[addr + ROM_TYPE] < 0x08) score++;
if(rom[addr + ROM_SIZE] < 0x10) score++;
if(rom[addr + RAM_SIZE] < 0x08) score++;
if(rom[addr + REGION] < 14) score++;
if(data[addr + COMPANY] == 0x33) score += 2; //0x33 indicates extended header
if(data[addr + ROM_TYPE] < 0x08) score++;
if(data[addr + ROM_SIZE] < 0x10) score++;
if(data[addr + RAM_SIZE] < 0x08) score++;
if(data[addr + REGION] < 14) score++;
if(score < 0) score = 0;
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

50
src/cart/cart_load.cpp Normal file
View File

@ -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

View File

@ -1,58 +1,29 @@
#ifdef CART_CPP
void Cartridge::load_cart_normal(const char *filename) {
if(!filename || !*filename) return;
uint8_t *data = 0;
void Cartridge::load_cart_normal(const char *base) {
uint8_t *data;
unsigned size;
if(load_file(filename, data, size, CompressionAuto) == false) return;
strcpy(cart.fn, filename);
strcpy(cart.fn, base);
load_begin(CartridgeNormal);
load_begin(ModeNormal);
if(load_image(base) == false) return;
//load ROM data, ignore 512-byte header if detected
if((size & 0x7fff) != 512) {
cart.rom = new uint8_t[cart.rom_size = size];
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;
cartinfo_t cartinfo;
read_header(cartinfo, cart.rom = image.data, cart.rom_size = image.size);
info = cartinfo;
if(load_file(get_patch_filename(cart.fn, "ups"), data, size, CompressionInspect) == true) {
apply_patch(data, size, cart.rom, cart.rom_size);
delete[] data;
info.patched = true;
if(cartinfo.ram_size > 0) {
load_ram(get_save_filename(base, "srm"), cart.ram, cart.ram_size = cartinfo.ram_size, 0xff);
}
info.crc32 = crc32_calculate(cart.rom, cart.rom_size);
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;
}
if(cartinfo.srtc || cartinfo.spc7110rtc) {
load_ram(get_save_filename(base, "rtc"), cart.rtc, cart.rtc_size = 20, 0x00);
}
load_end();
//set base filename
strcpy(info.filename, cart.fn);
strcpy(info.filename, base);
get_base_filename(info.filename);
}

View File

@ -1,86 +1,52 @@
#ifdef CART_CPP
void Cartridge::load_cart_st(const char *base, const char *slotA, const char *slotB) {
if(!base || !*base) return;
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;
uint8_t *data;
unsigned size;
if(load_file(cart.fn, data, size, CompressionAuto) == true) {
cart.rom = new(zeromemory) uint8_t[cart.rom_size = 0x040000];
memcpy(cart.rom, data, min(size, cart.rom_size));
delete[] data;
if(load_file(get_patch_filename(cart.fn, "ups"), data, size, CompressionInspect) == true) {
apply_patch(data, size, cart.rom, cart.rom_size);
delete[] data;
}
strcpy(cart.fn, base);
strcpy(stA.fn, slotA);
strcpy(stB.fn, slotB);
load_begin(ModeSufamiTurbo);
if(load_image(base) == false) return;
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_file(stA.fn, data, size, CompressionAuto) == true) {
stA.rom = new(zeromemory) uint8_t[stA.rom_size = 0x100000];
memcpy(stA.rom, data, min(size, stA.rom_size));
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;
}
if(load_image(slotB)) {
stB.rom = new(zeromemory) uint8_t[stB.rom_size = 0x100000];
memcpy(stB.rom, image.data, min(image.size, stB.rom_size));
delete[] image.data;
stA.ram = new uint8_t[stA.ram_size = 0x020000];
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_ram(get_save_filename(slotB, "srm"), stB.ram, stB.ram_size = 0x020000, 0xff);
}
load_end();
//set base filename
if(!*stA.fn && !*stB.fn) {
if(!*slotA && !*slotB) {
strcpy(info.filename, cart.fn);
get_base_filename(info.filename);
} else if(*stA.fn && !*stB.fn) {
strcpy(info.filename, stA.fn);
} else if(*slotA && !*slotB) {
strcpy(info.filename, slotA);
get_base_filename(info.filename);
} else if(!*stA.fn && *stB.fn) {
strcpy(info.filename, stB.fn);
} else if(!*slotA && *slotB) {
strcpy(info.filename, slotB);
get_base_filename(info.filename);
} else {
char filenameA[PATH_MAX], filenameB[PATH_MAX];
strcpy(filenameA, stA.fn);
strcpy(filenameA, slotA);
get_base_filename(filenameA);
strcpy(filenameB, stB.fn);
strcpy(filenameB, slotB);
get_base_filename(filenameB);
strcpy(info.filename, filenameA);
strcat(info.filename, " + ");

View File

@ -1,30 +1,53 @@
#include "../base.h"
#include "../reader/filereader.h"
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
* decode() "7e1234:56" -> 0x7e123456
* encode() 0x7e123456 -> "7e1234:56"
*****/
bool Cheat::decode(char *str, uint32 &addr, uint8 &data, uint8 &type) {
string t, part;
strcpy(t, str);
strlower(t());
if(strlen(t) == 8 || (strlen(t) == 9 && t()[6] == ':')) {
bool Cheat::decode(const char *str, unsigned &addr, uint8 &data, type_t &type) {
string t = str;
strlower(t);
#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;
replace(t, ":", "");
uint32 r = strhex((const char*)t);
unsigned r = strhex((const char*)t);
addr = r >> 8;
data = r & 0xff;
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;
replace(t, "-", "");
strtr(t, "df4709156bc8a23e", "0123456789abcdef");
uint32 r = strhex((const char*)t);
unsigned r = strhex((const char*)t);
//8421 8421 8421 8421 8421 8421
//abcd efgh ijkl mnop qrst uvwx
//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);
data = r >> 24;
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) {
sprintf(str, "%0.6x:%0.2x", addr, data);
sprintf(t, "%0.6x:%0.2x", addr, data);
str = t;
return true;
} else if(type == GameGenie) {
uint32 r = addr;
unsigned r = addr;
addr = (!!(r & 0x008000) << 23) | (!!(r & 0x004000) << 22) |
(!!(r & 0x002000) << 21) | (!!(r & 0x001000) << 20) |
(!!(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 & 0x020000) << 3) | (!!(r & 0x010000) << 2) |
(!!(r & 0x000800) << 1) | (!!(r & 0x000400) << 0);
sprintf(str, "%0.2x%0.2x-%0.4x", data, addr >> 16, addr & 0xffff);
strtr(str, "0123456789abcdef", "df4709156bc8a23e");
sprintf(t, "%0.2x%0.2x-%0.4x", data, addr >> 16, addr & 0xffff);
strtr(t, "0123456789abcdef", "df4709156bc8a23e");
str = t;
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
*****/
uint Cheat::mirror_address(uint addr) {
unsigned Cheat::mirror_address(unsigned addr) const {
if((addr & 0x40e000) != 0x0000) return addr;
//8k WRAM mirror
//$[00-3f|80-bf]:[0000-1fff] -> $7e:[0000-1fff]
return (0x7e0000 + (addr & 0x1fff));
}
void Cheat::set(uint32 addr) {
void Cheat::set(unsigned addr) {
addr = mirror_address(addr);
mask[addr >> 3] |= 1 << (addr & 7);
if((addr & 0xffe000) == 0x7e0000) {
//mirror $7e:[0000-1fff] to $[00-3f|80-bf]:[0000-1fff]
uint mirror;
for(int x = 0; x <= 0x3f; x++) {
unsigned mirror;
for(unsigned x = 0; x <= 0x3f; x++) {
mirror = ((0x00 + x) << 16) + (addr & 0x1fff);
mask[mirror >> 3] |= 1 << (mirror & 7);
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);
//is there more than one cheat code using the same address
//(and likely a different override value) that is enabled?
//if so, do not clear code lookup table entry for this address.
//if there is more than one cheat code using the same address,
//(eg with a different override value) then do not clear code
//lookup table entry.
uint8 r;
if(read(addr, r) == true)return;
if(read(addr, r) == true) return;
mask[addr >> 3] &= ~(1 << (addr & 7));
if((addr & 0xffe000) == 0x7e0000) {
//mirror $7e:[0000-1fff] to $[00-3f|80-bf]:[0000-1fff]
uint mirror;
for(int x = 0; x <= 0x3f; x++) {
unsigned mirror;
for(unsigned x = 0; x <= 0x3f; x++) {
mirror = ((0x00 + x) << 16) + (addr & 0x1fff);
mask[mirror >> 3] &= ~(1 << (mirror & 7));
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.
*****/
bool Cheat::read(uint32 addr, uint8 &data) {
bool Cheat::read(unsigned addr, uint8 &data) const {
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(addr == mirror_address(index[i].addr)) {
data = index[i].data;
if(addr == mirror_address(code[i].addr)) {
data = code[i].data;
return true;
}
}
@ -150,96 +179,74 @@ bool Cheat::read(uint32 addr, uint8 &data) {
*****/
void Cheat::update_cheat_status() {
for(unsigned i = 0; i < cheat_count; i++) {
if(index[i].enabled) {
cheat_enabled = true;
for(unsigned i = 0; i < code.size(); i++) {
if(code[i].enabled) {
cheat_system_enabled = true;
return;
}
}
cheat_enabled = false;
cheat_system_enabled = false;
}
/*****
* cheat list manipulation routines
*****/
bool Cheat::add(bool enable, char *code, char *desc) {
if(cheat_count >= CheatLimit) return false;
bool Cheat::add(bool enable, const char *code_, const char *desc_) {
unsigned addr;
uint8 data;
type_t type;
if(decode(code_, addr, data, type) == false) return false;
uint32 addr, len;
uint8 data, type;
if(decode(code, addr, data, type) == false) return false;
index[cheat_count].enabled = enable;
index[cheat_count].addr = addr;
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++;
unsigned n = code.size();
code[n].enabled = enable;
code[n].addr = addr;
code[n].data = data;
code[n].code = code_;
code[n].desc = desc_;
(enable) ? set(addr) : clear(addr);
update_cheat_status();
return true;
}
bool Cheat::edit(uint32 n, bool enable, char *code, char *desc) {
if(n >= cheat_count) return false;
uint32 addr, len;
uint8 data, type;
if(decode(code, addr, data, type) == false) return false;
bool Cheat::edit(unsigned n, bool enable, const char *code_, const char *desc_) {
unsigned addr;
uint8 data;
type_t type;
if(decode(code_, addr, data, type) == false) return false;
//disable current code and clear from code lookup table
index[n].enabled = false;
clear(index[n].addr);
code[n].enabled = false;
clear(code[n].addr);
//update code and enable in code lookup table
index[n].enabled = enable;
index[n].addr = addr;
index[n].data = data;
len = strlen(code);
len = len > 16 ? 16 : len;
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;
code[n].enabled = enable;
code[n].addr = addr;
code[n].data = data;
code[n].code = code_;
code[n].desc = desc_;
set(addr);
update_cheat_status();
return true;
}
bool Cheat::remove(uint32 n) {
if(n >= cheat_count) return false;
bool Cheat::remove(unsigned n) {
unsigned size = code.size();
if(n >= size) return false; //also verifies size cannot be < 1
for(unsigned i = n; i < cheat_count; i++) {
index[i].enabled = index[i + 1].enabled;
index[i].addr = index[i + 1].addr;
index[i].data = index[i + 1].data;
strcpy(index[i].desc, index[i + 1].desc);
}
cheat_count--;
for(unsigned i = n; i < size - 1; i++) code[i] = code[i + 1];
code.resize(size - 1);
update_cheat_status();
return true;
}
bool Cheat::get(uint32 n, bool &enable, uint32 &addr, uint8 &data, char *code, char *desc) {
if(n >= cheat_count) return false;
enable = index[n].enabled;
addr = index[n].addr;
data = index[n].data;
strcpy(code, index[n].code);
strcpy(desc, index[n].desc);
bool Cheat::get(unsigned n, cheat_t &cheat) const {
if(n >= code.size()) return false;
cheat = code[n];
return true;
}
@ -247,22 +254,23 @@ bool Cheat::get(uint32 n, bool &enable, uint32 &addr, uint8 &data, char *code, c
* code status modifier routines
*****/
bool Cheat::enabled(uint32 n) {
if(n >= cheat_count) return false;
return index[n].enabled;
bool Cheat::enabled(unsigned n) const {
return (n < code.size()) ? code[n].enabled : false;
}
void Cheat::enable(uint32 n) {
if(n >= cheat_count) return;
index[n].enabled = true;
set(index[n].addr);
void Cheat::enable(unsigned n) {
if(n >= code.size()) return;
code[n].enabled = true;
set(code[n].addr);
update_cheat_status();
}
void Cheat::disable(uint32 n) {
if(n >= cheat_count) return;
index[n].enabled = false;
clear(index[n].addr);
void Cheat::disable(unsigned n) {
if(n >= code.size()) return;
code[n].enabled = false;
clear(code[n].addr);
update_cheat_status();
}
@ -288,40 +296,39 @@ bool Cheat::load(const char *fn) {
split(part, ",", line[i]);
if(::count(part) != 3) continue;
trim(part[2], "\"");
add(part[1] == "enabled", part[0](), part[2]());
add(part[1] == "enabled", part[0], part[2]);
}
return true;
}
bool Cheat::save(const char *fn) {
bool Cheat::save(const char *fn) const {
file fp;
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()
<< index[i].code << " = "
<< (index[i].enabled ? "enabled" : "disabled") << ", \""
<< index[i].desc << "\"\r\n");
<< code[i].code << " = "
<< (code[i].enabled ? "enabled" : "disabled") << ", "
<< "\"" << code[i].desc << "\""
<< "\r\n");
}
fp.close();
return true;
}
/*****
* initialization routines
*****/
void Cheat::sort() {
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() {
cheat_enabled = false;
cheat_count = 0;
cheat_system_enabled = false;
memset(mask, 0, 0x200000);
for(unsigned i = 0; i <= CheatLimit; i++) {
index[i].enabled = false;
index[i].addr = 0x000000;
index[i].data = 0x00;
strcpy(index[i].code, "");
strcpy(index[i].desc, "");
}
code.reset();
}
Cheat::Cheat() {

View File

@ -1,51 +1,55 @@
class Cheat {
public:
enum { CheatLimit = 1024 };
enum Type {
ProActionReplay,
GameGenie,
public:
enum type_t {
ProActionReplay,
GameGenie,
};
struct CheatIndex {
bool enabled;
uint32 addr;
uint8 data;
char code[ 16 + 1];
char desc[128 + 1];
} index[CheatLimit + 1];
bool cheat_enabled;
uint32 cheat_count;
uint8 mask[0x200000];
struct cheat_t {
bool enabled;
unsigned addr;
uint8 data;
string code;
string desc;
inline bool enabled() { return cheat_enabled; }
inline uint count() { return cheat_count; }
inline bool exists(uint32 addr) { return bool(mask[addr >> 3] & 1 << (addr & 7)); }
cheat_t& operator=(const cheat_t&);
bool operator<(const cheat_t&);
};
bool decode(char *str, uint32 &addr, uint8 &data, uint8 &type);
bool encode(char *str, uint32 addr, uint8 data, uint8 type);
static bool decode(const char *str, unsigned &addr, uint8 &data, type_t &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 save(const char *fn);
bool save(const char *fn) const;
void sort();
void clear();
Cheat();
private:
uint mirror_address(uint addr);
void set(uint32 addr);
void clear(uint32 addr);
Cheat();
private:
bool cheat_system_enabled;
uint8 mask[0x200000];
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;

View File

@ -8,80 +8,61 @@ void OBC1::power() {
}
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.address = (ram_read(0x1ff6) & 0x7f);
status.shift = (ram_read(0x1ff6) & 3) << 1;
}
uint8 OBC1::read(uint addr) {
uint8 OBC1::read(unsigned addr) {
addr &= 0x1fff;
if((addr & 0x1ff8) != 0x1ff0) return ram_read(addr);
switch(addr) {
case 0x1ff0:
return ram_read(status.baseptr + (status.address << 2) + 0);
case 0x1ff1:
return ram_read(status.baseptr + (status.address << 2) + 1);
case 0x1ff2:
return ram_read(status.baseptr + (status.address << 2) + 2);
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);
switch(addr) { default: //never used, avoids compiler warning
case 0x1ff0: return ram_read(status.baseptr + (status.address << 2) + 0);
case 0x1ff1: return ram_read(status.baseptr + (status.address << 2) + 1);
case 0x1ff2: return ram_read(status.baseptr + (status.address << 2) + 2);
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;
if((addr & 0x1ff8) != 0x1ff0) return ram_write(addr, data);
switch(addr) {
case 0x1ff0:
ram_write(status.baseptr + (status.address << 2) + 0, data);
break;
case 0x1ff1:
ram_write(status.baseptr + (status.address << 2) + 1, data);
break;
case 0x1ff2:
ram_write(status.baseptr + (status.address << 2) + 2, data);
break;
case 0x1ff3:
ram_write(status.baseptr + (status.address << 2) + 3, data);
break;
case 0x1ff4: {
uint8 temp;
temp = ram_read(status.baseptr + (status.address >> 2) + 0x200);
temp = (temp & ~(3 << status.shift)) | ((data & 3) << status.shift);
ram_write(status.baseptr + (status.address >> 2) + 0x200, temp);
} break;
case 0x1ff5:
status.baseptr = (data & 1) ? 0x1800 : 0x1c00;
ram_write(addr, data);
break;
case 0x1ff6:
status.address = (data & 0x7f);
status.shift = (data & 3) << 1;
ram_write(addr, data);
break;
case 0x1ff7:
ram_write(addr, data);
break;
case 0x1ff0: ram_write(status.baseptr + (status.address << 2) + 0, data); break;
case 0x1ff1: ram_write(status.baseptr + (status.address << 2) + 1, data); break;
case 0x1ff2: ram_write(status.baseptr + (status.address << 2) + 2, data); break;
case 0x1ff3: ram_write(status.baseptr + (status.address << 2) + 3, data); break;
case 0x1ff4: {
uint8 temp = ram_read(status.baseptr + (status.address >> 2) + 0x200);
temp = (temp & ~(3 << status.shift)) | ((data & 3) << status.shift);
ram_write(status.baseptr + (status.address >> 2) + 0x200, temp);
} break;
case 0x1ff5: {
status.baseptr = (data & 1) ? 0x1800 : 0x1c00;
ram_write(addr, data);
} 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);
}
void OBC1::ram_write(uint addr, uint8 data) {
void OBC1::ram_write(unsigned addr, uint8 data) {
memory::cartram.write(addr & 0x1fff, data);
}

View File

@ -5,15 +5,15 @@ public:
void power();
void reset();
uint8 read(uint addr);
void write(uint addr, uint8 data);
uint8 read(unsigned addr);
void write(unsigned addr, uint8 data);
OBC1();
~OBC1();
private:
uint8 ram_read(uint addr);
void ram_write(uint addr, uint8 data);
uint8 ram_read(unsigned addr);
void ram_write(unsigned addr, uint8 data);
struct {
uint16 address;

View File

@ -139,7 +139,7 @@ unsigned SRTC::weekday(unsigned year, unsigned month, unsigned day) {
return (sum + 1) % 7; //1900-01-01 was a Monday
}
uint8 SRTC::mmio_read(uint addr) {
uint8 SRTC::mmio_read(unsigned addr) {
addr &= 0xffff;
if(addr == 0x2800) {
@ -160,7 +160,7 @@ uint8 SRTC::mmio_read(uint addr) {
return cpu.regs.mdr;
}
void SRTC::mmio_write(uint addr, uint8 data) {
void SRTC::mmio_write(unsigned addr, uint8 data) {
addr &= 0xffff;
if(addr == 0x2801) {

View File

@ -8,8 +8,8 @@ public:
void power();
void reset();
uint8 mmio_read (uint addr);
void mmio_write(uint addr, uint8 data);
uint8 mmio_read (unsigned addr);
void mmio_write(unsigned addr, uint8 data);
SRTC();

View File

@ -3,22 +3,22 @@ namespace config {
configuration& config() {
static configuration config;
return config;
}
integral_setting File::autodetect_type(config(), "file.autodetect_type",
"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"
"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"
"option is enabled.",
integral_setting::boolean, false);
integral_setting File::bypass_patch_crc32(config(), "file.bypass_patch_crc32",
"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"
"Setting this option to true will bypass the validation,\n"
"which may or may not result in a working image.\n"
"Enabling this option is strongly discouraged.",
}
integral_setting File::autodetect_type(config(), "file.autodetect_type",
"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"
"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"
"option is enabled.",
integral_setting::boolean, false);
integral_setting File::bypass_patch_crc32(config(), "file.bypass_patch_crc32",
"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"
"Setting this option to true will bypass the validation,\n"
"which may or may not result in a working image.\n"
"Enabling this option is strongly discouraged.",
integral_setting::boolean, false);
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::rom(config(), "path.rom",
"Default path to look for ROM files in (\"\" = use default directory)", "");
string_setting Path::patch(config(), "path.patch",
"Default path to look for ROM files in (\"\" = use default directory)", "");
string_setting Path::patch(config(), "path.patch",
"Default path for all UPS patch files (\"\" = use current directory)", "");
string_setting Path::save(config(), "path.save",
"Default path for all save RAM files (\"\" = use current directory)", "");
string_setting Path::cheat(config(), "path.cheat",
"Default path for all cheat files (\"\" = use current directory)", "");
string_setting Path::save(config(), "path.save",
"Default path for all save RAM files (\"\" = use current directory)", "");
string_setting Path::cheat(config(), "path.cheat",
"Default path for all cheat files (\"\" = use current directory)", "");
string_setting Path::bsx(config(), "path.bsx", "", "");
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);
integral_setting SNES::controller_port2(config(), "snes.controller_port2",
"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",
"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 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",
"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",
"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_pri3_enable("ppu.oam_pri3_enable", "Enable OAM Priority 3", integral_setting::boolean, true);
} //namespace config
} //namespace config

View File

@ -17,6 +17,8 @@ extern struct Path {
extern struct SNES {
static integral_setting controller_port1;
static integral_setting controller_port2;
static integral_setting expansion_port;
static integral_setting region;
} snes;
extern struct CPU {

View File

@ -1,255 +1,256 @@
#ifdef SCPU_CPP
#include "irq.cpp"
#include "joypad.cpp"
uint16 sCPU::vcounter() { return status.vcounter; }
uint16 sCPU::hcounter() { return status.hcounter; }
uint sCPU::dma_counter() { return (status.dma_counter + status.hcounter) & 7; }
/*****
* One PPU dot = 4 CPU clocks
*
* 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
* because the PPU skips one dot to alter the color burst phase of the video signal.
*
* Dot 323 range = { 1292, 1294, 1296 }
* Dot 327 range = { 1310, 1312, 1314 }
*****/
#define ntsc_color_burst_phase_shift_scanline() ( \
snes.region() == SNES::NTSC && status.vcounter == 240 && \
ppu.interlace() == false && ppu.field() == 1 \
)
uint16 sCPU::hdot() {
if(ntsc_color_burst_phase_shift_scanline() == true) return (status.hcounter >> 2);
return (status.hcounter - ((status.hcounter > 1292) << 1) - ((status.hcounter > 1310) << 1)) >> 2;
}
void sCPU::add_clocks(uint clocks) {
if(status.dram_refreshed == false) {
if(status.hcounter + clocks >= status.dram_refresh_position) {
status.dram_refreshed = true;
clocks += 40;
}
}
counter.sub(counter.irq_delay, clocks);
scheduler.addclocks_cpu(clocks);
clocks >>= 1;
#include "irq.cpp"
#include "joypad.cpp"
unsigned sCPU::dma_counter() {
return (status.dma_counter + status.hcounter) & 7;
}
/*****
* One PPU dot = 4 CPU clocks
*
* 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
* because the PPU skips one dot to alter the color burst phase of the video signal.
*
* Dot 323 range = { 1292, 1294, 1296 }
* Dot 327 range = { 1310, 1312, 1314 }
*****/
#define ntsc_color_burst_phase_shift_scanline() ( \
snes.region() == SNES::NTSC && status.vcounter == 240 && \
ppu.interlace() == false && ppu.field() == 1 \
)
uint16 sCPU::hdot() {
if(ntsc_color_burst_phase_shift_scanline() == true) return (status.hcounter >> 2);
return (status.hcounter - ((status.hcounter > 1292) << 1) - ((status.hcounter > 1310) << 1)) >> 2;
}
void sCPU::add_clocks(unsigned clocks) {
if(status.dram_refreshed == false) {
if(status.hcounter + clocks >= status.dram_refresh_position) {
status.dram_refreshed = true;
clocks += 40;
}
}
counter.sub(counter.irq_delay, clocks);
scheduler.addclocks_cpu(clocks);
clocks >>= 1;
while(clocks--) {
history.enqueue(status.vcounter, status.hcounter);
status.hcounter += 2;
if(status.hcounter >= status.line_clocks) scanline();
poll_interrupts();
}
}
void sCPU::scanline() {
status.hcounter = 0;
status.dma_counter = (status.dma_counter + status.line_clocks) & 7;
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;
if(cpu_version == 2) status.dram_refresh_position = 530 + 8 - dma_counter();
//hdma triggers once every visible scanline
status.line_rendered = false;
status.hdma_triggered = (status.vcounter <= (ppu.overscan() == false ? 224 : 239)) ? false : true;
ppu.scanline();
snes.scanline();
update_interrupts();
if(status.auto_joypad_poll == true && status.vcounter == (ppu.overscan() == false ? 227 : 242)) {
snes.input.poll();
run_auto_joypad_poll();
}
}
history.enqueue(status.vcounter, status.hcounter);
status.hcounter += 2;
if(status.hcounter >= status.line_clocks) scanline();
poll_interrupts();
snes.input.tick();
}
}
void sCPU::scanline() {
status.hcounter = 0;
status.dma_counter = (status.dma_counter + status.line_clocks) & 7;
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;
if(cpu_version == 2) status.dram_refresh_position = 530 + 8 - dma_counter();
//hdma triggers once every visible scanline
status.line_rendered = false;
status.hdma_triggered = (status.vcounter <= (ppu.overscan() == false ? 224 : 239)) ? false : true;
ppu.scanline();
snes.scanline();
update_interrupts();
if(status.auto_joypad_poll == true && status.vcounter == (ppu.overscan() == false ? 227 : 242)) {
snes.input.poll();
run_auto_joypad_poll();
}
}
void sCPU::frame() {
ppu.frame();
snes.frame();
status.vcounter = 0;
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)
if(ppu.interlace() == true && ppu.field() == 0) status.field_lines++;
status.hdmainit_triggered = false;
if(cpu_version == 1) {
status.hdmainit_trigger_position = 12 + 8 - dma_counter();
} else {
status.hdmainit_trigger_position = 12 + dma_counter();
}
}
/*****
* precycle_edge()
*
* Used for H/DMA bus synchronization
*****/
void sCPU::precycle_edge() {
if(status.dma_state == DMA_CPUsync) {
add_clocks(status.clock_count - (status.dma_clocks % status.clock_count));
status.dma_state = DMA_Inactive;
}
}
/*****
* cycle_edge()
*
* Used to test for H/DMA, which can trigger on the edge of every opcode cycle.
*****/
void sCPU::cycle_edge() {
if(status.line_rendered == false) {
if(status.hcounter >= status.line_render_position) {
status.line_rendered = true;
ppu.render_scanline();
}
}
if(status.hdmainit_triggered == false) {
if(status.hcounter >= status.hdmainit_trigger_position || status.vcounter) {
status.hdmainit_triggered = true;
hdma_init_reset();
if(hdma_enabled_channels()) {
status.hdma_pending = true;
status.hdma_mode = 0;
}
}
}
if(status.hdma_triggered == false) {
if(status.hcounter >= 1104) {
status.hdma_triggered = true;
if(hdma_active_channels()) {
status.hdma_pending = true;
status.hdma_mode = 1;
}
}
}
//H/DMA pending && DMA inactive?
//.. Run one full CPU cycle
//.. HDMA pending && HDMA enabled ? DMA sync + HDMA run
//.. DMA pending && DMA enabled ? DMA sync + DMA run
//.... HDMA during DMA && HDMA enabled ? DMA sync + HDMA run
//.. Run one bus CPU cycle
status.vcounter = 0;
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)
if(ppu.interlace() == true && ppu.field() == 0) status.field_lines++;
status.hdmainit_triggered = false;
if(cpu_version == 1) {
status.hdmainit_trigger_position = 12 + 8 - dma_counter();
} else {
status.hdmainit_trigger_position = 12 + dma_counter();
}
}
/*****
* precycle_edge()
*
* Used for H/DMA bus synchronization
*****/
void sCPU::precycle_edge() {
if(status.dma_state == DMA_CPUsync) {
add_clocks(status.clock_count - (status.dma_clocks % status.clock_count));
status.dma_state = DMA_Inactive;
}
}
/*****
* cycle_edge()
*
* Used to test for H/DMA, which can trigger on the edge of every opcode cycle.
*****/
void sCPU::cycle_edge() {
if(status.line_rendered == false) {
if(status.hcounter >= status.line_render_position) {
status.line_rendered = true;
ppu.render_scanline();
}
}
if(status.hdmainit_triggered == false) {
if(status.hcounter >= status.hdmainit_trigger_position || status.vcounter) {
status.hdmainit_triggered = true;
hdma_init_reset();
if(hdma_enabled_channels()) {
status.hdma_pending = true;
status.hdma_mode = 0;
}
}
}
if(status.hdma_triggered == false) {
if(status.hcounter >= 1104) {
status.hdma_triggered = true;
if(hdma_active_channels()) {
status.hdma_pending = true;
status.hdma_mode = 1;
}
}
}
//H/DMA pending && DMA inactive?
//.. Run one full CPU cycle
//.. HDMA pending && HDMA enabled ? DMA sync + HDMA run
//.. DMA pending && DMA enabled ? DMA sync + DMA run
//.... HDMA during DMA && HDMA enabled ? DMA sync + HDMA run
//.. Run one bus CPU cycle
//.. CPU sync
if(status.dma_state == DMA_Run) {
if(status.hdma_pending) {
status.hdma_pending = false;
if(hdma_enabled_channels()) {
dma_add_clocks(8 - dma_counter()); //DMA sync
status.hdma_mode == 0 ? hdma_init() : hdma_run();
if(!dma_enabled_channels()) status.dma_state = DMA_CPUsync;
}
}
if(status.dma_pending) {
status.dma_pending = false;
if(dma_enabled_channels()) {
dma_add_clocks(8 - dma_counter()); //DMA sync
dma_run();
status.dma_state = DMA_CPUsync;
}
}
}
if(status.dma_state == DMA_Inactive) {
if(status.dma_pending || status.hdma_pending) {
status.dma_clocks = 0;
status.dma_state = DMA_Run;
}
}
}
/*****
* last_cycle()
*
* 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.
*
* status.irq_delay is used to simulate hardware delay before interrupts can
* trigger during certain events (immediately after DMA, writes to $4200, etc)
*****/
void sCPU::last_cycle() {
if(counter.irq_delay) return;
status.nmi_pending |= nmi_test();
status.irq_pending |= irq_test();
event.irq = (status.nmi_pending || status.irq_pending);
}
void sCPU::timing_power() {
}
void sCPU::timing_reset() {
counter.nmi_hold = 0;
counter.irq_hold = 0;
counter.nmi_fire = 0;
counter.irq_fire = 0;
counter.irq_delay = 0;
counter.hw_math = 0;
status.clock_count = 0;
status.vcounter = 0;
status.hcounter = 0;
status.field_lines = (snes.region() == SNES::NTSC ? 525 : 625) >> 1;
status.line_clocks = 1364;
status.line_rendered = false;
status.line_render_position = min(1112U, (unsigned)config::ppu.hack.render_scanline_position);
status.dram_refreshed = false;
status.dram_refresh_position = (cpu_version == 1) ? 530 : 538;
status.hdmainit_triggered = false;
status.hdmainit_trigger_position = 0;
status.hdma_triggered = false;
status.irq_delay = 0;
status.nmi_valid = false;
status.nmi_line = false;
status.nmi_transition = false;
status.nmi_pending = false;
status.irq_valid = false;
status.irq_line = false;
status.irq_transition = false;
status.irq_pending = false;
update_interrupts();
status.dma_counter = 0;
status.dma_clocks = 0;
status.dma_pending = false;
status.hdma_pending = false;
status.hdma_mode = 0;
if(status.dma_state == DMA_Run) {
if(status.hdma_pending) {
status.hdma_pending = false;
if(hdma_enabled_channels()) {
dma_add_clocks(8 - dma_counter()); //DMA sync
status.hdma_mode == 0 ? hdma_init() : hdma_run();
if(!dma_enabled_channels()) status.dma_state = DMA_CPUsync;
}
}
if(status.dma_pending) {
status.dma_pending = false;
if(dma_enabled_channels()) {
dma_add_clocks(8 - dma_counter()); //DMA sync
dma_run();
status.dma_state = DMA_CPUsync;
}
}
}
if(status.dma_state == DMA_Inactive) {
if(status.dma_pending || status.hdma_pending) {
status.dma_clocks = 0;
status.dma_state = DMA_Run;
}
}
}
/*****
* last_cycle()
*
* 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.
*
* status.irq_delay is used to simulate hardware delay before interrupts can
* trigger during certain events (immediately after DMA, writes to $4200, etc)
*****/
void sCPU::last_cycle() {
if(counter.irq_delay) return;
status.nmi_pending |= nmi_test();
status.irq_pending |= irq_test();
event.irq = (status.nmi_pending || status.irq_pending);
}
void sCPU::timing_power() {
}
void sCPU::timing_reset() {
counter.nmi_hold = 0;
counter.irq_hold = 0;
counter.nmi_fire = 0;
counter.irq_fire = 0;
counter.irq_delay = 0;
counter.hw_math = 0;
status.clock_count = 0;
status.vcounter = 0;
status.hcounter = 0;
status.field_lines = (snes.region() == SNES::NTSC ? 525 : 625) >> 1;
status.line_clocks = 1364;
status.line_rendered = false;
status.line_render_position = min(1112U, (unsigned)config::ppu.hack.render_scanline_position);
status.dram_refreshed = false;
status.dram_refresh_position = (cpu_version == 1) ? 530 : 538;
status.hdmainit_triggered = false;
status.hdmainit_trigger_position = 0;
status.hdma_triggered = false;
status.irq_delay = 0;
status.nmi_valid = false;
status.nmi_line = false;
status.nmi_transition = false;
status.nmi_pending = false;
status.irq_valid = false;
status.irq_line = false;
status.irq_transition = false;
status.irq_pending = false;
update_interrupts();
status.dma_counter = 0;
status.dma_clocks = 0;
status.dma_pending = false;
status.hdma_pending = false;
status.hdma_mode = 0;
status.dma_state = DMA_Inactive;
history.reset();
//initial latch values for $213c/$213d
//[x]0035 : [y]0000 (53.0 -> 212) [lda $2137]
//[x]0038 : [y]0000 (56.5 -> 226) [nop : lda $2137]
add_clocks(186);
}
#undef ntsc_color_burst_phase_shift_scanline
history.reset();
//initial latch values for $213c/$213d
//[x]0035 : [y]0000 (53.0 -> 212) [lda $2137]
//[x]0038 : [y]0000 (56.5 -> 226) [nop : lda $2137]
add_clocks(186);
}
#undef ntsc_color_burst_phase_shift_scanline
#endif //ifdef SCPU_CPP

View File

@ -1,9 +1,9 @@
uint16 vcounter();
uint16 hcounter();
alwaysinline uint16 vcounter() { return status.vcounter; }
alwaysinline uint16 hcounter() { return status.hcounter; }
uint16 hdot();
uint dma_counter();
unsigned dma_counter();
void add_clocks(uint clocks);
void add_clocks(unsigned clocks);
void scanline();
void frame();
@ -14,30 +14,30 @@
void timing_power();
void timing_reset();
//timeshifting -- needed by NMI and IRQ timing
struct History {
struct Time {
uint16 vcounter;
uint16 hcounter;
} time[32];
unsigned index;
alwaysinline void enqueue(uint16 vcounter, uint16 hcounter) {
Time &t = time[index++];
index &= 31;
t.vcounter = vcounter;
t.hcounter = hcounter;
}
alwaysinline void query(unsigned offset, uint16 &vcounter, uint16 &hcounter) {
Time &t = time[(index - (offset >> 1)) & 31];
vcounter = t.vcounter;
hcounter = t.hcounter;
}
void reset() {
index = 0;
for(unsigned i = 0; i < 32; i++) time[i].vcounter = time[i].hcounter = 0;
}
History() { reset(); }
//timeshifting -- needed by NMI and IRQ timing
struct History {
struct Time {
uint16 vcounter;
uint16 hcounter;
} time[32];
unsigned index;
alwaysinline void enqueue(uint16 vcounter, uint16 hcounter) {
Time &t = time[index++];
index &= 31;
t.vcounter = vcounter;
t.hcounter = hcounter;
}
alwaysinline void query(unsigned offset, uint16 &vcounter, uint16 &hcounter) {
Time &t = time[(index - (offset >> 1)) & 31];
vcounter = t.vcounter;
hcounter = t.hcounter;
}
void reset() {
index = 0;
for(unsigned i = 0; i < 32; i++) time[i].vcounter = time[i].hcounter = 0;
}
History() { reset(); }
} history;
//irq.cpp

View File

@ -1,5 +1,5 @@
/*
bbase : version 0.15 ~byuu (2008-09-14)
bbase : version 0.17 ~byuu (2008-10-19)
license: public domain
*/
@ -15,7 +15,7 @@ typedef uint8_t uint8;
typedef uint16_t uint16;
typedef uint32_t uint32;
typedef uint64_t uint64;
typedef unsigned int uint;
typedef unsigned uint;
#include <algorithm>
using std::min;
@ -76,85 +76,4 @@ using std::max;
#define alwaysinline inline
#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

View File

@ -13,6 +13,7 @@ void pButton::create(unsigned style, unsigned width, unsigned height, const char
void pButton::set_text(const char *text) {
if(!button) return;
gtk_button_set_label(GTK_BUTTON(button), text ? text : "");
set_default_font(button);
}
pButton::pButton(Button &self_) : pFormControl(self_), self(self_) {

View File

@ -14,6 +14,20 @@ void hiro_pcanvas_expose(pCanvas *p) {
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) {
canvas = gtk_drawing_area_new();
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;
gtk_widget_modify_bg(canvas, GTK_STATE_NORMAL, &color);
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);
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() {

View File

@ -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) {
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_widget_set_size_request(editbox, width, height);
gtk_widget_show(editbox);
g_signal_connect_swapped(G_OBJECT(editbox), "changed", G_CALLBACK(hiro_peditbox_change), (gpointer)this);
} else {
GtkPolicyType hscroll = (style & Editbox::HorizontalScrollAlways) ? GTK_POLICY_ALWAYS :
(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_widget_show(editbox);
gtk_widget_show(scrollbox);
g_signal_connect_swapped(G_OBJECT(buffer), "changed", G_CALLBACK(hiro_peditbox_change), (gpointer)this);
}
set_default_font(editbox);

View File

@ -1,4 +1,5 @@
#include "hiro.h"
#include "hiro.hpp"
#include "port.cpp"
#include <nall/algorithm.hpp>
using nall::min;
@ -40,16 +41,6 @@ static void set_default_font(GtkWidget *widget) {
#include "slider.cpp"
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;
*default_path = 0;
screen = gdk_screen_get_default();

View File

@ -1,6 +1,10 @@
#ifndef HIRO_GTK_H
#define HIRO_GTK_H
#include <unistd.h>
#include <pwd.h>
#include <sys/stat.h>
#include <gtk/gtk.h>
#include <gdk/gdkx.h>
#include <cairo.h>
@ -8,28 +12,30 @@
#include <X11/extensions/dpms.h>
#include <X11/extensions/XTest.h>
extern int hiromain(int argc, const char *const argv[]);
namespace libhiro {
#include "widget.h"
#include "window.h"
#include "menucontrol.h"
#include "menugroup.h"
#include "menuitem.h"
#include "menucheckitem.h"
#include "menuradioitem.h"
#include "menuseparator.h"
#include "formcontrol.h"
#include "frame.h"
#include "canvas.h"
#include "label.h"
#include "button.h"
#include "checkbox.h"
#include "radiobox.h"
#include "editbox.h"
#include "listbox.h"
#include "combobox.h"
#include "progressbar.h"
#include "slider.h"
#include "widget.hpp"
#include "window.hpp"
#include "menucontrol.hpp"
#include "menugroup.hpp"
#include "menuitem.hpp"
#include "menucheckitem.hpp"
#include "menuradioitem.hpp"
#include "menuseparator.hpp"
#include "formcontrol.hpp"
#include "frame.hpp"
#include "canvas.hpp"
#include "label.hpp"
#include "button.hpp"
#include "checkbox.hpp"
#include "radiobox.hpp"
#include "editbox.hpp"
#include "listbox.hpp"
#include "combobox.hpp"
#include "progressbar.hpp"
#include "slider.hpp"
class pHiro {
public:

17
src/lib/hiro/gtk/port.cpp Normal file
View File

@ -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;
}

View File

@ -25,12 +25,20 @@ static gboolean hiro_pwindow_expose(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;
}
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;
}

View File

@ -1,4 +1,4 @@
#include "hiro.h"
#include "hiro.hpp"
using namespace nall;
#if defined(_WIN32)

View File

@ -1,6 +1,6 @@
/*
hiro
version: 0.006 (2008-08-12)
version: 0.007b (2008-10-26)
author: byuu
license: public domain
*/
@ -16,6 +16,10 @@
#include <nall/string.hpp>
#include <nall/utility.hpp>
extern char* realpath(const char*, char*);
extern char* userpath(char*);
int mkdir(const char*);
namespace libhiro {
class pHiro;
@ -92,8 +96,7 @@ struct event_t {
enum type_t {
Close,
Block,
KeyDown,
KeyUp,
Input,
Change,
Tick,
Activate,
@ -225,8 +228,7 @@ public:
nall::function<uintptr_t (event_t)> on_close;
nall::function<uintptr_t (event_t)> on_block;
nall::function<uintptr_t (event_t)> on_keydown;
nall::function<uintptr_t (event_t)> on_keyup;
nall::function<uintptr_t (event_t)> on_input;
Window();
@ -352,6 +354,8 @@ public:
Canvas();
nall::function<uintptr_t (event_t)> on_input;
private:
pFriends;
pCanvas &p;
@ -435,6 +439,8 @@ public:
unsigned get_text(char *text, unsigned length = -1U);
void set_text(const char *text = "");
nall::function<uintptr_t (event_t)> on_change;
Editbox();
private:

View File

@ -1,12 +1,5 @@
#include "hiro.h"
#include <nall/algorithm.hpp>
using nall::min;
using nall::max;
#include <nall/utf8.hpp>
using nall::utf8;
using nall::utf16;
#include "hiro.hpp"
#include "port.cpp"
namespace libhiro {
@ -66,10 +59,14 @@ bool pHiro::run() {
MSG msg;
if(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
//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);
DispatchMessage(&msg);
//}
#if defined(HIRO_WIN_TABSTOP)
}
#endif
}
return pending();
}
@ -147,7 +144,7 @@ bool pHiro::file_open(Window *focus, char *filename, const char *path, const cha
ofn.lpstrInitialDir = wdir;
ofn.lpstrFile = wfilename;
ofn.nMaxFile = MAX_PATH;
ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST;
ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
ofn.lpstrDefExt = L"";
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.lpstrFile = wfilename;
ofn.nMaxFile = MAX_PATH;
ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST;
ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
ofn.lpstrDefExt = L"";
bool result = GetSaveFileName(&ofn);
@ -290,13 +287,29 @@ LRESULT pHiro::wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
case WM_KEYDOWN: {
if(!p || p->self.type != Widget::WindowType) break;
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;
case WM_KEYUP: {
if(!p || p->self.type != Widget::WindowType) break;
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;
case WM_ERASEBKGND: {
@ -327,32 +340,45 @@ LRESULT pHiro::wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
MenuItem &w = (MenuItem&)*widget;
if(w.on_tick) w.on_tick(event_t(event_t::Tick, 0, &w));
} break;
case Widget::MenuCheckItemType: {
MenuCheckItem &w = (MenuCheckItem&)*widget;
w.check(!w.checked()); //invert check state
if(w.on_tick) w.on_tick(event_t(event_t::Tick, w.checked(), &w));
} break;
case Widget::MenuRadioItemType: {
MenuRadioItem &w = (MenuRadioItem&)*widget;
bool checked = w.checked();
w.check();
if(!checked && w.on_tick) w.on_tick(event_t(event_t::Tick, w.checked(), &w));
} break;
case Widget::ButtonType: {
Button &w = (Button&)*widget;
if(w.on_tick) w.on_tick(event_t(event_t::Tick, 0, &w));
} break;
case Widget::CheckboxType: {
Checkbox &w = (Checkbox&)*widget;
w.check(!w.checked()); //invert check state
if(w.on_tick) w.on_tick(event_t(event_t::Tick, w.checked(), &w));
} break;
case Widget::RadioboxType: {
Radiobox &w = (Radiobox&)*widget;
bool checked = w.checked();
w.check();
if(!checked && w.on_tick) w.on_tick(event_t(event_t::Tick, w.checked(), &w));
} 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: {
Combobox &combobox = (Combobox&)*widget;
if(HIWORD(wparam) == CBN_SELCHANGE) {
@ -384,13 +410,28 @@ LRESULT pHiro::wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
switch(widget->type) {
case Widget::ListboxType: {
Listbox &listbox = (Listbox&)*widget;
if(((LPNMHDR)lparam)->code == LVN_ITEMCHANGED
&& ((LPNMLISTVIEW)lparam)->uChanged & LVIF_STATE
&& ListView_GetItemState(listbox.p.hwnd, ((LPNMLISTVIEW)lparam)->iItem, LVIS_FOCUSED)
&& ListView_GetItemState(listbox.p.hwnd, ((LPNMLISTVIEW)lparam)->iItem, LVIS_SELECTED)
) {
if(listbox.on_change) listbox.on_change(event_t(event_t::Change, listbox.get_selection(), &listbox));
} else if(((LPNMHDR)lparam)->code == LVN_ITEMACTIVATE) {
LPNMHDR nmhdr = (LPNMHDR)lparam;
LPNMLISTVIEW nmlistview = (LPNMLISTVIEW)lparam;
if(nmhdr->code == LVN_ITEMCHANGED && (nmlistview->uChanged & LVIF_STATE)) {
//LVN_ITEMCHANGED is sent whenever an item gains or loses either focus or selection;
//it does not send a special message to indicate that no items are focused or selected.
//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));
}
} break;

View File

@ -5,39 +5,53 @@
#undef _WIN32_WINNT
#undef _WIN32_IE
#undef NOMINMAX
#undef _NO_OLDNAMES
#define WINVER 0x0501
#define _WIN32_WINNT 0x0501
#define _WIN32_IE 0x0600
#define NOMINMAX
#define _NO_OLDNAMES
#define UNICODE
#include <windows.h>
#include <commctrl.h>
#include <io.h>
#include <direct.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 {
#include "widget.h"
#include "window.h"
#include "menucontrol.h"
#include "menugroup.h"
#include "menuitem.h"
#include "menucheckitem.h"
#include "menuradioitem.h"
#include "menuseparator.h"
#include "formcontrol.h"
#include "frame.h"
#include "canvas.h"
#include "label.h"
#include "button.h"
#include "checkbox.h"
#include "radiobox.h"
#include "editbox.h"
#include "listbox.h"
#include "combobox.h"
#include "progressbar.h"
#include "slider.h"
#include "widget.hpp"
#include "window.hpp"
#include "menucontrol.hpp"
#include "menugroup.hpp"
#include "menuitem.hpp"
#include "menucheckitem.hpp"
#include "menuradioitem.hpp"
#include "menuseparator.hpp"
#include "formcontrol.hpp"
#include "frame.hpp"
#include "canvas.hpp"
#include "label.hpp"
#include "button.hpp"
#include "checkbox.hpp"
#include "radiobox.hpp"
#include "editbox.hpp"
#include "listbox.hpp"
#include "combobox.hpp"
#include "progressbar.hpp"
#include "slider.hpp"
class pHiro {
public:

View File

@ -94,4 +94,5 @@ void pListbox::reset() {
pListbox::pListbox(Listbox &self_) : pFormControl(self_), self(self_) {
column_count = 0;
lostfocus = false; //used for message parsing
}

View File

@ -14,4 +14,5 @@ public:
/* internal */
unsigned column_count;
bool lostfocus;
};

View File

@ -19,5 +19,22 @@ bool pMenuCheckItem::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_) {
}

View File

@ -5,6 +5,10 @@ public:
void uncheck();
bool checked();
void enable(bool = true);
void disable();
bool enabled();
MenuCheckItem &self;
pMenuCheckItem(MenuCheckItem&);
};

View File

@ -1,18 +1,11 @@
void pMenuControl::enable(bool state) {
EnableMenuItem(parent, instance, MF_BYCOMMAND | (state ? MF_ENABLED : MF_GRAYED));
void pMenuControl::enable(bool) {
}
void pMenuControl::disable() {
enable(false);
}
bool pMenuControl::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;
return true;
}
pMenuControl::pMenuControl(MenuControl &self_) : pWidget(self_), self(self_) {

View File

@ -1,8 +1,8 @@
class pMenuControl : public pWidget {
public:
void enable(bool = true);
void disable();
bool enabled();
virtual void enable(bool = true);
virtual void disable();
virtual bool enabled();
MenuControl &self;
pMenuControl(MenuControl&);

View File

@ -26,6 +26,23 @@ void pMenuGroup::attach(MenuControl &menucontrol) {
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_) {
group = 0;
}

View File

@ -4,6 +4,10 @@ public:
void create(const char *text);
void attach(MenuControl &menucontrol);
void enable(bool = true);
void disable();
bool enabled();
pMenuGroup(MenuGroup&);
/* internal */

View File

@ -2,5 +2,22 @@ void pMenuItem::create(const char *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_) {
}

View File

@ -2,6 +2,10 @@ class pMenuItem : public pMenuControl {
public:
void create(const char *text = "");
void enable(bool = true);
void disable();
bool enabled();
MenuItem &self;
pMenuItem(MenuItem&);
};

View File

@ -19,6 +19,23 @@ bool pMenuRadioItem::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_) {
create_checked = false;
}

View File

@ -4,6 +4,10 @@ public:
void check();
bool checked();
void enable(bool = true);
void disable();
bool enabled();
MenuRadioItem &self;
pMenuRadioItem(MenuRadioItem&);

View File

@ -1,5 +1,22 @@
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_) {
}

View File

@ -3,5 +3,9 @@ public:
MenuSeparator &self;
void create();
void enable(bool = true);
void disable();
bool enabled();
pMenuSeparator(MenuSeparator&);
};

40
src/lib/hiro/win/port.cpp Normal file
View File

@ -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;
}

View File

@ -1,4 +1,4 @@
#include "libfilter.h"
#include "libfilter.hpp"
using nall::min;
using nall::max;

View File

@ -16,6 +16,12 @@ T max(const T& t, const U& 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
#endif //ifndef NALL_ALGORITHM_HPP

View File

@ -58,8 +58,8 @@ public:
}
void resize(unsigned size) {
reserve(findsize(size));
buffersize = size;
if(size > poolsize) reserve(findsize(size));
if(size > buffersize) buffersize = size;
}
array() {

38
src/lib/nall/endian.hpp Normal file
View File

@ -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

View File

@ -5,7 +5,6 @@
#include <stdlib.h>
#include <string.h>
#include <nall/static.hpp>
#include <nall/stdint.hpp>
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 {
enum { axes = 8 };
enum { buttons = 96 };
enum {
none = joypad<number - 1>::limit,
up, down, left, right,
button_00, button_01, button_02, button_03,
button_04, button_05, button_06, button_07,
button_08, button_09, button_10, button_11,
button_12, button_13, button_14, button_15,
limit,
axis,
button = axis + axes,
limit = button + buttons,
};
};
template<> struct joypad<-1> {
enum { count = 16 };
enum { axes = 8 };
enum { buttons = 96 };
enum {
none,
up, down, left, right,
button_00, button_01, button_02, button_03,
button_04, button_05, button_06, button_07,
button_08, button_09, button_10, button_11,
button_12, button_13, button_14, button_15,
length = button_15 - none + 1, //number of syms per joypad
limit = keyboard::limit, //start joypad syms immediately after keyboard syms
axis,
button = axis + axes,
length = button + buttons - none, //number of syms per joypad
limit = mouse::limit,
};
static uint16_t index(int joypad_number, int joypad_enum) {
if(joypad_number < 0 || joypad_number > 15) return keyboard::none;
static uint16_t index(unsigned joypad_number, unsigned joypad_enum) {
if(joypad_number >= count) return keyboard::none;
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 keyboard_table[][64] = {
static const char sym_table[][64] = {
//keyboard
"none",
"escape", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "f10", "f11", "f12",
"print_screen", "scroll_lock", "pause", "tilde",
@ -81,24 +93,39 @@ static const char keyboard_table[][64] = {
"up", "down", "left", "right",
"tab", "return", "spacebar",
"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) {
if(key < keyboard::limit) return keyboard_table[key];
if(key < mouse::limit) return sym_table[key];
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<>::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<>::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)) {
sprintf(buffer, "joypad%0.2d.button_%0.2d", j, key - joypad<>::index(j, joypad<>::button_00));
if(key >= joypad<>::index(j, joypad<>::axis + 0)
&& 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 keyboard_table[0]; //"none"
return "none";
}
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) {
for(uint16_t i = 0; i < keyboard::limit; i++) {
if(!strcmp(keyboard_table[i], key)) return i;
for(unsigned i = 0; i < mouse::limit; i++) {
if(!strcmp(sym_table[i], key)) return i;
}
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;
uint8_t j = (*key - '0') * 10 + (*(key + 1) - '0');
if(j > 15) return keyboard::none;
key += 2;
if(!strcmp(key, ".up")) return joypad<>::index(j, joypad<>::up);
if(!strcmp(key, ".down")) return joypad<>::index(j, joypad<>::down);
if(!strcmp(key, ".left")) return joypad<>::index(j, joypad<>::left);
if(!strcmp(key, ".right")) return joypad<>::index(j, joypad<>::right);
if(memcmp(key, ".button_", 8)) return keyboard::none;
key += 8;
if(!*key || !*(key + 1)) return keyboard::none;
uint8_t button = (*key - '0') * 10 + (*(key + 1) - '0');
if(button > 15) return keyboard::none;
return joypad<>::index(j, joypad<>::button_00 + button);
if(!memcmp(key, ".axis", 5)) {
key += 5;
if(!*key || !*(key + 1)) return keyboard::none;
uint8_t axis = (*key - '0') * 10 + (*(key + 1) - '0');
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

View File

@ -1,40 +1,62 @@
#ifndef NALL_SORT_HPP
#ifndef 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 {
template<typename T> inline void swap(T &x, T &y) {
T temp = x;
x = y;
y = temp;
}
template<typename T>
void sort(T list[], unsigned length) {
for(unsigned d = 0; d < length; d++) {
unsigned min = d;
for(unsigned s = d + 1; s < length; s++) {
if(list[s] < list[min]) { min = s; }
}
if(min != d) {
swap(list[d], list[min]);
}
}
}
template<typename T, typename Comparator>
void sort(T list[], unsigned length, Comparator comparator) {
for(unsigned d = 0; d < length; d++) {
unsigned min = d;
for(unsigned s = d + 1; s < length; s++) {
if(comparator(list[s], list[min]) == true) { min = s; }
}
if(min != d) {
swap(list[d], list[min]);
}
}
template<typename T>
void sort(T list[], unsigned length) {
if(length <= 1) return; //nothing to sort
//use insertion sort to quickly sort smaller blocks
if(length < 64) {
for(unsigned i = 0; i < length; i++) {
unsigned min = i;
for(unsigned j = i + 1; j < length; j++) {
if(list[j] < list[min]) min = j;
}
if(min != i) swap(list[i], list[min]);
}
return;
}
//split list in half and recursively sort both
unsigned middle = length / 2;
sort(list, middle);
sort(list + middle, length - middle);
//left and right are sorted here; perform merge sort
T *buffer = new T[length];
unsigned offset = 0;
unsigned left = 0;
unsigned right = middle;
while(left < middle && right < length) {
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
#endif //ifndef NALL_SORT_HPP
} //namespace nall
#endif //ifndef NALL_SORT_HPP

View File

@ -1,13 +1,13 @@
#ifdef NALL_STRING_CPP
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);
//hexadecimal
if(x == '0' && (y == 'X' || y == 'x')) {
s += 2;
for(;;) {
while(true) {
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; }
@ -18,7 +18,7 @@ static int eval_integer(const char *&s) {
//binary
if(x == '0' && (y == 'B' || y == 'b')) {
s += 2;
for(;;) {
while(true) {
if(*s == '0' || *s == '1') { value = value * 2 + (*s++ - '0'); continue; }
return value;
}
@ -27,7 +27,7 @@ static int eval_integer(const char *&s) {
//octal (or decimal '0')
if(x == '0') {
s += 1;
for(;;) {
while(true) {
if(*s >= '0' && *s <= '7') { value = value * 8 + (*s++ - '0'); continue; }
return value;
}
@ -35,35 +35,45 @@ static int eval_integer(const char *&s) {
//decimal
if(x >= '0' && x <= '9') {
for(;;) {
while(true) {
if(*s >= '0' && *s <= '9') { value = value * 10 + (*s++ - '0'); continue; }
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) {
if(!*s) throw "nall::bad_eval";
while(*s == ' ' || *s == '\t') s++; //trim whitespace
if(!*s) throw "unrecognized_token";
int value = 0, x = *s, y = *(s + 1);
if(*s == '(') {
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, 14);
else if(x == '+') value = +eval(++s, 14);
else if(x == '-') value = -eval(++s, 14);
else if(x == '!') value = !eval(++s, 13);
else if(x == '~') value = ~eval(++s, 13);
else if(x == '+') value = +eval(++s, 13);
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
if(!*s) break;
x = *s, y = *(s + 1);
@ -109,18 +119,18 @@ static int eval(const char *&s, int depth = 0) {
if(depth >= 3) break;
if(x == '|' && y == '|') { value = eval(++++s, 3) || value; continue; }
if(depth >= 2) break;
if(x == '?') {
int lhs = eval(++s, 2);
if(*s != ':') throw "nall::bad_eval";
if(*s != ':') throw "mismatched_ternary";
int rhs = eval(++s, 2);
value = value ? lhs : rhs;
continue;
}
if(depth >= 2) break;
if(depth > 0 && x == ')') break;
throw "nall::bad_eval";
throw "unrecognized_token";
}
return value;

View File

@ -3,6 +3,12 @@
namespace nall {
template<typename T> inline void swap(T &x, T &y) {
T temp = x;
x = y;
y = temp;
}
template<typename T>
struct base_from_member {
T value;

View File

@ -89,7 +89,7 @@ public:
}
void reserve(unsigned size) {
if(size == poolsize)return;
if(size == poolsize) return;
if(size < poolsize) {
for(unsigned i = size; i < objectsize; i++) { pool[i].~T(); }
@ -100,9 +100,9 @@ public:
poolsize = size;
}
void resize(int size) {
if(size == objectsize)return;
if(size > poolsize)reserve(size);
void resize(unsigned size) {
if(size == objectsize) return;
if(size > poolsize) reserve(size);
if(size < objectsize) {
for(unsigned i = size; i < objectsize; i++) { pool[i].~T(); }
@ -122,14 +122,24 @@ public:
~linear_vector() { reset(); }
inline operator T&() {
if(objectsize == 0)resize(1);
if(objectsize == 0)throw "vector[] out of bounds";
if(objectsize == 0) resize(1);
if(objectsize == 0) 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";
inline operator const T&() const {
if(objectsize == 0) 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];
}
};
@ -144,7 +154,7 @@ public:
unsigned capacity() const { return poolsize; }
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) {
free(pool);
@ -156,10 +166,10 @@ public:
}
void reserve(unsigned size) {
if(size == poolsize)return;
if(size == poolsize) return;
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;
}
@ -170,12 +180,12 @@ public:
poolsize = size;
}
void resize(int size) {
if(size == objectsize)return;
if(size > poolsize)reserve(size);
void resize(unsigned size) {
if(size == objectsize) return;
if(size > poolsize) reserve(size);
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;
@ -190,16 +200,26 @@ public:
~ptr_vector() { reset(); }
inline operator T&() {
if(objectsize == 0)resize(1);
if(objectsize == 0)throw "vector[] out of bounds";
if(!pool[0])pool[0] = new T;
if(objectsize == 0) resize(1);
if(objectsize == 0) throw "vector[] out of bounds";
if(!pool[0]) pool[0] = new T;
return *pool[0];
}
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;
inline operator const T&() const {
if(objectsize == 0 || !pool[0]) 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";
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];
}
};

View File

@ -1,10 +1,8 @@
#include <alsa/asoundlib.h>
#include <ruby/ruby.h>
namespace ruby {
#include "alsa.h"
#include "alsa.hpp"
class pAudioALSA {
public:

View File

@ -1,10 +1,8 @@
#include <ao/ao.h>
#include <ruby/ruby.h>
namespace ruby {
#include "ao.h"
#include "ao.hpp"
class pAudioAO {
public:

View File

@ -1,11 +1,9 @@
#include <windows.h>
#include <dsound.h>
#include <ruby/ruby.h>
namespace ruby {
#include "directsound.h"
#include "directsound.hpp"
class pAudioDS {
public:

View File

@ -1,11 +1,9 @@
#include <AL/al.h>
#include <AL/alc.h>
#include <ruby/ruby.h>
namespace ruby {
#include "openal.h"
#include "openal.hpp"
class pAudioOpenAL {
public:

View File

@ -18,11 +18,9 @@
#define SNDCTL_DSP_POLICY _IOW('P', 45, int)
#endif
#include <ruby/ruby.h>
namespace ruby {
#include "oss.h"
#include "oss.hpp"
class pAudioOSS {
public:

Some files were not shown because too many files have changed in this diff Show More