mirror of
https://github.com/bsnes-emu/bsnes.git
synced 2025-09-24 05:11:49 +02:00
Compare commits
17 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
9b03874f32 | ||
|
a9bff19b5b | ||
|
20be19f876 | ||
|
f748a34e49 | ||
|
0af5703c47 | ||
|
f73d0908c4 | ||
|
18389cb8f7 | ||
|
448a8336b1 | ||
|
233e645772 | ||
|
f0627239bb | ||
|
ae67f268a8 | ||
|
b2331ddb85 | ||
|
2a2f50a8bc | ||
|
30b19613d5 | ||
|
98fc865130 | ||
|
f6a04682f5 | ||
|
87b91f0ace |
14
readme.txt
14
readme.txt
@@ -1,5 +1,5 @@
|
||||
bsnes
|
||||
Version: 0.036
|
||||
Version: 0.037a
|
||||
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
|
||||
|
10
src/base.h
10
src/base.h
@@ -1,4 +1,4 @@
|
||||
#define BSNES_VERSION "0.036"
|
||||
#define BSNES_VERSION "0.037a"
|
||||
#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
|
||||
|
@@ -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;
|
||||
}
|
||||
|
102
src/cart/cart.h
102
src/cart/cart.h
@@ -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
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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
50
src/cart/cart_load.cpp
Normal 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
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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, " + ");
|
||||
|
@@ -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() {
|
||||
|
@@ -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;
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
|
@@ -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) {
|
||||
|
@@ -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();
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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 {
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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_) {
|
||||
|
@@ -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() {
|
||||
|
@@ -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);
|
||||
|
@@ -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();
|
||||
|
@@ -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:
|
@@ -1,19 +1,27 @@
|
||||
void hiro_plistbox_change(pListbox *p) {
|
||||
static void hiro_plistbox_change(pListbox *p) {
|
||||
//only send message when active item changes
|
||||
if(p->listbox_selection == p->get_selection()) return;
|
||||
if(p->self.on_change) p->self.on_change(event_t(event_t::Change, p->listbox_selection = p->get_selection(), &p->self));
|
||||
|
||||
p->listbox_selection = p->get_selection();
|
||||
if(p->self.on_change) {
|
||||
p->self.on_change(event_t(event_t::Change, p->listbox_selection, &p->self));
|
||||
}
|
||||
}
|
||||
|
||||
void hiro_plistbox_activate(pListbox *p) {
|
||||
if(p->self.on_activate) p->self.on_activate(event_t(event_t::Activate, p->listbox_selection = p->get_selection(), &p->self));
|
||||
static void hiro_plistbox_activate(pListbox *p) {
|
||||
p->listbox_selection = p->get_selection();
|
||||
if(p->self.on_activate) {
|
||||
p->self.on_activate(event_t(event_t::Activate, p->listbox_selection, &p->self));
|
||||
}
|
||||
}
|
||||
|
||||
void pListbox::create(unsigned style, unsigned width, unsigned height, const char *columns, const char *text) {
|
||||
bool header = style & Listbox::Header;
|
||||
GtkPolicyType hscroll = (style & Listbox::HorizontalScrollAlways) ? GTK_POLICY_ALWAYS :
|
||||
(style & Listbox::HorizontalScrollNever) ? GTK_POLICY_NEVER :
|
||||
(style & Listbox::HorizontalScrollNever ) ? GTK_POLICY_NEVER :
|
||||
GTK_POLICY_AUTOMATIC;
|
||||
GtkPolicyType vscroll = (style & Listbox::VerticalScrollAlways) ? GTK_POLICY_ALWAYS :
|
||||
(style & Listbox::VerticalScrollNever) ? GTK_POLICY_NEVER :
|
||||
(style & Listbox::VerticalScrollNever ) ? GTK_POLICY_NEVER :
|
||||
GTK_POLICY_AUTOMATIC;
|
||||
|
||||
scrollbox = gtk_scrolled_window_new(0, 0);
|
||||
@@ -35,7 +43,7 @@ void pListbox::create(unsigned style, unsigned width, unsigned height, const cha
|
||||
gtk_widget_show(listbox);
|
||||
gtk_widget_show(scrollbox);
|
||||
|
||||
//alternate colors for each listbox entry if there are multiple columns ...
|
||||
//alternate colors for each listbox entry if there are multiple columns
|
||||
gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(listbox), count(list) >= 2 ? true : false);
|
||||
for(unsigned i = 0; i < count(list); i++) {
|
||||
renderer = gtk_cell_renderer_text_new();
|
||||
@@ -68,58 +76,65 @@ void pListbox::set_column_width(unsigned column, unsigned width) {
|
||||
}
|
||||
|
||||
void pListbox::add_item(const char *text) {
|
||||
lstring list;
|
||||
lstring list;
|
||||
split(list, "\t", text);
|
||||
gtk_list_store_append(store, &iter);
|
||||
for(unsigned i = 0; i < count(list); i++) {
|
||||
gtk_list_store_set(store, &iter, i, list[i](), -1);
|
||||
gtk_list_store_set(store, &iter, i, (const char*)list[i], -1);
|
||||
}
|
||||
}
|
||||
|
||||
void pListbox::set_item(unsigned index, const char *text) {
|
||||
GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(listbox));
|
||||
GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(listbox));
|
||||
for(unsigned i = 0; i <= index; i++) {
|
||||
i == 0 ?
|
||||
gtk_tree_model_get_iter_first(model, &iter) :
|
||||
gtk_tree_model_iter_next(model, &iter);
|
||||
}
|
||||
|
||||
lstring list;
|
||||
lstring list;
|
||||
split(list, "\t", text);
|
||||
for(unsigned i = 0; i < count(list); i++) {
|
||||
gtk_list_store_set(store, &iter, i, list[i](), -1);
|
||||
gtk_list_store_set(store, &iter, i, (const char*)list[i], -1);
|
||||
}
|
||||
}
|
||||
|
||||
int pListbox::get_selection() {
|
||||
GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(listbox));
|
||||
GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(listbox));
|
||||
if(gtk_tree_model_get_iter_first(model, &iter) == false) { return -1; }
|
||||
if(gtk_tree_selection_iter_is_selected(selection, &iter) == true) { return 0; }
|
||||
GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(listbox));
|
||||
GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(listbox));
|
||||
if(gtk_tree_model_get_iter_first(model, &iter) == false) return -1;
|
||||
if(gtk_tree_selection_iter_is_selected(selection, &iter) == true) return 0;
|
||||
for(unsigned i = 1; i < 100000; i++) {
|
||||
if(gtk_tree_model_iter_next(model, &iter) == false) { return -1; }
|
||||
if(gtk_tree_selection_iter_is_selected(selection, &iter) == true) { return i; }
|
||||
if(gtk_tree_model_iter_next(model, &iter) == false) return -1;
|
||||
if(gtk_tree_selection_iter_is_selected(selection, &iter) == true) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void pListbox::set_selection(int index) {
|
||||
int current = get_selection();
|
||||
GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(listbox));
|
||||
GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(listbox));
|
||||
int current = get_selection();
|
||||
GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(listbox));
|
||||
GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(listbox));
|
||||
gtk_tree_selection_unselect_all(selection);
|
||||
if(index < 0) { goto end; }
|
||||
if(gtk_tree_model_get_iter_first(model, &iter) == false) { goto end; }
|
||||
if(index == 0) { gtk_tree_selection_select_iter(selection, &iter); goto end; }
|
||||
for(unsigned i = 1; i < 100000; i++) {
|
||||
if(gtk_tree_model_iter_next(model, &iter) == false) { goto end; }
|
||||
if(index == i) { gtk_tree_selection_select_iter(selection, &iter); goto end; }
|
||||
if(index < 0) return; //nothing to select?
|
||||
|
||||
if(gtk_tree_model_get_iter_first(model, &iter)) {
|
||||
if(index == 0) {
|
||||
gtk_tree_selection_select_iter(selection, &iter);
|
||||
} else {
|
||||
for(unsigned i = 1; i < 100000; i++) {
|
||||
if(gtk_tree_model_iter_next(model, &iter) == false) break;
|
||||
if(index == i) {
|
||||
gtk_tree_selection_select_iter(selection, &iter);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
end:
|
||||
if(current != index); //{ owner->message(Message::Changed, (uintptr_t)this); }
|
||||
}
|
||||
|
||||
void pListbox::reset() {
|
||||
listbox_selection = -1;
|
||||
gtk_list_store_clear(GTK_LIST_STORE(store));
|
||||
gtk_tree_view_set_model(GTK_TREE_VIEW(listbox), GTK_TREE_MODEL(store));
|
||||
}
|
||||
|
17
src/lib/hiro/gtk/port.cpp
Normal file
17
src/lib/hiro/gtk/port.cpp
Normal 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;
|
||||
}
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#include "hiro.h"
|
||||
#include "hiro.hpp"
|
||||
using namespace nall;
|
||||
|
||||
#if defined(_WIN32)
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
hiro
|
||||
version: 0.006 (2008-08-12)
|
||||
version: 0.008 (2008-10-27)
|
||||
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:
|
@@ -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;
|
||||
|
@@ -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:
|
@@ -94,4 +94,5 @@ void pListbox::reset() {
|
||||
|
||||
pListbox::pListbox(Listbox &self_) : pFormControl(self_), self(self_) {
|
||||
column_count = 0;
|
||||
lostfocus = false; //used for message parsing
|
||||
}
|
||||
|
@@ -14,4 +14,5 @@ public:
|
||||
|
||||
/* internal */
|
||||
unsigned column_count;
|
||||
bool lostfocus;
|
||||
};
|
@@ -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_) {
|
||||
}
|
||||
|
@@ -5,6 +5,10 @@ public:
|
||||
void uncheck();
|
||||
bool checked();
|
||||
|
||||
void enable(bool = true);
|
||||
void disable();
|
||||
bool enabled();
|
||||
|
||||
MenuCheckItem &self;
|
||||
pMenuCheckItem(MenuCheckItem&);
|
||||
};
|
@@ -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_) {
|
||||
|
@@ -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&);
|
@@ -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;
|
||||
}
|
||||
|
@@ -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 */
|
@@ -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_) {
|
||||
}
|
||||
|
@@ -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&);
|
||||
};
|
@@ -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;
|
||||
}
|
||||
|
@@ -4,6 +4,10 @@ public:
|
||||
void check();
|
||||
bool checked();
|
||||
|
||||
void enable(bool = true);
|
||||
void disable();
|
||||
bool enabled();
|
||||
|
||||
MenuRadioItem &self;
|
||||
pMenuRadioItem(MenuRadioItem&);
|
||||
|
@@ -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_) {
|
||||
}
|
||||
|
@@ -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
40
src/lib/hiro/win/port.cpp
Normal 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;
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
#include "libfilter.h"
|
||||
#include "libfilter.hpp"
|
||||
using nall::min;
|
||||
using nall::max;
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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
38
src/lib/nall/endian.hpp
Normal 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
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
@@ -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];
|
||||
}
|
||||
};
|
||||
|
@@ -1,10 +1,8 @@
|
||||
#include <alsa/asoundlib.h>
|
||||
|
||||
#include <ruby/ruby.h>
|
||||
|
||||
namespace ruby {
|
||||
|
||||
#include "alsa.h"
|
||||
#include "alsa.hpp"
|
||||
|
||||
class pAudioALSA {
|
||||
public:
|
||||
|
@@ -1,10 +1,8 @@
|
||||
#include <ao/ao.h>
|
||||
|
||||
#include <ruby/ruby.h>
|
||||
|
||||
namespace ruby {
|
||||
|
||||
#include "ao.h"
|
||||
#include "ao.hpp"
|
||||
|
||||
class pAudioAO {
|
||||
public:
|
||||
|
@@ -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:
|
||||
|
@@ -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:
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user