Update to v087r30 release.

byuu says:

Changelog:
- DMA channel masks added (some are 27-bit source/target and some are
  14-bit length -- hooray, varuint_t class.)
- No more state.pending flags. Instead, we set dma.pending flag when we
  want a transfer (fixes GBA Video - Pokemon audio) [Cydrak]
- fixed OBJ Vmosaic [Cydrak, krom]
- OBJ cannot read <=0x13fff in BG modes 3-5 (fixes the garbled tile at
  the top-left of some games)
- DMA timing should be much closer to hardware now, but probably not
  perfect
- PPU frame blending uses blargg's bit-perfect, rounded method (slower,
  but what can you do?)
- GBA carts really unload now
- added nall/gba/cartridge.hpp: used when there is no manifest. Scans
  ROMs for library tags, and selects the first valid one found
- added EEPROM auto-detection when EEPROM size=0. Forces disk/save state
  size to 8192 (otherwise states could crash between pre and post
  detect.)
    - detects first read after a set read address command when the size
      is zero, and sets all subsequent bit-lengths to that value, prints
      detected size to terminal
- added nall/nes/cartridge.hpp: moves iNES detection out of emulation
  core.

Important to note: long-term goal is to remove all
nall/(system)/cartridge.hpp detections from the core and replace with
databases. All in good time.
Anyway, the GBA workarounds should work for ~98.5% of the library, if my
pre-scanning was correct (~40 games with odd tags. I reject ones without
numeric versions now, too.)

I think we're basically at a point where we can release a new version
now. Compatibility should be relatively high (at least for a first
release), and fixes are only going to affect one or two games at a time.
I'd like to start doing some major cleaning house internally (rename
NES->Famicom, SNES->SuperFamicom and such.) Would be much wiser to do
that on a .01 WIP to minimize regressions.

The main problems with a release now:
- speed is pretty bad, haven't really optimized much yet (not sure how
  much we can improve it yet, this usually isn't easy)
- sound isn't -great-, but the GBA audio sucks anyway :P
- couple of known bugs (Sonic X video, etc.)
This commit is contained in:
Tim Allen 2012-04-22 20:49:19 +10:00
parent 4c29e6fbab
commit 4b2944c39b
51 changed files with 606 additions and 378 deletions

View File

@ -1,7 +1,7 @@
#ifndef BASE_HPP
#define BASE_HPP
static const char Version[] = "087.29";
static const char Version[] = "087.30";
#include <nall/platform.hpp>
#include <nall/algorithm.hpp>
@ -56,15 +56,15 @@ template<typename R, typename... P> struct hook<R (P...)> {
#define privileged private
#endif
typedef int1_t int1;
typedef int2_t int2;
typedef int3_t int3;
typedef int4_t int4;
typedef int5_t int5;
typedef int6_t int6;
typedef int7_t int7;
typedef int8_t int8;
typedef int9_t int9;
typedef int1_t int1;
typedef int2_t int2;
typedef int3_t int3;
typedef int4_t int4;
typedef int5_t int5;
typedef int6_t int6;
typedef int7_t int7;
typedef int8_t int8;
typedef int9_t int9;
typedef int10_t int10;
typedef int11_t int11;
typedef int12_t int12;
@ -90,15 +90,15 @@ typedef int31_t int31;
typedef int32_t int32;
typedef int64_t int64;
typedef uint1_t uint1;
typedef uint2_t uint2;
typedef uint3_t uint3;
typedef uint4_t uint4;
typedef uint5_t uint5;
typedef uint6_t uint6;
typedef uint7_t uint7;
typedef uint8_t uint8;
typedef uint9_t uint9;
typedef uint1_t uint1;
typedef uint2_t uint2;
typedef uint3_t uint3;
typedef uint4_t uint4;
typedef uint5_t uint5;
typedef uint6_t uint6;
typedef uint7_t uint7;
typedef uint8_t uint8;
typedef uint9_t uint9;
typedef uint10_t uint10;
typedef uint11_t uint11;
typedef uint12_t uint12;
@ -125,6 +125,6 @@ typedef uint32_t uint32;
typedef uint_t<33> uint33;
typedef uint64_t uint64;
typedef varuint_t varuint;
typedef varuint_t<unsigned> varuint;
#endif

View File

@ -1,7 +1,5 @@
#include <gb/gb.hpp>
#include <nall/crc32.hpp>
#define CARTRIDGE_CPP
namespace GB {
@ -21,35 +19,36 @@ void Cartridge::load(System::Revision revision, const string &markup, const uint
romdata = allocate<uint8>(romsize = size, 0xff);
if(data) memcpy(romdata, data, size);
info.mapper = Mapper::Unknown;
info.ram = false;
info.battery = false;
info.rtc = false;
info.rumble = false;
information.markup = markup;
information.mapper = Mapper::Unknown;
information.ram = false;
information.battery = false;
information.rtc = false;
information.rumble = false;
info.romsize = 0;
info.ramsize = 0;
information.romsize = 0;
information.ramsize = 0;
XML::Document document(markup);
auto &mapperid = document["cartridge"]["mapper"].data;
if(mapperid == "none" ) info.mapper = Mapper::MBC0;
if(mapperid == "MBC1" ) info.mapper = Mapper::MBC1;
if(mapperid == "MBC2" ) info.mapper = Mapper::MBC2;
if(mapperid == "MBC3" ) info.mapper = Mapper::MBC3;
if(mapperid == "MBC5" ) info.mapper = Mapper::MBC5;
if(mapperid == "MMM01") info.mapper = Mapper::MMM01;
if(mapperid == "HuC1" ) info.mapper = Mapper::HuC1;
if(mapperid == "HuC3" ) info.mapper = Mapper::HuC3;
if(mapperid == "none" ) information.mapper = Mapper::MBC0;
if(mapperid == "MBC1" ) information.mapper = Mapper::MBC1;
if(mapperid == "MBC2" ) information.mapper = Mapper::MBC2;
if(mapperid == "MBC3" ) information.mapper = Mapper::MBC3;
if(mapperid == "MBC5" ) information.mapper = Mapper::MBC5;
if(mapperid == "MMM01") information.mapper = Mapper::MMM01;
if(mapperid == "HuC1" ) information.mapper = Mapper::HuC1;
if(mapperid == "HuC3" ) information.mapper = Mapper::HuC3;
info.rtc = document["cartridge"]["rtc"].data == "true";
info.rumble = document["cartridge"]["rumble"].data == "true";
information.rtc = document["cartridge"]["rtc"].data == "true";
information.rumble = document["cartridge"]["rumble"].data == "true";
info.romsize = numeral(document["cartridge"]["rom"]["size"].data);
info.ramsize = numeral(document["cartridge"]["ram"]["size"].data);
info.battery = document["cartridge"]["ram"]["battery"].data == "true";
information.romsize = numeral(document["cartridge"]["rom"]["size"].data);
information.ramsize = numeral(document["cartridge"]["ram"]["size"].data);
information.battery = document["cartridge"]["ram"]["nonvolatile"].data == "true";
switch(info.mapper) { default:
switch(information.mapper) { default:
case Mapper::MBC0: mapper = &mbc0; break;
case Mapper::MBC1: mapper = &mbc1; break;
case Mapper::MBC2: mapper = &mbc2; break;
@ -60,7 +59,7 @@ void Cartridge::load(System::Revision revision, const string &markup, const uint
case Mapper::HuC3: mapper = &huc3; break;
}
ramdata = new uint8_t[ramsize = info.ramsize]();
ramdata = new uint8_t[ramsize = information.ramsize]();
system.load(revision);
loaded = true;

View File

@ -21,7 +21,7 @@ struct Cartridge : MMIO, property<Cartridge> {
};
struct Information {
string xml;
string markup;
Mapper mapper;
bool ram;
@ -31,7 +31,7 @@ struct Cartridge : MMIO, property<Cartridge> {
unsigned romsize;
unsigned ramsize;
} info;
} information;
readonly<bool> loaded;
readonly<string> sha256;

View File

@ -1,7 +1,7 @@
#ifdef CARTRIDGE_CPP
void Cartridge::serialize(serializer &s) {
if(info.battery) s.array(ramdata, ramsize);
if(information.battery) s.array(ramdata, ramsize);
s.integer(bootrom_enable);
s.integer(mbc1.ram_enable);

View File

@ -18,7 +18,6 @@ namespace GB {
*/
#include <libco/libco.h>
#include <nall/gameboy/cartridge.hpp>
namespace GB {
struct Thread {

View File

@ -8,6 +8,7 @@ namespace GBA {
Cartridge cartridge;
bool Cartridge::load(const string &markup, const uint8_t *data, unsigned size) {
information.markup = markup;
XML::Document document(markup);
for(unsigned addr = 0; addr < rom.size; addr++) {
@ -31,6 +32,8 @@ bool Cartridge::load(const string &markup, const uint8_t *data, unsigned size) {
if(info["type"].data == "EEPROM") {
has_eeprom = true;
eeprom.size = numeral(info["size"].data);
eeprom.bits = eeprom.size <= 512 ? 6 : 14;
if(eeprom.size == 0) eeprom.size = 8192, eeprom.bits = 0; //auto-detect size
eeprom.mask = size > 16 * 1024 * 1024 ? 0x0fffff00 : 0x0f000000;
eeprom.test = size > 16 * 1024 * 1024 ? 0x0dffff00 : 0x0d000000;
for(unsigned n = 0; n < eeprom.size; n++) eeprom.data[n] = 0xff;
@ -51,7 +54,7 @@ bool Cartridge::load(const string &markup, const uint8_t *data, unsigned size) {
}
void Cartridge::unload() {
if(loaded) return;
if(loaded == false) return;
loaded = false;
}
@ -120,6 +123,7 @@ Cartridge::Cartridge() {
Cartridge::~Cartridge() {
delete[] rom.data;
delete[] ram.data;
delete[] eeprom.data;
delete[] flashrom.data;
}

View File

@ -8,6 +8,10 @@ struct Cartridge : property<Cartridge> {
readonly<bool> has_eeprom;
readonly<bool> has_flashrom;
struct Information {
string markup;
} information;
bool load(const string &markup, const uint8_t *data, unsigned size);
void unload();
void power();

View File

@ -10,6 +10,16 @@ void Cartridge::EEPROM::write(unsigned addr, bool bit) {
bool Cartridge::EEPROM::read() {
bool bit = 1;
//EEPROM size auto-detection
if(bits == 0 && mode == Mode::ReadAddress) {
print("EEPROM address bits: ", --addressbits, "\n");
bits = addressbits == 6 ? 6 : 14;
size = 8192;
mode = Mode::ReadData;
offset = 0;
//fallthrough
}
if(mode == Mode::ReadData) {
if(offset >= 4) bit = read(address * 64 + (offset - 4));
if(++offset == 68) mode = Mode::Wait;
@ -28,10 +38,12 @@ void Cartridge::EEPROM::write(bool bit) {
if(bit == 1) mode = Mode::ReadAddress;
offset = 0;
address = 0;
addressbits = 0;
}
else if(mode == Mode::ReadAddress) {
address = (address << 1) | bit;
addressbits++;
if(++offset == bits) {
mode = Mode::ReadValidate;
offset = 0;
@ -65,8 +77,6 @@ void Cartridge::EEPROM::write(bool bit) {
}
void Cartridge::EEPROM::power() {
bits = (size <= 512 ? 6 : 14);
mode = Mode::Wait;
offset = 0;
address = 0;

View File

@ -14,6 +14,7 @@ struct EEPROM {
enum class Mode : unsigned { Wait, Command, ReadAddress, ReadValidate, ReadData, WriteAddress, WriteData, WriteValidate } mode;
unsigned offset;
unsigned address;
unsigned addressbits;
bool read(unsigned addr);
void write(unsigned addr, bool bit);

View File

@ -110,6 +110,10 @@ void CPU::power() {
dma.target = 0;
dma.length = 0;
dma.control = 0;
dma.pending = 0;
dma.run.target = 0;
dma.run.source = 0;
dma.run.length = 0;
}
for(auto &timer : regs.timer) {
timer.period = 0;
@ -143,6 +147,22 @@ void CPU::power() {
CPU::CPU() {
iwram = new uint8[ 32 * 1024];
ewram = new uint8[256 * 1024];
regs.dma[0].source.bits(27); regs.dma[0].run.source.bits(27);
regs.dma[0].target.bits(27); regs.dma[0].run.target.bits(27);
regs.dma[0].length.bits(14); regs.dma[0].run.length.bits(14);
regs.dma[1].source.bits(28); regs.dma[1].run.source.bits(28);
regs.dma[1].target.bits(27); regs.dma[1].run.target.bits(27);
regs.dma[1].length.bits(14); regs.dma[1].run.length.bits(14);
regs.dma[2].source.bits(28); regs.dma[2].run.source.bits(28);
regs.dma[2].target.bits(27); regs.dma[2].run.target.bits(27);
regs.dma[2].length.bits(14); regs.dma[2].run.length.bits(14);
regs.dma[3].source.bits(28); regs.dma[3].run.source.bits(28);
regs.dma[3].target.bits(28); regs.dma[3].run.target.bits(28);
regs.dma[3].length.bits(16); regs.dma[3].run.length.bits(16);
}
CPU::~CPU() {

View File

@ -27,9 +27,13 @@ struct CPU : Processor::ARM, Thread, MMIO {
void dma_run();
void dma_transfer(Registers::DMA &dma);
void dma_vblank();
void dma_hblank();
void dma_hdma();
void timer_step(unsigned clocks);
void timer_increment(unsigned n);
void timer_fifo_run(unsigned n);
void serialize(serializer&);
CPU();

View File

@ -1,45 +1,28 @@
void CPU::dma_run() {
for(unsigned n = 0; n < 4; n++) {
auto &dma = regs.dma[n];
if(dma.control.enable == false) continue;
switch(dma.control.timingmode) {
case 0: break;
case 1: if(pending.dma.vblank == false) continue; break;
case 2: if(pending.dma.hblank == false) continue; break;
case 3:
if(n == 0) {
continue;
}
if(n == 1 || n == 2) {
if(apu.fifo[n - 1].size > 16) continue;
dma.control.targetmode = 2;
dma.control.size = 1;
dma.run.length = 4;
}
if(n == 3) {
if(pending.dma.hdma == false) continue;
}
if(dma.pending) {
dma.pending = false;
dma_transfer(dma);
if(dma.control.irq) regs.irq.flag.dma[n] = 1;
if(dma.control.drq && n == 3) regs.irq.flag.cartridge = 1;
}
dma_transfer(dma);
if(dma.control.irq) regs.irq.flag.dma[n] = 1;
}
pending.dma.vblank = false;
pending.dma.hblank = false;
pending.dma.hdma = false;
}
void CPU::dma_transfer(Registers::DMA &dma) {
unsigned size = dma.control.size ? Word : Half;
unsigned seek = dma.control.size ? 4 : 2;
sequential() = false;
do {
step(bus.speed(dma.run.source, size));
uint32 word = bus.read(dma.run.source, size);
step(bus.speed(dma.run.target, size));
bus.write(dma.run.target, size, word);
step(2);
sequential() = true;
switch(dma.control.sourcemode) {
case 0: dma.run.source += seek; break;
@ -52,8 +35,26 @@ void CPU::dma_transfer(Registers::DMA &dma) {
case 3: dma.run.target += seek; break;
}
} while(--dma.run.length);
sequential() = false;
if(dma.control.targetmode == 3) dma.run.target = dma.target;
if(dma.control.repeat == 1) dma.run.length = dma.length;
if(dma.control.repeat == 0) dma.control.enable = false;
}
void CPU::dma_vblank() {
for(auto &dma : regs.dma) {
if(dma.control.enable && dma.control.timingmode == 1) dma.pending = true;
}
}
void CPU::dma_hblank() {
for(auto &dma : regs.dma) {
if(dma.control.enable && dma.control.timingmode == 2) dma.pending = true;
}
}
void CPU::dma_hdma() {
auto &dma = regs.dma[3];
if(dma.control.enable && dma.control.timingmode == 3) dma.pending = true;
}

View File

@ -192,9 +192,12 @@ void CPU::write(uint32 addr, uint8 byte) {
bool enable = dma.control.enable;
dma.control = (dma.control & ~(255 << shift)) | (byte << shift);
if(enable == 0 && dma.control.enable) {
if(dma.control.timingmode == 0) dma.pending = true; //immediate transfer mode
dma.run.target = dma.target;
dma.run.source = dma.source;
dma.run.length = dma.length;
} else if(dma.control.enable == 0) {
dma.pending = false;
}
return;
}

View File

@ -15,16 +15,17 @@ struct Registers {
};
struct DMA {
uint32 source;
uint32 target;
uint16 length;
varuint source;
varuint target;
varuint length;
DMAControl control;
//internal
bool pending;
struct Run {
uint32 target;
uint32 source;
uint16 length;
varuint target;
varuint source;
varuint length;
} run;
} dma[4];

View File

@ -21,11 +21,24 @@ void CPU::timer_increment(unsigned n) {
if(timer.control.irq) regs.irq.flag.timer[n] = 1;
if(apu.fifo[0].timer == n) apu.fifo[0].read();
if(apu.fifo[1].timer == n) apu.fifo[1].read();
if(apu.fifo[0].timer == n) timer_fifo_run(0);
if(apu.fifo[1].timer == n) timer_fifo_run(1);
if(n < 3 && regs.timer[n + 1].control.enable && regs.timer[n + 1].control.cascade) {
timer_increment(n + 1);
}
}
}
void CPU::timer_fifo_run(unsigned n) {
apu.fifo[n].read();
if(apu.fifo[n].size > 16) return;
auto &dma = regs.dma[1 + n];
if(dma.control.enable && dma.control.timingmode == 3) {
dma.pending = true;
dma.control.targetmode = 2;
dma.control.size = 1;
dma.run.length = 4;
}
}

View File

@ -14,14 +14,15 @@ void PPU::render_object(Object &obj) {
auto &output = layer[OBJ];
unsigned rowsize = regs.control.objmapping == 0 ? 32 >> obj.colors : obj.width / 8;
unsigned baseaddr = 0x10000 + obj.character * 32;
unsigned baseaddr = obj.character * 32;
if(obj.vflip && obj.affine == 0) {
py ^= obj.height - 1;
}
if(obj.mosaic && regs.mosaic.objvsize) {
py = (py / (1 + regs.mosaic.objvsize)) * (1 + regs.mosaic.objvsize);
signed mosaicy = (regs.vcounter / (1 + regs.mosaic.objvsize)) * (1 + regs.mosaic.objvsize);
py = obj.y >= 160 || mosaicy - obj.y >= 0 ? mosaicy - obj.y : 0;
}
int16 pa = objectparam[obj.affineparam].pa;
@ -57,7 +58,7 @@ void PPU::render_object(Object &obj) {
unsigned offset = (y / 8) * rowsize + (x / 8);
offset = offset * 64 + (y & 7) * 8 + (x & 7);
uint8 color = vram[baseaddr + (offset >> !obj.colors)];
uint8 color = object_vram_read(baseaddr + (offset >> !obj.colors));
if(obj.colors == 0) color = (x & 1) ? color >> 4 : color & 15;
if(color) {
if(obj.mode & 2) {
@ -73,3 +74,10 @@ void PPU::render_object(Object &obj) {
fy += pc;
}
}
uint8 PPU::object_vram_read(unsigned addr) const {
if(regs.control.bgmode == 3 || regs.control.bgmode == 4 || regs.control.bgmode == 5) {
if(addr <= 0x3fff) return 0u;
}
return vram[0x10000 + (addr & 0x7fff)];
}

View File

@ -105,7 +105,7 @@ void PPU::scanline() {
if(regs.vcounter == 160) {
if(regs.status.irqvblank) cpu.regs.irq.flag.vblank = 1;
cpu.pending.dma.vblank = true;
cpu.dma_vblank();
}
if(regs.status.irqvcoincidence) {
@ -138,11 +138,11 @@ void PPU::scanline() {
step(960);
regs.status.hblank = 1;
if(regs.status.irqhblank) cpu.regs.irq.flag.hblank = 1;
if(regs.vcounter < 160) cpu.pending.dma.hblank = true;
if(regs.vcounter < 160) cpu.dma_hblank();
step(240);
regs.status.hblank = 0;
if(regs.vcounter < 160) cpu.pending.dma.hdma = true;
if(regs.vcounter < 160) cpu.dma_hdma();
step(32);
if(++regs.vcounter == 228) regs.vcounter = 0;

View File

@ -33,6 +33,7 @@ struct PPU : Thread, MMIO {
void render_objects();
void render_object(Object&);
uint8 object_vram_read(unsigned addr) const;
void render_mosaic_background(unsigned id);
void render_mosaic_object();

View File

@ -2,7 +2,7 @@ void PPU::render_forceblank() {
uint16 *line = output + regs.vcounter * 240;
uint16 *last = blur + regs.vcounter * 240;
for(unsigned x = 0; x < 240; x++) {
line[x] = ((last[x] >> 1) & 0x3def) + ((0x7fff >> 1) & 0x3def);
line[x] = (0x7fff + last[x] - ((0x7fff ^ last[x]) & 0x0421)) >> 1;
last[x] = 0x7fff;
}
}
@ -59,7 +59,7 @@ void PPU::render_screen() {
}
//output pixel; blend with previous pixel to simulate GBA LCD blur
line[x] = ((last[x] >> 1) & 0x3def) + ((color >> 1) & 0x3def);
line[x] = (color + last[x] - ((color ^ last[x]) & 0x0421)) >> 1;
last[x] = color;
}
}

View File

@ -1,10 +1,9 @@
#ifndef NALL_GAMEBOY_CARTRIDGE_HPP
#define NALL_GAMEBOY_CARTRIDGE_HPP
#ifndef NALL_GB_CARTRIDGE_HPP
#define NALL_GB_CARTRIDGE_HPP
namespace nall {
class GameBoyCartridge {
public:
struct GameBoyCartridge {
string markup;
inline GameBoyCartridge(uint8_t *data, unsigned size);
@ -100,26 +99,12 @@ GameBoyCartridge::GameBoyCartridge(uint8_t *romdata, unsigned romsize) {
if(info.mapper == "MBC2") info.ramsize = 512; //512 x 4-bit
markup.append(
"<?xml version='1.0' encoding='UTF-8'?>\n",
"<cartridge mapper='", info.mapper, "' rtc='", info.rtc, "' rumble='", info.rumble, "'>\n",
" <rom size='0x", hex(romsize), "'/>\n");
if(info.ramsize > 0) markup.append(
" <ram size='0x", hex(info.ramsize), "' battery='", info.battery, "'/>\n");
markup.append(
"</cartridge>\n");
/*
markup.append("cartridge mapper=", info.mapper);
if(info.rtc) markup.append(" rtc");
if(info.rumble) markup.append(" rumble");
markup.append("\n");
markup.append("\t" "rom size=", hex(romsize), "\n"); //TODO: trust/check info.romsize?
if(info.ramsize > 0)
markup.append("\t" "ram size=", hex(info.ramsize), info.battery ? " non-volatile\n" : "\n");
*/
markup = "<?xml version='1.0' encoding='UTF-8'?>\n";
markup.append("<cartridge mapper='", info.mapper, "' rtc='", info.rtc, "' rumble='", info.rumble, "'>\n");
markup.append(" <rom size='0x", hex(romsize), "'/>\n");
if(info.ramsize > 0) markup.append(" <ram size='0x", hex(info.ramsize), "' nonvolatile='", info.battery, "'/>\n");
markup.append("</cartridge>\n");
markup.transform("'", "\"");
}
}

65
bsnes/nall/gba/cartridge.hpp Executable file
View File

@ -0,0 +1,65 @@
#ifndef NALL_GBA_CARTRIDGE_HPP
#define NALL_GBA_CARTRIDGE_HPP
#include <nall/sha256.hpp>
#include <nall/vector.hpp>
namespace nall {
struct GameBoyAdvanceCartridge {
string markup;
string identifiers;
inline GameBoyAdvanceCartridge(const uint8_t *data, unsigned size);
};
GameBoyAdvanceCartridge::GameBoyAdvanceCartridge(const uint8_t *data, unsigned size) {
struct Identifier {
string name;
unsigned size;
};
vector<Identifier> idlist;
idlist.append({"SRAM_V", 6});
idlist.append({"SRAM_F_V", 8});
idlist.append({"EEPROM_V", 8});
idlist.append({"FLASH_V", 7});
idlist.append({"FLASH512_V", 10});
idlist.append({"FLASH1M_V", 9});
lstring list;
for(auto &id : idlist) {
for(signed n = 0; n < size - 16; n++) {
if(!memcmp(data + n, (const char*)id.name, id.size)) {
const char *p = (const char*)data + n + id.size;
if(p[0] >= '0' && p[0] <= '9'
&& p[1] >= '0' && p[1] <= '9'
&& p[2] >= '0' && p[2] <= '9'
) {
char text[16];
memcpy(text, data + n, id.size + 3);
text[id.size + 3] = 0;
list.appendonce(text);
}
}
}
}
identifiers = list.concatenate(",");
markup = "<?xml version='1.0' encoding='UTF-8'?>\n";
markup.append("<cartridge sha256='", sha256(data, size), "'>\n");
markup.append(" <rom size='", size, "'/>\n");
if(0);
else if(identifiers.beginswith("SRAM_V" )) markup.append(" <ram type='SRAM' size='32768'/>\n");
else if(identifiers.beginswith("SRAM_F_V" )) markup.append(" <ram type='FRAM' size='32768'/>\n");
else if(identifiers.beginswith("EEPROM_V" )) markup.append(" <ram type='EEPROM' size='0'/>\n");
else if(identifiers.beginswith("FLASH_V" )) markup.append(" <ram type='FlashROM' size='65536'/>\n");
else if(identifiers.beginswith("FLASH512_V")) markup.append(" <ram type='FlashROM' size='65536'/>\n");
else if(identifiers.beginswith("FLASH1M_V" )) markup.append(" <ram type='FlashROM' size='131072'/>\n");
if(identifiers.empty() == false) markup.append(" <!-- detected: ", identifiers, " -->\n");
markup.append("</cartridge>\n");
markup.transform("'", "\"");
}
}
#endif

171
bsnes/nall/nes/cartridge.hpp Executable file
View File

@ -0,0 +1,171 @@
#ifndef NALL_NES_CARTRIDGE_HPP
#define NALL_NES_CARTRIDGE_HPP
#include <nall/sha256.hpp>
namespace nall {
struct FamicomCartridge {
string markup;
inline FamicomCartridge(const uint8_t *data, unsigned size);
};
FamicomCartridge::FamicomCartridge(const uint8_t *data, unsigned size) {
markup = "<?xml version='1.0' encoding='UTF-8'?>\n";
if(size < 16) return;
if(data[0] != 'N') return;
if(data[1] != 'E') return;
if(data[2] != 'S') return;
if(data[3] != 26) return;
unsigned mapper = ((data[7] >> 4) << 4) | (data[6] >> 4);
unsigned mirror = ((data[6] & 0x08) >> 2) | (data[6] & 0x01);
unsigned prgrom = data[4] * 0x4000;
unsigned chrrom = data[5] * 0x2000;
unsigned prgram = 0u;
unsigned chrram = chrrom == 0u ? 8192u : 0u;
markup.append("<cartridge sha256='", sha256(data, size), "'>\n");
switch(mapper) {
default:
markup.append(" <board type='NES-NROM-256'/>\n");
markup.append(" <mirror mode='", mirror == 0 ? "horizontal" : "vertical", "'/>\n");
break;
case 1:
markup.append(" <board type='NES-SXROM'/>\n");
markup.append(" <chip type='MMC1B2'/>\n");
prgram = 8192;
break;
case 2:
markup.append(" <board type='NES-UOROM'/>\n");
markup.append(" <mirror mode='", mirror == 0 ? "horizontal" : "vertical", "'/>\n");
break;
case 3:
markup.append(" <board type='NES-CNROM'/>\n");
markup.append(" <mirror mode='", mirror == 0 ? "horizontal" : "vertical", "'/>\n");
break;
case 4:
//MMC3
markup.append(" <board type='NES-TLROM'/>\n");
markup.append(" <chip type='MMC3B'/>\n");
prgram = 8192;
//MMC6
//markup.append(" <board type='NES-HKROM'/>\n");
//markup.append(" <chip type='MMC6'/>\n");
//prgram = 1024;
break;
case 5:
markup.append(" <board type='NES-ELROM'/>\n");
markup.append(" <chip type='MMC5'/>\n");
prgram = 65536;
break;
case 7:
markup.append(" <board type='NES-AOROM'/>\n");
break;
case 9:
markup.append(" <board type='NES-PNROM'/>\n");
markup.append(" <chip type='MMC2'/>\n");
prgram = 8192;
break;
case 10:
markup.append(" <board type='NES-FKROM'/>\n");
markup.append(" <chip type='MMC4'/>\n");
prgram = 8192;
break;
case 16:
markup.append(" <board type='BANDAI-FCG'/>\n");
markup.append(" <chip type='LZ93D50'/>\n");
break;
case 21:
case 23:
case 25:
//VRC4
markup.append(" <board type='KONAMI-VRC-4'/>\n");
markup.append(" <chip type='VRC4'>\n");
markup.append(" <pinout a0='1' a1='0'/>\n");
markup.append(" </chip>\n");
prgram = 8192;
break;
case 22:
//VRC2
markup.append(" <board type='KONAMI-VRC-2'/>\n");
markup.append(" <chip type='VRC2'>\n");
markup.append(" <pinout a0='0' a1='1'/>\n");
markup.append(" </chip>\n");
break;
case 24:
markup.append(" <board type='KONAMI-VRC-6'/>\n");
markup.append(" <chip type='VRC6'/>\n");
break;
case 26:
markup.append(" <board type='KONAMI-VRC-6'/>\n");
markup.append(" <chip type='VRC6'/>\n");
prgram = 8192;
break;
case 34:
markup.append(" <board type='NES-BNROM'/>\n");
markup.append(" <mirror mode='", mirror == 0 ? "horizontal" : "vertical", "'/>\n");
break;
case 66:
markup.append(" <board type='NES-GNROM'/>\n");
markup.append(" <mirror mode='", mirror == 0 ? "horizontal" : "vertical", "'/>\n");
break;
case 69:
markup.append(" <board type='SUNSOFT-5B'/>\n");
markup.append(" <chip type='5B'/>\n");
prgram = 8192;
break;
case 73:
markup.append(" <board type='KONAMI-VRC-3'/>\n");
markup.append(" <chip type='VRC3'/>\n");
markup.append(" <mirror mode='", mirror == 0 ? "horizontal" : "vertical", "'/>\n");
prgram = 8192;
break;
case 75:
markup.append(" <board type='KONAMI-VRC-1'/>\n");
markup.append(" <chip type='VRC1'/>\n");
break;
case 85:
markup.append(" <board type='KONAMI-VRC-7/'>\n");
markup.append(" <chip type='VRC7'/>\n");
prgram = 8192;
break;
}
markup.append(" <prg>\n");
if(prgrom) markup.append(" <rom size='", prgrom, "'/>\n");
if(prgram) markup.append(" <ram size='", prgram, "' nonvolatile='true'/>\n");
markup.append(" </prg>\n");
markup.append(" <chr>\n");
if(chrrom) markup.append(" <rom size='", chrrom, "'/>\n");
if(chrram) markup.append(" <ram size='", chrram, "'/>\n");
markup.append(" </chr>\n");
markup.append("</cartridge>\n");
markup.transform("'", "\"");
}
}
#endif

View File

@ -3,10 +3,9 @@
namespace nall {
class SnesCartridge {
public:
struct SuperFamicomCartridge {
string markup;
inline SnesCartridge(const uint8_t *data, unsigned size);
inline SuperFamicomCartridge(const uint8_t *data, unsigned size);
//private:
inline void read_header(const uint8_t *data, unsigned size);
@ -105,7 +104,7 @@ public:
bool has_st018;
};
SnesCartridge::SnesCartridge(const uint8_t *data, unsigned size) {
SuperFamicomCartridge::SuperFamicomCartridge(const uint8_t *data, unsigned size) {
read_header(data, size);
string xml;
@ -538,7 +537,7 @@ SnesCartridge::SnesCartridge(const uint8_t *data, unsigned size) {
markup.append("</cartridge>\n");
}
void SnesCartridge::read_header(const uint8_t *data, unsigned size) {
void SuperFamicomCartridge::read_header(const uint8_t *data, unsigned size) {
type = TypeUnknown;
mapper = LoROM;
dsp1_mapper = DSP1Unmapped;
@ -766,7 +765,7 @@ void SnesCartridge::read_header(const uint8_t *data, unsigned size) {
}
}
unsigned SnesCartridge::find_header(const uint8_t *data, unsigned size) {
unsigned SuperFamicomCartridge::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);
@ -781,7 +780,7 @@ unsigned SnesCartridge::find_header(const uint8_t *data, unsigned size) {
}
}
unsigned SnesCartridge::score_header(const uint8_t *data, unsigned size, unsigned addr) {
unsigned SuperFamicomCartridge::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;
@ -862,7 +861,7 @@ unsigned SnesCartridge::score_header(const uint8_t *data, unsigned size, unsigne
return score;
}
unsigned SnesCartridge::gameboy_ram_size(const uint8_t *data, unsigned size) {
unsigned SuperFamicomCartridge::gameboy_ram_size(const uint8_t *data, unsigned size) {
if(size < 512) return 0;
switch(data[0x0149]) {
case 0x00: return 0 * 1024;
@ -875,7 +874,7 @@ unsigned SnesCartridge::gameboy_ram_size(const uint8_t *data, unsigned size) {
}
}
bool SnesCartridge::gameboy_has_rtc(const uint8_t *data, unsigned size) {
bool SuperFamicomCartridge::gameboy_has_rtc(const uint8_t *data, unsigned size) {
if(size < 512) return false;
if(data[0x0147] == 0x0f ||data[0x0147] == 0x10) return true;
return false;

View File

@ -111,6 +111,8 @@ namespace nall {
struct lstring : vector<string> {
inline optional<unsigned> find(const char*) const;
inline string concatenate(const char*) const;
template<unsigned Limit = 0> inline lstring& split(const char*, const char*);
template<unsigned Limit = 0> inline lstring& isplit(const char*, const char*);
template<unsigned Limit = 0> inline lstring& qsplit(const char*, const char*);

View File

@ -138,6 +138,14 @@ optional<unsigned> lstring::find(const char *key) const {
return { false, 0 };
}
string lstring::concatenate(const char *separator) const {
string output;
for(unsigned i = 0; i < size(); i++) {
output.append(operator[](i), i < size() - 1 ? separator : "");
}
return output;
}
bool lstring::operator==(const lstring &source) const {
if(this == &source) return true;
if(size() != source.size()) return false;

View File

@ -5,7 +5,7 @@
#include <nall/type_traits.hpp>
namespace nall {
template<unsigned bits> class uint_t {
template<unsigned bits> struct uint_t {
private:
typedef typename type_if<bits <= 8 * sizeof(unsigned), unsigned, uintmax_t>::type type_t;
type_t data;
@ -35,7 +35,7 @@ namespace nall {
template<unsigned s> inline uint_t(const uint_t<s> &i) : data(uclip<bits>(i)) {}
};
template<unsigned bits> class int_t {
template<unsigned bits> struct int_t {
private:
typedef typename type_if<bits <= 8 * sizeof(signed), signed, intmax_t>::type type_t;
type_t data;
@ -65,73 +65,46 @@ namespace nall {
template<unsigned s> inline int_t(const int_t<s> &i) : data(sclip<bits>(i)) {}
};
class varuint_t {
template<typename type_t> struct varuint_t {
private:
unsigned data;
unsigned mask;
type_t data;
type_t mask;
public:
inline operator unsigned() const { return data; }
inline unsigned operator ++(int) { unsigned r = data; data = (data + 1) & mask; return r; }
inline unsigned operator --(int) { unsigned r = data; data = (data - 1) & mask; return r; }
inline unsigned operator ++() { return data = (data + 1) & mask; }
inline unsigned operator --() { return data = (data - 1) & mask; }
inline unsigned operator =(const unsigned i) { return data = (i) & mask; }
inline unsigned operator |=(const unsigned i) { return data = (data | i) & mask; }
inline unsigned operator ^=(const unsigned i) { return data = (data ^ i) & mask; }
inline unsigned operator &=(const unsigned i) { return data = (data & i) & mask; }
inline unsigned operator<<=(const unsigned i) { return data = (data << i) & mask; }
inline unsigned operator>>=(const unsigned i) { return data = (data >> i) & mask; }
inline unsigned operator +=(const unsigned i) { return data = (data + i) & mask; }
inline unsigned operator -=(const unsigned i) { return data = (data - i) & mask; }
inline unsigned operator *=(const unsigned i) { return data = (data * i) & mask; }
inline unsigned operator /=(const unsigned i) { return data = (data / i) & mask; }
inline unsigned operator %=(const unsigned i) { return data = (data % i) & mask; }
inline operator type_t() const { return data; }
inline type_t operator ++(int) { type_t r = data; data = (data + 1) & mask; return r; }
inline type_t operator --(int) { type_t r = data; data = (data - 1) & mask; return r; }
inline type_t operator ++() { return data = (data + 1) & mask; }
inline type_t operator --() { return data = (data - 1) & mask; }
inline type_t operator =(const type_t i) { return data = (i) & mask; }
inline type_t operator |=(const type_t i) { return data = (data | i) & mask; }
inline type_t operator ^=(const type_t i) { return data = (data ^ i) & mask; }
inline type_t operator &=(const type_t i) { return data = (data & i) & mask; }
inline type_t operator<<=(const type_t i) { return data = (data << i) & mask; }
inline type_t operator>>=(const type_t i) { return data = (data >> i) & mask; }
inline type_t operator +=(const type_t i) { return data = (data + i) & mask; }
inline type_t operator -=(const type_t i) { return data = (data - i) & mask; }
inline type_t operator *=(const type_t i) { return data = (data * i) & mask; }
inline type_t operator /=(const type_t i) { return data = (data / i) & mask; }
inline type_t operator %=(const type_t i) { return data = (data % i) & mask; }
inline void bits(unsigned bits) { mask = (1U << (bits - 1)) + ((1U << (bits - 1)) - 1); data &= mask; }
inline varuint_t() : data(0), mask(~0U) {}
inline varuint_t(const unsigned i) : data(i), mask(~0U) {}
};
class varuintmax_t {
private:
uintmax_t data;
uintmax_t mask;
public:
inline operator uintmax_t() const { return data; }
inline uintmax_t operator ++(int) { uintmax_t r = data; data = (data + 1) & mask; return r; }
inline uintmax_t operator --(int) { uintmax_t r = data; data = (data - 1) & mask; return r; }
inline uintmax_t operator ++() { return data = (data + 1) & mask; }
inline uintmax_t operator --() { return data = (data - 1) & mask; }
inline uintmax_t operator =(const uintmax_t i) { return data = (i) & mask; }
inline uintmax_t operator |=(const uintmax_t i) { return data = (data | i) & mask; }
inline uintmax_t operator ^=(const uintmax_t i) { return data = (data ^ i) & mask; }
inline uintmax_t operator &=(const uintmax_t i) { return data = (data & i) & mask; }
inline uintmax_t operator<<=(const uintmax_t i) { return data = (data << i) & mask; }
inline uintmax_t operator>>=(const uintmax_t i) { return data = (data >> i) & mask; }
inline uintmax_t operator +=(const uintmax_t i) { return data = (data + i) & mask; }
inline uintmax_t operator -=(const uintmax_t i) { return data = (data - i) & mask; }
inline uintmax_t operator *=(const uintmax_t i) { return data = (data * i) & mask; }
inline uintmax_t operator /=(const uintmax_t i) { return data = (data / i) & mask; }
inline uintmax_t operator %=(const uintmax_t i) { return data = (data % i) & mask; }
inline void bits(unsigned bits) { mask = (1ULL << (bits - 1)) + ((1ULL << (bits - 1)) - 1); data &= mask; }
inline varuintmax_t() : data(0), mask(~0ULL) {}
inline varuintmax_t(const uintmax_t i) : data(i), mask(~0ULL) {}
inline void bits(type_t bits) { mask = (1ull << (bits - 1)) + ((1ull << (bits - 1)) - 1); data &= mask; }
inline varuint_t() : data(0ull), mask((type_t)~0ull) {}
inline varuint_t(const type_t i) : data(i), mask((type_t)~0ull) {}
};
}
//typedefs
typedef nall::uint_t< 1> uint1_t;
typedef nall::uint_t< 2> uint2_t;
typedef nall::uint_t< 3> uint3_t;
typedef nall::uint_t< 4> uint4_t;
typedef nall::uint_t< 5> uint5_t;
typedef nall::uint_t< 6> uint6_t;
typedef nall::uint_t< 7> uint7_t;
//typedef nall::uint_t< 8> uint8_t;
typedef nall::uint_t< 9> uint9_t;
typedef nall::uint_t< 1> uint1_t;
typedef nall::uint_t< 2> uint2_t;
typedef nall::uint_t< 3> uint3_t;
typedef nall::uint_t< 4> uint4_t;
typedef nall::uint_t< 5> uint5_t;
typedef nall::uint_t< 6> uint6_t;
typedef nall::uint_t< 7> uint7_t;
//typedef nall::uint_t< 8> uint8_t;
typedef nall::uint_t< 9> uint9_t;
typedef nall::uint_t<10> uint10_t;
typedef nall::uint_t<11> uint11_t;
typedef nall::uint_t<12> uint12_t;
@ -139,6 +112,7 @@ namespace nall {
typedef nall::uint_t<14> uint14_t;
typedef nall::uint_t<15> uint15_t;
//typedef nall::uint_t<16> uint16_t;
typedef nall::uint_t<17> uint17_t;
typedef nall::uint_t<18> uint18_t;
typedef nall::uint_t<19> uint19_t;
@ -156,15 +130,16 @@ namespace nall {
typedef nall::uint_t<31> uint31_t;
//typedef nall::uint_t<32> uint32_t;
typedef nall::int_t< 1> int1_t;
typedef nall::int_t< 2> int2_t;
typedef nall::int_t< 3> int3_t;
typedef nall::int_t< 4> int4_t;
typedef nall::int_t< 5> int5_t;
typedef nall::int_t< 6> int6_t;
typedef nall::int_t< 7> int7_t;
//typedef nall::int_t< 8> int8_t;
typedef nall::int_t< 9> int9_t;
typedef nall::int_t< 1> int1_t;
typedef nall::int_t< 2> int2_t;
typedef nall::int_t< 3> int3_t;
typedef nall::int_t< 4> int4_t;
typedef nall::int_t< 5> int5_t;
typedef nall::int_t< 6> int6_t;
typedef nall::int_t< 7> int7_t;
//typedef nall::int_t< 8> int8_t;
typedef nall::int_t< 9> int9_t;
typedef nall::int_t<10> int10_t;
typedef nall::int_t<11> int11_t;
typedef nall::int_t<12> int12_t;
@ -172,6 +147,7 @@ namespace nall {
typedef nall::int_t<14> int14_t;
typedef nall::int_t<15> int15_t;
//typedef nall::int_t<16> int16_t;
typedef nall::int_t<17> int17_t;
typedef nall::int_t<18> int18_t;
typedef nall::int_t<19> int19_t;

View File

@ -56,6 +56,12 @@ namespace nall {
new(pool + objectsize++) T(data);
}
bool appendonce(const T& data) {
if(find(data) == true) return false;
append(data);
return true;
}
void insert(unsigned position, const T& data) {
append(data);
for(signed n = size() - 1; n > position; n--) pool[n] = pool[n - 1];

View File

@ -2,7 +2,6 @@
namespace NES {
#include "ines.cpp"
#include "chip/chip.cpp"
#include "board/board.cpp"
Cartridge cartridge;
@ -16,12 +15,14 @@ void Cartridge::main() {
}
void Cartridge::load(const string &markup, const uint8_t *data, unsigned size) {
information.markup = markup;
if((size & 0xff) == 0) {
sha256 = nall::sha256(data, size);
board = Board::load(markup, data, size);
} else {
sha256 = nall::sha256(data + 16, size - 16);
board = Board::load(!markup.empty() ? markup : iNES(data, size), data + 16, size - 16);
board = Board::load(markup, data + 16, size - 16);
}
if(board == nullptr) return;

View File

@ -17,6 +17,10 @@ struct Cartridge : Thread, property<Cartridge> {
readonly<bool> loaded;
readonly<string> sha256;
struct Information {
string markup;
} information;
void serialize(serializer&);
Cartridge();

View File

@ -1,162 +0,0 @@
static string iNES(const uint8_t *data, unsigned size) {
if(size < 16) return "";
if(data[0] != 'N') return "";
if(data[1] != 'E') return "";
if(data[2] != 'S') return "";
if(data[3] != 0x1a) return "";
string output;
unsigned mapper = ((data[7] >> 4) << 4) | (data[6] >> 4);
unsigned mirror = ((data[6] & 0x08) >> 2) | (data[6] & 0x01);
unsigned prgrom = data[4] * 0x4000;
unsigned chrrom = data[5] * 0x2000;
unsigned prgram = 0;
unsigned chrram = chrrom == 0 ? 8192 : 0;
//print("iNES mapper: ", mapper, "\n");
output.append("<?xml version='1.0' encoding='UTF-8'?>\n");
output.append("<cartridge>\n");
switch(mapper) {
default:
output.append(" <board type='NES-NROM-256'/>\n");
output.append(" <mirror mode='", mirror == 0 ? "horizontal" : "vertical", "'/>\n");
break;
case 1:
output.append(" <board type='NES-SXROM'/>\n");
output.append(" <chip type='MMC1B2'/>\n");
prgram = 8192;
break;
case 2:
output.append(" <board type='NES-UOROM'/>\n");
output.append(" <mirror mode='", mirror == 0 ? "horizontal" : "vertical", "'/>\n");
break;
case 3:
output.append(" <board type='NES-CNROM'/>\n");
output.append(" <mirror mode='", mirror == 0 ? "horizontal" : "vertical", "'/>\n");
break;
case 4:
//MMC3
output.append(" <board type='NES-TLROM'/>\n");
output.append(" <chip type='MMC3B'/>\n");
prgram = 8192;
//MMC6
//output.append(" <board type='NES-HKROM'/>\n");
//output.append(" <chip type='MMC6'/>\n");
//prgram = 1024;
break;
case 5:
output.append(" <board type='NES-ELROM'/>\n");
output.append(" <chip type='MMC5'/>\n");
prgram = 65536;
break;
case 7:
output.append(" <board type='NES-AOROM'/>\n");
break;
case 9:
output.append(" <board type='NES-PNROM'/>\n");
output.append(" <chip type='MMC2'/>\n");
prgram = 8192;
break;
case 10:
output.append(" <board type='NES-FKROM'/>\n");
output.append(" <chip type='MMC4'/>\n");
prgram = 8192;
break;
case 16:
output.append(" <board type='BANDAI-FCG'/>\n");
output.append(" <chip type='LZ93D50'/>\n");
break;
case 21:
case 23:
case 25:
//VRC4
output.append(" <board type='KONAMI-VRC-4'/>\n");
output.append(" <chip type='VRC4'>\n");
output.append(" <pinout a0='1' a1='0'/>\n");
output.append(" </chip>\n");
prgram = 8192;
break;
case 22:
//VRC2
output.append(" <board type='KONAMI-VRC-2'/>\n");
output.append(" <chip type='VRC2'>\n");
output.append(" <pinout a0='0' a1='1'/>\n");
output.append(" </chip>\n");
break;
case 24:
output.append(" <board type='KONAMI-VRC-6'/>\n");
output.append(" <chip type='VRC6'/>\n");
break;
case 26:
output.append(" <board type='KONAMI-VRC-6'/>\n");
output.append(" <chip type='VRC6'/>\n");
prgram = 8192;
break;
case 34:
output.append(" <board type='NES-BNROM'/>\n");
output.append(" <mirror mode='", mirror == 0 ? "horizontal" : "vertical", "'/>\n");
break;
case 66:
output.append(" <board type='NES-GNROM'/>\n");
output.append(" <mirror mode='", mirror == 0 ? "horizontal" : "vertical", "'/>\n");
break;
case 69:
output.append(" <board type='SUNSOFT-5B'/>\n");
output.append(" <chip type='5B'/>\n");
prgram = 8192;
break;
case 73:
output.append(" <board type='KONAMI-VRC-3'/>\n");
output.append(" <chip type='VRC3'/>\n");
output.append(" <mirror mode='", mirror == 0 ? "horizontal" : "vertical", "'/>\n");
prgram = 8192;
break;
case 75:
output.append(" <board type='KONAMI-VRC-1'/>\n");
output.append(" <chip type='VRC1'/>\n");
break;
case 85:
output.append(" <board type='KONAMI-VRC-7/'>\n");
output.append(" <chip type='VRC7'/>\n");
prgram = 8192;
break;
}
output.append(
" <prg>\n"
" <rom size='0x", hex(prgrom), "'/>\n"
" <ram size='0x", hex(prgram), "' nonvolatile='true'/>\n"
" </prg>\n"
" <chr>\n"
" <rom size='0x", hex(chrrom), "'/>\n"
" <ram size='0x", hex(chrram), "'/>\n"
" </chr>\n"
"</cartridge>\n"
);
//print(output, "\n");
return output;
}

View File

@ -11,8 +11,10 @@ namespace SNES {
Cartridge cartridge;
void Cartridge::load(Mode cartridge_mode, const char *markup) {
void Cartridge::load(Mode cartridge_mode, const string &markup) {
mode = cartridge_mode;
information.markup = markup;
region = Region::NTSC;
ram_size = 0;

View File

@ -52,7 +52,7 @@ struct Cartridge : property<Cartridge> {
uint8_t *data;
unsigned size;
Slot slot;
NonVolatileRAM() : id(""), data(0), size(0), slot(Slot::Base) {}
NonVolatileRAM() : id(""), data(nullptr), size(0), slot(Slot::Base) {}
NonVolatileRAM(const string id, uint8_t *data, unsigned size, Slot slot = Slot::Base)
: id(id), data(data), size(size), slot(slot) {}
};
@ -76,13 +76,14 @@ struct Cartridge : property<Cartridge> {
linear_vector<Mapping> mapping;
struct Information {
string markup;
struct NSS {
lstring setting;
lstring option[16];
} nss;
} information;
void load(Mode, const char*);
void load(Mode, const string&);
void unload();
void serialize(serializer&);

View File

@ -11,8 +11,10 @@
#include <nall/filemap.hpp>
#include <nall/input.hpp>
#include <nall/bps/patch.hpp>
#include <nall/nes/cartridge.hpp>
#include <nall/snes/cartridge.hpp>
#include <nall/gameboy/cartridge.hpp>
#include <nall/gb/cartridge.hpp>
#include <nall/gba/cartridge.hpp>
using namespace nall;
#include <phoenix/phoenix.hpp>
@ -45,6 +47,7 @@ struct Application {
string normalFont;
string boldFont;
string titleFont;
string monospaceFont;
void run();
Application(int argc, char **argv);

View File

@ -2,3 +2,4 @@
#include "main-window.cpp"
#include "file-browser.cpp"
#include "dip-switches.cpp"
#include "information-window.cpp"

View File

@ -1,3 +1,4 @@
#include "main-window.hpp"
#include "file-browser.hpp"
#include "dip-switches.hpp"
#include "information-window.hpp"

View File

@ -0,0 +1,43 @@
InformationWindow *informationWindow = nullptr;
InformationWindow::InformationWindow() {
setTitle("Information");
layout.setMargin(5);
markup.setFont(application->monospaceFont);
markupXML.setChecked();
markupXML.setText("Show original XML markup");
append(layout);
layout.append(markup, {~0, ~0}, 5);
layout.append(markupXML, {~0, 0});
setGeometry({128, 128, 600, 360});
windowManager->append(this, "InformationWindow");
markupXML.onToggle = { &InformationWindow::update, this };
}
void InformationWindow::update() {
string markupData = interface->markup();
if(markupXML.checked()) {
markup.setText(markupData);
return;
}
XML::Document document(markupData);
markupData = "";
parse(document, markupData, 0);
markup.setText(markupData);
}
void InformationWindow::parse(XML::Node &header, string &output, unsigned depth) {
for(auto &node : header) {
if(node.name.beginswith("?")) continue;
for(unsigned n = 0; n < depth; n++) output.append(" ");
output.append(node.name);
if(node.children.size() == 0 && !node.data.empty()) output.append(": ", node.data);
output.append("\n");
parse(node, output, depth + 1);
}
}

View File

@ -0,0 +1,11 @@
struct InformationWindow : Window {
VerticalLayout layout;
TextEdit markup;
CheckBox markupXML;
void update();
void parse(XML::Node &node, string &output, unsigned depth);
InformationWindow();
};
extern InformationWindow *informationWindow;

View File

@ -16,7 +16,7 @@ MainWindow::MainWindow() {
cartridgeLoadSatellaview.setText("BS-X Satellaview ...");
cartridgeLoadSufamiTurbo.setText("Sufami Turbo ...");
nesMenu.setText("&NES");
nesMenu.setText("&Famicom");
nesPower.setText("&Power Cycle");
nesReset.setText("&Reset");
nesPort1.setText("Controller Port &1");
@ -31,7 +31,7 @@ MainWindow::MainWindow() {
nesPort2Device[config->nes.controllerPort2Device].setChecked();
nesCartridgeUnload.setText("&Unload Cartridge");
snesMenu.setText("&SNES");
snesMenu.setText("&Super Famicom");
snesPower.setText("&Power Cycle");
snesReset.setText("&Reset");
snesPort1.setText("Controller Port &1");
@ -98,6 +98,7 @@ MainWindow::MainWindow() {
toolsStateLoad3.setText("Slot &3");
toolsStateLoad4.setText("Slot &4");
toolsStateLoad5.setText("Slot &5");
toolsInformationWindow.setText("&Information ...");
toolsShrinkWindow.setText("Shrink &Window");
toolsCheatEditor.setText("&Cheat Editor ...");
toolsStateManager.setText("State &Manager ...");
@ -185,6 +186,7 @@ MainWindow::MainWindow() {
toolsStateLoad.append(toolsStateLoad4);
toolsStateLoad.append(toolsStateLoad5);
toolsMenu.append(toolsSeparator);
toolsMenu.append(toolsInformationWindow);
toolsMenu.append(toolsShrinkWindow);
toolsMenu.append(toolsCheatEditor);
toolsMenu.append(toolsStateManager);
@ -364,6 +366,7 @@ MainWindow::MainWindow() {
toolsStateLoad4.onActivate = [&] { interface->loadState(4); };
toolsStateLoad5.onActivate = [&] { interface->loadState(5); };
toolsInformationWindow.onActivate = [&] { informationWindow->setVisible(); };
toolsShrinkWindow.onActivate = [&] { utility->resizeMainWindow(true); };
toolsCheatEditor.onActivate = [&] { cheatEditor->setVisible(); };
toolsStateManager.onActivate = [&] { stateManager->setVisible(); };

View File

@ -79,6 +79,7 @@ struct MainWindow : Window {
Item toolsStateLoad4;
Item toolsStateLoad5;
Separator toolsSeparator;
Item toolsInformationWindow;
Item toolsShrinkWindow;
Item toolsCheatEditor;
Item toolsStateManager;

View File

@ -7,6 +7,10 @@ void InterfaceGB::initialize() {
GB::system.init();
}
string InterfaceGB::markup() {
return GB::cartridge.information.markup;
}
bool InterfaceGB::cartridgeLoaded() {
return GB::cartridge.loaded();
}
@ -31,9 +35,7 @@ bool InterfaceGB::loadCartridge(GB::System::Revision revision, const string &fil
string markup;
markup.readfile(interface->base.filename("manifest.xml", ".xml"));
GameBoyCartridge info(data, size);
if(markup.empty()) markup = info.markup;
if(markup.empty()) markup = GameBoyCartridge(data, size).markup;
GB::cartridge.load(revision, markup, data, size);
GB::system.power();

View File

@ -1,6 +1,8 @@
struct InterfaceGB : InterfaceCore, GB::Interface {
void initialize();
string markup();
bool cartridgeLoaded();
bool loadCartridge(GB::System::Revision revision, const string &filename);
void unloadCartridge();

View File

@ -5,6 +5,10 @@ void InterfaceGBA::initialize() {
GBA::system.init();
}
string InterfaceGBA::markup() {
return GBA::cartridge.information.markup;
}
bool InterfaceGBA::cartridgeLoaded() {
return GBA::cartridge.loaded();
}
@ -29,6 +33,7 @@ bool InterfaceGBA::loadCartridge(const string &filename) {
string markup;
markup.readfile(interface->base.filename("manifest.xml", ".xml"));
if(markup.empty()) markup = GameBoyAdvanceCartridge(data, size).markup;
GBA::cartridge.load(markup, data, size);
GBA::system.power();

View File

@ -1,6 +1,8 @@
struct InterfaceGBA : InterfaceCore, GBA::Interface {
void initialize();
string markup();
bool cartridgeLoaded();
bool loadCartridge(const string &filename);
void unloadCartridge();

View File

@ -38,7 +38,7 @@ string CartridgePath::title() const {
title = notdir(nall::basename(title));
return title;
}
return notdir(nall::basename(name));
return notdir(name);
}
void Interface::bindControllers() {
@ -80,13 +80,17 @@ void Interface::updateDSP() {
}
}
string Interface::markup() {
if(core) return core->markup();
return "";
}
bool Interface::cartridgeLoaded() {
if(core) return core->cartridgeLoaded();
return false;
}
void Interface::loadCartridge(Mode mode) {
utility->setMode(this->mode = mode);
switch(mode) {
case Mode::NES: core = &nes; break;
case Mode::SNES: core = &snes; break;
@ -94,6 +98,7 @@ void Interface::loadCartridge(Mode mode) {
case Mode::GBA: core = &gba; break;
default: core = nullptr; break;
}
utility->setMode(this->mode = mode);
bindControllers();
cheatEditor->load(game.filename("cheats.xml", ".cht"));
@ -122,6 +127,7 @@ void Interface::unloadCartridge() {
setCheatCodes();
if(core) core->unloadCartridge();
core = nullptr;
cartridgeTitle = "";
utility->setMode(mode = Mode::None);
}

View File

@ -3,6 +3,7 @@
struct InterfaceCore {
bool loadFirmware(string filename, string keyname, uint8_t *targetdata, unsigned targetsize);
virtual string markup() = 0;
virtual bool cartridgeLoaded() = 0;
virtual void unloadCartridge() = 0;
@ -51,6 +52,8 @@ struct Interface : property<Interface> {
void setController(unsigned port, unsigned device);
void updateDSP();
string markup();
bool cartridgeLoaded();
void loadCartridge(Mode mode);
bool loadCartridge(string filename); //auto-detect system-type based on file extension

View File

@ -3,6 +3,10 @@ void InterfaceNES::initialize() {
NES::system.init();
}
string InterfaceNES::markup() {
return NES::cartridge.information.markup;
}
void InterfaceNES::setController(bool port, unsigned device) {
if(port == 0) config->nes.controllerPort1Device = device;
if(port == 1) config->nes.controllerPort2Device = device;
@ -42,6 +46,7 @@ bool InterfaceNES::loadCartridge(const string &filename) {
string markup;
markup.readfile(interface->base.filename("manifest.xml", ".xml"));
if(markup.empty()) markup = FamicomCartridge(data, size).markup;
NES::cartridge.load(markup, data, size);
NES::system.power();

View File

@ -1,5 +1,8 @@
struct InterfaceNES : InterfaceCore, NES::Interface {
void initialize();
string markup();
void setController(bool port, unsigned device);
bool cartridgeLoaded();

View File

@ -5,6 +5,10 @@ void InterfaceSNES::initialize() {
SNES::system.init();
}
string InterfaceSNES::markup() {
return SNES::cartridge.information.markup;
}
void InterfaceSNES::setController(bool port, unsigned device) {
if(port == 0) config->snes.controllerPort1Device = device;
if(port == 1) config->snes.controllerPort2Device = device;
@ -63,7 +67,7 @@ bool InterfaceSNES::loadCartridge(string basename) {
string markup;
markup.readfile(interface->base.filename("manifest.xml", ".xml"));
if(markup.empty()) markup = SnesCartridge(data, size).markup;
if(markup.empty()) markup = SuperFamicomCartridge(data, size).markup;
SNES::cartridge.rom.copy(data, size);
SNES::cartridge.load(SNES::Cartridge::Mode::Normal, markup);
@ -91,7 +95,7 @@ bool InterfaceSNES::loadSatellaviewSlottedCartridge(string basename, string slot
string markup;
markup.readfile(interface->base.filename("manifest.xml", ".xml"));
if(markup.empty()) markup = SnesCartridge(data[0], size[0]).markup;
if(markup.empty()) markup = SuperFamicomCartridge(data[0], size[0]).markup;
SNES::cartridge.rom.copy(data[0], size[0]);
if(data[1]) SNES::bsxflash.memory.copy(data[1], size[1]);
@ -121,7 +125,7 @@ bool InterfaceSNES::loadSatellaviewCartridge(string basename, string slotname) {
string markup;
markup.readfile(interface->base.filename("manifest.xml", ".xml"));
if(markup.empty()) markup = SnesCartridge(data[0], size[0]).markup;
if(markup.empty()) markup = SuperFamicomCartridge(data[0], size[0]).markup;
SNES::cartridge.rom.copy(data[0], size[0]);
if(data[1]) SNES::bsxflash.memory.copy(data[1], size[1]);
@ -156,7 +160,7 @@ bool InterfaceSNES::loadSufamiTurboCartridge(string basename, string slotAname,
string markup;
markup.readfile(interface->base.filename("manifest.xml", ".xml"));
if(markup.empty()) markup = SnesCartridge(data[0], size[0]).markup;
if(markup.empty()) markup = SuperFamicomCartridge(data[0], size[0]).markup;
SNES::cartridge.rom.copy(data[0], size[0]);
if(data[1]) SNES::sufamiturbo.slotA.rom.copy(data[1], size[1]);
@ -188,7 +192,7 @@ bool InterfaceSNES::loadSuperGameBoyCartridge(string basename, string slotname)
string markup;
markup.readfile(interface->base.filename("manifest.xml", ".xml"));
if(markup.empty()) markup = SnesCartridge(data[0], size[0]).markup;
if(markup.empty()) markup = SuperFamicomCartridge(data[0], size[0]).markup;
string gbMarkup;
gbMarkup.readfile(interface->slot[0].filename("manifest.xml", ".xml"));

View File

@ -1,6 +1,8 @@
struct InterfaceSNES : InterfaceCore, SNES::Interface {
void initialize();
string markup();
void setController(bool port, unsigned device);
bool cartridgeLoaded();

View File

@ -54,9 +54,11 @@ Application::Application(int argc, char **argv) {
utility = new Utility;
string fontFamily = Intrinsics::platform() == Intrinsics::Platform::Windows ? "Tahoma, " : "Sans, ";
string monoFamily = Intrinsics::platform() == Intrinsics::Platform::Windows ? "Lucida Console, " : "Liberation Mono, ";
normalFont = {fontFamily, "8"};
boldFont = {fontFamily, "8, Bold"};
titleFont = {fontFamily, "16, Bold"};
monospaceFont = {monoFamily, "8"};
compositionEnable = compositor::enabled();
if(config->video.compositionMode == 2) compositor::enable(false);
@ -65,6 +67,7 @@ Application::Application(int argc, char **argv) {
mainWindow = new MainWindow;
fileBrowser = new FileBrowser;
dipSwitches = new DipSwitches;
informationWindow = new InformationWindow;
settingsWindow = new SettingsWindow;
cheatDatabase = new CheatDatabase;
cheatEditor = new CheatEditor;
@ -133,6 +136,7 @@ Application::~Application() {
delete cheatEditor;
delete cheatDatabase;
delete settingsWindow;
delete informationWindow;
delete dipSwitches;
delete fileBrowser;
delete mainWindow;

View File

@ -47,6 +47,7 @@ void Utility::setMode(Interface::Mode mode) {
interface->updateDSP();
mainWindow->synchronize();
resizeMainWindow();
informationWindow->update();
}
void Utility::resizeMainWindow(bool shrink) {