Compare commits

...

80 Commits
v087 ... v092

Author SHA1 Message Date
Tim Allen
032e924495 Update to v092 release.
In the release thread, byuu says:

    The first official release of higan has been posted. higan is the
    new name for bsnes, and it continues with the latter's version
    numbering.

    Note that as of now, bsnes still exists. It's a module distributed
    inside of higan. bsnes is now specific to my SNES emulator.

    Due to last minute changes to the emulator interface, and missing
    support in ananke, I wasn't able to include Cydrak's Nintendo DS
    emulator dasShiny in this build, but I hope to do so in the next
    release.

    http://code.google.com/p/higan/downloads/list

    For both new and experienced users, please read the higan user guide
    first:

    http://byuu.org/higan/user-guide

In the v091 WIP thread, byuu says:

    r15->r16:
    - BS-X MaskROM handling (partial ... need to split bsx/flash away
      from sfc/chip, restructure code - it requires tagging the base
      cart markup for now, but it needs to parse the slotted cart
      markup)
    - phoenixflags / phoenixlink += -m32
    - nall/sort stability
    - if(input.poll(scancode[activeScancode]) == false) return;
    - MSU1 / USART need to use interface->path(1)
    - MSU1 needs to use Markup::Document, not XML::Document
    - case-insensitive folder listings
    - remove nall/emulation/system.hpp files (move to ananke)
    - remove rom/ram id= checks with indexing
    X have cores ask for manifest.bml (skipped for v092's release, too
      big a change)
    - rename compatibility profile to balanced (so people don't assume
      it has better compatibility than accuracy)
2013-01-14 23:15:21 +11:00
Tim Allen
b389d17c9a Update to v091r15 release.
byuu says:

Changelog:
- all media types always show base name in the title now (eg Super Game
  Boy + Mega Man II)
- Game Boy loading via ananke has been fixed
- phoenix is dynamically linked on Windows now (needed for ananke)
- Linux port shows the higan program icon (once you install the program
  to get the bitmap into /usr/local/share/pixmaps)
- paths.cfg defaults to "userpath()/Emulation/System Name/" when it is
  created from scratch

[Later, after the v092 release, byuu posted this additional changelog:
    - new compilation rules for win32
    - OS::setName
    - default to ~/Emulation/media.name for paths.cfg
]
2013-01-14 23:14:44 +11:00
Tim Allen
d59ae34e12 Update to higan v091r14 and ananke v00r03 releases.
byuu says:

higan changelog:
- generates title displayed in emulator window by asking the core
- core builds title solely from "information/title" ... if it's not
  there, you don't get a title at all
- sub-system load menu is gone ... since there are multiple revisions of
  the SGB, this never really worked well anyway
- to load an SGB, BS-X or ST cartridge, load the base cartridge first
- "File->Load Game" moved to "Load->Import Game" ... may cause a bit of
  confusion to new users, but I don't like having a single-item menu,
  we'll just have to explain it to new users
- browser window redone to look like ananke
  - home button here goes to ~/Emulation rather than just ~ like ananke,
    since this is the home of game folders
  - game folder icon is now the executable icon for the Tango theme
    (orange diamond), meant to represent a complete game rather than
    a game file or archive

ananke changelog:
- outputs GBC games to "Game Boy Color/" instead of "Game Boy/"
- adds the file basename to "information/title"

Known issues:
- using ananke to load a GB game trips the Super Famicom SGB mode and
  fails (need to make the full-path auto-detection ignore non-bootable
  systems)
- need to dump and test some BS-X media before releasing
- ananke lacks BS-X Satellaview cartridge support
- v092 isn't going to let you retarget the ananke/higan game folder path
  of ~/Emulation, you will have to wait for a future version if that
  bothers you so greatly

[Later, after the v092 release, byuu posted this additional changelog:
    - kill laevateinn
    - add title()
    - add bootable, remove load
    - combine file, library
    - combine [][][] paths
    - fix SFC subtype handling XML->BML
    - update file browser to use buttons
    - update file browser keyboard handling
    - update system XML->BML
    - fix sufami turbo hashing
    - remove Cartridge::manifest
]
2013-01-14 23:13:48 +11:00
Tim Allen
85f2e9a6d4 Update to ananke v00r02 release.
byuu says:

This should be basically final now.

Works with all media types (nes, sfc, gb, gbc, gba, bs, st), strips
headers, can use internal or external firmware, imports saves on first
run.

Added a custom file dialog. It seems both GTK+ and Windows XP have
(un)intelligent file sorting, which puts eg "ActRaiser 2 (NA)" before
"ActRaiser (NA)". So, screw 'em.
2012-12-26 17:46:57 +11:00
Tim Allen
019fc1a2c6 Update to v091r13 release, and ananke v00r01.
byuu says (about higan):

- dropped release/ root node for individual games (still there in
  ananke's database.)
- Memory export uses smarter names (vram.rwm -> video.ram, etc.)
- cheat database moved from XML to BML (3.1MB to 1.9MB file size.)
- cheat codes moved from XML to BML
- resource manifest moved from XML to BML

What can I say, I like consistency. But I'll leave the shaders alone
until I get around to shader folders.

byuu says (about ananke):

Works with higan v091r13. Only does SNES stuff so far.
2012-12-26 17:46:57 +11:00
Tim Allen
84e98833ca Update to v091r11 release.
byuu says:

This release refines HSU1 support as a bidirectional protocol, nests SFC
manifests as "release/cartridge" and "release/information" (but release/
is not guaranteed to be finalized just yet), removes the database
integration, and adds support for ananke.

ananke represents inevitability. It's a library that, when installed,
higan can use to load files from the command-line, and also from a new
File -> Load Game menu option.

I need to change the build rules a bit for it to work on Windows (need
to make phoenix a DLL, basically), but it works now on Linux.

Right now, it only takes *.sfc file names, looks them up in the included
database, converts them to game folders, and returns the game folder
path for higan to load.

The idea is to continue expanding it to support everything we can that
I don't want in the higan core:
- load *.sfc, *.smc, *.swc, *.fig files
- remove SNES copier headers
- split apart merged firmware files
- pull in external firmware files (eg dsp1b.rom - these are staying
  merged, just as SPC7110 prg+dat are merged)
- load *.zip and *.7z archives
- prompt for selection on multi-file archives
- generate manifest files based on heuristics
- apply BPS patches

The "Load" menu option has been renamed to "Library", to represent games
in your library. I'm going to add some sort of suffix to indicate
unverified games, and use a different folder icon for those (eg
manifests built on heuristics rather than from the database.)

So basically, to future end users:
File -> Load Game will be how they play games.
Library -> (specific system) can be thought of as an infinitely-sized
    recent games list.

purify will likely become a simple stub that invokes ananke's functions.
No reason to duplicate all that code.
2012-12-26 17:46:57 +11:00
Tim Allen
d4751c5244 Update to v091r10 release.
byuu says:

This release adds HSU1 support, and fixes the reduce() memory mapping
function.
2012-12-26 17:46:57 +11:00
Tim Allen
ab345ff20c Update to v091r09 release.
[r07 and r08 were not posted to the WIP thread. -Ed.]

byuu says:

I'd appreciate it if you guys wouldn't mind testing out the database
functionality.

Save this file as database.bml (remove the date) inside
~/.config/higan/Super Famicom.sfc/ or %APPDATA%/higan/Super Famicom.sfc/

    http://byuu.org/snes/database/database_2012-10-21.bml

Now load any of the 20 games in the database from the file dialog. They
need to be named *.sfc, have no copier header, and have firmware
appended (for Mario Kart only so far.)

If anyone actually does test it, please let me know how it goes for you
and what you think. Note that future versions of higan will have the
database.bml file included with the release.
2012-12-26 17:46:57 +11:00
Tim Allen
c495c132a7 Update to v091r06 release.
byuu says:

This release adds initial database support.

The way it works is you can now load game folders as you always have, or
you can load a game file. If you load a game file, it tries to create
a game folder for you by looking up the file's sha256 in a database. If
it can't find it, sorry, the game won't play. I'm not hooking up the
oldschool "make up a manifest" code here. The easiest way to handle this
is to get me every game so I can dump it and add it to the database :D

The database entries are complete entries that can be copied directly.
So it describes the board, the information, file layout, etc. That'll be
what comes with higan releases in the future.

Internally, I'm separating the information and board descriptions, and
will use a tool to merge the two together.

Here's a current database copy, with one game in it. Still hammering out
some details, but it's mostly how it's going to look.

    cartridge region=NTSC
	board type=1CB5B-20
	    superfx revision=2
		rom name=program.rom size=0x200000
		ram name=save.rwm size=0x8000
		map id=io address=00-3f,80-bf:3000-32ff
		map id=rom address=00-3f:8000-ffff mask=0x8000
		map id=rom address=40-5f:0000-ffff
		map id=ram address=00-3f,80-bf:6000-7fff size=0x2000
		map id=ram address=70-71:0000-ffff
	information
	    name:   Super Mario World 2 - Yoshi's Island (SNS) (1.1)
	    title:  Super Mario World 2: Yoshi's Island
	    sha256: bd763c1a56365c244be92e6cffefd318780a2a19eda7d5baf1c6d5bd6c1b3e06
	    board:  SHVC-1CB5B-20
	    rom:    0x200000
	    ram:    0x8000
	layout
	    file name=program.rom size=0x200000
2012-12-26 17:46:57 +11:00
Tim Allen
ef746bbda4 Update to v091r05 release.
[No prior releases were posted to the WIP thread. -Ed.]

byuu says:

Super Famicom mapping system has been reworked as discussed with the
mask= changes. offset becomes base, mode is gone. Also added support for
comma-separated fields in the address fields, to reduce the number of
map lines needed.

    <?xml version="1.0" encoding="UTF-8"?>
    <cartridge region="NTSC">
      <superfx revision="2">
	<rom name="program.rom" size="0x200000"/>
	<ram name="save.rwm" size="0x8000"/>
	<map id="io" address="00-3f,80-bf:3000-32ff"/>
	<map id="rom" address="00-3f:8000-ffff" mask="0x8000"/>
	<map id="rom" address="40-5f:0000-ffff"/>
	<map id="ram" address="00-3f,80-bf:6000-7fff" size="0x2000"/>
	<map id="ram" address="70-71:0000-ffff"/>
      </superfx>
    </cartridge>

Or in BML:

    cartridge region=NTSC
      superfx revision=2
	rom name=program.rom size=0x200000
	ram name=save.rwm size=0x8000
	map id=io address=00-3f,80-bf:3000-32ff
	map id=rom address=00-3f:8000-ffff mask=0x8000
	map id=rom address=40-5f:0000-ffff
	map id=ram address=00-3f,80-bf:6000-7fff size=0x2000
	map id=ram address=70-71:0000-ffff

As a result of the changes, old mappings will no longer work. The above
XML example will run Super Mario World 2: Yoshi's Island. Otherwise,
you'll have to write your own.

All that's left now is to work some sort of database mapping system in,
so I can start dumping carts en masse.

The NES changes that FitzRoy asked for are mostly in as well.

Also, part of the reason I haven't released a WIP ... but fuck it, I'm
not going to wait forever to post a new WIP.

I've added a skeleton driver to emulate Campus Challenge '92 and
Powerfest '94. There's no actual emulation, except for the stuff I can
glean from looking at the pictures of the board. It has a DSP-1 (so
SR/DR registers), four ROMs that map in and out, RAM, etc.

I've also added preliminary mapping to upload high scores to a website,
but obviously I need the ROMs first.
2012-12-26 17:46:57 +11:00
Tim Allen
94b2538af5 Update to higan v091 release.
byuu says:

Basically just a project rename, with s/bsnes/higan and the new icon
from lowkee added in.

It won't compile on Windows because I forgot to update the resource.rc
file, and a path transform command isn't working on Windows.
It was really just meant as a starting point, so that v091 WIPs can flow
starting from .00 with the new name (it overshadows bsnes v091, so
publicly speaking this "shouldn't exist" and will probably be deleted
from Google Code when v092 is ready.)
2012-12-26 17:46:36 +11:00
Tim Allen
7f404e6edb Update to v091 release.
byuu says:

A few issues crept up in the last release, this should take care of
them.

First, it seems that the 32-bit runtime on 64-bit versions of Windows
have 64-bit time functions; whereas true 32-bit Windows does not. This
was causing a DLL error when attempting to load bsnes v090.

Second, when there were more than 2,000 files in the same folder on
Windows, it was lagging the file browser. With OV2's help, I've fixed
that and it'll now load the list instantly.

Lastly, I've included the missing video shaders this time.
2012-08-11 12:18:19 +10:00
Tim Allen
47dffcae85 Update to v090 release.
byuu says:

Most notably, this release adds Nintendo DS emulation. The Nintendo DS
module was written entirely by Cydrak, so please give him all of the
credit for it. I for one am extremely grateful to be allowed to use his
module in bsnes.

The Nintendo DS emulator's standalone name is dasShiny. You will need
the Nintendo DS firmware, which I cannot provide, in order to use it. It
also cannot (currently?) detect the save type used by NDS games. As
such, manifest.xml files must be created manually for this purpose. The
long-term plan is to create a database of save types for each game.
Also, you will need an analog input device for the touch screen for now
(joypad axes work well.)

There have also been a lot of changes from my end: a unified
manifest.xml format across all systems, major improvements to SPC7110
emulation, enhancements to RTC emulation, MSU1 enhancements, icons in
the file browser list, improvements to SNES coprocessor memory mapping,
cleanups and improvements in the libraries used to build bsnes, etc.

I've also included kaijuu (which allows launching game folders directly
with bsnes) and purify (which allows opening images that are compressed,
have copier headers, and have wrong extensions); both of which are fully
GUI-based.

This release only loads game folders, not files. Use purify to load ROM
files in bsnes.

Note that this will likely be the last release for a long time, and that
I will probably rename the emulator for the next release, due to how
many additional systems it now supports.
2012-08-08 00:08:37 +10:00
Tim Allen
be625cc0fb Update to v089r18 release.
byuu says:

Changelog:
- fixed bsnes to let config files and system folders to be in the same
  folder as the executable
- fixed RawInput driver to compile again without linear_vector
- fixed phoenix/Windows to compile again without linear_vector
- fixed old vs new name warnings on MinGW w64 (technically the warnings
  were erroneous, but I worked around them anyway)
- added memory export hotkey (SNES driver only; mainly for FEoEZ
  translation)
- restored WRAM randomization for v090 stability (we can discuss that
  idea for v091+)
- fixed SuperFX / SA-1 "0x" prefix in the header generation (drop it
  into the latest purify if you want)
- added nall/Makefile uname support for UnxUtils (was breaking
  compilation with full UnxUtils in your path otherwise)
2012-08-07 23:28:00 +10:00
Tim Allen
4cb8b51606 Update to purify v00r07 release.
byuu says:

This update uses the latest manifest.xml mappings. It also adds a new
"Update Manifests" button that can be used to quickly regenerate all
manifests (sans Famicom games ... since I strip the iNES header, that
information is gone. We can't support Famicom manifest.xml updates until
we have a database.) This is different than the before wrapping of the
functionality on the convert games button. You can also trigger this on
the command-line with "purify synchronize"

g6672D, great catch. This was fixed, thank you.

[g6672D's bug was: "SA-1 and SuperFX are missing the "0x" for
program.rom/save.rwm size." - Ed.]
2012-07-23 22:53:28 +10:00
Tim Allen
87cb164f7c Update to v089r17 release.
byuu says:

This implements the spec from the XML part 3 thread:

    http://board.byuu.org/viewtopic.php?f=16&t=2998

It's also propagated the changes to nall and purify, so you can test
this one.

This is basically it, after years of effort I feel I finally have
a fully consistent and logical XML board format.
The only things left to change will be: modifications if emulation turns
out to be incorrect (eg we missed some MMIO mirrors, or mirrored too
much), and new additions.

And of course, I'm giving it a bit of time for good arguments against
the format.

Other than that, this release removes linear_vector and pointer_vector,
as vector is better than linear_vector and I've never used
pointer_vector.
vector also gets move(), which is a way to use move-semantics across
types. It lets you steal the underlying memory pool, effectively
destroying the vector without a copy.
This works really nicely with the move for read() functions to return
vector<uint8> instead of taking (uint8_t*&, unsigned&) parameters.
2012-07-22 22:27:18 +10:00
Tim Allen
c1318961d8 Update to v089r16 release.
byuu says:

Changelog:
- eliminated <mmio>, <mcu> tags [they are merged to their parent nodes
  now]
- added <ram name= size=> tag to EpsonRTC, SharpRTC
- added <firmware> tag to DSP-n, ST-01n, ST-018, Cx4
- interface->path(0) now returns the system folder, which can be used
  for storage now
- as a fun proof-of-concept, I've simulated SNES warm power cycles by
  saving and loading work RAM (same effect if you instantly swapped
  carts)
  - long-term, I'm not sure how I want to do this. The power menu option
    makes no sense with warm RAM
  - I like the idea of decaying RAM based on timestamp from last play;
    and power can just force the timestamp to 0 (which will corrupt all
    RAM)
- Interface::firmware is gone. The cores now load firmware inside their
  boot up routines
- you now get a message on the screen if the emulator can't find
  firmware, should help with "I just get a black screen" messages

I'd like to start preparing for a v090 release. I think we're almost
there now. Have to update nall/cartridge and purify to handle XML
changes first.
2012-07-22 22:27:14 +10:00
Tim Allen
791e64951b Update to v089r15 release.
byuu says:

Changelog:
- SuperFX has its own ROM and RAM
- Cx4 has its own ROM
- SPC7110 has its own ProgramROM, DataROM and RAM
- OBC1 has its own RAM
- BsxCartridge has its own ROM, RAM and PSRAM
- mapping changes to accommodate the above
2012-07-09 21:40:23 +10:00
Tim Allen
27af50099f Update to v089r14 release.
byuu says:

Changelog:
- NSS emulation improvement (DIP is 8-bits, not 16-bits; can be remapped
  via XML now like all the other chips)
- SA-1 memory map improvements (IRAM and BWRAM can be saved; ROM, IRAM
  and BWRAM are separate from Cartridge::ROM, RAM; no MCU)
- S-DD1 memory map improvements (ROM, RAM inside mapping; no MCU)
- SPC7110 memory map improvements (ROM, RAM inside mapping; no MCU; not
  finished yet [have to handle 8mbit expansion somehow now)

I have tried multiple times now to get the SuperFX core to use internal
ROM and RAM (separate from Cartridge::ROM, RAM) to no avail.
Not sure what the hell is going on there. Trace logs of 430MB are not
enticing ...

So right now: SuperFX, SPC7110 and BS-X cheat by mapping stuff to
Cartridge::ROM, RAM still. They need to not do that.
2012-07-08 12:57:34 +10:00
Tim Allen
fbd52c7e5f Update to v089r13 release.
[also, replace the old purify tool with the new tool by the same name.
There were some previous releases outside the WIP thread, but this is
the first one that actually works with a WIP release. -Ed.]

byuu says:

Fixes up loading issues with recent purify changes, and purify also
works on BS/ST file types now and should be a bit more crash-resistant.
2012-06-25 22:49:39 +10:00
Tim Allen
36795e8061 Update to v089r12 release.
byuu says:

Changelog:
- Game Boy XML uses <cartridge><board type="MBC3"/> instead of
  <cartridge mapper="MBC3">
- if you run bsnes with a filename argument, it will invoke "purify
  filename" and exit immediately
  - this chains: purify will turn the file into a game folder, and then
    invoke bsnes with the game folder name
    - net result: you can drag a ZIP file onto bsnes or associate SMC
      headered ROMs with bsnes and they'll just work
- new nall: unified usage of - vs _ vs nothing on filenames; fancier
  lstring; fancier image (constructor for creating from filename or from
  memory); etc
- new phoenix: images in ListView, GTK+ merges the check box into the
  first column like the other targets do, etc
- browser list now uses icons to differentiate system folders from game
  folders (the game folder icon sucks, I'm open to suggestions though,
  as long as it's available on Debian Squeeze in /usr/share/icons, no
  custom stuff please)
2012-06-18 20:13:51 +10:00
Tim Allen
ec8350794a Update to v089r11 release.
byuu says:

Changelog:
- SPC7110 $480b (and its settings in $4805-6 + $4807) is now fully
  emulated
- decompressor restructured and commented accordingly

The final parts remaining for the SPC7110 core, all within the
decompression engine:
- need to detect when 15+ input bytes are read for one output byte and
  simulate a crash somehow (don't have to perfectly simulate corrupted
  data stream)
- need to emulate time required to decompress data (doesn't have to be
  perfect, just something other than instantaneous)
- need to determine what $480c status flags d6-d0 are for, as best we
  can anyway
2012-06-06 19:57:53 +10:00
Tim Allen
4545e7c62d Update to v089r10 release.
byuu says:

Changelog:
- Cydrak merged all three SPC7110 decompression routines into one, cuts
  the size in half
- fixed masking of $4803.d7 and $4813.d7
- data port out of bounds accesses emulated correctly for app SPC7110
  boards
- all(?) data port $4810 reload cases now supported
- basic timing for $4805-6 seeking; reworked delay timing to work better
  as well
- fixed $480c.d7 flag (1 = ready, not busy)
- AbsoluteInput returns -32768 if presentation window lacks focus and
  you don't always allow input
2012-05-31 22:27:46 +10:00
Tim Allen
3302398907 Update to v089r09 release.
byuu says:

Changelog:
- SPC7110 data port emulation greatly improved
- SPC7110 $480b.d1 emulated (but $4805-4806 does not work right for mode
  2 decompression yet)
- MSU1 audio output will be muted when the S-DSP FLG ($6c).d6 (mute)
  flag is set
- MSU1 will read filenames from manifest now (defaults to msu1.rom and
  track-#.pcm if missing ... for now)
- bugfixes with MSU1 load state and track seek (and $4804 was wrapping
  into $4805 to change the track#)
- Link coprocessor removed (it was meant for ST018 HLE, which never
  happened)

Notes for things I forgot but need to address:
- $4813 needs to be uint7 for the set_data_offset() to not allow reading
  A23 as set ever (or we can mask)
- AbsoluteInput when window doesn't have focus should return -32768, not
  0
- need to support input ID lists that aren't linear (0-7), but arbitrary
  (0,1,6,7 or whatever)
2012-05-29 22:20:46 +10:00
Tim Allen
189e707594 Update to v089r08 release.
byuu says:

Changelog:
- Super Game Boy, BS-X Satellaview and Sufami Turbo cartridges all load
  manifests that specify their file names, and they all work
- Sufami Turbo can now properly handle carts without RAM, or empty slots
  entirely
- Emulator::Interface structures no longer specify any file names, ever
- exposed "capability.(cheats,states)" now. So far, this just means the
  GBA doesn't show the cheat editor, since it doesn't support cheat
  codes yet
- as such, state manager and cheat editor windows auto-hide (may be
  a tiny bit inconvenient, but it makes not having to sync them or deal
  with input when no cart is loaded easier)
- added "AbsoluteInput" type, which returns mouse coordinates from
  -32767,-32767 (top left) to +32767,+32767 (bottom right) or
  -32768,-32768 (offscreen)

AbsoluteInput is just something I'm toying with. Idea is to support eg
Super Scope or Justifier, or possibly some future Famicom controllers
that are absolute-indexed. The coordinates are scaled, so the bigger
your window, the more precise they are. But obviously you can't get more
precise than the emulated system, so 1x scale will behave the same
anyway. I haven't hooked it up yet, need to mess with the idea of custom
cursors via phoenix for that first. Also not sure if it will feel
smoother or not ... if you resize the window, your mouse will seem to
move slower. Still, not having to capture the mouse for SS/JS may be
nicer yet. But we'll see ... just experimenting for now.
2012-05-28 09:50:50 +10:00
Tim Allen
9a8a54c75e Update to v089r07 release.
byuu says:

Not even purify makes compatible images for this WIP.
Unless you want to figure it out yourself, I'd suggest waiting for an
updated tool before using subsequent WIPs.

Changelog:
- MSU1 initializes data port + audio track to 0
- MSU1 implements audio track error flag on $2000.d3
- manifest.xml now controls file names for cartridge folders ... mostly

Regressions:
- Super Game Boy support is broken
- Sufami Turbo support is broken

So, basically Emulator::Interface() now has:

    void load(const string &manifest);
    void save();

The first one will analyze the manifest, and call all the ROM + RAM
loadRequest() commands necessary to run the game.
The second one will call saveRequest() commands on all writable and
non-volatile storage (basically if it's a RAM type and has a filename
specified, it gets saved to disk.)
save() shrinks the size of Emulator::Interface() by hiding information
one is unlikely to care about. It also makes it much easier to save.
The core auto-calls this when you unload a game as well. So the only
time you ever have to worry about it is if you want to save RAM files
mid-game (in case you want to do periodic backups in case of a crash.)
2012-05-26 18:18:42 +10:00
Tim Allen
d418eda97c Update to v089r06 release.
[Yes, the release number is re-used. -Ed.]

byuu says:

I had some bugs in r07 that I couldn't track down, DKJM2's clock was
getting all out of sync.
So I just reverted to r05, blew away both RTCs entirely, and wrote them
cleanly from scratch (obviously looking off the old code.) A bit
extreme, but it worked.
I believe I found the error in the process, day and month were resetting
the counter to 0 instead of 1, it wasn't handling leap year, etc.
While I was at it, I fixed the day-of-week calculation. The SharpRTC
epoch is actually 1000-01-01, and not 1900-01-01.
I'm sure you guys will be really happy that if it ever becomes 1000AD
again and you're playing a ROM hack that uses the SharpRTC and relies on
its weekday value that it will show the correct day now ...
Kind of a pain to compute, but nothing compared to the seventh circle of
hell that was my IBM dBase III Julian<>Gregorian conversion functions :/
Also found a few bugs in the Epson code this way. And I moved the round
seconds actions and flag clear to +125us after flag set.

So, if you had the old r06 or r07, please delete those.

Unfortunately, this took all of my energy today, so the file names
inside manifest changes will have to be in the next WIP.

EDIT: ran a diff against old r07 and new r06.
- added if(days == 31) case twice in EpsonRTC::tick_day()
- forgot weekday = 0; in SharpRTC::load()
- need to move the cartridge+cheat objects up in sfc/Makefile again
- System::init() needs assert(interface != nullptr /* not 0 */)
2012-05-25 09:26:06 +10:00
Tim Allen
5dbd5f4d0f Update to v089r07 release.
byuu says:

Changelog:
- EpsonRTC emulation improved further (stop/pause blocks IRQs, verified
  secondhi >= 3 triggers 30-second adjust (even on invalid BCD),
  second-changed flag is mirrored to minute+hour+day+month+weekday,
  improved busy timing, etc.)
- SharpRTC rewritten, works like EpsonRTC now in that it has its own
  timing thread and ticks with the emulation
- won't attempt to read from an unopen file stream now (I think this is
  what was crashing Sufami Turbo without SRAM?)
- added Tools -> Synchronize Time option below load/save state options.
  Only appears when you play a game with an emulated RTC chip

Just realized that I used 125ms for the 30-second adjust instead of
125us, so I'll fix that in the next WIP.
Aside from that, this is as good as the emulation is going to get.
There's still a couple of absolutely psychopathic edge cases that are
just too damn difficult to simulate.
So that leaves us with data port control + decompression status
registers to investigate before SPC7110 will be finished.
2012-05-23 21:27:45 +10:00
Tim Allen
d6001a2df4 Update to v089r06 release.
byuu says:

Changelog:
- renamed SRTC -> SharpRTC
- renamed RTC4513 -> EpsonRTC (consistent with DSP naming schema)
- full emulation of invalid BCD values for EpsonRTC
- fixed EpsonRTC IRQ mask

Remaining SPC7110 tasks:
- RTC: test 30-second adjust with all values from 00-7f
- RTC: hold is supposed to tick the clock one second after being
  released?
- RTC: wait times are too long (need to use >32KHz oscillation to
  simulate it properly)
- Data Port: test $4818 more thoroughly (not too important)
- Decompression: test $480c more thoroughly (very important)
- Decompression: perform some tests on DMA transferring data, especially
  with $4807 set

Write-offs, at least for now:
- Decompression: emulation of the crash/glitch behavior seen on the real
  chip when fed invalid data
- Decompression: I can find no use of $4808
- ALU: Booth cycles for MUL/DIV (this could actually be rather important
  if the game reads simpler values quickly [some shoddy games did this
  with the CPU ALU])
- RTC: delay after hold release for $4841 accesses
- RTC: 125uS delay after 30-second adjust that will screw with registers
  in odd ways if your read or write too soon
- RTC: psychotic behavior of reading too early returning port address
  - 1
2012-05-22 22:10:00 +10:00
Tim Allen
bd61432322 Update to v089r05 release.
byuu says:

I split the RTC-4513 code from the SPC7110 code (and obviously in the
XML mapping as well), since they are separate chips on the FEoEZ PCB.
In this way, you can use just the RTC-4513 in homebrew now if you want.
It's a bit nicer than the Sharp RTC from Dai Kaijuu Monogatari II.
This was needed anyway, it has an internal oscillator that's not
divisible by the SNES clock used by the SPC7110; and both the RTC and
decompression code need to be running their own threads anyway.

In the process, I rewrote the way variables are stored to use named
integers rather than a block of memory. Makes the code a lot easier on
the eyes, and more importantly, will make emulating bad BCD values
a whole lot easier.
2012-05-21 20:56:48 +10:00
Tim Allen
0611fefefa Update to v089r04 release.
byuu says:

Changelog: (SPC7110)
- emulated $480b.d0 + $4807 deinterleave mode
- cleaned up decompression core (I'd still like to wipe out those static
  variables, those are bad for save states.)
- improved emulation of data port ($481a only increments, never reads)
- improved emulation of RTC (block appropriate bits in the hour register
  based on 12/24h mode select; $4840 modes != 1 all disable the chip;
  added MDR; etc.)
2012-05-19 16:28:54 +10:00
Tim Allen
6ac7b733bd Update to v089r03 release.
byuu says:

Substantial improvements to SPC7110 emulation. Added all of the findings
from http://byuu.org/temp/spc7110-mmio.txt that I understood.

I also completely rewrote the RTC. We only had about ~40% of the chip
emulated before. Turns out there's cool stuff like spare RAM, calendar
disable, 12-hour mode, IRQs, IRQ masking, duty cycles, etc. So I went
ahead and emulated all of it. The upper bits on hour+ don't work as
nocash described though, not sure what doc he was reading. The Epson
RTC-4513 manual I have doesn't explain any of the registers.
The new RTC core also ticks seconds based on the emulated clock, and not
on the system clock. This is going to suck for people wanting to keep
the in-game clock synced with their computer, who also abuse fast
forward and save states. Fast forward makes the clock run faster, and
save states will jump the clock to the time it was at when you took the
save state. (It will keep track of the number of seconds between
unloading the game and loading it again, so time passes normally there.)
This is required, however, and how I'm going to rearrange all of the
RTCs for all systems. Any other method can be detected by the game, and
is thus not faithful emulation. To help with this, I'll probably make an
RTC time tool so that you can adjust the time when the emulator isn't
running, but I don't intend to bundle that into bsnes.
New state format bit-packs the RTCRAM values, and it also uses a 64-bit
timestamp. So it's 16 bytes now instead of 20 bytes. S-RTC will drop
from 16 to 12 when it's done.
The RTC busy flag timing needs to be refined with more hardware tests,
there's a slim chance of the game hanging on save at the moment.

The SPC7110 ALU delays are emulated now, too. They may not be perfectly
accurate, but they get the basic gist down.
The only hack that needs to be removed now is the decompression busy
flag. That's ... not going to be fun.

I also redid the mouse emulation. I was polling the mouse position
multiple times per latch. So it should be a bit more precise now,
I hope.
I read it regardless of latch state, dunno if that's good or not.
2012-05-16 10:27:34 +10:00
Tim Allen
a1e4c67a05 Update to v089r02 release.
(r01 was not posted to the WIP thread)

byuu says:

r01 changelog:
- major improvements to SPC7110 MCU (memory controller unit)
- revised SPC7110 memory map to reflect aforementioned improvements
- added "Toggle Tracer" hotkey to target-ethos (only works for SFC so
  far, I plan to use this as a lightweight laevateinn for FEoEZ)

r02 changelog:
- quick fix: SRAM is mapped to 00-3f|80-bf:6000-7fff
- quick fix: $4830.d7 is SRAM chip enable, not SRAM write enable. Reads
  return 0x00 when this bit is clear
2012-05-14 23:32:55 +10:00
Tim Allen
73ebe093b8 Update to v089 release.
byuu says:

Changelog to v089:
- fix SA-1 Mini Yonku Shining Scorpion
- load from command-line
- remove SNES::Cartridge::NVRAM
- fix SGB save RAM
- update cheats.xml
- already mapped inputs cancel input assign

BS-X wasn't broken after all. I forgot that I ran purify on my BS-X
images, and that the BS Zelda ZIP I have has the disable ROM bit set.
Whoops.
2012-05-12 12:34:35 +10:00
Tim Allen
c3f9d421da Update to v088r16 release.
byuu says:

Changelog:
- fixed BGnxOFS to not cache when MOSAIC is not in effect [fixes Air
  Strike Patrol "Good Luck" text]
- added GameBoy::Interface::Hook for SGB bindings [SGB works again]
- do not create bsnes/ folder unless it is absolutely needed (eg you
  create a save state or state manager archive)
- SuperFX works [needed to call system.init() in Interface::Interface()]

Last chance for any bug reports, at this point I pretty much consider
ethos to be finished. It's shipping without BS-X BIOS game loading
support. Sorry, I can't figure that one out.
2012-05-10 09:35:29 +10:00
Tim Allen
689fc49047 Update to v088r15 release.
byuu says:

Changelog:
- default placement of presentation window optimized for 1024x768
  displays or larger (sorry if yours is smaller, move the window
  yourself.)
- Direct3D waits until a previous Vblank ends before waiting for the
  next Vblank to begin (fixes video timing analysis, and ---really---
  fast computers.)
- Window::setVisible(false) clears modality, but also fixed in Browser
  code as well (fixes loading images on Windows hanging)
- Browser won't consume full CPU resources (but timing analysis will,
  I don't want stalls to affect the results.)
- closing settings window while analyzing stops analysis
- you can load the SGB BIOS without a game (why the hell you would want
  to ...)
- escape closes the Browser window (it won't close other dialogs, it has
  to be hooked up per-window)
- just for fun, joypad hat up/down moves in Browser file list, any
  joypad button loads selected game [not very useful, lacks repeat, and
  there aren't GUI load file open buttons]
- Super Scope and Justifier crosshairs render correctly (probably
  doesn't belong in the core, but it's not something I suspect people
  want to do themselves ...)
- you can load GB, SGB, GB, SGB ... without problems (not happy with how
  I did this, but I don't want to add an Interface::setInterface()
  function yet)
- PAL timing works as I want now (if you want 50fps on a 60hz monitor,
  you must not use sync video) [needed to update the DSP frequency when
  toggling video/audio sync]
- not going to save input port selection for now (lot of work), but it
  will properly keep your port setting across cartridge loads at least
  [just goes to controller on emulator restart]
- SFC overscan on and off both work as expected now (off centers image,
  on shows entire image)
- laevateinn compiles properly now
- ethos goes to ~/.config/bsnes now that target-ui is dead [honestly,
  I recommend deleting the old folder and starting over]
- Emulator::Interface callbacks converted to virtual binding structure
  that GUI inherits from (simplifies binding callbacks)
    - this breaks Super Game Boy for a bit, I need to rethink
      system-specific bindings without direct inheritance

Timing analysis works spectacularly well on Windows, too. You won't get
your 100% perfect rate (unless maybe you leave the analysis running
overnight?), but it'll get really freaking close this way.
2012-05-08 09:29:03 +10:00
Tim Allen
cb97d98ad2 Update to v088r14 release.
byuu says:

Changelog:
- added NSS DIP switch settings window (when loading NSS carts with
  appropriate manifest.xml file)
- added video shader selection (they go in ~/.config/bsnes/Video
  Shaders/ now)
- added driver selection
- added timing settings (not only allows video/audio settings, also has
  code to dynamically compute the values for you ... and it actually
  works pretty good!)
- moved "None" controller device to bottom of list (it is the least
  likely to be used, after all)
- added Interface::path() to support MSU1, USART, Link
- input and hotkey mappings remember list position after assignment
- and more!

target-ethos now has all of the functionality of target-ui, and more.
Final code size for the port is 101.2KB (ethos) vs 167.6KB (ui).
A ~67% reduction in code size, yet it does even more! And you can add or
remove an entire system with only three lines of code (Makefile include,
header include, interface append.)

The only problem left is that the BS-X BIOS won't load the BS Zelda no
Densetsu file.
I can't figure out why it's not working, would appreciate any
assistance, but otherwise I'm probably just going to leave it broken for
v089, sorry.

So the show stoppers for a new release at this point are:
- fix laevateinn to compile with the new interface changes (shouldn't be
  too hard, it'll still use the old, direct interface.)
- clean up Emulator::Interface as much as possible (trim down
  Information, mediaRequest should use an alternate struct designed to
  load firmware / slots separately)
- enhance purify to strip SNES ROM headers, and it really needs a GUI
  interface
- it would be highly desirable to make a launcher that can create
  a cartridge folder from an existing ROM set (* ethos will need to
  accept command-line arguments for this.)
- probably need to remember which controller was selected in each port
  for each system across runs
- need to fix the cursor for Super Scope / Justifier games (move from
  19-bit to 32-bit colors broke it)
- have to refactor that cache.(hv)offset thing to fix ASP
2012-05-07 09:27:42 +10:00
Tim Allen
3cb04b101b Update to v088r13 release.
byuu says:

Changelog:
- fixed Super Game Boy input
- Sufami Turbo prompts to load second slot now (you can cancel to leave
  it empty)
- NEC/Hitachi/ARM DSP firmware is loaded; NEC RAM is saved
- folders are grouped properly: Sufami Turbo save RAM saves to its slot
  folder, etc.
- title shows properly (SGB shows GB game name only, BS-X slotted shows
  game name and optional slot name, etc.)
    - above extends to saving cheats and such in their correct folders
      as well
- added cheat editor and cheat database
    - and hooked up the requisite SGB mode loads and can use GB cheats,
      because that's kinda cool
- added state manager
- input settings, cheat editor and state manager all have erase (one)
  and reset (all) buttons now
- lots of cleanup and restructuring on Emulator::Interface; *almost*
  finished with it now

Remaining:
- BS-X BIOS won't show the data pack
- need XML mapping information window
- need NSS DIP switch settings window
- need video shaders
- need driver selection
- need to hide controllers that have no inputs from the input mapping
  list (tempted to just remove "None" as a controller option ...)

ethos is currently 88KB of code, ui is 167KB. We're missing about 5-10KB
of code in ethos to complete it, so the rewrite nearly cut the GUI code
size in half, while support all of the same functionality and allowing
the easy addition and removal of entire systems.
2012-05-06 16:34:46 +10:00
Tim Allen
5d273c5265 Update to v088r12 release.
byuu says:

Changelog:
- all hotkeys from target-ui now exist in target-ethos
- controller port menus now show up when you load a system (hidden if
  there are no options to choose from)
- tools menu auto-hides with no game open ... not much point to it then
- since we aren't using RawInput's multi-KB/MS support anyway, input and
  hotkey mappings remove KB0:: and turn MS0:: into Mouse::, makes it
  a lot easier to read
- added mute audio, sync video, sync audio, mask overscan
- added video settings: saturation, gamma, luminance, overscan
  horizontal, overscan vertical
- added audio settings: frequency, latency, resampler, volume
- added input settings: when focus is lost [ ] pause emulator [ ] allow
  input
- pausing and autopausing works
- status messages hooked up (show a message in status bar for a few
  seconds, then revert to normal status text)
- sub systems (SGB, BSX, ST) sorted below primary systems list
- added geometry settings cache
- Emulator::Interface cleanups and simplifications
- save states go into (cart foldername.extension/bsnes/state-#.bsa) now.
  Idea is to put emulator-specific data in their own subfolders

Caveats / Missing:
- SGB input does not work
- Sufami Turbo second slot doesn't work yet
- BS-X BIOS won't show the data pack
- need XML mapping information window
- need cheat editor and cheat database
- need state manager
- need video shaders
- need driver selection
- need NSS DIP switch settings
- need to hide controllers that have no inputs from the input mapping
  list

So for video settings, I used to have contrast/brightness/gamma.
Contrast was just a multiplier on intensity of each channel, and
brightness was an addition or subtraction against each channel. They
kind of overlapped and weren't that effective. The new setup has
saturation, gamma and luminance.

Saturation of 100% is normal. If you lower it, color information goes
away. 0% = grayscale. If you raise it, color intensity increases (and
clamps.) This is wonderful for GBA games, since they are oversaturated
to fucking death. Of course we'll want to normalize that inside the
core, so the same sat. value works on all systems, but for now it's
nice. If you raise saturation above 100%, it basically acts like
contrast used to. It's just that lowering it fades to grayscale rather
than black.

Adding doesn't really work well for brightness, it throws off the
relative distance between channels and looks like shit.  So now we have
luminance, which takes over the old contrast <100% role, and just fades
the pixels toward black. Obviously, luminance > 100% would be the same
as saturation > 100%, so that isn't allowed, it caps at 100% now.
Gamma's the same old function. Gamma curve on the lower-half of the
color range.
Effects are applied in the order they appear in the GUI: color ->
saturate -> gammify -> luminate -> output.
2012-05-04 22:47:41 +10:00
Tim Allen
8703d57030 Update to v088r11 release.
byuu says:

Changelog:
- phoenix has added Window::setModal(bool modal = true);
- file dialog is now modal. This allows emulation cores to request data
  and get it immediately before continuing the loading process
- save data is hooked up for most systems, still need to handle
  subsystem slot saves (Sufami Turbo, basically.)
- toggle fullscreen key binding added (Alt+Enter for now. I think F11 is
  probably better though, Enter is often mapped to game start button.)
- video scaling is in (center, scale, stretch), works the same in
  windowed and fullscreen mode (stretch hides resize window option), all
  in the settings menu now
- enough structure to map all saved paths for the browser and to load
  BS-X slotted carts, BS-X carts, single Sufami Turbo carts

Caveats / Missing:
- Super Game Boy input doesn't work yet (due to change in callback
  binding)
- doesn't load secondary Sufami Turbo slot yet
- BS-X BIOS isn't show the data pack games to load for some reason (ugh,
  I hate the shit out of debugging BS-X stuff ...)
- need mute audio, sync audio+video toggle, save/load state menu and
  quick keys, XML mapping information window
- need cheat editor and cheat database
- need state manager
- need to sort subsystems below main systems in load menu (basically
  just see if media.slot.size() > 0)
- need video shaders (will probably leave off filters for the time being
  ... due to that 24/30-bit thing)
- need video adjustments (contrast etc, overscan masks)
- need audio adjustments (frequency, latency, resampler, volume,
  per-system frequency)
- need driver selection and input focus policy (driver crash detection
  would be nice too)
- need NSS DIP switch settings (that one will be really fun)
- need to save and load window geometry settings
- need to hook up controller selection (won't be fun), create a map to
  hide controllers with no inputs to reassign
2012-05-03 22:36:47 +10:00
Tim Allen
9ad8b7eaac Update to v088r10 release.
byuu says:

ethos is going to be absolutely amazing. You guys are in for a treat :D
I'm impressing the hell out of myself with how well-structured this code
is, it's allowing me to do amazing new things.

Just a small sampling of what's in store (and already implemented):

The file browser will display folders as "[ folder name ]", and
cartridge folders as "Game Name" (no extension, no /) [icons would be
nicer, but well ... phoenix.]
Folders are sorted above cartridge folders.
Cartridge folders for other systems do not show up in the list.
Not only are unique paths stored for each image type, your position in
the list is saved across runs.
Some voodoo was added to GTK+ so that all targets even scroll directly
to that item when you open the list. Load->System->Enter restarts your
last game.
That sounds really simple and obvious, but it makes an -incredible-
difference. Didn't realize it until I tried an implementation of it,
wow.

The input mapping list now lets you bind as many hotkeys as you want to
any given input.
So SFC::Port1::Joypad::B = Keyboard::Z or Joypad::Button1 ... no need to
remap everything to switch between keyboard and joypad. Either one
activates the key.
There is a separate Hotkeys tab now. This should hopefully end the
confusion about how to remap hotkeys that users experience.
Hotkeys are different, too. Instead of OR logic, they use AND logic.
So Fullscreen = Keyboard::Alt and Keyboard::Enter. Both must be pressed
to enter the key. This lets you easily implement "super" modifier keys.

The actual codebase has new features the old UI never had, and has about
~50% of the old functionality (so far, of course), yet is only ~25% as
much code.
The entire GUI no longer needs to pull in all the headers for each
emulated system. It just needs a small interface header file.
Then bind the entire system with exactly **two** lines of code.
Everything is dynamically generated for you after that.
2012-05-01 22:27:50 +10:00
Tim Allen
76553756a2 Update to v088r09 release.
byuu says:

Lots of work on ethos, nothing more.
Settings window is in, InputManager pulls all the inputs from all cores
and binds them to ruby inputs, main window adds menu and dynamically
maps in all systems and cartridge slots and options and such, file
browser's back in, RAM is loaded and saved, etc. It's barely usable, but
you have to set up your inputs from the config file by hand for now.
2012-05-01 22:27:50 +10:00
Tim Allen
4fd20f0ae0 Update to v088r08 release.
byuu says:

From this WIP, I'm starting on the impossible task of
a declarative-based GUI, which I'm calling Ethos.
base/ becomes emulator/, and we add emulator/interface.hpp, which is
a base API that all emulation cores must implement in full.
(Right now, it's kind of a hybrid to work with the old GUI and the new
GUI at the same time, of course.)
Unlike the old interfaces, the new base class also provides all general
usability hooks: loading and saving files and states, cheat codes, etc.
The new interface also contains information and vector structs to
describe all possible loading methods, controller bindings, etc; and
gives names for them all.
The actual GUI in fact should not include eg <gba/gba.hpp> anymore.
Should speed up GUI compilation.

So the idea going forward is that ethos will build a list of emulators
right when the application starts up.
Once you've appended an emulator to that list, you're done. No more GUI
changes are needed to support that system.
The GUI will have code to parse the emulator interfaces list, and build
all the requisite GUI options dynamically, declarative style.

Ultimately, once the project is finished, the new GUI should look ~99%
identical to the current GUI. But it'll probably be a whole lot smaller.
2012-05-01 22:27:48 +10:00
Tim Allen
bb4db22a7d Update to v088r07 release.
byuu says:

(r05 and r06 were save points between large core modifications)

I would really appreciate extensive regression testing (especially
around SuperFX, Cx4, ST018, DSP-n, ST-01n, NES, GB) at this point.
The most critical core changes should be completed now. And it was an
unbelievable amount of restructuring.

Changelog:
- SuperFX core moved to Processor::GSU
- SNES::CPU core moved to Processor::R65816
- SNES::SMP core moved to Processor::SPC700
- NES::CPU core renamed to Processor::R6502
- use filestream to load RAM files from interface
- save states store SHA256 instead of CRC32 (CRC32 usage removed
  entirely from bsnes)
- nes/ -> fc/ and NES -> FC
- snes/ -> sfc/ and SNES -> SFC
- SuperFamicom::MappedRAM::copy uses stream instead of data+size
- Linux port uses gcc-4.7 (still using only gcc-4.6 subset, so you can
  make a gcc-4.6 symlink for now if you like.)
- all profiles and all targets compile and work properly

All eight instruction set cores have been moved to processor/ now.
Consistency's a wonderful thing.
The last remnants of NES/SNES are now limited to target-ui code; and the
nall/(system) folder names.
I'm building with gcc-4.7 on my Linux box now because the resultant
binaries are up to 20% faster (seriously) than gcc-4.6.
2012-05-01 22:02:14 +10:00
Tim Allen
67c13f749f Update to v088r04 release.
byuu says:

This will hopefully be a short-lived WIP, I just want to save
a breakpoint before I attempt something else.
NES, GB, GBC and GBA all load via const stream& now.
NES CPU core moved to Processor::RP2A03.
2012-04-28 16:35:51 +10:00
Tim Allen
616372e96b Update to v088r03 release.
byuu says:

static vector<uint8_t> file::read(const string &filename); replaces:
static bool file::read(const string &filename, uint8_t *&data, unsigned
&size); This allows automatic deletion of the underlying data.

Added vectorstream, which is obviously a vector<uint8_t> wrapper for
a data stream.  Plan is for all data accesses inside my emulation cores
to take stream objects, especially MSU1.  This lets you feed the core
anything: memorystream, filestream, zipstream, gzipstream, httpstream,
etc.  There will still be exceptions for link and serial, those need
actual library files on disk. But those aren't official hardware devices
anyway.

So to help with speed a bit, I'm rethinking the video rendering path.

Previous system:
- core outputs system-native samples (SNES = 19-bit LRGB, NES = 9-bit
  emphasis+palette, DMG = 2-bit grayscale, etc.)
- interfaceSystem transforms samples to 30-bit via lookup table inside
  the emulation core
- interfaceSystem masks off overscan areas, if enabled
- interfaceUI runs filter to produce new target buffer, if enabled
- interfaceUI transforms 30-bit video to native display depth (24-bit or
  30-bit), and applies color-adjustments (gamma, etc) at the same time

New system:
- all cores now generate an internal palette, and call
  Interface::videoColor(uint32_t source, uint16_t red, uint16_t green,
  uint16_t blue) to get native display color post-adjusted (gamma, etc
  applied already.)
- all cores output to uint32_t* buffer now (output video.palette[color]
  instead of just color)
- interfaceUI runs filter to produce new target buffer, if enabled
- interfaceUI memcpy()'s buffer to the video card

videoColor() is pretty neat. source is the raw pixel (as per the
old-format, 19-bit SNES, 9-bit NES, etc), and you can create a color
from that if you really want to. Or return that value to get a buffer
just like v088 and below.  red, green, blue are 16-bits per channel,
because why the hell not, right? Just lop off all the bits you don't
want. If you have more bits on your display than that, fuck you :P

The last step is extremely difficult to avoid. Video cards can and do
have pitches that differ from the width of the texture.  Trying to make
the core account for this would be really awful. And even if we did
that, the emulation routine would need to write directly to a video card
RAM buffer.  Some APIs require you to lock the video buffer while
writing, so this would leave the video buffer locked for a long time.
Probably not catastrophic, but still awful.  And lastly, if the
emulation core tried writing directly to the display texture, software
filters would no longer be possible (unless you -really- jump through
hooks and divert to a memory buffer when a filter is enabled, but ...
fuck.)

Anyway, the point of all that work was to eliminate an extra video copy,
and the need for a really painful 30-bit to 24-bit conversion (three
shifts, three masks, three array indexes.) So this basically reverts us,
performance-wise, to where we were pre-30 bit support.

[...]

The downside to this is that we're going to need a filter for each
output depth. Since the array type is uint32_t*, and I don't intend to
support higher or lower depths, we really only need 24+30-bit versions
of each filter.  Kinda shitty, but oh well.
2012-04-27 22:12:53 +10:00
Tim Allen
bba597fc6f Update to v088r02 release.
byuu says:

Basically, the current implementation of nall/array is deprecated now.
The old method was for non-reference types, it acted like a vector for
POD types (raw memory allocation instead of placement new construction.)
And for reference types, it acted like an unordered set. Yeah, not good.

As of right now, nall/array is no longer used. The vector type usage was
replaced with actual vectors.
I've created nall/set, which now contains the specialization for
reference types.
nall/set basically acts much like std::unordered_set. No auto-sort, only
one of each type is allowed, automatic growth.
This will be the same both for reference and non-reference types ...
however, the non-reference type wasn't implemented just yet.
Future plans for nall/array are for it to be a statically allocated
block of memory, ala array<type, size>, which is meant for RAII memory
usage.
Have to work on the specifics, eg the size as a template parameter may
be problematic. I'd like to return allocated chunks of memory (eg
file::read) in this container so that I don't have to manually free the
data anymore.

I also removed nall/moduloarray, and moved that into the SNES DSP class,
since that's the only thing that uses it.
2012-04-26 20:56:15 +10:00
Tim Allen
abe639ea91 Update to v088r01 release.
byuu says:

- GB::CPU::Core -> Processor::LR35902
- Processor::LR35902 -> jump table to switch table
- GB::LCD -> GB::PPU
- static frequency for DMG (no multiplication on clock ticks)
- GB::PPU::interface->videoRefresh() moved outside scheduler (use host
  thread)
- namespace NES  -> Famicom
- namespace SNES -> SuperFamicom
- namespace GB   -> GameBoy
- namespace GBA  -> GameBoyAdvance
- removed boot.rom writeout in GB::System
2012-04-26 20:51:13 +10:00
Tim Allen
77bb5b7891 Update to v088 release.
byuu says:

Changes to v088:
- OBJ mosaic Y fix
- Laevateinn compilation
- Remove filebrowser extra code
- Fix -march=native on Windows
- Fix purify mkdir
- GBA sound volume
- Add .gbb
- free firmware memory after file load
- Add GBA game to profile list (Yoshi's Island should work)
2012-04-24 23:17:52 +10:00
Tim Allen
4b2944c39b 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.)
2012-04-22 20:49:19 +10:00
Tim Allen
4c29e6fbab Update to v087r29 release.
byuu says:

Changelog:
- revised NES XML tag nesting
- program.rom is going to refer to PRG+CHR combined. Split is going to
  have to use different file names
- slot loader is gone (good riddance!)
- "Cartridge -> Load Game Boy Advance Cartridge ..." has become "Load ->
  Game Boy Advance ..."
- Load Satellaview Slotted Cartridge is gone. If you load an SNES
  cartridge and it sees <bsx><slot>, it asks if you want to load a BS-X
  data pack
- If you load a Sufami Turbo cartridge with <cartridge linkable="true">,
  it asks if you want to link in another Sufami Turbo cartridge
- if you try and load the same exact Sufami Turbo cartridge in both
  slots, it yells at you for being an idiot :P
2012-04-20 22:48:09 +10:00
Tim Allen
a454e9d927 Update to v087r28 release.
byuu says:

Be sure to run make install, and move required images to their appropriate system profile folders.
I still have no warnings in place if those images aren't present.

Changelog:
- OBJ mosaic should hopefully be emulated correctly now (thanks to krom
  and Cydrak for testing the hardware behavior)
- emulated dummy serial registers, fixes Sonic Advance (you may still
  need to specify 512KB FlashROM with an appropriate ID, I used
  Panaonic's)
- GBA core exits scheduler (PPU thread) and calls
  interface->videoRefresh() from main thread (not required, just nice)
- SRAM, FRAM, EEPROM and FlashROM initialized to 0xFF if it does not
  exist (probably not needed, but FlashROM likes to reset to 0xFF
  anyway)
- GBA manifest.xml for file-mode will now use "gamename.xml" instead of
  "gamename.gba.xml"
- started renaming "NES" to "Famicom" and "SNES" to "Super Famicom" in
  the GUI (may or may not change source code in the long-term)
- removed target-libsnes/
- added profile/

Profiles are the major new feature. So far we have:

    Famicom.sys/{nothing (yet?)}
    Super Famicom.sys/{ipl.rom}
    Game Boy.sys/{boot.rom}
    Game Boy Color.sys/{boot.rom}
    Game Boy Advance.sys/{bios.rom[not included]}
    Super Game Boy.sfc/{boot.rom,program.rom[not included]}
    BS-X Satellaview.sfc/{program.rom,bsx.ram,bsx.pram}
    Sufami Turbo.sfc/{program.rom}

The SGB, BSX and ST cartridges ask you to load GB, BS or ST cartridges
directly now. No slot loader for them.  So the obvious downsides: you
can't quickly pick between different SGB BIOSes, but why would you want
to? Just use SGB2/JP.  It's still possible, so I'll sacrifice a little
complexity for a rare case to make it a lot easier for the more common
case.  ST cartridges currently won't let you load the secondary slot.
BS-X Town cart is the only useful game to load with nothing in the slot,
but only barely, since games are all seeded on flash and not on PSRAM
images. We can revisit a way to boot the BIOS directly if and when we
get the satellite uplink emulated and data can be downloaded onto the
PSRAM :P BS-X slotted cartridges still require the secondary slot.

My plan for BS-X slotted cartridges is to require a manifest.xml to
specify that it has the BS-X slot present.  Otherwise, we have to load
the ROM into the SNES cartridge class, and parse its header before we
can find out if it has one. Screw that.  If it's in the XML, I can tell
before loading the ROM if I need to present you with an optional slot
loading dialog.  I will probably do something similar for Sufami Turbo.
Not all games even work with a secondary slot, so why ask you to load
a second slot for them? Let the XML request a second slot. A complete
Sufami Turbo ROM set will be trivial anyway.  Not sure how I want to do
the sub dialog yet. We want basic file loading, but we don't want it to
look like the dialog 'didn't do anything' if it pops back open
immediately again. Maybe change the background color of the dialog to
a darker gray? Tacky, but it'd give you the visual cue without the need
for some subtle text changes.
2012-04-18 23:58:04 +10:00
Tim Allen
d2241f1931 Update to v087r27 release.
byuu says:

Changelog:
- serialize processor.pc.data, not processor.pc
- call CPU processor.setMode() in ARM serialize
- serialize BIOS.mdr
- support SRAM > 32KB
- EEPROM, FlashROM serialize
- EEPROM lose nall/bitarray.hpp
- noise line feed after envelope
- space out PSR read
- ST018 needs byte reads fixed (don't align) [fixes HNMS2]
- flush sram/eeprom/flashrom to 0 on cartridge load
- APU/PPU dont sync back to CPU if syncing for state
- fixed APU sync problems in GB/GBC core that could possibly wreck save
  states as well

Quite a lot of little problems there. I believe GBA save states are
fixed now.
2012-04-17 22:16:54 +10:00
Tim Allen
0cd0dcd811 Update to v087r26 release.
byuu says:

Changelog:
- fixed FIFO[1] reset behavior (fixes audio in Sword of Mana)
- added FlashROM emulation (both sizes)
- GBA parses RAM settings from manifest.xml now
- save RAM is written to disk now
- added save state support (it's currently broken, though)
- fixed ROM/RAM access timings
- open bus should mostly work (we don't do the PC+12 stuff yet)
- emulated the undocumented memory control register (mirror IWRAM,
  disable I+EWRAM, EWRAM wait state count)
- emulated keypad interrupts
- emulated STOP (freezes video, audio, DMA and timers; only breaks on
  keypad IRQs)
- probably a lot more, it was a long night ...

Show stoppers, missing things, broken things, etc:
- ST018 is still completely broken
- GBC audio sequencer apparently needs work
- GBA audio FIFO buffer seems too quiet
- PHI / ROM prefetch needs to be emulated (no idea on how to do this,
  especially PHI)
- SOUNDBIAS 64/128/256khz modes should output at that resolution
  (really, we need to simulate PWM properly, no idea on how to do this)
- object mosaic top-left coordinates are wrong (minor, fixing will
  actually make the effect look worse)
- need to emulate PPU greenswap and color palette distortion (no idea on
  how do this)
- need GBA save type database (I would also LIKE to blacklist
  / patch-out trainers, but that's a discussion for another day.)
- some ARM ops advance the prefetch buffer, so you can read PC+12 in
  some cases
2012-04-16 22:19:39 +10:00
Tim Allen
1c18812f47 Update to v087r25 release.
byuu says:

(r24 was a point release during merging of changes.)

This release is almost entirely Cydrak's direct work:
- Added ARM::sequential() and some WAITCNT timings
- Added Bus::io(uint32 pc), intended for prefetch timing
- Added ARM::load() with data rotation (fixed Mother 3 graphics)
- Added ARM::store() for data mirroring
- LDM, STM, and instruction fetch still use read/write()
- ARM::vector() no longer unmasks FIQs
- Set THUMB shifter flags via bit(), consistent with ARM
- Replace shifter loops with conditional tests

My changes:
- fixed sprite clipping on left-edge of screen
- added first system folder, GBA.system
- sudo make install is now make install
- make install will create GBA.system for you in your home folder

Windows users, take data/GBA.system and put it in the same folder as
bsnes.exe, and give it a BIOS named bios.rom
Or place it in your home folder (%APPDATA%/bsnes)
Also note that this is highly experimental, I'll probably be changing
things a lot before release.

EDIT: I botched the cartridge timing change. Will fix in r26. It'll
still run a bit too fast for now, unfortunately.
2012-04-15 16:49:56 +10:00
Tim Allen
28885db586 Update to v087r23 release.
byuu says:

Changelog:
- fixed cascading timers and readouts (speed hit from 320fps to 240fps;
  would be 155fps with r20 timers) (fixes Spyro)
- OBJ mode 3 acts like OBJ mode 2 now (may not be correct, but nobody
  has info on it)
- added background + object vertical+horizontal mosaic in all modes
  (linear+affine+bitmap)
- object mosaic uses sprite (0,0) for start coordinates, not screen
  (0,0) (again, nobody seems to have info on it)
- BIOS cannot be read by r(15)>=0x02000000; returns last BIOS read
  instead (I can't believe games rely on this to work ... fixes SMA
  Mario Bros.)

Mosaic is what concerns me the most, I've no idea if I'm doing it
correctly. But anything is probably better than nothing, so there's
that. I don't really notice the effect in Metroid Fusion. So either it's
broken, or it's really subtle.
2012-04-14 17:26:45 +10:00
Tim Allen
d423ae0a29 Update to v087r22 release.
byuu says:

Changelog:
- fixed below pixel green channel on color blending
- added semi-transparent objects [Exophase's method]
- added full support for windows (both inputs, OBJ windows, and output, with optional color effect disable)
- EEPROM uses nall::bitarray now to be friendlier to saving memory to disk
- removed incomplete mosaic support for now (too broken, untested)
- improved sprite priority. Hopefully it's right now.

Just about everything should look great now. It took 25 days, but we
finally have the BIOS rendering correctly.

In order to do OBJ windows, I had to drop my above/below buffers
entirely. I went with the nuclear option.  There's separate layers for
all BGs and objects. I build the OBJ window table during object
rendering.  So as a result, after rendering I go back and apply windows
(and the object window that now exists.) After that, I have to do
a painful Z-buffer select of the top two most important pixels.  Since
I now know the layers, the blending enable tests are a lot nicer, at
least.  But this obviously has quite a speed hit: 390fps to 325fps for
Mr. Driller 2 title screen.

TONC says that "bad" window coordinates do really insane things. GBAtek
says it's a simple y2 < y1 || y2 > 160 ? 160 : y2; x2 < x1 || x2 > 240
? 240 : x2; I like the GBAtek version more, so I went with that. I sure
hope it's right ... but my guess is the hardware does this with
a counter that wraps around or something.  Also, say you have two OBJ
mode 2 sprites that overlap each other, but with different priorities.
The lower (more important) priority sprite has a clear pixel, but the
higher priority sprite has a set pixel. Do we set the "inside OBJ
window" flag to true here? Eg does the value OR, or does it hold the
most important sprite's pixel value? Cydrak suspects it's OR-based,
I concur from what I can see.

Mosaic, I am at a loss. I really need a lot more information in order to
implement it.  For backgrounds, does it apply to the Vcounter of the
entire screen? Or does it apply post-scroll? Or does it even apply after
every adjust in affine/bitmap modes?  I'm betting the hcounter
background mosaic starts at the leftmost edge of the screen, and repeats
previous pixels to apply the effect. Like SNES, very simple.  For
sprites, the SNES didn't have this. Does the mosaic grid start at (0,0)
of the screen, or at (0,0) of each sprite? The latter will look a lot
nicer, but be a lot more complex. Is mosaic on affine objects any
different than mosaic of linear(tiled) objects?

With that out of the way, we still have to fix the CPU memory access
timing, add the rest of the CPU penalty cycles, the memory rotation
/ alignment / extend behavior needs to be fixed, the shifter desperately
needs to be moved from loops to single shift operations, and I need to
add flash memory support.
2012-04-13 21:50:53 +10:00
Tim Allen
303a0a67d0 Update to v087r21 release.
byuu says:

Timer speedup added. Boosts Mr. Driller 2 title from 170fps to 400fps.
Other games still benefit, but not as amazingly. I don't dip below
160fps ever here.
Reverted the memory speed to 2 for everything for now, to fix
Castlevania slowdown. We obviously need to add the N/S stuff before we
do that.
Added linear BG and linear OBJ mosaic-Y. Did not add mosaic-X, or any
mosaic to the affine/bitmap modes, because I'm not sure when to apply
the compensation.
Rewrote layer stuff. It now has two layers (above and below), and it
performs the four blending modes as needed.
Didn't add semi-transparent sprites because the docs are too confusing.
Added a blur filter directly into the PPU for now. This obviously
violates my interface, but F-Zero needed it for HUD display. We can
remove it when we have an official release with a blur filter available.
The filter still doesn't warp colors like a real GBA, because I don't
know the formula.
2012-04-10 21:41:35 +10:00
Tim Allen
01b4cb9919 Update to v087r20 release.
byuu says:

Changelog:
- HALT waits 16 cycles before testing IRQs instead of 1 (probably less
  precise, but provides a massive speedup) [we will need to work on this
  later]
- MMIO regs for CPU/PPU simplified by combining array accesses
- custom VRAM/PRAM/OAM read/write functions that emulate 8->16-bit
  writes
- 16-bit PRAM data (decent speedup)
- emulated memory access speed (but don't handle non-sequential
  penalties or PPU access penalties yet) [amazingly, doesn't help speed
  at all]
- misc. code cleanups

For this WIP, FPS for Mr. Driller 2 went from 88fps to 172fps.
Compatibility should be unchanged. Timers are still an interesting
avenue to increase performance, but will be very tough to handle the
16MHz timers with eg a period of 65535 (overflow every single tick.) And
that's basically the last major speed boost we'll be able to get.
Blending and windowing is going to hurt performance, but it remains to
be seen how much.
2012-04-09 16:41:27 +10:00
Tim Allen
17b5bae86a Update to v087r19 release.
byuu says:

Changelog:
- added FIFO buffer emulation (with DMA and all that jazz) [Cydrak]
- fixed timers and vcounter assign [Cydrak]
- emulated EEPROM (you have to change size manually for 14-bit mode, we
  need a database badly now) [SMA runs now]
- removed OAM array, now decoding directly to struct Object {} [128] and
  ObjectParam {} [32] (faster this way)
- check forceblank (still doesn't remove all garble between transitions,
  though??)
- lots of other stuff

Delete your settings.cfg, or manually change frequencyGBA to 32768, or
bad things will happen (this may change back to 256KHz-4MHz later.)

15 of 16 games are fully playable now, and look and sound great.
The major missing detail right now is PPU blending support, and we
really need to optimize the hell out of the code.
2012-04-09 16:19:32 +10:00
Tim Allen
6189c93f3d Update to v087r18 release.
byuu says:

Merged Cydrak's r17c changes:
- BG affine mode added
- BG bitmap mode added
- OBJ affine mode added
- fixed IRQ bug in THUMB mode (fixed almost every game)
- timers added (broke almost every game, whee.)

Cydrak is absolutely amazingly awesome and patient. This really wouldn't
be happening without him.

Also fixed some things from my end, including greatly improved sprite
priorities, and a much better priority sorter. Mr. Driller looks a lot
better now.
2012-04-07 18:17:49 +10:00
Tim Allen
1de484262c Update to v087r17 release.
byuu says:

Emulated GBC sound plus the new extensions to it.
I am kind of surprised by how little developers utilized the GBC audio
portion.
Mr. Driller now has sound effects, and Pinobee no Daibouken has BGM.
I still have yet to emulate the GBA extra sound channels and PWM. Need
to emulate timers and DMA 2 refresh mode before I can do that.

Also, I moved both GBC and GBA audio to use length = data; if(++length
== 0); rather than length = 64 - data; if(--length == 0); so that
I could return literal values for register reads.
I thought there was a good reason we used the latter version, but
I can't hear any audible difference even in GBC games, so oh well.
Lastly, I think the pattern[++offset] in the wave channel was a bug in
the DMG/GBC only. I really, really hope it doesn't apply to the GBA,
because that will make bank selection a serious pain in the ass.
2012-04-06 14:29:50 +10:00
Tim Allen
b8d0ec29b2 Update to v087r16 release.
byuu says:

Fixed the r15 mask per Cydrak.
Added DMA support (immediate + Vblank + Hblank + HDMA) with IRQ support.
Basically only missing FIFO reload mode for the APU on channel 2.
Added background linear renderer (tilemap mode.)
Added really inefficient pixel priority selector, so that all BGs+OBJ
could be visible onscreen at the same time.

As a result of the above:
* Mr. Driller is our first fully playable game
* Bakunetsu Dodge Ball Fighters is also fully playable
* Pinobee no Daibouken is also fully playable

Most games (15 of 16 tested) are now showing *something*, many things
look really really good in fact.

Absolutely essential missing components:
- APU
- CPU timers and their interrupts
- DMA FIFO mode
- OBJ affine mode
- BG affine mode
- BG bitmap mode
- PPU windows (BG and OBJ)
- PPU mosaic
- PPU blending modes
- SRAM / EEPROM (going to rely on a database, not heuristics. Homebrew
  will require a manifest file.)
2012-04-04 09:50:40 +10:00
Tim Allen
79a47d133a Update to v087r15 release.
byuu says:

Added linear (eg non-affine) sprite rendering, 4bpp and 8bpp with hflip
and vflip. Nothing else.
You can now see the Nintendo logo and Gameboy text at the end of the BIOS.
It's a start =)
2012-04-03 10:47:28 +10:00
Tim Allen
c8bb4949b1 Update to v087r14 release.
byuu says:

Fixed aforementioned issues.

[From a previous post:
- mul was using r(d) instead of r(n) for accumulate.
- mull didn't remove c/v clear.
- APU register mask was broken, so SOUNDBIAS was reading out wrong.
- APU was only mapping 0x088 and not 0x089 as well.
- Halfword reads in CPU+PPU+APU were all reading from the low address
  each time.]

All CPU+PPU registers are now hooked up (not that they do anything.)
SOUNDBIAS for APU was hooked up, got tired of working on it for the rest :P
I recall from the GB APU that you can't just assign values for the APU
MMIO regs. They do odd reload things as well.
Also, was using MMIO read code like this:

    return (
       (flaga << 0)
    || (flagb << 1)
    || (flagc << 2)
    );

Logical or doesn't work so well with building flags :P
Bad habit from how I split multiple conditionals across several lines.

So ... r14 is basically what r13 should have been yesterday, delaying my
schedule by yet another day :(
2012-04-01 11:41:15 +10:00
Tim Allen
f587cb0dcc Update to v087r13 release.
byuu says:

Contains all of Cydrak's fixes, sans PPU.
On the PPU front, I've hooked up 100% of read and write registers.
All three DISPSTAT IRQs (Vblank, Hblank, Vcoincidence) are connected now
as well.
Super Mario Advance now runs without *appearing* to crash, although it's
hard to tell since I have no video or sound :P
ARM Wrestler is known to run, as is the BIOS.
2012-03-31 19:17:36 +11:00
Tim Allen
ea086fe33f Update to v087r12 release.
byuu says:

Enough to get through the BIOS and into cartridge ROM.
I am a bit annoyed that I was basically told that the GBA PPU wasn't
that bad. Sprites are a clusterfuck, easily worse than Mode7, docs don't
even begin to explain them in enough detail.
This is going to be fun.
2012-03-31 19:14:31 +11:00
Tim Allen
c66cc73374 Update to v087r11 release.
byuu says:

Added all of the above fixes and changes. [A lot of individual fixes for
the ARM core from Cydrak - Ed.] Also new is pipeline_decode() to fetch
data, and IME/IE/IF support, and an ARM::processor.irqline flag that
triggers IRQs at 0x18. Only Vblank is hooked up, which is what SWI 4 was
waiting on previously.
I'm sure my interrupt support is horribly broken and wrong. I was never
able to really figure out IE/IF on the Game Boy, so there's no question
this is even worse.
It's now going crazy and writing 0 to IE forever now after the Vblank
IRQ triggers.
2012-03-29 22:58:10 +11:00
Tim Allen
5a1dcf5079 Update to v087r10 release.
byuu says:

Changelog:
- fixed THUMB hi immediate reads (immediate * 4)
- cartridge is properly mirrored to 32MB (eg 12mbit repeats as
  lo8+hi4+hi4+lo8+hi4+hi4) [so it's a bit slower than a standard memcpy
  fill]
- added ARM - load/store halfword register offset
- added ARM - load/store halfword immediate offset
- added ARM - load signed halfword/byte register offset
- added ARM - load signed halfword/byte immediate offset
- added decode() function to make opcode bit testing a lot clearer
  (didn't apply it to the debugger yet)

All ARMv4M and all THUMBv4 instructions should now be implemented.
Although I'm not sure if my implementations of the new instructions are
correct.
2012-03-27 22:02:57 +11:00
Tim Allen
e16dd58184 Update to v087r09 release.
byuu says:

Split apart necdsp: core is now in processor/upd96050 (wish I had
a better name for it, but there's no family name that is maskable.)
I would like to support the uPD7720 in the core as well, just for
completeness' sake, but I'll have to modify the decoder to drop one bit
from each mode.
So ... I'll do that later. Worst part is even if I do, I won't be able
to test it :(

Added all of Cydrak's changes. I also simplified LDMIA/STMIA and
PUSH/POP by merging the outer loops.
Probably infinitesimally slower, but less code is nicer. Maybe GCC
optimization will expand it, who knows.
2012-03-26 21:13:02 +11:00
Tim Allen
395e5a5639 Update to v087r08 release.
byuu says:

Added some more ARM opcodes, hooked up MMIO. Bind it with mmio[(addr
000-3ff)] = this; inside CPU/PPU/APU, goes to read(), write().
Also moved the Hitachi HG51B core to processor/, and split it apart from
the snes/chip/hitachidsp implementation.
This one actually worked really well. Very clean split between MMIO/DMA
and the processor core. I may move a more generic DMA function inside
the core, not sure yet.

I still believe the HG51B169 to be a variant of the HG51BS family, but
given they're meant to be incredibly flexible microcontrollers, it's
possible that each variant gets its own instruction set.
So, who knows. We'll worry about it if we ever find another HG51B DSP,
I guess.

GBA BIOS is constantly reading from 04000300, but it never writes. If
I return prng()&1, I can get it to proceed until it hits a bad opcode
(stc opcode, which the GBA lacks a coprocessor so ... bad codepath.)
Without it, it just reads that register forever and keeps resetting the
system, or something ...

I guess we're going to have to try and get ARMwrestler working, because
the BIOS seems to need too much emulation code to do anything at all.
2012-03-24 18:52:36 +11:00
Tim Allen
ec939065bd Update to v087r07 release.
There was a "v087r07pre" release that I unfortunately missed.

byuu says (about v087r07pre):

Creates the bsnes/processor folder. This has a shared ARM core there
which both the GBA and ST018 inherit.
There are going to be separate decoders, and revision-specific checks,
to support the differences between v3+.
In the future, I also want to move the other processor cores here:
- GBZ80 (GB, GBC)
- 65816 (SNES CPU, SA-1)
- NEC uPD (7725, 96050, maybe 7720 just for fun)
- Hitachi HG51B169
- SuperFX
- SPC700
- 65(C?)02

Basically, the GBA/ST018 forces my hand to start coding a bit more like
a multi-system emulator.

Right now, the ST018 is broken. Hence the pre. Apparently the GBA core
being used now has some bugs. So this'll be a nice way to stress-test
the GBA core a bit before we make it to ARMwrestler.

byuu says (about v087r07):

Both snes/chip/armdsp and gba/cpu use processor/arm now.
Fixed THUMB to execute the BL prefix and suffix separately. I can now
get the GBA BIOS stuck in some kind of infinite loop. Hooray ...
I guess?
2012-03-23 21:43:39 +11:00
Tim Allen
77578cd0a4 Update to v087r06 release.
byuu says:

I believe I've implemented every THUMB instruction now, although I'm
sure there are dozens of bugs in the implementation.

It seems that the last jump taken is ending up being off-by-two. It's
probably due to not masking/adjusting PC correctly at certain points.

I don't know if any other bugs are being hit prior to this or not.
I don't implement any I/O registers yet, and the BIOS seems to be poking
at a few of them along the way, so ... who knows.
I could also be reading the log wrong, but it looks to me like there's
some PSR setting the mode flag register to 0, which is supposed to be an
undefined behavior mode ... perhaps mrs has no effect on the m/t bits,
and it just affects the i/f bits?
2012-03-22 22:47:25 +11:00
Tim Allen
04087a74b0 Update to v087r05 release.
byuu says:

Implemented all of the ARMv3 instructions, and the bx rm instruction as
well. Already hit THUMB mode right at the start of the BIOS, sigh.
Implemented the first THUMB instruction to get that rolling. Also tried
to support the S flag to LDM/STM, but not sure how successful I was.
2012-03-21 22:08:16 +11:00
Tim Allen
6701403745 Update to v087r04 release.
byuu says:

GBA stuff re-added. Only thing missing that was there before is the ARM
branch opcode.
Since we're going to be staring at it for a very long time, I added
a more interesting test video pattern.

Went from 6fps to 912fps. Amazing what being able to divide can do for
a frame rate.
2012-03-19 22:19:53 +11:00
Tim Allen
95c62f92ac Update to v087r03 release.
byuu says:

Fixing the PPU stepping increased FPS to 250. Promising, at least, since
the ARM core is still severely overclocked.
However, I reverted back to r02. This one patches gameboy/ and GameBoy::
to gb/ and GB:: and that's it.
Sorry, I just couldn't shake this bad feeling about the code. There were
some poorly hacked-together constructs. I'd rather just redo two days of
work than feel bad about the codebase for the next several years. Going
to attempt the GBA bridge again. Third time's a charm, I suppose (there
was a pre-r03 WIP I abandoned as well.)
This isn't unprecedented, GB core took a few attempts like this as well.
2012-03-19 21:27:40 +11:00
Tim Allen
0f54be93b7 Update to v087r04 release.
byuu says:

Changelog:
- gameboy/ -> gb/
- GameBoy -> GB
- basic memory map for GBA
- enough code to execute the first BIOS instruction (b 0x68)

I have the code resetting r(15) to 0 on an exception just as a test.
Since that flushes the pipeline, that means we're basically executing "b
0x68" at 8MHz, and nothing else.
... and I am getting __6 motherfucking FPS__ at 4.4GHz on an i7.

Something is seriously, horribly, unfuckingbelievably wrong here, and
I can't figure out what it is.
My *fully complete* ARM core on the ST018 is even less efficient and
runs at 21.47MHz, and yet I get 60fps even after emulating the SNES
CPU+PPU @ 10+MHz each as well.

... I'm stuck. I can't proceed until we figure out what in the holy fuck
is going on here. So ... if anyone can help, please do. If we can't fix
this, the GBA emulation is dead.
I was able to profile on Windows, and I've included that in this WIP
under out/log.txt.
But it looks normal to me. But yeah, there's NO. FUCKING. WAY. This code
should be running this slowly.
2012-03-18 23:35:53 +11:00
Tim Allen
8db134843f Update to v087r03 release.
byuu says:

I wanted to keep this a secret, but unlike other recent additions, this
will easily take several weeks, maybe months, to show anything.
Assuming I can even pull it off. Nothing technically overwhelming here,
I'm more worried about the near-impossibility of debugging the CPU.
2012-03-18 12:04:22 +11:00
Tim Allen
06e83c6154 Update to v087r02 release.
byuu says:

Changelog:
- extended USART with quit(), readable(), writable() [both emulation and
  hardware]
    - quit() returns true on hardware when Ctrl+C (SIGINT) is generated
      (breaks main loop); no effect under emulation yet (hard to
      simulate)
    - readable() returns true when data is ready to be read
      (non-blocking support for read())
    - writable() returns true when data can be written (non-blocking
      support for write()) [always true under emulation, since we have
      no buffer size limit]
2012-03-10 23:47:19 +11:00
Tim Allen
cbfbec4dc3 Update to v087r01 release.
byuu says:

Changelog:
- fixes ARM core unaligned memory reads (fixes HNMS2 AI, hopefully completely,
  we'll see though) [Cydrak]
- ARM 40000010 writes are now connected to d2 rather than the timer
- ARM bus_readbyte() removed (would love to do the same for writebyte if
  we can ... then we can drop back to bus_read + bus_write only)
- USART with IObit set acts as a regular gamepad now (don't have this
  hooked up with real hardware, but oh well, it's technically possible
  so there's that)
- OpenGL/GLX will use 30-bit when you have a 30-bit display; no need for
  config file video.depth anymore
2012-03-10 23:37:36 +11:00
2096 changed files with 183418 additions and 150635 deletions

5
.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
purify/*.o
purify/purify
purify/analyze-gba
ananke/ananke.o
ananke/libananke.so

51
ananke/Makefile Normal file
View File

@@ -0,0 +1,51 @@
include nall/Makefile
include phoenix/Makefile
path := /usr/local/lib
flags := -I. -O3 -fomit-frame-pointer
ifeq ($(arch),win32)
flags := -m32 $(flags)
endif
all:
$(cpp) $(flags) -fPIC -o ananke.o -c ananke.cpp
ifeq ($(platform),x)
$(cpp) $(flags) -shared -Wl,-soname,libananke.so.1 -o libananke.so ananke.o
else ifeq ($(platform),win)
$(cpp) $(flags) -fPIC -o phoenix.o -c phoenix/phoenix.cpp $(phoenixflags)
$(cpp) $(flags) -shared -o phoenix.dll phoenix.o $(phoenixlink)
$(cpp) $(flags) -shared -o ananke.dll ananke.o -L. -lphoenix
endif
resource: force
sourcery resource/resource.bml resource/resource.cpp resource/resource.hpp
clean:
-@$(call delete,*.o)
-@$(call delete,*.so)
install: uninstall
ifeq ($(platform),x)
if [ ! -d ~/.config/ananke ]; then mkdir ~/.config/ananke; fi
sudo cp libananke.so $(path)/libananke.so.1
sudo ln -s $(path)/libananke.so.1 $(path)/libananke.so
endif
uninstall:
ifeq ($(platform),x)
if [ -f $(path)/libananke.so ]; then sudo rm $(path)/libananke.so; fi
if [ -f $(path)/libananke.so.1 ]; then sudo rm $(path)/libananke.so.1; fi
endif
sync:
ifeq ($(shell id -un),byuu)
if [ -d ./nall ]; then rm -r ./nall; fi
if [ -d ./phoenix ]; then rm -r ./phoenix; fi
cp -r ../nall ./nall
cp -r ../phoenix ./phoenix
rm -r nall/test
rm -r phoenix/nall
rm -r phoenix/test
endif
force:

143
ananke/ananke.cpp Normal file
View File

@@ -0,0 +1,143 @@
#include <nall/nall.hpp>
#include <nall/beat/patch.hpp>
#include "heuristics/famicom.hpp"
#include "heuristics/super-famicom.hpp"
#include "heuristics/game-boy.hpp"
#include "heuristics/game-boy-advance.hpp"
using namespace nall;
#include <phoenix/phoenix.hpp>
using namespace phoenix;
namespace Database {
#include "database/super-famicom.hpp"
#include "database/sufami-turbo.hpp"
#include "database/bsx-satellaview.hpp"
};
struct Ananke {
#include "configuration.cpp"
struct Information {
string path; //path to selected file
string name; //name of selected file (inside of archive if .zip)
string archive; //pathname of archive
string manifest; //manifest from successfully applied patch
} information;
//archive.cpp
vector<uint8_t> extractROM();
vector<uint8_t> extractFile(const string &filename);
//patch.cpp
void applyBeatPatch(vector<uint8_t> &buffer);
//famicom.cpp
void copyFamicomSaves(const string &pathname);
string createFamicomHeuristic(vector<uint8_t> &buffer);
string openFamicom(vector<uint8_t> &buffer);
//super-famicom.cpp
void copySuperFamicomSaves(const string &pathname);
string createSuperFamicomDatabase(vector<uint8_t> &buffer, Markup::Node &document, const string &manifest);
string createSuperFamicomHeuristic(vector<uint8_t> &buffer);
void createSuperFamicomHeuristicFirmware(vector<uint8_t> &buffer, const string &pathname, bool firmware_appended);
string openSuperFamicom(vector<uint8_t> &buffer);
//sufami-turbo.cpp
void copySufamiTurboSaves(const string &pathname);
string createSufamiTurboDatabase(vector<uint8_t> &buffer, Markup::Node &document, const string &manifest);
string createSufamiTurboHeuristic(vector<uint8_t> &buffer);
string openSufamiTurbo(vector<uint8_t> &buffer);
//bsx-satellaview.cpp
string createBsxSatellaviewDatabase(vector<uint8_t> &buffer, Markup::Node &document, const string &manifest);
string createBsxSatellaviewHeuristic(vector<uint8_t> &buffer);
string openBsxSatellaview(vector<uint8_t> &buffer);
//game-boy.cpp
void copyGameBoySaves(const string &pathname);
string createGameBoyHeuristic(vector<uint8_t> &buffer);
string openGameBoy(vector<uint8_t> &buffer);
//game-boy-advance.cpp
void copyGameBoyAdvanceSaves(const string &pathname);
string createGameBoyAdvanceHeuristic(vector<uint8_t> &buffer);
string openGameBoyAdvance(vector<uint8_t> &buffer);
static bool supported(const string &filename);
string open(string filename = "");
};
#include "resource/resource.cpp"
#include "file-dialog.cpp"
#include "archive.cpp"
#include "patch.cpp"
#include "famicom.cpp"
#include "super-famicom.cpp"
#include "sufami-turbo.cpp"
#include "bsx-satellaview.cpp"
#include "game-boy.cpp"
#include "game-boy-advance.cpp"
FileDialog *fileDialog = nullptr;
bool Ananke::supported(const string &filename) {
string extension = nall::extension(filename);
if(extension == "fc" ) return true;
if(extension == "nes") return true;
if(extension == "sfc") return true;
if(extension == "smc") return true;
if(extension == "st" ) return true;
if(extension == "bs" ) return true;
if(extension == "gb" ) return true;
if(extension == "gbc") return true;
if(extension == "gba") return true;
if(extension == "zip") return true;
return false;
}
string Ananke::open(string filename) {
if(filename.empty()) {
if(!fileDialog) fileDialog = new FileDialog;
fileDialog->setPath(config.path);
filename = fileDialog->open();
}
if(filename.empty()) return "";
information.path = dir(filename);
information.name = notdir(filename);
config.path = information.path; //remember last used directory
vector<uint8_t> buffer;
if(filename.endswith(".zip")) {
information.archive = filename;
buffer = extractROM();
} else {
buffer = file::read(filename);
}
if(buffer.size() == 0) return ""; //failed to read file
applyBeatPatch(buffer);
if(information.name.endswith(".fc") || information.name.endswith(".nes")) return openFamicom(buffer);
if(information.name.endswith(".sfc") || information.name.endswith(".smc")) return openSuperFamicom(buffer);
if(information.name.endswith(".st")) return openSufamiTurbo(buffer);
if(information.name.endswith(".bs")) return openBsxSatellaview(buffer);
if(information.name.endswith(".gb") || information.name.endswith(".gbc")) return openGameBoy(buffer);
if(information.name.endswith(".gba")) return openGameBoyAdvance(buffer);
return "";
}
extern "C" string ananke_browse(const string &filename) {
Ananke ananke;
return ananke.open();
}
extern "C" string ananke_open(const string &filename) {
Ananke ananke;
return ananke.open(filename);
}

29
ananke/archive.cpp Normal file
View File

@@ -0,0 +1,29 @@
vector<uint8_t> Ananke::extractROM() {
unzip archive;
if(archive.open(information.archive)) {
for(auto &file : archive.file) {
if(
file.name.endswith(".fc") || file.name.endswith(".nes")
|| file.name.endswith(".sfc") || file.name.endswith(".smc")
|| file.name.endswith(".gb") || file.name.endswith(".gbc")
|| file.name.endswith(".gba")
) {
information.name = notdir(file.name);
return archive.extract(file);
}
}
}
return vector<uint8_t>();
}
vector<uint8_t> Ananke::extractFile(const string &filename) {
unzip archive;
if(archive.open(information.archive)) {
for(auto &file : archive.file) {
if(notdir(file.name) == filename) {
return archive.extract(file);
}
}
}
return vector<uint8_t>();
}

View File

@@ -0,0 +1,60 @@
string Ananke::createBsxSatellaviewDatabase(vector<uint8_t> &buffer, Markup::Node &document, const string &manifest) {
string pathname = {
userpath(), "Emulation/BS-X Satellaview/",
document["release/information/name"].text(),
" (", document["release/information/region"].text(), ")",
" (", document["release/information/revision"].text(), ")",
".bs/"
};
directory::create(pathname);
//strip "release" root node from database entry (since a single game manifest isn't part of a database)
string markup = manifest;
markup.replace("\n ", "\n");
markup.replace("information", "\ninformation");
markup.ltrim<1>("release\n");
file::write({pathname, "manifest.bml"}, markup);
file::write({pathname, "program.rom"}, buffer);
return "";
}
string Ananke::createBsxSatellaviewHeuristic(vector<uint8_t> &buffer) {
string pathname = {
userpath(), "Emulation/BS-X Satellaview/",
nall::basename(information.name),
" (!).bs/"
};
directory::create(pathname);
file::write({pathname, "manifest.bml"}, {
"cartridge\n"
" rom name=program.rom size=0x", hex(buffer.size()), " type=FlashROM\n",
"\n",
"information\n",
" title: ", nall::basename(information.name), "\n"
});
file::write({pathname, "program.rom"}, buffer);
return "";
}
string Ananke::openBsxSatellaview(vector<uint8_t> &buffer) {
string sha256 = nall::sha256(buffer.data(), buffer.size());
string databaseText = string::read({configpath(), "ananke/database/BS-X Satellaview.bml"}).strip();
if(databaseText.empty()) databaseText = string{Database::BsxSatellaview}.strip();
lstring databaseItem = databaseText.split("\n\n");
for(auto &item : databaseItem) {
item.append("\n");
auto document = Markup::Document(item);
if(document["release/information/sha256"].text() == sha256) {
return createBsxSatellaviewDatabase(buffer, document, item);
}
}
return createBsxSatellaviewHeuristic(buffer);
}

13
ananke/configuration.cpp Normal file
View File

@@ -0,0 +1,13 @@
struct Configuration : configuration {
string path;
Configuration() {
append(path = userpath(), "Path");
directory::create({configpath(), "ananke/"});
load({configpath(), "ananke/settings.cfg"});
}
~Configuration() {
save({configpath(), "ananke/settings.cfg"});
}
} config;

View File

@@ -0,0 +1,19 @@
string BsxSatellaview = R"(
database revision=2013-01-12
release
cartridge
rom name=program.rom size=0x80000 type=MaskROM
information
title:
name: Same Game - Character Cassette
region: JP
revision: 1.0
board: BSMC-CR-01
serial: BSMC-ZS5J-JPN
sha256: 80c34b50817d58820bc8c88d2d9fa462550b4a76372e19c6467cbfbc8cf5d9ef
configuration
rom name=program.rom size=0x80000 type=MaskROM
)";

View File

@@ -0,0 +1,162 @@
string SufamiTurbo = R"(
database revision=2013-01-12
release
cartridge linkable
rom name=program.rom size=0x80000
ram name=save.ram size=0x800
information
title: SDウルトラバトル
name: SD Ultra Battle - Ultraman Densetsu
region: JP
revision: 1.0
serial: SFT-0101-JPN
sha256: 2bb55214fb668ca603d7b944b14f105dfb10b987a8902d420fe4ae1cb69c1d4a
configuration
rom name=program.rom size=0x80000
ram name=save.ram size=0x800
linkable
release
cartridge linkable
rom name=program.rom size=0x80000
ram name=save.ram size=0x800
information
title: SDウルトラバトル
name: SD Ultra Battle - Seven Densetsu
region: JP
revision: 1.0
serial: SFT-0102-JPN
sha256: 2fec5f2bc7dee010af10569a3d2bc18715a79a126940800c3eade5abbd625e3f
configuration
rom name=program.rom size=0x80000
ram name=save.ram size=0x800
linkable
release
cartridge linkable
rom name=program.rom size=0x80000
ram name=save.ram size=0x800
information
title:
name: Poi Poi Ninja World
region: JP
revision: 1.0
serial: SFT-0103-JPN
sha256: 602b20b788640f5743487108a10f3f77bca5ce2d24208b25b1ca498a96eb0d69
configuration
rom name=program.rom size=0x80000
ram name=save.ram size=0x800
linkable
release
cartridge linkable
rom name=program.rom size=0x80000
ram name=save.ram size=0x2000
information
title: SDガンダムジェネレーション
name: SD Gundam Generation - Ichinen Sensouki
region: JP
revision: 1.0
serial: SFT-0104-JPN
sha256: 3e82215bed08274874b30d461fc4a965c6bca932229da5d46d56e36f484d65eb
configuration
rom name=program.rom size=0x80000
ram name=save.ram size=0x2000
linkable
release
cartridge linkable
rom name=program.rom size=0x80000
ram name=save.ram size=0x2000
information
title: SDガンダムジェネレーション
name: SD Gundam Generation - Grips Senki
region: JP
revision: 1.0
serial: SFT-0105-JPN
sha256: 8547a08ed11fe408eac282a90ac46654bd2e5f49bda3aec8e5edf166a0a4b9af
configuration
rom name=program.rom size=0x80000
ram name=save.ram size=0x2000
linkable
release
cartridge
rom name=program.rom size=0x80000
information
title:
name: Gegege no Kitarou - Youkai Donjara
region: JP
revision: 1.0
serial: SFT-0106-JPN
sha256: d93b3a570e7cf343f680ab0768a50b77e3577f9c555007e2de3decd6bc4765c8
configuration
rom name=program.rom size=0x80000
release
cartridge linkable
rom name=program.rom size=0x80000
ram name=save.ram size=0x2000
information
title: SDガンダムジェネレーション
name: SD Gundam Generation - Axis Senki
region: JP
revision: 1.0
serial: SFT-0107-JPN
sha256: 2a9d7c9a61318861028a73ca03e32a48cff162d76cba36fbaab8690b212efe9b
configuration
rom name=program.rom size=0x80000
ram name=save.ram size=0x2000
linkable
release
cartridge linkable
rom name=program.rom size=0x80000
ram name=save.ram size=0x2000
information
title: SDガンダムジェネレーション
name: SD Gundam Generation - Babylonia Kenkoku Senki
region: JP
revision: 1.0
serial: SFT-0108-JPN
sha256: 60ac017c18f534e8cf24ca7f38e22ce92db95ea6c30b2d59d76f13c4f1c8a6e4
configuration
rom name=program.rom size=0x80000
ram name=save.ram size=0x2000
linkable
release
cartridge linkable
rom name=program.rom size=0x80000
ram name=save.ram size=0x2000
information
title: SDガンダムジェネレーション
name: SD Gundam Generation - Zanscar Senki
region: JP
revision: 1.0
serial: SFT-0110-JPN
sha256: 5951a58a91d8e397d0a237ccc2b1248e17c7312cb9cc11cbc350200a97b4e021
configuration
rom name=program.rom size=0x80000
ram name=save.ram size=0x2000
linkable
release
cartridge linkable
rom name=program.rom size=0x80000
ram name=save.ram size=0x800
information
title: SDガンダムジェネレーション
name: SD Gundam Generation - Colony Kakutouki
region: JP
revision: 1.0
serial: SFT-0111-JPN
sha256: e639b5d5d722432b6809ccc6801dc584e1a3016379f34b335ed2dfa73b1ebf69
configuration
rom name=program.rom size=0x80000
ram name=save.ram size=0x800
linkable
)";

File diff suppressed because it is too large Load Diff

32
ananke/famicom.cpp Normal file
View File

@@ -0,0 +1,32 @@
void Ananke::copyFamicomSaves(const string &pathname) {
if(!file::exists({pathname, "save.ram"})) {
if(file::exists({information.path, nall::basename(information.name), ".sav"})) {
file::copy({information.path, nall::basename(information.name), ".srm"}, {pathname, "save.ram"});
}
}
}
string Ananke::createFamicomHeuristic(vector<uint8_t> &buffer) {
string pathname = {
userpath(), "Emulation/Famicom/",
nall::basename(information.name),
" (!).fc/"
};
directory::create(pathname);
FamicomCartridge info(buffer.data(), buffer.size());
string markup = info.markup();
markup.append("\ninformation\n title: ", nall::basename(information.name), "\n");
if(!information.manifest.empty()) markup = information.manifest; //override with embedded beat manifest, if one exists
file::write({pathname, "manifest.bml"}, markup);
file::write({pathname, "program.rom"}, buffer.data() + 16, info.prgrom);
if(info.chrrom > 0) file::write({pathname, "character.rom"}, buffer.data() + 16 + info.prgrom, info.chrrom);
copyFamicomSaves(pathname);
return pathname;
}
string Ananke::openFamicom(vector<uint8_t> &buffer) {
return createFamicomHeuristic(buffer);
}

125
ananke/file-dialog.cpp Normal file
View File

@@ -0,0 +1,125 @@
struct FileDialog : Window {
VerticalLayout layout;
HorizontalLayout pathLayout;
LineEdit pathEdit;
Button homeButton;
Button upButton;
ListView fileList;
HorizontalLayout controlLayout;
Label filterLabel;
Button openButton;
string open() {
setModal();
setVisible();
fileList.setFocused();
filename = "";
bool backspace = false;
dialogActive = true;
while(dialogActive) {
OS::processEvents();
if(Keyboard::pressed(Keyboard::Scancode::Escape)) onClose();
if(Keyboard::pressed(Keyboard::Scancode::Backspace)) {
if(backspace == false) {
backspace = true;
if(fileList.focused()) upButton.onActivate();
}
} else {
backspace = false;
}
usleep(20 * 1000);
}
return filename;
}
void setPath(const string &path) {
pathname = string{path}.transform("\\", "/");
if(pathname.empty()) pathname = userpath();
if(pathname.endswith("/") == false) pathname.append("/");
pathEdit.setText(pathname);
fileList.reset();
filenameList.reset();
lstring folders = directory::ifolders(pathname);
for(auto &folder : folders) {
fileList.append(string{folder}.rtrim<1>("/"));
fileList.setImage(filenameList.size(), 0, {resource::folder, sizeof resource::folder});
filenameList.append({pathname, folder});
}
lstring files = directory::ifiles(pathname);
for(auto &file : files) {
if(Ananke::supported(file) == false) continue; //ignore unsupported extensions
fileList.append(file);
if(extension(file) == "zip") {
fileList.setImage(filenameList.size(), 0, {resource::archive, sizeof resource::archive});
} else {
fileList.setImage(filenameList.size(), 0, {resource::file, sizeof resource::file});
}
filenameList.append({pathname, file});
}
fileList.setSelection(0);
fileList.setSelected();
fileList.setFocused();
}
FileDialog() {
setFrameGeometry({64, 64, 480, 600});
setTitle("Load Image");
layout.setMargin(5);
homeButton.setImage({resource::home, sizeof resource::home});
upButton.setImage({resource::up, sizeof resource::up});
filterLabel.setText("Filter: *.fc, *.sfc, *.st, *.bs, *.gb, *.gbc, *.gba, *.nes, *.smc, *.zip");
openButton.setText("Open");
append(layout);
layout.append(pathLayout, {~0, 0}, 5);
pathLayout.append(pathEdit, {~0, 0}, 5);
pathLayout.append(homeButton, {28, 28}, 5);
pathLayout.append(upButton, {28, 28});
layout.append(fileList, {~0, ~0}, 5);
layout.append(controlLayout, {~0, 0});
controlLayout.append(filterLabel, {~0, 0}, 5);
controlLayout.append(openButton, {80, 0});
pathEdit.onActivate = [&] {
string path = pathEdit.text();
setPath(path);
};
homeButton.onActivate = [&] {
setPath(userpath());
};
upButton.onActivate = [&] {
setPath(parentdir(pathname));
};
fileList.onActivate = openButton.onActivate = [&] {
if(fileList.selected() == false) return;
string name = filenameList(fileList.selection());
if(name.empty()) return;
if(name.endswith("/")) return setPath(name);
filename = name;
onClose();
};
onClose = [&] {
dialogActive = false;
setModal(false);
setVisible(false);
};
}
private:
bool dialogActive;
string pathname;
string filename;
lstring filenameList;
};

View File

@@ -0,0 +1,37 @@
void Ananke::copyGameBoyAdvanceSaves(const string &pathname) {
if(!file::exists({pathname, "save.ram"})) {
if(file::exists({information.path, nall::basename(information.name), ".sav"})) {
file::copy({information.path, nall::basename(information.name), ".sav"}, {pathname, "save.ram"});
}
}
if(!file::exists({pathname, "rtc.ram"})) {
if(file::exists({information.path, nall::basename(information.name), ".rtc"})) {
file::copy({information.path, nall::basename(information.name), ".rtc"}, {pathname, "rtc.ram"});
}
}
}
string Ananke::createGameBoyAdvanceHeuristic(vector<uint8_t> &buffer) {
string pathname = {
userpath(), "Emulation/Game Boy Advance/",
nall::basename(information.name),
" (!).gba/"
};
directory::create(pathname);
GameBoyAdvanceCartridge info(buffer.data(), buffer.size());
string markup = info.markup;
markup.append("\ninformation\n title: ", nall::basename(information.name), "\n");
if(!information.manifest.empty()) markup = information.manifest; //override with embedded beat manifest, if one exists
file::write({pathname, "manifest.bml"}, markup);
file::write({pathname, "program.rom"}, buffer);
copyGameBoyAdvanceSaves(pathname);
return pathname;
}
string Ananke::openGameBoyAdvance(vector<uint8_t> &buffer) {
return createGameBoyAdvanceHeuristic(buffer);
}

39
ananke/game-boy.cpp Normal file
View File

@@ -0,0 +1,39 @@
void Ananke::copyGameBoySaves(const string &pathname) {
if(!file::exists({pathname, "save.ram"})) {
if(file::exists({information.path, nall::basename(information.name), ".sav"})) {
file::copy({information.path, nall::basename(information.name), ".sav"}, {pathname, "save.ram"});
}
}
if(!file::exists({pathname, "rtc.ram"})) {
if(file::exists({information.path, nall::basename(information.name), ".rtc"})) {
file::copy({information.path, nall::basename(information.name), ".rtc"}, {pathname, "rtc.ram"});
}
}
}
string Ananke::createGameBoyHeuristic(vector<uint8_t> &buffer) {
GameBoyCartridge info(buffer.data(), buffer.size());
string pathname = {
userpath(),
"Emulation/Game Boy", (info.info.cgb ? " Color" : ""), "/",
nall::basename(information.name),
" (!).", (info.info.cgb ? "gbc" : "gb"), "/"
};
directory::create(pathname);
string markup = info.markup;
markup.append("\ninformation\n title: ", nall::basename(information.name), "\n");
if(!information.manifest.empty()) markup = information.manifest; //override with embedded beat manifest, if one exists
file::write({pathname, "manifest.bml"}, markup);
file::write({pathname, "program.rom"}, buffer);
copyGameBoySaves(pathname);
return pathname;
}
string Ananke::openGameBoy(vector<uint8_t> &buffer) {
return createGameBoyHeuristic(buffer);
}

View File

@@ -0,0 +1,173 @@
#ifndef NALL_EMULATION_FAMICOM_HPP
#define NALL_EMULATION_FAMICOM_HPP
#include <nall/sha256.hpp>
#include <nall/string.hpp>
namespace nall {
struct FamicomCartridge {
string markup;
inline FamicomCartridge(const uint8_t *data, unsigned size);
//private:
unsigned mapper;
unsigned mirror;
unsigned prgrom;
unsigned prgram;
unsigned chrrom;
unsigned chrram;
};
FamicomCartridge::FamicomCartridge(const uint8_t *data, unsigned size) {
markup = "";
if(size < 16) return;
if(data[0] != 'N') return;
if(data[1] != 'E') return;
if(data[2] != 'S') return;
if(data[3] != 26) return;
mapper = ((data[7] >> 4) << 4) | (data[6] >> 4);
mirror = ((data[6] & 0x08) >> 2) | (data[6] & 0x01);
prgrom = data[4] * 0x4000;
chrrom = data[5] * 0x2000;
prgram = 0u;
chrram = chrrom == 0u ? 8192u : 0u;
markup.append("cartridge\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=MMC6n");
//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");
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");
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 name=program.rom size=0x", hex(prgrom), "\n");
if(prgram) markup.append(" ram name=save.ram size=0x", hex(prgram), "\n");
markup.append(" chr\n");
if(chrrom) markup.append(" rom name=character.rom size=0x", hex(chrrom), "\n");
if(chrram) markup.append(" ram size=0x", hex(chrram), "\n");
}
}
#endif

View File

@@ -0,0 +1,63 @@
#ifndef NALL_EMULATION_GAME_BOY_ADVANCE_HPP
#define NALL_EMULATION_GAME_BOY_ADVANCE_HPP
#include <nall/sha256.hpp>
#include <nall/string.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 = "";
markup.append("cartridge\n");
markup.append(" rom name=program.rom size=0x", hex(size), "\n");
if(0);
else if(identifiers.beginswith("SRAM_V" )) markup.append(" ram name=save.ram type=SRAM size=0x8000\n");
else if(identifiers.beginswith("SRAM_F_V" )) markup.append(" ram name=save.ram type=FRAM size=0x8000\n");
else if(identifiers.beginswith("EEPROM_V" )) markup.append(" ram name=save.ram type=EEPROM size=0x0\n");
else if(identifiers.beginswith("FLASH_V" )) markup.append(" ram name=save.ram type=FlashROM size=0x10000\n");
else if(identifiers.beginswith("FLASH512_V")) markup.append(" ram name=save.ram type=FlashROM size=0x10000\n");
else if(identifiers.beginswith("FLASH1M_V" )) markup.append(" ram name=save.ram type=FlashROM size=0x20000\n");
//if(identifiers.empty() == false) markup.append(" #detected: ", identifiers, "\n");
}
}
#endif

View File

@@ -1,10 +1,12 @@
#ifndef NALL_GAMEBOY_CARTRIDGE_HPP
#define NALL_GAMEBOY_CARTRIDGE_HPP
#ifndef NALL_EMULATION_GAME_BOY_HPP
#define NALL_EMULATION_GAME_BOY_HPP
#include <nall/sha256.hpp>
#include <nall/string.hpp>
namespace nall {
class GameBoyCartridge {
public:
struct GameBoyCartridge {
string markup;
inline GameBoyCartridge(uint8_t *data, unsigned size);
@@ -18,6 +20,9 @@ public:
unsigned romsize;
unsigned ramsize;
bool cgb;
bool cgbonly;
} info;
};
@@ -48,6 +53,9 @@ GameBoyCartridge::GameBoyCartridge(uint8_t *romdata, unsigned romsize) {
memcpy(romdata, header, 0x8000);
}
info.cgb = (romdata[0x0143] & 0x80) == 0x80;
info.cgbonly = (romdata[0x0143] & 0xc0) == 0xc0;
switch(romdata[0x0147]) {
case 0x00: info.mapper = "none"; break;
case 0x01: info.mapper = "MBC1"; break;
@@ -100,15 +108,11 @@ GameBoyCartridge::GameBoyCartridge(uint8_t *romdata, unsigned romsize) {
if(info.mapper == "MBC2") info.ramsize = 512; //512 x 4-bit
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 = "";
markup.append("cartridge\n");
markup.append(" board type=", info.mapper, "\n");
markup.append(" rom name=program.rom size=0x", hex(romsize), "\n");
if(info.ramsize > 0) markup.append(" ram name=save.ram size=0x", hex(info.ramsize), "\n");
}
}

View File

@@ -0,0 +1,26 @@
#ifndef NALL_EMULATION_SATELLAVIEW_HPP
#define NALL_EMULATION_SATELLAVIEW_HPP
#include <nall/sha256.hpp>
#include <nall/string.hpp>
namespace nall {
struct SatellaviewCartridge {
string markup;
inline SatellaviewCartridge(const uint8_t *data, unsigned size);
};
SatellaviewCartridge::SatellaviewCartridge(const uint8_t *data, unsigned size) {
markup = "";
markup.append("<?xml version='1.0' encoding='UTF-8'?>\n");
markup.append("<cartridge sha256='", sha256(data, size) ,"'>\n");
markup.append(" <rom name='program.rom' size='0x", hex(size), "'/>\n");
markup.append("</cartridge>\n");
markup.transform("'", "\"");
}
}
#endif

View File

@@ -0,0 +1,33 @@
#ifndef NALL_EMULATION_SUFAMI_TURBO_HPP
#define NALL_EMULATION_SUFAMI_TURBO_HPP
#include <nall/sha256.hpp>
#include <nall/string.hpp>
namespace nall {
struct SufamiTurboCartridge {
string markup;
inline SufamiTurboCartridge(const uint8_t *data, unsigned size);
};
SufamiTurboCartridge::SufamiTurboCartridge(const uint8_t *data, unsigned size) {
markup = "";
if(size < 0x20000) return; //too small to be a valid game?
if(memcmp(data, "BANDAI SFC-ADX", 14)) return; //missing required header?
unsigned romsize = data[0x36] * 0x20000; //128KB
unsigned ramsize = data[0x37] * 0x800; //2KB
bool linkable = data[0x35] != 0x00; //TODO: unconfirmed
markup.append("<?xml version='1.0' encoding='UTF-8'?>\n");
markup.append("<cartridge linkable='", linkable, "' sha256='", sha256(data, size) ,"'>\n");
markup.append(" <rom name='program.rom' size='0x", hex(romsize), "'/>\n");
markup.append(" <ram name='save.ram' size='0x", hex(ramsize), "'/>\n");
markup.append("</cartridge>\n");
markup.transform("'", "\"");
}
}
#endif

View File

@@ -0,0 +1,814 @@
#ifndef NALL_EMULATION_SUPER_FAMICOM_HPP
#define NALL_EMULATION_SUPER_FAMICOM_HPP
#include <nall/sha256.hpp>
#include <nall/string.hpp>
namespace nall {
struct SuperFamicomCartridge {
string markup;
inline SuperFamicomCartridge(const uint8_t *data, unsigned size);
//private:
inline void read_header(const uint8_t *data, unsigned size);
inline unsigned find_header(const uint8_t *data, unsigned size);
inline unsigned score_header(const uint8_t *data, unsigned size, unsigned addr);
enum HeaderField {
CartName = 0x00,
Mapper = 0x15,
RomType = 0x16,
RomSize = 0x17,
RamSize = 0x18,
CartRegion = 0x19,
Company = 0x1a,
Version = 0x1b,
Complement = 0x1c, //inverse checksum
Checksum = 0x1e,
ResetVector = 0x3c,
};
enum Mode {
ModeNormal,
ModeBsxSlotted,
ModeBsx,
ModeSufamiTurbo,
ModeSuperGameBoy,
};
enum Type {
TypeNormal,
TypeBsxSlotted,
TypeBsxBios,
TypeBsx,
TypeSufamiTurboBios,
TypeSufamiTurbo,
TypeSuperGameBoy1Bios,
TypeSuperGameBoy2Bios,
TypeGameBoy,
TypeUnknown,
};
enum Region {
NTSC,
PAL,
};
enum MemoryMapper {
LoROM,
HiROM,
ExLoROM,
ExHiROM,
SuperFXROM,
SA1ROM,
SPC7110ROM,
BSCLoROM,
BSCHiROM,
BSXROM,
STROM,
};
enum DSP1MemoryMapper {
DSP1Unmapped,
DSP1LoROM1MB,
DSP1LoROM2MB,
DSP1HiROM,
};
bool loaded; //is a base cartridge inserted?
unsigned crc32; //crc32 of all cartridges (base+slot(s))
unsigned rom_size;
unsigned ram_size;
bool firmware_appended; //true if firmware is appended to end of ROM data
Mode mode;
Type type;
Region region;
MemoryMapper mapper;
DSP1MemoryMapper dsp1_mapper;
bool has_bsx_slot;
bool has_superfx;
bool has_sa1;
bool has_srtc;
bool has_sdd1;
bool has_spc7110;
bool has_spc7110rtc;
bool has_cx4;
bool has_dsp1;
bool has_dsp2;
bool has_dsp3;
bool has_dsp4;
bool has_obc1;
bool has_st010;
bool has_st011;
bool has_st018;
};
SuperFamicomCartridge::SuperFamicomCartridge(const uint8_t *data, unsigned size) {
firmware_appended = false;
//skip copier header
if((size & 0x7fff) == 512) data += 512, size -= 512;
markup = "";
if(size < 0x8000) return;
read_header(data, size);
markup = "";
if(type == TypeGameBoy) return;
if(type == TypeBsx) return;
if(type == TypeSufamiTurbo) return;
const char *range = (rom_size > 0x200000) || (ram_size > 32 * 1024) ? "0000-7fff" : "0000-ffff";
markup.append("cartridge region=", region == NTSC ? "NTSC" : "PAL", "\n");
if(type == TypeSuperGameBoy1Bios || type == TypeSuperGameBoy2Bios) {
markup.append(
" rom name=program.rom size=0x", hex(rom_size), "\n"
" map id=rom address=00-7f,80-ff:8000-ffff\n"
" icd2 revision=1\n"
" rom name=boot.rom size=0x100\n"
" map id=io address=00-3f,80-bf:6000-7fff\n"
);
if((rom_size & 0x7fff) == 0x100) {
firmware_appended = true;
rom_size -= 0x100;
}
}
else if(has_cx4) {
markup.append(
" hitachidsp model=HG51B169 frequency=20000000\n"
" rom id=program name=program.rom size=0x", hex(rom_size), "\n"
" rom id=data name=cx4.data.rom size=0xc00\n"
" ram id=data size=0xc00\n"
" map id=io address=00-3f,80-bf:6000-7fff\n"
" map id=rom address=00-7f,80-ff:8000-ffff mask=0x8000\n"
" map id=ram address=70-77:0000-7fff\n"
);
if((rom_size & 0x7fff) == 0xc00) {
firmware_appended = true;
rom_size -= 0xc00;
}
}
else if(has_spc7110) {
markup.append(
" spc7110\n"
" rom id=program name=program.rom size=0x100000\n"
" rom id=data name=data.rom size=0x", hex(rom_size - 0x100000), "\n"
" ram name=save.ram size=0x", hex(ram_size), "\n"
" map id=io address=00-3f,80-bf:4800-483f\n"
" map id=io address=50:0000-ffff\n"
" map id=rom address=00-3f,80-bf:8000-ffff\n"
" map id=rom address=c0-ff:0000-ffff\n"
" map id=ram address=00-3f,80-bf:6000-7fff mask=0xe000\n"
);
}
else if(has_sdd1) {
markup.append(
" sdd1\n"
" rom name=program.rom size=0x", hex(rom_size), "\n"
);
if(ram_size > 0) markup.append(
" ram name=save.ram size=0x", hex(ram_size), "\n"
);
markup.append(
" map id=io address=00-3f,80-bf:4800-4807\n"
" map id=rom address=00-3f,80-bf:8000-ffff mask=0x8000\n"
" map id=rom address=c0-ff:0000-ffff\n"
);
if(ram_size > 0) markup.append(
" map id=ram address=20-3f,a0-bf:6000-7fff mask=0xe000\n"
" map id=ram address=70-7f:0000-7fff\n"
);
}
else if(mapper == LoROM) {
markup.append(
" rom name=program.rom size=0x", hex(rom_size), "\n"
);
if(ram_size > 0) markup.append(
" ram name=save.ram size=0x", hex(ram_size), "\n"
);
markup.append(
" map id=rom address=00-7f,80-ff:8000-ffff mask=0x8000\n"
);
if(ram_size > 0) markup.append(
" map id=ram address=70-7f,f0-ff:", range, "\n"
);
}
else if(mapper == HiROM) {
markup.append(
" rom name=program.rom size=0x", hex(rom_size), "\n"
);
if(ram_size > 0) markup.append(
" ram name=save.ram size=0x", hex(ram_size), "\n"
);
markup.append(
" map id=rom address=00-3f,80-bf:8000-ffff\n"
" map id=rom address=40-7f,c0-ff:0000-ffff\n"
);
if(ram_size > 0) markup.append(
" map id=ram address=10-3f,90-bf:6000-7fff mask=0xe000\n"
);
}
else if(mapper == ExLoROM) {
markup.append(
" rom name=program.rom size=0x", hex(rom_size), "\n"
);
if(ram_size > 0) markup.append(
" ram name=save.ram size=0x", hex(ram_size), "\n"
);
markup.append(
" map id=rom address=00-3f,80-bf:8000-ffff mask=0x8000\n"
" map id=rom address=40-7f:0000-ffff\n"
);
if(ram_size > 0) markup.append(
" map id=ram address=20-3f,a0-bf:6000-7fff\n"
" map id=ram address=70-7f:0000-7fff\n"
);
}
else if(mapper == ExHiROM) {
markup.append(
" rom name=program.rom size=0x", hex(rom_size), "\n"
);
if(ram_size > 0) markup.append(
" ram name=save.ram size=0x", hex(ram_size), "\n"
);
markup.append(
" map id=rom address=00-3f:8000-ffff offset=0x400000\n"
" map id=rom address=40-7f:0000-ffff offset=0x400000\n"
" map id=rom address=80-bf:8000-ffff offset=0x000000\n"
" map id=rom address=c0-ff:0000-ffff offset=0x000000\n"
);
if(ram_size > 0) markup.append(
" map id=ram address=20-3f,a0-bf:6000-7fff mask=0xe000\n"
" map id=ram address=70-7f:", range, "\n"
);
}
else if(mapper == SuperFXROM) {
markup.append(
" superfx revision=3\n"
" rom name=program.rom size=0x", hex(rom_size), "\n"
);
if(ram_size > 0) markup.append(
" ram name=save.ram size=0x", hex(ram_size), "\n"
);
markup.append(
" map id=io address=00-3f,80-bf:3000-32ff\n"
" map id=rom address=00-3f,80-bf:8000-ffff mask=0x8000\n"
" map id=rom address=40-5f,c0-df:0000-ffff\n"
);
if(ram_size > 0) markup.append(
" map id=ram address=00-3f,80-bf:6000-7fff size=0x2000\n"
" map id=ram address=70-71,f0-f1:0000-ffff\n"
);
}
else if(mapper == SA1ROM) {
markup.append(
" sa1\n"
" rom name=program.rom size=0x", hex(rom_size), "\n"
);
if(ram_size > 0) markup.append(
" ram id=bitmap name=save.ram size=0x", hex(ram_size), "\n"
);
markup.append(
" ram id=internal size=0x800\n"
" map id=io address=00-3f,80-bf:2200-23ff\n"
" map id=rom address=00-3f,80-bf:8000-ffff\n"
" map id=rom address=c0-ff:0000-ffff\n"
);
if(ram_size > 0) markup.append(
" map id=bwram address=00-3f,80-bf:6000-7fff\n"
" map id=bwram address=40-4f:0000-ffff\n"
);
markup.append(
" map id=iram address=00-3f,80-bf:3000-37ff\n"
);
}
else if(mapper == BSCLoROM) {
markup.append(
" rom name=program.rom size=0x", hex(rom_size), "\n"
" ram name=save.ram size=0x", hex(ram_size), "\n"
" map id=rom address=00-1f:8000-ffff offset=0x000000 mask=0x8000\n"
" map id=rom address=20-3f:8000-ffff offset=0x100000 mask=0x8000\n"
" map id=rom address=80-9f:8000-ffff offset=0x200000 mask=0x8000\n"
" map id=rom address=a0-bf:8000-ffff offset=0x100000 mask=0x8000\n"
" map id=ram address=70-7f,f0-ff:0000-7fff\n"
" bsxslot\n"
" map id=rom address=c0-ef:0000-ffff\n"
);
}
else if(mapper == BSCHiROM) {
markup.append(
" rom name=program.rom size=0x", hex(rom_size), "\n"
" ram name=save.ram size=0x", hex(ram_size), "\n"
" map id=rom address=00-1f,80-9f:8000-ffff\n"
" map id=rom address=40-5f,c0-df:0000-ffff\n"
" map id=ram address=20-3f,a0-bf:6000-7fff\n"
" bsxslot\n"
" map id=rom address=20-3f,a0-bf:8000-ffff\n"
" map id=rom address=60-7f,e0-ff:0000-ffff\n"
);
}
else if(mapper == BSXROM) {
markup.append(
" bsx\n"
" rom name=program.rom size=0x", hex(rom_size), "\n"
" ram id=save name=save.ram size=0x", hex(ram_size), "\n"
" ram id=download name=bsx.ram size=0x40000\n"
" map id=io address=00-3f,80-bf:5000-5fff\n"
" map id=rom address=00-3f,80-bf:8000-ffff\n"
" map id=rom address=40-7f,c0-ff:0000-ffff\n"
" map id=ram address=20-3f:6000-7fff\n"
);
}
else if(mapper == STROM) {
markup.append(
" rom name=program.rom size=0x", hex(rom_size), "\n"
" map id=rom address='00-1f,80-9f:8000-ffff mask=0x8000\n"
" sufamiturbo\n"
" slot id=A\n"
" map id=rom address=20-3f,a0-bf:8000-ffff mask=0x8000\n"
" map id=ram address=60-63,e0-e3:8000-ffff\n"
" slot id=B\n"
" map id=rom address=40-5f,c0-df:8000-ffff mask=0x8000\n"
" map id=ram address=70-73,f0-f3:8000-ffff\n"
);
}
if(has_spc7110rtc) {
markup.append(
" epsonrtc\n"
" ram name=rtc.ram size=0x10\n"
" map id=io address=00-3f,80-bf:4840-4842\n"
);
}
if(has_srtc) {
markup.append(
" sharprtc\n"
" ram name=rtc.ram size=0x10\n"
" map id=io address=00-3f,80-bf:2800-2801\n"
);
}
if(has_obc1) {
markup.append(
" obc1\n"
" ram name=save.ram size=0x2000\n"
" map id=io address=00-3f,80-bf:6000-7fff\n"
);
}
if(has_dsp1) {
markup.append(
" necdsp model=uPD7725 frequency=8000000\n"
" rom id=program name=dsp1b.program.rom size=0x1800\n"
" rom id=data name=dsp1b.data.rom size=0x800\n"
" ram id=data size=0x200\n"
);
if(dsp1_mapper == DSP1LoROM1MB) markup.append(
" map id=io address=20-3f,a0-bf:8000-ffff select=0x4000\n"
);
if(dsp1_mapper == DSP1LoROM2MB) markup.append(
" map id=io address=60-6f,e0-ef:0000-7fff select=0x4000\n"
);
if(dsp1_mapper == DSP1HiROM) markup.append(
" map id=io address=00-1f,80-9f:6000-7fff select=0x1000\n"
);
if((size & 0x7fff) == 0x2000) {
firmware_appended = true;
rom_size -= 0x2000;
}
}
if(has_dsp2) {
markup.append(
" necdsp model=uPD7725 frequency=8000000\n"
" rom id=program name=dsp2.program.rom size=0x1800\n"
" rom id=data name=dsp2.data.rom size=0x800\n"
" ram id=data size=0x200\n"
" map id=io address=20-3f,a0-bf:8000-ffff select=0x4000\n"
);
if((size & 0x7fff) == 0x2000) {
firmware_appended = true;
rom_size -= 0x2000;
}
}
if(has_dsp3) {
markup.append(
" necdsp model=uPD7725 frequency=8000000\n"
" rom id=program name=dsp3.program.rom size=0x1800\n"
" rom id=data name=dsp3.data.rom size=0x800\n"
" ram id=data size=0x200\n"
" map id=io address=20-3f,a0-bf:8000-ffff select=0x4000\n"
);
if((size & 0x7fff) == 0x2000) {
firmware_appended = true;
rom_size -= 0x2000;
}
}
if(has_dsp4) {
markup.append(
" necdsp model=uPD7725 frequency=8000000\n"
" rom id=program name=dsp4.program.rom size=0x1800\n"
" rom id=data name=dsp4.data.rom size=0x800\n"
" ram id=data size=0x200\n"
" map address=30-3f,b0-bf:8000-ffff select=0x4000\n"
);
if((size & 0x7fff) == 0x2000) {
firmware_appended = true;
rom_size -= 0x2000;
}
}
if(has_st010) {
markup.append(
" necdsp model=uPD96050 frequency=11000000\n"
" rom id=program name=st010.program.rom size=0xc000\n"
" rom id=data name=st010.data.rom size=0x1000\n"
" ram id=data name=save.ram size=0x1000\n"
" map id=io address=60-67,e0-e7:0000-3fff select=0x0001\n"
" map id=ram address=68-6f,e8-ef:0000-7fff\n"
);
if((size & 0xffff) == 0xd000) {
firmware_appended = true;
rom_size -= 0xd000;
}
}
if(has_st011) {
markup.append(
" necdsp model=uPD96050 frequency=15000000\n"
" rom id=program name=st011.program.rom size=0xc000\n"
" rom id=data name=st011.data.rom size=0x1000\n"
" ram id=data name=save.ram size=0x1000\n"
" map id=io address=60-67,e0-e7:0000-3fff select=0x0001\n"
" map id=ram address=68-6f,e8-ef:0000-7fff\n"
);
if((size & 0xffff) == 0xd000) {
firmware_appended = true;
rom_size -= 0xd000;
}
}
if(has_st018) {
markup.append(
" armdsp frequency=21477272\n"
" rom id=program name=st018.program.rom size=0x20000\n"
" rom id=data name=st018.data.rom size=0x8000\n"
" ram name=save.ram size=0x4000\n"
" map id=io address=00-3f,80-bf:3800-38ff\n"
);
if((size & 0x3ffff) == 0x28000) {
firmware_appended = true;
rom_size -= 0x28000;
}
}
}
void SuperFamicomCartridge::read_header(const uint8_t *data, unsigned size) {
type = TypeUnknown;
mapper = LoROM;
dsp1_mapper = DSP1Unmapped;
region = NTSC;
rom_size = size;
ram_size = 0;
has_bsx_slot = false;
has_superfx = false;
has_sa1 = false;
has_srtc = false;
has_sdd1 = false;
has_spc7110 = false;
has_spc7110rtc = false;
has_cx4 = false;
has_dsp1 = false;
has_dsp2 = false;
has_dsp3 = false;
has_dsp4 = false;
has_obc1 = false;
has_st010 = false;
has_st011 = false;
has_st018 = false;
//=====================
//detect Game Boy carts
//=====================
if(size >= 0x0140) {
if(data[0x0104] == 0xce && data[0x0105] == 0xed && data[0x0106] == 0x66 && data[0x0107] == 0x66
&& data[0x0108] == 0xcc && data[0x0109] == 0x0d && data[0x010a] == 0x00 && data[0x010b] == 0x0b) {
type = TypeGameBoy;
return;
}
}
if(size < 32768) {
type = TypeUnknown;
return;
}
const unsigned index = find_header(data, size);
const uint8_t mapperid = data[index + Mapper];
const uint8_t rom_type = data[index + RomType];
const uint8_t rom_size = data[index + RomSize];
const uint8_t company = data[index + Company];
const uint8_t regionid = data[index + CartRegion] & 0x7f;
ram_size = 1024 << (data[index + RamSize] & 7);
if(ram_size == 1024) ram_size = 0; //no RAM present
if(rom_size == 0 && ram_size) ram_size = 0; //fix for Bazooka Blitzkrieg's malformed header (swapped ROM and RAM sizes)
//0, 1, 13 = NTSC; 2 - 12 = PAL
region = (regionid <= 1 || regionid >= 13) ? NTSC : PAL;
//=======================
//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) {
type = TypeBsx;
mapper = BSXROM;
region = NTSC; //BS-X only released in Japan
return;
}
}
}
}
//=========================
//detect Sufami Turbo carts
//=========================
if(!memcmp(data, "BANDAI SFC-ADX", 14)) {
if(!memcmp(data + 16, "SFC-ADX BACKUP", 14)) {
type = TypeSufamiTurboBios;
} else {
type = TypeSufamiTurbo;
}
mapper = STROM;
region = NTSC; //Sufami Turbo only released in Japan
return; //RAM size handled outside this routine
}
//==========================
//detect Super Game Boy BIOS
//==========================
if(!memcmp(data + index, "Super GAMEBOY2", 14)) {
type = TypeSuperGameBoy2Bios;
return;
}
if(!memcmp(data + index, "Super GAMEBOY", 13)) {
type = TypeSuperGameBoy1Bios;
return;
}
//=====================
//detect standard carts
//=====================
//detect presence of BS-X flash cartridge connector (reads extended header information)
if(data[index - 14] == 'Z') {
if(data[index - 11] == 'J') {
uint8_t n13 = data[index - 13];
if((n13 >= 'A' && n13 <= 'Z') || (n13 >= '0' && n13 <= '9')) {
if(company == 0x33 || (data[index - 10] == 0x00 && data[index - 4] == 0x00)) {
has_bsx_slot = true;
}
}
}
}
if(has_bsx_slot) {
if(!memcmp(data + index, "Satellaview BS-X ", 21)) {
//BS-X base cart
type = TypeBsxBios;
mapper = BSXROM;
region = NTSC; //BS-X only released in Japan
return; //RAM size handled internally by load_cart_bsx() -> BSXCart class
} else {
type = TypeBsxSlotted;
mapper = (index == 0x7fc0 ? BSCLoROM : BSCHiROM);
region = NTSC; //BS-X slotted cartridges only released in Japan
}
} else {
//standard cart
type = TypeNormal;
if(index == 0x7fc0 && size >= 0x401000) {
mapper = ExLoROM;
} else if(index == 0x7fc0 && mapperid == 0x32) {
mapper = ExLoROM;
} else if(index == 0x7fc0) {
mapper = LoROM;
} else if(index == 0xffc0) {
mapper = HiROM;
} else { //index == 0x40ffc0
mapper = ExHiROM;
}
}
if(mapperid == 0x20 && (rom_type == 0x13 || rom_type == 0x14 || rom_type == 0x15 || rom_type == 0x1a)) {
has_superfx = true;
mapper = SuperFXROM;
ram_size = 1024 << (data[index - 3] & 7);
if(ram_size == 1024) ram_size = 0;
}
if(mapperid == 0x23 && (rom_type == 0x32 || rom_type == 0x34 || rom_type == 0x35)) {
has_sa1 = true;
mapper = SA1ROM;
}
if(mapperid == 0x35 && rom_type == 0x55) {
has_srtc = true;
}
if(mapperid == 0x32 && (rom_type == 0x43 || rom_type == 0x45)) {
has_sdd1 = true;
}
if(mapperid == 0x3a && (rom_type == 0xf5 || rom_type == 0xf9)) {
has_spc7110 = true;
has_spc7110rtc = (rom_type == 0xf9);
mapper = SPC7110ROM;
}
if(mapperid == 0x20 && rom_type == 0xf3) {
has_cx4 = true;
}
if((mapperid == 0x20 || mapperid == 0x21) && rom_type == 0x03) {
has_dsp1 = true;
}
if(mapperid == 0x30 && rom_type == 0x05 && company != 0xb2) {
has_dsp1 = true;
}
if(mapperid == 0x31 && (rom_type == 0x03 || rom_type == 0x05)) {
has_dsp1 = true;
}
if(has_dsp1 == true) {
if((mapperid & 0x2f) == 0x20 && size <= 0x100000) {
dsp1_mapper = DSP1LoROM1MB;
} else if((mapperid & 0x2f) == 0x20) {
dsp1_mapper = DSP1LoROM2MB;
} else if((mapperid & 0x2f) == 0x21) {
dsp1_mapper = DSP1HiROM;
}
}
if(mapperid == 0x20 && rom_type == 0x05) {
has_dsp2 = true;
}
if(mapperid == 0x30 && rom_type == 0x05 && company == 0xb2) {
has_dsp3 = true;
}
if(mapperid == 0x30 && rom_type == 0x03) {
has_dsp4 = true;
}
if(mapperid == 0x30 && rom_type == 0x25) {
has_obc1 = true;
}
if(mapperid == 0x30 && rom_type == 0xf6 && rom_size >= 10) {
has_st010 = true;
}
if(mapperid == 0x30 && rom_type == 0xf6 && rom_size < 10) {
has_st011 = true;
}
if(mapperid == 0x30 && rom_type == 0xf5) {
has_st018 = true;
}
}
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);
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 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;
uint16_t resetvector = data[addr + ResetVector] | (data[addr + ResetVector + 1] << 8);
uint16_t checksum = data[addr + Checksum ] | (data[addr + Checksum + 1] << 8);
uint16_t complement = data[addr + Complement ] | (data[addr + Complement + 1] << 8);
uint8_t resetop = data[(addr & ~0x7fff) | (resetvector & 0x7fff)]; //first opcode executed upon reset
uint8_t 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.
if(resetvector < 0x8000) return 0;
//some images duplicate the header in multiple locations, and others have completely
//invalid header information that cannot be relied upon.
//below code will analyze the first opcode executed at the specified reset vector to
//determine the probability that this is the correct header.
//most likely opcodes
if(resetop == 0x78 //sei
|| resetop == 0x18 //clc (clc; xce)
|| resetop == 0x38 //sec (sec; xce)
|| resetop == 0x9c //stz $nnnn (stz $4200)
|| resetop == 0x4c //jmp $nnnn
|| resetop == 0x5c //jml $nnnnnn
) score += 8;
//plausible opcodes
if(resetop == 0xc2 //rep #$nn
|| resetop == 0xe2 //sep #$nn
|| resetop == 0xad //lda $nnnn
|| resetop == 0xae //ldx $nnnn
|| resetop == 0xac //ldy $nnnn
|| resetop == 0xaf //lda $nnnnnn
|| resetop == 0xa9 //lda #$nn
|| resetop == 0xa2 //ldx #$nn
|| resetop == 0xa0 //ldy #$nn
|| resetop == 0x20 //jsr $nnnn
|| resetop == 0x22 //jsl $nnnnnn
) score += 4;
//implausible opcodes
if(resetop == 0x40 //rti
|| resetop == 0x60 //rts
|| resetop == 0x6b //rtl
|| resetop == 0xcd //cmp $nnnn
|| resetop == 0xec //cpx $nnnn
|| resetop == 0xcc //cpy $nnnn
) score -= 4;
//least likely opcodes
if(resetop == 0x00 //brk #$nn
|| resetop == 0x02 //cop #$nn
|| resetop == 0xdb //stp
|| resetop == 0x42 //wdm
|| resetop == 0xff //sbc $nnnnnn,x
) score -= 8;
//at times, both the header and reset vector's first opcode will match ...
//fallback and rely on info validity in these cases to determine more likely header.
//a valid checksum is the biggest indicator of a valid header.
if((checksum + complement) == 0xffff && (checksum != 0) && (complement != 0)) score += 4;
if(addr == 0x007fc0 && mapper == 0x20) score += 2; //0x20 is usually LoROM
if(addr == 0x00ffc0 && mapper == 0x21) score += 2; //0x21 is usually HiROM
if(addr == 0x007fc0 && mapper == 0x22) score += 2; //0x22 is usually ExLoROM
if(addr == 0x40ffc0 && mapper == 0x25) score += 2; //0x25 is usually ExHiROM
if(data[addr + Company] == 0x33) score += 2; //0x33 indicates extended header
if(data[addr + RomType] < 0x08) score++;
if(data[addr + RomSize] < 0x10) score++;
if(data[addr + RamSize] < 0x08) score++;
if(data[addr + CartRegion] < 14) score++;
if(score < 0) score = 0;
return score;
}
}
#endif

7
bsnes/nall/Makefile → ananke/nall/Makefile Executable file → Normal file
View File

@@ -19,6 +19,9 @@ ifeq ($(platform),)
ifeq ($(uname),)
platform := win
delete = del $(subst /,\,$1)
else ifneq ($(findstring Windows,$(uname)),)
platform := win
delete = del $(subst /,\,$1)
else ifneq ($(findstring CYGWIN,$(uname)),)
platform := win
delete = del $(subst /,\,$1)
@@ -35,9 +38,9 @@ ifeq ($(compiler),)
ifeq ($(platform),win)
compiler := gcc
else ifeq ($(platform),osx)
compiler := gcc-mp-4.6
compiler := gcc-mp-4.7
else
compiler := gcc-4.6
compiler := gcc-4.7
endif
endif

0
bsnes/nall/algorithm.hpp → ananke/nall/algorithm.hpp Executable file → Normal file
View File

11
bsnes/nall/any.hpp → ananke/nall/any.hpp Executable file → Normal file
View File

@@ -2,7 +2,7 @@
#define NALL_ANY_HPP
#include <typeinfo>
#include <nall/type_traits.hpp>
#include <nall/traits.hpp>
namespace nall {
struct any {
@@ -26,8 +26,9 @@ namespace nall {
return *this;
}
any() : container(0) {}
template<typename T> any(const T& value_) : container(0) { operator=(value_); }
any() : container(nullptr) {}
~any() { if(container) delete container; }
template<typename T> any(const T& value_) : container(nullptr) { operator=(value_); }
private:
struct placeholder {
@@ -59,12 +60,12 @@ namespace nall {
}
template<typename T> T* any_cast(any *value) {
if(!value || value->type() != typeid(T)) return 0;
if(!value || value->type() != typeid(T)) return nullptr;
return &static_cast<any::holder<T>*>(value->container)->value;
}
template<typename T> const T* any_cast(const any *value) {
if(!value || value->type() != typeid(T)) return 0;
if(!value || value->type() != typeid(T)) return nullptr;
return &static_cast<any::holder<T>*>(value->container)->value;
}
}

2
bsnes/nall/atoi.hpp → ananke/nall/atoi.hpp Executable file → Normal file
View File

@@ -1,6 +1,8 @@
#ifndef NALL_ATOI_HPP
#define NALL_ATOI_HPP
#include <nall/stdint.hpp>
namespace nall {
//note: this header is intended to form the base for user-defined literals;

118
ananke/nall/base64.hpp Normal file
View File

@@ -0,0 +1,118 @@
#ifndef NALL_BASE64_HPP
#define NALL_BASE64_HPP
#include <nall/stdint.hpp>
#include <nall/string.hpp>
namespace nall {
struct base64 {
static bool encode(char *&output, const uint8_t* input, unsigned inlength) {
output = new char[inlength * 8 / 6 + 8]();
unsigned i = 0, o = 0;
while(i < inlength) {
switch(i % 3) {
case 0: {
output[o++] = enc(input[i] >> 2);
output[o] = enc((input[i] & 3) << 4);
break;
}
case 1: {
uint8_t prev = dec(output[o]);
output[o++] = enc(prev + (input[i] >> 4));
output[o] = enc((input[i] & 15) << 2);
break;
}
case 2: {
uint8_t prev = dec(output[o]);
output[o++] = enc(prev + (input[i] >> 6));
output[o++] = enc(input[i] & 63);
break;
}
}
i++;
}
return true;
}
static string encode(const string &data) {
char *buffer = nullptr;
encode(buffer, (const uint8_t*)(const char*)data, data.length());
string result = buffer;
delete[] buffer;
return result;
}
static bool decode(uint8_t *&output, unsigned &outlength, const char *input) {
unsigned inlength = strlen(input), infix = 0;
output = new uint8_t[inlength + 1]();
unsigned i = 0, o = 0;
while(i < inlength) {
uint8_t x = dec(input[i]);
switch(i++ & 3) {
case 0: {
output[o] = x << 2;
break;
}
case 1: {
output[o++] |= x >> 4;
output[o] = (x & 15) << 4;
break;
}
case 2: {
output[o++] |= x >> 2;
output[o] = (x & 3) << 6;
break;
}
case 3: {
output[o++] |= x;
break;
}
}
}
outlength = o;
return true;
}
static string decode(const string &data) {
uint8_t *buffer = nullptr;
unsigned size = 0;
decode(buffer, size, (const char*)data);
string result = (const char*)buffer;
delete[] buffer;
return result;
}
private:
static char enc(uint8_t n) {
//base64 for URL encodings (URL = -_, MIME = +/)
static char lookup_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
return lookup_table[n & 63];
}
static uint8_t dec(char n) {
if(n >= 'A' && n <= 'Z') return n - 'A';
if(n >= 'a' && n <= 'z') return n - 'a' + 26;
if(n >= '0' && n <= '9') return n - '0' + 52;
if(n == '-') return 62;
if(n == '_') return 63;
return 0;
}
};
}
#endif

View File

@@ -0,0 +1,84 @@
#ifndef NALL_BEAT_ARCHIVE_HPP
#define NALL_BEAT_ARCHIVE_HPP
#include <nall/beat/base.hpp>
namespace nall {
struct beatArchive : beatBase {
bool create(const string &beatname, string pathname, const string &metadata = "") {
if(fp.open(beatname, file::mode::write) == false) return false;
if(pathname.endswith("/") == false) pathname.append("/");
checksum = ~0;
writeString("BPA1");
writeNumber(metadata.length());
writeString(metadata);
lstring list;
ls(list, pathname, pathname);
for(auto &name : list) {
if(name.endswith("/")) {
name.rtrim<1>("/");
writeNumber(0 | ((name.length() - 1) << 1));
writeString(name);
} else {
file stream;
if(stream.open({pathname, name}, file::mode::read) == false) return false;
writeNumber(1 | ((name.length() - 1) << 1));
writeString(name);
unsigned size = stream.size();
writeNumber(size);
uint32_t checksum = ~0;
while(size--) {
uint8_t data = stream.read();
write(data);
checksum = crc32_adjust(checksum, data);
}
writeChecksum(~checksum);
}
}
writeChecksum(~checksum);
fp.close();
return true;
}
bool unpack(const string &beatname, string pathname) {
if(fp.open(beatname, file::mode::read) == false) return false;
if(pathname.endswith("/") == false) pathname.append("/");
checksum = ~0;
if(readString(4) != "BPA1") return false;
unsigned length = readNumber();
while(length--) read();
directory::create(pathname);
while(fp.offset() < fp.size() - 4) {
unsigned data = readNumber();
string name = readString((data >> 1) + 1);
if(name.position("\\") || name.position("../")) return false; //block path exploits
if((data & 1) == 0) {
directory::create({pathname, name});
} else {
file stream;
if(stream.open({pathname, name}, file::mode::write) == false) return false;
unsigned size = readNumber();
uint32_t checksum = ~0;
while(size--) {
uint8_t data = read();
stream.write(data);
checksum = crc32_adjust(checksum, data);
}
if(readChecksum(~checksum) == false) return false;
}
}
return readChecksum(~checksum);
}
};
}
#endif

92
ananke/nall/beat/base.hpp Normal file
View File

@@ -0,0 +1,92 @@
#ifndef NALL_BEAT_BASE_HPP
#define NALL_BEAT_BASE_HPP
namespace nall {
struct beatBase {
protected:
file fp;
uint32_t checksum;
void ls(lstring &list, const string &path, const string &basepath) {
lstring paths = directory::folders(path);
for(auto &pathname : paths) {
list.append(string{path, pathname}.ltrim<1>(basepath));
ls(list, {path, pathname}, basepath);
}
lstring files = directory::files(path);
for(auto &filename : files) {
list.append(string{path, filename}.ltrim<1>(basepath));
}
}
void write(uint8_t data) {
fp.write(data);
checksum = crc32_adjust(checksum, data);
}
void writeNumber(uint64_t data) {
while(true) {
uint64_t x = data & 0x7f;
data >>= 7;
if(data == 0) return write(0x80 | x);
write(x);
data--;
}
}
void writeString(const string &text) {
unsigned length = text.length();
for(unsigned n = 0; n < length; n++) write(text[n]);
}
void writeChecksum(uint32_t checksum) {
write(checksum >> 0);
write(checksum >> 8);
write(checksum >> 16);
write(checksum >> 24);
}
uint8_t read() {
uint8_t data = fp.read();
checksum = crc32_adjust(checksum, data);
return data;
}
uint64_t readNumber() {
uint64_t data = 0, shift = 1;
while(true) {
uint8_t x = read();
data += (x & 0x7f) * shift;
if(x & 0x80) break;
shift <<= 7;
data += shift;
}
return data;
}
string readString(unsigned length) {
string text;
text.reserve(length + 1);
for(unsigned n = 0; n < length; n++) {
text[n] = fp.read();
checksum = crc32_adjust(checksum, text[n]);
}
text[length] = 0;
return text;
}
bool readChecksum(uint32_t source) {
uint32_t checksum = 0;
checksum |= read() << 0;
checksum |= read() << 8;
checksum |= read() << 16;
checksum |= read() << 24;
return checksum == source;
}
};
}
#endif

View File

@@ -1,5 +1,5 @@
#ifndef NALL_BPS_DELTA_HPP
#define NALL_BPS_DELTA_HPP
#ifndef NALL_BEAT_DELTA_HPP
#define NALL_BEAT_DELTA_HPP
#include <nall/crc32.hpp>
#include <nall/file.hpp>
@@ -24,7 +24,7 @@ protected:
struct Node {
unsigned offset;
Node *next;
inline Node() : offset(0), next(0) {}
inline Node() : offset(0), next(nullptr) {}
inline ~Node() { if(next) delete next; }
};

View File

@@ -1,5 +1,5 @@
#ifndef NALL_BPS_LINEAR_HPP
#define NALL_BPS_LINEAR_HPP
#ifndef NALL_BEAT_LINEAR_HPP
#define NALL_BEAT_LINEAR_HPP
#include <nall/crc32.hpp>
#include <nall/file.hpp>

View File

@@ -1,5 +1,5 @@
#ifndef NALL_BPS_METADATA_HPP
#define NALL_BPS_METADATA_HPP
#ifndef NALL_BEAT_METADATA_HPP
#define NALL_BEAT_METADATA_HPP
#include <nall/crc32.hpp>
#include <nall/file.hpp>

242
ananke/nall/beat/multi.hpp Normal file
View File

@@ -0,0 +1,242 @@
#ifndef NALL_BEAT_MULTI_HPP
#define NALL_BEAT_MULTI_HPP
#include <nall/beat/patch.hpp>
#include <nall/beat/linear.hpp>
#include <nall/beat/delta.hpp>
namespace nall {
struct bpsmulti {
enum : unsigned {
CreatePath = 0,
CreateFile = 1,
ModifyFile = 2,
MirrorFile = 3,
};
enum : unsigned {
OriginSource = 0,
OriginTarget = 1,
};
bool create(const string &patchName, const string &sourcePath, const string &targetPath, bool delta = false, const string &metadata = "") {
if(fp.open()) fp.close();
fp.open(patchName, file::mode::write);
checksum = ~0;
writeString("BPM1"); //signature
writeNumber(metadata.length());
writeString(metadata);
lstring sourceList, targetList;
ls(sourceList, sourcePath, sourcePath);
ls(targetList, targetPath, targetPath);
for(auto &targetName : targetList) {
if(targetName.endswith("/")) {
targetName.rtrim<1>("/");
writeNumber(CreatePath | ((targetName.length() - 1) << 2));
writeString(targetName);
} else if(auto position = sourceList.find(targetName)) { //if sourceName == targetName
file sp, dp;
sp.open({sourcePath, targetName}, file::mode::read);
dp.open({targetPath, targetName}, file::mode::read);
bool identical = sp.size() == dp.size();
uint32_t cksum = ~0;
for(unsigned n = 0; n < sp.size(); n++) {
uint8_t byte = sp.read();
if(identical && byte != dp.read()) identical = false;
cksum = crc32_adjust(cksum, byte);
}
if(identical) {
writeNumber(MirrorFile | ((targetName.length() - 1) << 2));
writeString(targetName);
writeNumber(OriginSource);
writeChecksum(~cksum);
} else {
writeNumber(ModifyFile | ((targetName.length() - 1) << 2));
writeString(targetName);
writeNumber(OriginSource);
if(delta == false) {
bpslinear patch;
patch.source({sourcePath, targetName});
patch.target({targetPath, targetName});
patch.create({temppath(), "temp.bps"});
} else {
bpsdelta patch;
patch.source({sourcePath, targetName});
patch.target({targetPath, targetName});
patch.create({temppath(), "temp.bps"});
}
auto buffer = file::read({temppath(), "temp.bps"});
writeNumber(buffer.size());
for(auto &byte : buffer) write(byte);
}
} else {
writeNumber(CreateFile | ((targetName.length() - 1) << 2));
writeString(targetName);
auto buffer = file::read({targetPath, targetName});
writeNumber(buffer.size());
for(auto &byte : buffer) write(byte);
writeChecksum(crc32_calculate(buffer.data(), buffer.size()));
}
}
//checksum
writeChecksum(~checksum);
fp.close();
return true;
}
bool apply(const string &patchName, const string &sourcePath, const string &targetPath) {
directory::remove(targetPath); //start with a clean directory
directory::create(targetPath);
if(fp.open()) fp.close();
fp.open(patchName, file::mode::read);
checksum = ~0;
if(readString(4) != "BPM1") return false;
auto metadataLength = readNumber();
while(metadataLength--) read();
while(fp.offset() < fp.size() - 4) {
auto encoding = readNumber();
unsigned action = encoding & 3;
unsigned targetLength = (encoding >> 2) + 1;
string targetName = readString(targetLength);
if(action == CreatePath) {
directory::create({targetPath, targetName, "/"});
} else if(action == CreateFile) {
file fp;
fp.open({targetPath, targetName}, file::mode::write);
auto fileSize = readNumber();
while(fileSize--) fp.write(read());
uint32_t cksum = readChecksum();
} else if(action == ModifyFile) {
auto encoding = readNumber();
string originPath = encoding & 1 ? targetPath : sourcePath;
string sourceName = (encoding >> 1) == 0 ? targetName : readString(encoding >> 1);
auto patchSize = readNumber();
vector<uint8_t> buffer;
buffer.resize(patchSize);
for(unsigned n = 0; n < patchSize; n++) buffer[n] = read();
bpspatch patch;
patch.modify(buffer.data(), buffer.size());
patch.source({originPath, sourceName});
patch.target({targetPath, targetName});
if(patch.apply() != bpspatch::result::success) return false;
} else if(action == MirrorFile) {
auto encoding = readNumber();
string originPath = encoding & 1 ? targetPath : sourcePath;
string sourceName = (encoding >> 1) == 0 ? targetName : readString(encoding >> 1);
file::copy({originPath, sourceName}, {targetPath, targetName});
uint32_t cksum = readChecksum();
}
}
uint32_t cksum = ~checksum;
if(read() != (uint8_t)(cksum >> 0)) return false;
if(read() != (uint8_t)(cksum >> 8)) return false;
if(read() != (uint8_t)(cksum >> 16)) return false;
if(read() != (uint8_t)(cksum >> 24)) return false;
fp.close();
return true;
}
protected:
file fp;
uint32_t checksum;
//create() functions
void ls(lstring &list, const string &path, const string &basepath) {
lstring paths = directory::folders(path);
for(auto &pathname : paths) {
list.append(string{path, pathname}.ltrim<1>(basepath));
ls(list, {path, pathname}, basepath);
}
lstring files = directory::files(path);
for(auto &filename : files) {
list.append(string{path, filename}.ltrim<1>(basepath));
}
}
void write(uint8_t data) {
fp.write(data);
checksum = crc32_adjust(checksum, data);
}
void writeNumber(uint64_t data) {
while(true) {
uint64_t x = data & 0x7f;
data >>= 7;
if(data == 0) {
write(0x80 | x);
break;
}
write(x);
data--;
}
}
void writeString(const string &text) {
unsigned length = text.length();
for(unsigned n = 0; n < length; n++) write(text[n]);
}
void writeChecksum(uint32_t cksum) {
write(cksum >> 0);
write(cksum >> 8);
write(cksum >> 16);
write(cksum >> 24);
}
//apply() functions
uint8_t read() {
uint8_t data = fp.read();
checksum = crc32_adjust(checksum, data);
return data;
}
uint64_t readNumber() {
uint64_t data = 0, shift = 1;
while(true) {
uint8_t x = read();
data += (x & 0x7f) * shift;
if(x & 0x80) break;
shift <<= 7;
data += shift;
}
return data;
}
string readString(unsigned length) {
string text;
text.reserve(length + 1);
for(unsigned n = 0; n < length; n++) text[n] = read();
text[length] = 0;
return text;
}
uint32_t readChecksum() {
uint32_t checksum = 0;
checksum |= read() << 0;
checksum |= read() << 8;
checksum |= read() << 16;
checksum |= read() << 24;
return checksum;
}
};
}
#endif

View File

@@ -1,5 +1,5 @@
#ifndef NALL_BPS_PATCH_HPP
#define NALL_BPS_PATCH_HPP
#ifndef NALL_BEAT_PATCH_HPP
#define NALL_BEAT_PATCH_HPP
#include <nall/crc32.hpp>
#include <nall/file.hpp>

28
bsnes/nall/bit.hpp → ananke/nall/bit.hpp Executable file → Normal file
View File

@@ -1,32 +1,52 @@
#ifndef NALL_BIT_HPP
#define NALL_BIT_HPP
#include <nall/stdint.hpp>
namespace nall {
template<unsigned bits>
constexpr inline uintmax_t uclamp(const uintmax_t x) {
inline uintmax_t uclamp(const uintmax_t x) {
enum : uintmax_t { b = 1ull << (bits - 1), y = b * 2 - 1 };
return y + ((x - y) & -(x < y)); //min(x, y);
}
template<unsigned bits>
constexpr inline uintmax_t uclip(const uintmax_t x) {
inline uintmax_t uclip(const uintmax_t x) {
enum : uintmax_t { b = 1ull << (bits - 1), m = b * 2 - 1 };
return (x & m);
}
template<unsigned bits>
constexpr inline intmax_t sclamp(const intmax_t x) {
inline intmax_t sclamp(const intmax_t x) {
enum : intmax_t { b = 1ull << (bits - 1), m = b - 1 };
return (x > m) ? m : (x < -b) ? -b : x;
}
template<unsigned bits>
constexpr inline intmax_t sclip(const intmax_t x) {
inline intmax_t sclip(const intmax_t x) {
enum : uintmax_t { b = 1ull << (bits - 1), m = b * 2 - 1 };
return ((x & m) ^ b) - b;
}
namespace bit {
constexpr inline uintmax_t mask(const char *s, uintmax_t sum = 0) {
return (
*s == '0' || *s == '1' ? mask(s + 1, (sum << 1) | 1) :
*s == ' ' || *s == '_' ? mask(s + 1, sum) :
*s ? mask(s + 1, sum << 1) :
sum
);
}
constexpr inline uintmax_t test(const char *s, uintmax_t sum = 0) {
return (
*s == '0' || *s == '1' ? test(s + 1, (sum << 1) | (*s - '0')) :
*s == ' ' || *s == '_' ? test(s + 1, sum) :
*s ? test(s + 1, sum << 1) :
sum
);
}
//lowest(0b1110) == 0b0010
constexpr inline uintmax_t lowest(const uintmax_t x) {
return x & -x;

0
bsnes/nall/bmp.hpp → ananke/nall/bmp.hpp Executable file → Normal file
View File

View File

0
bsnes/nall/config.hpp → ananke/nall/config.hpp Executable file → Normal file
View File

25
ananke/nall/crc16.hpp Normal file
View File

@@ -0,0 +1,25 @@
#ifndef NALL_CRC16_HPP
#define NALL_CRC16_HPP
#include <nall/stdint.hpp>
namespace nall {
inline uint16_t crc16_adjust(uint16_t crc16, uint8_t data) {
for(unsigned n = 0; n < 8; n++) {
if((crc16 & 1) ^ (data & 1)) crc16 = (crc16 >> 1) ^ 0x8408;
else crc16 >>= 1;
data >>= 1;
}
return crc16;
}
inline uint16_t crc16_calculate(const uint8_t *data, unsigned length) {
uint16_t crc16 = ~0;
for(unsigned n = 0; n < length; n++) {
crc16 = crc16_adjust(crc16, data[n]);
}
return ~crc16;
}
}
#endif

0
bsnes/nall/crc32.hpp → ananke/nall/crc32.hpp Executable file → Normal file
View File

224
ananke/nall/directory.hpp Normal file
View File

@@ -0,0 +1,224 @@
#ifndef NALL_DIRECTORY_HPP
#define NALL_DIRECTORY_HPP
#include <nall/file.hpp>
#include <nall/intrinsics.hpp>
#include <nall/sort.hpp>
#include <nall/string.hpp>
#include <nall/vector.hpp>
#if defined(PLATFORM_WINDOWS)
#include <nall/windows/utf8.hpp>
#else
#include <dirent.h>
#include <stdio.h>
#include <sys/types.h>
#endif
namespace nall {
struct directory {
static bool create(const string &pathname, unsigned permissions = 0755); //recursive
static bool remove(const string &pathname); //recursive
static bool exists(const string &pathname);
static lstring folders(const string &pathname, const string &pattern = "*") {
lstring folders = directory::ufolders(pathname, pattern);
folders.sort();
return folders;
}
static lstring files(const string &pathname, const string &pattern = "*") {
lstring files = directory::ufiles(pathname, pattern);
files.sort();
return files;
}
static lstring contents(const string &pathname, const string &pattern = "*") {
lstring folders = directory::ufolders(pathname); //pattern search of contents should only filter files
lstring files = directory::ufiles(pathname, pattern);
folders.sort();
files.sort();
for(auto &file : files) folders.append(file);
return folders;
}
static lstring ifolders(const string &pathname, const string &pattern = "*") {
lstring folders = ufolders(pathname, pattern);
folders.isort();
return folders;
}
static lstring ifiles(const string &pathname, const string &pattern = "*") {
lstring files = ufiles(pathname, pattern);
files.isort();
return files;
}
static lstring icontents(const string &pathname, const string &pattern = "*") {
lstring folders = directory::ufolders(pathname); //pattern search of contents should only filter files
lstring files = directory::ufiles(pathname, pattern);
folders.isort();
files.isort();
for(auto &file : files) folders.append(file);
return folders;
}
private:
//internal functions; these return unsorted lists
static lstring ufolders(const string &pathname, const string &pattern = "*");
static lstring ufiles(const string &pathname, const string &pattern = "*");
};
#if defined(PLATFORM_WINDOWS)
inline bool directory::create(const string &pathname, unsigned permissions) {
string path;
lstring list = string{pathname}.transform("\\", "/").rtrim<1>("/").split("/");
bool result = true;
for(auto &part : list) {
path.append(part, "/");
result &= (_wmkdir(utf16_t(path)) == 0);
}
return result;
}
inline bool directory::remove(const string &pathname) {
lstring list = directory::contents(pathname);
for(auto &name : list) {
if(name.endswith("/")) directory::remove({pathname, name});
else file::remove({pathname, name});
}
return _wrmdir(utf16_t(pathname)) == 0;
}
inline bool directory::exists(const string &pathname) {
string name = pathname;
name.trim<1>("\"");
DWORD result = GetFileAttributes(utf16_t(name));
if(result == INVALID_FILE_ATTRIBUTES) return false;
return (result & FILE_ATTRIBUTE_DIRECTORY);
}
inline lstring directory::ufolders(const string &pathname, const string &pattern) {
lstring list;
string path = pathname;
path.transform("/", "\\");
if(!strend(path, "\\")) path.append("\\");
path.append("*");
HANDLE handle;
WIN32_FIND_DATA data;
handle = FindFirstFile(utf16_t(path), &data);
if(handle != INVALID_HANDLE_VALUE) {
if(wcscmp(data.cFileName, L".") && wcscmp(data.cFileName, L"..")) {
if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
string name = (const char*)utf8_t(data.cFileName);
if(wildcard(name, pattern)) list.append(name);
}
}
while(FindNextFile(handle, &data) != false) {
if(wcscmp(data.cFileName, L".") && wcscmp(data.cFileName, L"..")) {
if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
string name = (const char*)utf8_t(data.cFileName);
if(wildcard(name, pattern)) list.append(name);
}
}
}
FindClose(handle);
}
for(auto &name : list) name.append("/"); //must append after sorting
return list;
}
inline lstring directory::ufiles(const string &pathname, const string &pattern) {
lstring list;
string path = pathname;
path.transform("/", "\\");
if(!strend(path, "\\")) path.append("\\");
path.append("*");
HANDLE handle;
WIN32_FIND_DATA data;
handle = FindFirstFile(utf16_t(path), &data);
if(handle != INVALID_HANDLE_VALUE) {
if((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
string name = (const char*)utf8_t(data.cFileName);
if(wildcard(name, pattern)) list.append(name);
}
while(FindNextFile(handle, &data) != false) {
if((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
string name = (const char*)utf8_t(data.cFileName);
if(wildcard(name, pattern)) list.append(name);
}
}
FindClose(handle);
}
return list;
}
#else
inline bool directory::create(const string &pathname, unsigned permissions) {
string path;
lstring list = string{pathname}.rtrim<1>("/").split("/");
bool result = true;
for(auto &part : list) {
path.append(part, "/");
result &= (mkdir(path, permissions) == 0);
}
return result;
}
inline bool directory::remove(const string &pathname) {
lstring list = directory::contents(pathname);
for(auto &name : list) {
if(name.endswith("/")) directory::remove({pathname, name});
else file::remove({pathname, name});
}
return rmdir(pathname) == 0;
}
inline bool directory::exists(const string &pathname) {
DIR *dp = opendir(pathname);
if(!dp) return false;
closedir(dp);
return true;
}
inline lstring directory::ufolders(const string &pathname, const string &pattern) {
lstring list;
DIR *dp;
struct dirent *ep;
dp = opendir(pathname);
if(dp) {
while(ep = readdir(dp)) {
if(!strcmp(ep->d_name, ".")) continue;
if(!strcmp(ep->d_name, "..")) continue;
if(ep->d_type & DT_DIR) {
if(wildcard(ep->d_name, pattern)) list.append(ep->d_name);
}
}
closedir(dp);
}
for(auto &name : list) name.append("/"); //must append after sorting
return list;
}
inline lstring directory::ufiles(const string &pathname, const string &pattern) {
lstring list;
DIR *dp;
struct dirent *ep;
dp = opendir(pathname);
if(dp) {
while(ep = readdir(dp)) {
if(!strcmp(ep->d_name, ".")) continue;
if(!strcmp(ep->d_name, "..")) continue;
if((ep->d_type & DT_DIR) == 0) {
if(wildcard(ep->d_name, pattern)) list.append(ep->d_name);
}
}
closedir(dp);
}
return list;
}
#endif
}
#endif

0
bsnes/nall/dl.hpp → ananke/nall/dl.hpp Executable file → Normal file
View File

0
bsnes/nall/dsp.hpp → ananke/nall/dsp.hpp Executable file → Normal file
View File

View File

0
bsnes/nall/dsp/core.hpp → ananke/nall/dsp/core.hpp Executable file → Normal file
View File

View File

View File

View File

@@ -1,33 +1,47 @@
#ifndef NALL_SNES_USART_HPP
#define NALL_SNES_USART_HPP
#ifndef NALL_EMULATION_SUPER_FAMICOM_USART_HPP
#define NALL_EMULATION_SUPER_FAMICOM_USART_HPP
#include <nall/platform.hpp>
#include <nall/function.hpp>
#include <nall/serial.hpp>
#include <nall/stdint.hpp>
#include <signal.h>
#include <sys/time.h>
#include <sys/resource.h>
#define usartproc dllexport
static nall::function<bool ()> usart_quit;
static nall::function<void (unsigned milliseconds)> usart_usleep;
static nall::function<bool ()> usart_readable;
static nall::function<uint8_t ()> usart_read;
static nall::function<bool ()> usart_writable;
static nall::function<void (uint8_t data)> usart_write;
extern "C" usartproc void usart_init(
nall::function<bool ()> quit,
nall::function<void (unsigned milliseconds)> usleep,
nall::function<bool ()> readable,
nall::function<uint8_t ()> read,
nall::function<bool ()> writable,
nall::function<void (uint8_t data)> write
) {
usart_quit = quit;
usart_usleep = usleep;
usart_readable = readable;
usart_read = read;
usart_writable = writable;
usart_write = write;
}
extern "C" usartproc void usart_main();
extern "C" usartproc void usart_main(int, char**);
//
static nall::serial usart;
static bool usart_is_virtual = true;
static bool usart_sigint = false;
static bool usart_virtual() {
return usart_is_virtual;
@@ -35,10 +49,18 @@ static bool usart_virtual() {
//
static bool usarthw_quit() {
return usart_sigint;
}
static void usarthw_usleep(unsigned milliseconds) {
usleep(milliseconds);
}
static bool usarthw_readable() {
return usart.readable();
}
static uint8_t usarthw_read() {
while(true) {
uint8_t buffer[1];
@@ -47,23 +69,34 @@ static uint8_t usarthw_read() {
}
}
static bool usarthw_writable() {
return usart.writable();
}
static void usarthw_write(uint8_t data) {
uint8_t buffer[1] = { data };
usart.write((uint8_t*)&buffer, 1);
}
static void sigint(int) {
signal(SIGINT, SIG_DFL);
usart_sigint = true;
}
int main(int argc, char **argv) {
bool result = false;
if(argc == 1) result = usart.open("/dev/ttyACM0", 57600, true);
if(argc == 2) result = usart.open(argv[1], 57600, true);
if(result == false) {
setpriority(PRIO_PROCESS, 0, -20); //requires superuser privileges; otherwise priority = +0
signal(SIGINT, sigint);
if(usart.open("/dev/ttyACM0", 57600, true) == false) {
printf("error: unable to open USART hardware device\n");
return 0;
}
usart_is_virtual = false;
usart_init(usarthw_usleep, usarthw_read, usarthw_write);
usart_main();
usart_init(usarthw_quit, usarthw_usleep, usarthw_readable, usarthw_read, usarthw_writable, usarthw_write);
usart_main(argc, argv);
usart.close();
return 0;
}

0
bsnes/nall/endian.hpp → ananke/nall/endian.hpp Executable file → Normal file
View File

87
snespurify/nall/file.hpp → ananke/nall/file.hpp Executable file → Normal file
View File

@@ -6,6 +6,7 @@
#include <nall/string.hpp>
#include <nall/utility.hpp>
#include <nall/windows/utf8.hpp>
#include <nall/stream/memory.hpp>
namespace nall {
inline FILE* fopen_utf8(const string &utf8_filename, const char *mode) {
@@ -16,24 +17,77 @@ namespace nall {
#endif
}
class file {
public:
enum class mode : unsigned { read, write, readwrite, writeread };
struct file {
enum class mode : unsigned { read, write, modify, append, readwrite = modify, writeread = append };
enum class index : unsigned { absolute, relative };
enum class time : unsigned { create, modify, access };
static bool read(const string &filename, uint8_t *&data, unsigned &size) {
static bool copy(const string &sourcename, const string &targetname) {
file rd, wr;
if(rd.open(sourcename, mode::read) == false) return false;
if(wr.open(targetname, mode::write) == false) return false;
for(unsigned n = 0; n < rd.size(); n++) wr.write(rd.read());
return true;
}
static bool move(const string &sourcename, const string &targetname) {
#if !defined(_WIN32)
return rename(sourcename, targetname) == 0;
#else
return _wrename(utf16_t(sourcename), utf16_t(targetname)) == 0;
#endif
}
static bool remove(const string &filename) {
return unlink(filename) == 0;
}
static bool truncate(const string &filename, unsigned size) {
#if !defined(_WIN32)
return truncate(filename, size) == 0;
#else
bool result = false;
FILE *fp = fopen(filename, "rb+");
if(fp) {
result = _chsize(fileno(fp), size) == 0;
fclose(fp);
}
return result;
#endif
}
static vector<uint8_t> read(const string &filename) {
vector<uint8_t> memory;
file fp;
if(fp.open(filename, mode::read)) {
memory.resize(fp.size());
fp.read(memory.data(), memory.size());
}
return memory;
}
static bool read(const string &filename, uint8_t *data, unsigned size) {
file fp;
if(fp.open(filename, mode::read) == false) return false;
size = fp.size();
data = new uint8_t[size];
fp.read(data, size);
fp.close();
return true;
}
static bool read(const string &filename, const uint8_t *&data, unsigned &size) {
return file::read(filename, (uint8_t*&)data, size);
static bool write(const string &filename, const string &text) {
file fp;
if(fp.open(filename, mode::write) == false) return false;
fp.print(text);
fp.close();
return true;
}
static bool write(const string &filename, const vector<uint8_t> &buffer) {
file fp;
if(fp.open(filename, mode::write) == false) return false;
fp.write(buffer.data(), buffer.size());
fp.close();
return true;
}
static bool write(const string &filename, const uint8_t *data, unsigned size) {
@@ -44,6 +98,11 @@ namespace nall {
return true;
}
static string sha256(const string &filename) {
auto buffer = read(filename);
return nall::sha256(buffer.data(), buffer.size());
}
uint8_t read() {
if(!fp) return 0xff; //file not open
if(file_mode == mode::write) return 0xff; //reads not permitted
@@ -133,13 +192,13 @@ namespace nall {
file_offset = req_offset;
}
int offset() {
if(!fp) return -1; //file not open
unsigned offset() const {
if(!fp) return 0; //file not open
return file_offset;
}
int size() {
if(!fp) return -1; //file not open
unsigned size() const {
if(!fp) return 0; //file not open
return file_size;
}
@@ -193,7 +252,7 @@ namespace nall {
}
}
bool open() {
bool open() const {
return fp;
}
@@ -231,7 +290,7 @@ namespace nall {
file() {
memset(buffer, 0, sizeof buffer);
buffer_offset = -1;
buffer_offset = -1; //invalidate buffer
buffer_dirty = false;
fp = 0;
file_offset = 0;

0
bsnes/nall/filemap.hpp → ananke/nall/filemap.hpp Executable file → Normal file
View File

0
bsnes/nall/function.hpp → ananke/nall/function.hpp Executable file → Normal file
View File

10
bsnes/nall/gzip.hpp → ananke/nall/gzip.hpp Executable file → Normal file
View File

@@ -19,12 +19,10 @@ struct gzip {
};
bool gzip::decompress(const string &filename) {
uint8_t *data;
unsigned size;
if(file::read(filename, data, size) == false) return false;
bool result = decompress(data, size);
delete[] data;
return result;
if(auto memory = file::read(filename)) {
return decompress(memory.data(), memory.size());
}
return false;
}
bool gzip::decompress(const uint8_t *data, unsigned size) {

2
bsnes/nall/http.hpp → ananke/nall/http.hpp Executable file → Normal file
View File

@@ -7,9 +7,9 @@
#include <netinet/in.h>
#include <netdb.h>
#else
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
#endif
#include <nall/platform.hpp>

539
ananke/nall/image.hpp Normal file
View File

@@ -0,0 +1,539 @@
#ifndef NALL_IMAGE_HPP
#define NALL_IMAGE_HPP
#include <nall/bmp.hpp>
#include <nall/filemap.hpp>
#include <nall/interpolation.hpp>
#include <nall/png.hpp>
#include <nall/stdint.hpp>
#include <algorithm>
namespace nall {
struct image {
uint8_t *data;
unsigned width;
unsigned height;
unsigned pitch;
bool endian; //0 = little, 1 = big
unsigned depth;
unsigned stride;
struct Channel {
uint64_t mask;
unsigned depth;
unsigned shift;
inline bool operator==(const Channel &source) {
return mask == source.mask && depth == source.depth && shift == source.shift;
}
inline bool operator!=(const Channel &source) {
return !operator==(source);
}
} alpha, red, green, blue;
typedef double (*interpolation)(double, double, double, double, double);
static inline unsigned bitDepth(uint64_t color);
static inline unsigned bitShift(uint64_t color);
static inline uint64_t normalize(uint64_t color, unsigned sourceDepth, unsigned targetDepth);
inline bool operator==(const image &source);
inline bool operator!=(const image &source);
inline image& operator=(const image &source);
inline image& operator=(image &&source);
inline image(const image &source);
inline image(image &&source);
inline image(bool endian, unsigned depth, uint64_t alphaMask, uint64_t redMask, uint64_t greenMask, uint64_t blueMask);
inline image(const string &filename);
inline image(const uint8_t *data, unsigned size);
inline image();
inline ~image();
inline uint64_t read(const uint8_t *data) const;
inline void write(uint8_t *data, uint64_t value) const;
inline void free();
inline bool empty() const;
inline void allocate(unsigned width, unsigned height);
inline void clear(uint64_t color);
inline bool load(const string &filename);
//inline bool loadBMP(const uint8_t *data, unsigned size);
inline bool loadPNG(const uint8_t *data, unsigned size);
inline void scale(unsigned width, unsigned height, interpolation op);
inline void transform(bool endian, unsigned depth, uint64_t alphaMask, uint64_t redMask, uint64_t greenMask, uint64_t blueMask);
inline void alphaBlend(uint64_t alphaColor);
protected:
inline uint64_t interpolate(double mu, const uint64_t *s, interpolation op);
inline void scaleX(unsigned width, interpolation op);
inline void scaleY(unsigned height, interpolation op);
inline bool loadBMP(const string &filename);
inline bool loadPNG(const string &filename);
};
//static
unsigned image::bitDepth(uint64_t color) {
unsigned depth = 0;
if(color) while((color & 1) == 0) color >>= 1;
while((color & 1) == 1) { color >>= 1; depth++; }
return depth;
}
unsigned image::bitShift(uint64_t color) {
unsigned shift = 0;
if(color) while((color & 1) == 0) { color >>= 1; shift++; }
return shift;
}
uint64_t image::normalize(uint64_t color, unsigned sourceDepth, unsigned targetDepth) {
while(sourceDepth < targetDepth) {
color = (color << sourceDepth) | color;
sourceDepth += sourceDepth;
}
if(targetDepth < sourceDepth) color >>= (sourceDepth - targetDepth);
return color;
}
//public
bool image::operator==(const image &source) {
if(width != source.width) return false;
if(height != source.height) return false;
if(pitch != source.pitch) return false;
if(endian != source.endian) return false;
if(stride != source.stride) return false;
if(alpha != source.alpha) return false;
if(red != source.red) return false;
if(green != source.green) return false;
if(blue != source.blue) return false;
return memcmp(data, source.data, width * height * stride) == 0;
}
bool image::operator!=(const image &source) {
return !operator==(source);
}
image& image::operator=(const image &source) {
free();
width = source.width;
height = source.height;
pitch = source.pitch;
endian = source.endian;
stride = source.stride;
alpha = source.alpha;
red = source.red;
green = source.green;
blue = source.blue;
data = new uint8_t[width * height * stride];
memcpy(data, source.data, width * height * stride);
return *this;
}
image& image::operator=(image &&source) {
free();
width = source.width;
height = source.height;
pitch = source.pitch;
endian = source.endian;
stride = source.stride;
alpha = source.alpha;
red = source.red;
green = source.green;
blue = source.blue;
data = source.data;
source.data = nullptr;
return *this;
}
image::image(const image &source) : data(nullptr) {
operator=(source);
}
image::image(image &&source) : data(nullptr) {
operator=(std::forward<image>(source));
}
image::image(bool endian, unsigned depth, uint64_t alphaMask, uint64_t redMask, uint64_t greenMask, uint64_t blueMask) : data(nullptr) {
width = 0, height = 0, pitch = 0;
this->endian = endian;
this->depth = depth;
this->stride = (depth / 8) + ((depth & 7) > 0);
alpha.mask = alphaMask, red.mask = redMask, green.mask = greenMask, blue.mask = blueMask;
alpha.depth = bitDepth(alpha.mask), alpha.shift = bitShift(alpha.mask);
red.depth = bitDepth(red.mask), red.shift = bitShift(red.mask);
green.depth = bitDepth(green.mask), green.shift = bitShift(green.mask);
blue.depth = bitDepth(blue.mask), blue.shift = bitShift(blue.mask);
}
image::image(const string &filename) : data(nullptr) {
width = 0, height = 0, pitch = 0;
this->endian = 0;
this->depth = 32;
this->stride = 4;
alpha.mask = 255u << 24, red.mask = 255u << 16, green.mask = 255u << 8, blue.mask = 255u << 0;
alpha.depth = bitDepth(alpha.mask), alpha.shift = bitShift(alpha.mask);
red.depth = bitDepth(red.mask), red.shift = bitShift(red.mask);
green.depth = bitDepth(green.mask), green.shift = bitShift(green.mask);
blue.depth = bitDepth(blue.mask), blue.shift = bitShift(blue.mask);
load(filename);
}
image::image(const uint8_t *data, unsigned size) : data(nullptr) {
width = 0, height = 0, pitch = 0;
this->endian = 0;
this->depth = 32;
this->stride = 4;
alpha.mask = 255u << 24, red.mask = 255u << 16, green.mask = 255u << 8, blue.mask = 255u << 0;
alpha.depth = bitDepth(alpha.mask), alpha.shift = bitShift(alpha.mask);
red.depth = bitDepth(red.mask), red.shift = bitShift(red.mask);
green.depth = bitDepth(green.mask), green.shift = bitShift(green.mask);
blue.depth = bitDepth(blue.mask), blue.shift = bitShift(blue.mask);
loadPNG(data, size);
}
image::image() : data(nullptr) {
width = 0, height = 0, pitch = 0;
this->endian = 0;
this->depth = 32;
this->stride = 4;
alpha.mask = 255u << 24, red.mask = 255u << 16, green.mask = 255u << 8, blue.mask = 255u << 0;
alpha.depth = bitDepth(alpha.mask), alpha.shift = bitShift(alpha.mask);
red.depth = bitDepth(red.mask), red.shift = bitShift(red.mask);
green.depth = bitDepth(green.mask), green.shift = bitShift(green.mask);
blue.depth = bitDepth(blue.mask), blue.shift = bitShift(blue.mask);
}
image::~image() {
free();
}
uint64_t image::read(const uint8_t *data) const {
uint64_t result = 0;
if(endian == 0) {
for(signed n = stride - 1; n >= 0; n--) result = (result << 8) | data[n];
} else {
for(signed n = 0; n < stride; n++) result = (result << 8) | data[n];
}
return result;
}
void image::write(uint8_t *data, uint64_t value) const {
if(endian == 0) {
for(signed n = 0; n < stride; n++) { data[n] = value; value >>= 8; }
} else {
for(signed n = stride - 1; n >= 0; n--) { data[n] = value; value >>= 8; }
}
}
void image::free() {
if(data) delete[] data;
data = nullptr;
}
bool image::empty() const {
if(data == nullptr) return true;
if(width == 0 || height == 0) return true;
return false;
}
void image::allocate(unsigned width, unsigned height) {
if(data != nullptr && this->width == width && this->height == height) return;
free();
data = new uint8_t[width * height * stride]();
pitch = width * stride;
this->width = width;
this->height = height;
}
void image::clear(uint64_t color) {
uint8_t *dp = data;
for(unsigned n = 0; n < width * height; n++) {
write(dp, color);
dp += stride;
}
}
bool image::load(const string &filename) {
if(loadBMP(filename) == true) return true;
if(loadPNG(filename) == true) return true;
return false;
}
void image::scale(unsigned outputWidth, unsigned outputHeight, interpolation op) {
if(width != outputWidth) scaleX(outputWidth, op);
if(height != outputHeight) scaleY(outputHeight, op);
}
void image::transform(bool outputEndian, unsigned outputDepth, uint64_t outputAlphaMask, uint64_t outputRedMask, uint64_t outputGreenMask, uint64_t outputBlueMask) {
image output(outputEndian, outputDepth, outputAlphaMask, outputRedMask, outputGreenMask, outputBlueMask);
output.allocate(width, height);
#pragma omp parallel for
for(unsigned y = 0; y < height; y++) {
uint8_t *dp = output.data + output.pitch * y;
uint8_t *sp = data + pitch * y;
for(unsigned x = 0; x < width; x++) {
uint64_t color = read(sp);
sp += stride;
uint64_t a = (color & alpha.mask) >> alpha.shift;
uint64_t r = (color & red.mask) >> red.shift;
uint64_t g = (color & green.mask) >> green.shift;
uint64_t b = (color & blue.mask) >> blue.shift;
a = normalize(a, alpha.depth, output.alpha.depth);
r = normalize(r, red.depth, output.red.depth);
g = normalize(g, green.depth, output.green.depth);
b = normalize(b, blue.depth, output.blue.depth);
output.write(dp, (a << output.alpha.shift) | (r << output.red.shift) | (g << output.green.shift) | (b << output.blue.shift));
dp += output.stride;
}
}
operator=(std::move(output));
}
void image::alphaBlend(uint64_t alphaColor) {
uint64_t alphaR = (alphaColor & red.mask) >> red.shift;
uint64_t alphaG = (alphaColor & green.mask) >> green.shift;
uint64_t alphaB = (alphaColor & blue.mask) >> blue.shift;
#pragma omp parallel for
for(unsigned y = 0; y < height; y++) {
uint8_t *dp = data + pitch * y;
for(unsigned x = 0; x < width; x++) {
uint64_t color = read(dp);
uint64_t colorA = (color & alpha.mask) >> alpha.shift;
uint64_t colorR = (color & red.mask) >> red.shift;
uint64_t colorG = (color & green.mask) >> green.shift;
uint64_t colorB = (color & blue.mask) >> blue.shift;
double alphaScale = (double)colorA / (double)((1 << alpha.depth) - 1);
colorA = (1 << alpha.depth) - 1;
colorR = (colorR * alphaScale) + (alphaR * (1.0 - alphaScale));
colorG = (colorG * alphaScale) + (alphaG * (1.0 - alphaScale));
colorB = (colorB * alphaScale) + (alphaB * (1.0 - alphaScale));
write(dp, (colorA << alpha.shift) | (colorR << red.shift) | (colorG << green.shift) | (colorB << blue.shift));
dp += stride;
}
}
}
//protected
uint64_t image::interpolate(double mu, const uint64_t *s, double (*op)(double, double, double, double, double)) {
uint64_t aa = (s[0] & alpha.mask) >> alpha.shift, ar = (s[0] & red.mask) >> red.shift,
ag = (s[0] & green.mask) >> green.shift, ab = (s[0] & blue.mask) >> blue.shift;
uint64_t ba = (s[1] & alpha.mask) >> alpha.shift, br = (s[1] & red.mask) >> red.shift,
bg = (s[1] & green.mask) >> green.shift, bb = (s[1] & blue.mask) >> blue.shift;
uint64_t ca = (s[2] & alpha.mask) >> alpha.shift, cr = (s[2] & red.mask) >> red.shift,
cg = (s[2] & green.mask) >> green.shift, cb = (s[2] & blue.mask) >> blue.shift;
uint64_t da = (s[3] & alpha.mask) >> alpha.shift, dr = (s[3] & red.mask) >> red.shift,
dg = (s[3] & green.mask) >> green.shift, db = (s[3] & blue.mask) >> blue.shift;
int64_t A = op(mu, aa, ba, ca, da);
int64_t R = op(mu, ar, br, cr, dr);
int64_t G = op(mu, ag, bg, cg, dg);
int64_t B = op(mu, ab, bb, cb, db);
A = max(0, min(A, (1 << alpha.depth) - 1));
R = max(0, min(R, (1 << red.depth) - 1));
G = max(0, min(G, (1 << green.depth) - 1));
B = max(0, min(B, (1 << blue.depth) - 1));
return (A << alpha.shift) | (R << red.shift) | (G << green.shift) | (B << blue.shift);
}
void image::scaleX(unsigned outputWidth, interpolation op) {
uint8_t *outputData = new uint8_t[outputWidth * height * stride];
unsigned outputPitch = outputWidth * stride;
double step = (double)width / (double)outputWidth;
const uint8_t *terminal = data + pitch * height;
#pragma omp parallel for
for(unsigned y = 0; y < height; y++) {
uint8_t *dp = outputData + outputPitch * y;
uint8_t *sp = data + pitch * y;
double fraction = 0.0;
uint64_t s[4] = { sp < terminal ? read(sp) : 0 }; //B,C (0,1) = center of kernel { 0, 0, 1, 2 }
s[1] = s[0];
s[2] = sp + stride < terminal ? read(sp += stride) : s[1];
s[3] = sp + stride < terminal ? read(sp += stride) : s[2];
for(unsigned x = 0; x < width; x++) {
while(fraction <= 1.0) {
if(dp >= outputData + outputPitch * height) break;
write(dp, interpolate(fraction, (const uint64_t*)&s, op));
dp += stride;
fraction += step;
}
s[0] = s[1]; s[1] = s[2]; s[2] = s[3];
if(sp + stride < terminal) s[3] = read(sp += stride);
fraction -= 1.0;
}
}
free();
data = outputData;
width = outputWidth;
pitch = width * stride;
}
void image::scaleY(unsigned outputHeight, interpolation op) {
uint8_t *outputData = new uint8_t[width * outputHeight * stride];
double step = (double)height / (double)outputHeight;
const uint8_t *terminal = data + pitch * height;
#pragma omp parallel for
for(unsigned x = 0; x < width; x++) {
uint8_t *dp = outputData + stride * x;
uint8_t *sp = data + stride * x;
double fraction = 0.0;
uint64_t s[4] = { sp < terminal ? read(sp) : 0 };
s[1] = s[0];
s[2] = sp + pitch < terminal ? read(sp += pitch) : s[1];
s[3] = sp + pitch < terminal ? read(sp += pitch) : s[2];
for(unsigned y = 0; y < height; y++) {
while(fraction <= 1.0) {
if(dp >= outputData + pitch * outputHeight) break;
write(dp, interpolate(fraction, (const uint64_t*)&s, op));
dp += pitch;
fraction += step;
}
s[0] = s[1]; s[1] = s[2]; s[2] = s[3];
if(sp + pitch < terminal) s[3] = read(sp += pitch);
fraction -= 1.0;
}
}
free();
data = outputData;
height = outputHeight;
}
bool image::loadBMP(const string &filename) {
uint32_t *outputData;
unsigned outputWidth, outputHeight;
if(bmp::read(filename, outputData, outputWidth, outputHeight) == false) return false;
allocate(outputWidth, outputHeight);
const uint32_t *sp = outputData;
uint8_t *dp = data;
for(unsigned y = 0; y < outputHeight; y++) {
for(unsigned x = 0; x < outputWidth; x++) {
uint32_t color = *sp++;
uint64_t a = normalize((uint8_t)(color >> 24), 8, alpha.depth);
uint64_t r = normalize((uint8_t)(color >> 16), 8, red.depth);
uint64_t g = normalize((uint8_t)(color >> 8), 8, green.depth);
uint64_t b = normalize((uint8_t)(color >> 0), 8, blue.depth);
write(dp, (a << alpha.shift) | (r << red.shift) | (g << green.shift) | (b << blue.shift));
dp += stride;
}
}
delete[] outputData;
return true;
}
bool image::loadPNG(const uint8_t *pngData, unsigned pngSize) {
png source;
if(source.decode(pngData, pngSize) == false) return false;
allocate(source.info.width, source.info.height);
const uint8_t *sp = source.data;
uint8_t *dp = data;
auto decode = [&]() -> uint64_t {
uint64_t p, r, g, b, a;
switch(source.info.colorType) {
case 0: //L
r = g = b = source.readbits(sp);
a = (1 << source.info.bitDepth) - 1;
break;
case 2: //R,G,B
r = source.readbits(sp);
g = source.readbits(sp);
b = source.readbits(sp);
a = (1 << source.info.bitDepth) - 1;
break;
case 3: //P
p = source.readbits(sp);
r = source.info.palette[p][0];
g = source.info.palette[p][1];
b = source.info.palette[p][2];
a = (1 << source.info.bitDepth) - 1;
break;
case 4: //L,A
r = g = b = source.readbits(sp);
a = source.readbits(sp);
break;
case 6: //R,G,B,A
r = source.readbits(sp);
g = source.readbits(sp);
b = source.readbits(sp);
a = source.readbits(sp);
break;
}
a = normalize(a, source.info.bitDepth, alpha.depth);
r = normalize(r, source.info.bitDepth, red.depth);
g = normalize(g, source.info.bitDepth, green.depth);
b = normalize(b, source.info.bitDepth, blue.depth);
return (a << alpha.shift) | (r << red.shift) | (g << green.shift) | (b << blue.shift);
};
for(unsigned y = 0; y < height; y++) {
for(unsigned x = 0; x < width; x++) {
write(dp, decode());
dp += stride;
}
}
return true;
}
bool image::loadPNG(const string &filename) {
filemap map;
if(map.open(filename, filemap::mode::read) == false) return false;
return loadPNG(map.data(), map.size());
}
}
#endif

0
bsnes/nall/inflate.hpp → ananke/nall/inflate.hpp Executable file → Normal file
View File

0
bsnes/nall/input.hpp → ananke/nall/input.hpp Executable file → Normal file
View File

View File

View File

52
ananke/nall/invoke.hpp Normal file
View File

@@ -0,0 +1,52 @@
#ifndef NALL_INVOKE_HPP
#define NALL_INVOKE_HPP
//void invoke(const string &name, const string& args...);
//if a program is specified, it is executed with the arguments provided
//if a file is specified, the file is opened using the program associated with said file type
//if a folder is specified, the folder is opened using the associated file explorer
//if a URL is specified, the default web browser is opened and pointed at the URL requested
//path environment variable is always consulted
//execution is asynchronous (non-blocking); use system() for synchronous execution
#include <nall/string.hpp>
#ifdef _WIN32
#include <nall/windows/utf8.hpp>
#endif
namespace nall {
#ifdef _WIN32
template<typename... Args>
inline void invoke(const string &name, Args&&... args) {
lstring argl(std::forward<Args>(args)...);
for(auto &arg : argl) if(arg.position(" ")) arg = {"\"", arg, "\""};
string arguments = argl.concatenate(" ");
ShellExecuteW(NULL, NULL, utf16_t(name), utf16_t(arguments), NULL, SW_SHOWNORMAL);
}
#else
template<typename... Args>
inline void invoke(const string &name, Args&&... args) {
pid_t pid = fork();
if(pid == 0) {
const char *argv[1 + sizeof...(args) + 1], **argp = argv;
lstring argl(std::forward<Args>(args)...);
*argp++ = (const char*)name;
for(auto &arg : argl) *argp++ = (const char*)arg;
*argp++ = nullptr;
if(execvp(name, (char* const*)argv) < 0) {
execlp("xdg-open", "xdg-open", (const char*)name, nullptr);
}
exit(0);
}
}
#endif
}
#endif

10
snesfilter/nall/ips.hpp → ananke/nall/ips.hpp Executable file → Normal file
View File

@@ -11,8 +11,6 @@ struct ips {
inline bool apply();
inline void source(const uint8_t *data, unsigned size);
inline void modify(const uint8_t *data, unsigned size);
inline bool source(const string &filename);
inline bool modify(const string &filename);
inline ips();
inline ~ips();
@@ -88,14 +86,6 @@ void ips::modify(const uint8_t *data, unsigned size) {
modifyData = data, modifySize = size;
}
bool ips::source(const string &filename) {
return file::read(filename, sourceData, sourceSize);
}
bool ips::modify(const string &filename) {
return file::read(filename, modifyData, modifySize);
}
ips::ips() : data(nullptr), sourceData(nullptr), modifyData(nullptr) {
}

0
bsnes/nall/lzss.hpp → ananke/nall/lzss.hpp Executable file → Normal file
View File

3
bsnes/nall/map.hpp → ananke/nall/map.hpp Executable file → Normal file
View File

@@ -27,7 +27,7 @@ struct map {
signed middle = (first + last) / 2;
if(name < list[middle].name) last = middle - 1; //search lower half
else if(list[middle].name < name) first = middle + 1; //search upper half
else return { true, middle }; //match found
else return { true, (unsigned)middle }; //match found
}
return { false, 0u };
}
@@ -68,6 +68,7 @@ struct map {
}
inline RHS& operator()(const LHS &name) {
if(auto position = find(name)) return list[position()].data;
return insert(name, RHS());
}

0
bsnes/nall/mosaic.hpp → ananke/nall/mosaic.hpp Executable file → Normal file
View File

View File

View File

@@ -17,24 +17,24 @@ struct context {
unsigned blockHeight;
unsigned blockStride;
unsigned blockOffset;
array<unsigned> block;
vector<unsigned> block;
unsigned tileWidth;
unsigned tileHeight;
unsigned tileStride;
unsigned tileOffset;
array<unsigned> tile;
vector<unsigned> tile;
unsigned mosaicWidth;
unsigned mosaicHeight;
unsigned mosaicStride;
unsigned mosaicOffset;
array<unsigned> mosaic;
vector<unsigned> mosaic;
unsigned paddingWidth;
unsigned paddingHeight;
unsigned paddingColor;
array<unsigned> palette;
vector<unsigned> palette;
inline unsigned objectWidth() const { return blockWidth * tileWidth * mosaicWidth + paddingWidth; }
inline unsigned objectHeight() const { return blockHeight * tileHeight * mosaicHeight + paddingHeight; }
@@ -52,7 +52,7 @@ struct context {
return result;
}
inline void eval(array<unsigned> &buffer, const string &expression_) {
inline void eval(vector<unsigned> &buffer, const string &expression_) {
string expression = expression_;
bool function = false;
for(auto &c : expression) {

View File

58
ananke/nall/nall.hpp Normal file
View File

@@ -0,0 +1,58 @@
#ifndef NALL_HPP
#define NALL_HPP
//include the most common nall headers with one statement
//does not include the most obscure components with high cost and low usage
#include <nall/platform.hpp>
#include <nall/algorithm.hpp>
#include <nall/any.hpp>
#include <nall/atoi.hpp>
#include <nall/base64.hpp>
#include <nall/bit.hpp>
#include <nall/bmp.hpp>
#include <nall/config.hpp>
#include <nall/crc16.hpp>
#include <nall/crc32.hpp>
#include <nall/directory.hpp>
#include <nall/dl.hpp>
#include <nall/endian.hpp>
#include <nall/file.hpp>
#include <nall/filemap.hpp>
#include <nall/function.hpp>
#include <nall/gzip.hpp>
#include <nall/http.hpp>
#include <nall/image.hpp>
#include <nall/inflate.hpp>
#include <nall/interpolation.hpp>
#include <nall/intrinsics.hpp>
#include <nall/invoke.hpp>
#include <nall/map.hpp>
#include <nall/png.hpp>
#include <nall/property.hpp>
#include <nall/random.hpp>
#include <nall/serializer.hpp>
#include <nall/set.hpp>
#include <nall/sha256.hpp>
#include <nall/sort.hpp>
#include <nall/stdint.hpp>
#include <nall/stream.hpp>
#include <nall/string.hpp>
#include <nall/traits.hpp>
#include <nall/unzip.hpp>
#include <nall/utility.hpp>
#include <nall/varint.hpp>
#include <nall/vector.hpp>
#include <nall/zip.hpp>
#if defined(PLATFORM_WINDOWS)
#include <nall/windows/registry.hpp>
#include <nall/windows/utf8.hpp>
#endif
#if defined(PLATFORM_X)
#include <nall/serial.hpp>
#endif
#endif

View File

@@ -59,13 +59,10 @@
#endif
#if defined(_WIN32)
#define getcwd _getcwd
#define ftruncate _chsize
#define mkdir(n, m) _wmkdir(nall::utf16_t(n))
#define putenv _putenv
#define rmdir _rmdir
#define usleep(n) Sleep(n / 1000)
#define vsnprintf _vsnprintf
#define getcwd _getcwd
#define putenv _putenv
#define vsnprintf _vsnprintf
inline void usleep(unsigned milliseconds) { Sleep(milliseconds / 1000); }
#endif
//================
@@ -86,48 +83,4 @@
#define alwaysinline inline
#endif
//=========================
//file system functionality
//=========================
#if defined(_WIN32)
inline char* realpath(const char *filename, char *resolvedname) {
wchar_t fn[_MAX_PATH] = L"";
_wfullpath(fn, nall::utf16_t(filename), _MAX_PATH);
strcpy(resolvedname, nall::utf8_t(fn));
for(unsigned n = 0; resolvedname[n]; n++) if(resolvedname[n] == '\\') resolvedname[n] = '/';
return resolvedname;
}
inline char* userpath(char *path) {
wchar_t fp[_MAX_PATH] = L"";
SHGetFolderPathW(0, CSIDL_APPDATA | CSIDL_FLAG_CREATE, 0, 0, fp);
strcpy(path, nall::utf8_t(fp));
for(unsigned n = 0; path[n]; n++) if(path[n] == '\\') path[n] = '/';
return path;
}
inline char* getcwd(char *path) {
wchar_t fp[_MAX_PATH] = L"";
_wgetcwd(fp, _MAX_PATH);
strcpy(path, nall::utf8_t(fp));
for(unsigned n = 0; path[n]; n++) if(path[n] == '\\') path[n] = '/';
return path;
}
#else
//realpath() already exists
inline char* userpath(char *path) {
*path = 0;
struct passwd *userinfo = getpwuid(getuid());
if(userinfo) strcpy(path, userinfo->pw_dir);
return path;
}
inline char *getcwd(char *path) {
return getcwd(path, PATH_MAX);
}
#endif
#endif

10
snesfilter/nall/png.hpp → ananke/nall/png.hpp Executable file → Normal file
View File

@@ -58,12 +58,10 @@ protected:
};
bool png::decode(const string &filename) {
uint8_t *data;
unsigned size;
if(file::read(filename, data, size) == false) return false;
bool result = decode(data, size);
delete[] data;
return result;
if(auto memory = file::read(filename)) {
return decode(memory.data(), memory.size());
}
return false;
}
bool png::decode(const uint8_t *sourceData, unsigned sourceSize) {

View File

@@ -1,5 +1,5 @@
#ifndef NALL_PRIORITYQUEUE_HPP
#define NALL_PRIORITYQUEUE_HPP
#ifndef NALL_PRIORITY_QUEUE_HPP
#define NALL_PRIORITY_QUEUE_HPP
#include <limits>
#include <nall/function.hpp>

0
bsnes/nall/property.hpp → ananke/nall/property.hpp Executable file → Normal file
View File

View File

0
bsnes/nall/random.hpp → ananke/nall/random.hpp Executable file → Normal file
View File

29
snespurify/nall/serial.hpp → ananke/nall/serial.hpp Executable file → Normal file
View File

@@ -9,14 +9,39 @@
#include <nall/stdint.hpp>
namespace nall {
class serial {
public:
struct serial {
bool readable() {
if(port_open == false) return false;
fd_set fdset;
FD_ZERO(&fdset);
FD_SET(port, &fdset);
timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = 0;
int result = select(FD_SETSIZE, &fdset, nullptr, nullptr, &timeout);
if(result < 1) return false;
return FD_ISSET(port, &fdset);
}
//-1 on error, otherwise return bytes read
int read(uint8_t *data, unsigned length) {
if(port_open == false) return -1;
return ::read(port, (void*)data, length);
}
bool writable() {
if(port_open == false) return false;
fd_set fdset;
FD_ZERO(&fdset);
FD_SET(port, &fdset);
timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = 0;
int result = select(FD_SETSIZE, nullptr, &fdset, nullptr, &timeout);
if(result < 1) return false;
return FD_ISSET(port, &fdset);
}
//-1 on error, otherwise return bytes written
int write(const uint8_t *data, unsigned length) {
if(port_open == false) return -1;

View File

158
ananke/nall/set.hpp Normal file
View File

@@ -0,0 +1,158 @@
#ifndef NALL_SET_HPP
#define NALL_SET_HPP
//set
//* unordered
//* intended for unique items
//* dynamic growth
//* reference-based variant
#include <stdlib.h>
#include <algorithm>
#include <initializer_list>
#include <utility>
#include <nall/algorithm.hpp>
#include <nall/bit.hpp>
#include <nall/sort.hpp>
#include <nall/traits.hpp>
#include <nall/utility.hpp>
namespace nall {
template<typename T, typename Enable = void> struct set;
template<typename T> struct set<T, typename std::enable_if<!std::is_reference<T>::value>::type> {
struct exception_out_of_bounds{};
protected:
T *pool;
unsigned poolsize, objectsize;
public:
unsigned size() const { return objectsize; }
unsigned capacity() const { return poolsize; }
};
//reference set
template<typename TR> struct set<TR, typename std::enable_if<std::is_reference<TR>::value>::type> {
struct exception_out_of_bounds{};
protected:
typedef typename std::remove_reference<TR>::type T;
T **pool;
unsigned poolsize, objectsize;
public:
unsigned size() const { return objectsize; }
unsigned capacity() const { return poolsize; }
void reset() {
if(pool) free(pool);
pool = nullptr;
poolsize = 0;
objectsize = 0;
}
void reserve(unsigned size) {
if(size == poolsize) return;
pool = (T**)realloc(pool, sizeof(T*) * size);
poolsize = size;
objectsize = min(objectsize, size);
}
void resize(unsigned size) {
if(size > poolsize) reserve(bit::round(size)); //amortize growth
objectsize = size;
}
bool append(T& data) {
if(find(data)) return false;
unsigned offset = objectsize++;
if(offset >= poolsize) resize(offset + 1);
pool[offset] = &data;
return true;
}
template<typename... Args>
bool append(T& data, Args&&... args) {
bool result = append(data);
append(std::forward<Args>(args)...);
return result;
}
bool remove(T& data) {
if(auto position = find(data)) {
for(signed i = position(); i < objectsize - 1; i++) pool[i] = pool[i + 1];
resize(objectsize - 1);
return true;
}
return false;
}
optional<unsigned> find(const T& data) {
for(unsigned n = 0; n < objectsize; n++) if(pool[n] == &data) return {true, n};
return {false, 0u};
}
template<typename... Args> set(Args&&... args) : pool(nullptr), poolsize(0), objectsize(0) {
construct(std::forward<Args>(args)...);
}
~set() {
reset();
}
set& operator=(const set &source) {
if(&source == this) return *this;
if(pool) free(pool);
objectsize = source.objectsize;
poolsize = source.poolsize;
pool = (T**)malloc(sizeof(T*) * poolsize);
memcpy(pool, source.pool, sizeof(T*) * objectsize);
return *this;
}
set& operator=(const set &&source) {
if(&source == this) return *this;
if(pool) free(pool);
pool = source.pool;
poolsize = source.poolsize;
objectsize = source.objectsize;
source.pool = nullptr;
source.reset();
return *this;
}
T& operator[](unsigned position) const {
if(position >= objectsize) throw exception_out_of_bounds();
return *pool[position];
}
struct iterator {
bool operator!=(const iterator &source) const { return position != source.position; }
T& operator*() { return source.operator[](position); }
iterator& operator++() { position++; return *this; }
iterator(const set &source, unsigned position) : source(source), position(position) {}
private:
const set &source;
unsigned position;
};
iterator begin() { return iterator(*this, 0); }
iterator end() { return iterator(*this, objectsize); }
const iterator begin() const { return iterator(*this, 0); }
const iterator end() const { return iterator(*this, objectsize); }
private:
void construct() {}
void construct(const set &source) { operator=(source); }
void construct(const set &&source) { operator=(std::move(source)); }
template<typename... Args> void construct(T& data, Args&&... args) {
append(data);
construct(std::forward<Args>(args)...);
}
};
}
#endif

0
bsnes/nall/sha256.hpp → ananke/nall/sha256.hpp Executable file → Normal file
View File

77
ananke/nall/sort.hpp Normal file
View File

@@ -0,0 +1,77 @@
#ifndef NALL_SORT_HPP
#define NALL_SORT_HPP
#include <algorithm>
#include <nall/utility.hpp>
//class: merge sort
//average: O(n log n)
//worst: O(n log n)
//memory: O(n)
//stack: O(log n)
//stable?: yes
//note: merge sort was chosen over quick sort, because:
//* it is a stable sort
//* it lacks O(n^2) worst-case overhead
#define NALL_SORT_INSERTION
//#define NALL_SORT_SELECTION
namespace nall {
template<typename T, typename Comparator>
void sort(T list[], unsigned size, const Comparator &lessthan) {
if(size <= 1) return; //nothing to sort
//use insertion sort to quickly sort smaller blocks
if(size < 64) {
#if defined(NALL_SORT_INSERTION)
for(signed i = 1, j; i < size; i++) {
T copy = std::move(list[i]);
for(j = i - 1; j >= 0; j--) {
if(!lessthan(copy, list[j])) break;
list[j + 1] = std::move(list[j]);
}
list[j + 1] = std::move(copy);
}
#elif defined(NALL_SORT_SELECTION)
for(unsigned i = 0; i < size; i++) {
unsigned min = i;
for(unsigned j = i + 1; j < size; j++) {
if(lessthan(list[j], list[min])) min = j;
}
if(min != i) std::swap(list[i], list[min]);
}
#endif
return;
}
//split list in half and recursively sort both
unsigned middle = size / 2;
sort(list, middle, lessthan);
sort(list + middle, size - middle, lessthan);
//left and right are sorted here; perform merge sort
T *buffer = new T[size];
unsigned offset = 0, left = 0, right = middle;
while(left < middle && right < size) {
if(!lessthan(list[right], list[left])) {
buffer[offset++] = std::move(list[left++]);
} else {
buffer[offset++] = std::move(list[right++]);
}
}
while(left < middle) buffer[offset++] = std::move(list[left++]);
while(right < size) buffer[offset++] = std::move(list[right++]);
for(unsigned i = 0; i < size; i++) list[i] = std::move(buffer[i]);
delete[] buffer;
}
template<typename T>
void sort(T list[], unsigned size) {
return sort(list, size, [](const T &l, const T &r) { return l < r; });
}
}
#endif

0
bsnes/nall/stdint.hpp → ananke/nall/stdint.hpp Executable file → Normal file
View File

0
bsnes/nall/stream.hpp → ananke/nall/stream.hpp Executable file → Normal file
View File

View File

@@ -1,4 +1,5 @@
#ifdef NALL_STREAM_INTERNAL_HPP
#ifndef NALL_STREAM_AUTO_HPP
#define NALL_STREAM_AUTO_HPP
namespace nall {

View File

@@ -0,0 +1,42 @@
#ifndef NALL_STREAM_FILE_HPP
#define NALL_STREAM_FILE_HPP
#include <nall/file.hpp>
namespace nall {
struct filestream : stream {
using stream::read;
using stream::write;
bool seekable() const { return true; }
bool readable() const { return true; }
bool writable() const { return pwritable; }
bool randomaccess() const { return false; }
unsigned size() const { return pfile.size(); }
unsigned offset() const { return pfile.offset(); }
void seek(unsigned offset) const { pfile.seek(offset); }
uint8_t read() const { return pfile.read(); }
void write(uint8_t data) const { pfile.write(data); }
filestream(const string &filename) {
pfile.open(filename, file::mode::readwrite);
pwritable = pfile.open();
if(!pwritable) pfile.open(filename, file::mode::read);
}
filestream(const string &filename, file::mode mode) {
pfile.open(filename, mode);
pwritable = mode == file::mode::write || mode == file::mode::readwrite;
}
private:
mutable file pfile;
bool pwritable;
};
}
#endif

View File

@@ -1,9 +1,15 @@
#ifdef NALL_STREAM_INTERNAL_HPP
#ifndef NALL_STREAM_GZIP_HPP
#define NALL_STREAM_GZIP_HPP
#include <nall/gzip.hpp>
namespace nall {
struct gzipstream : memorystream {
inline gzipstream(const stream &stream) {
using stream::read;
using stream::write;
gzipstream(const stream &stream) {
unsigned size = stream.size();
uint8_t *data = new uint8_t[size];
stream.read(data, size);
@@ -18,7 +24,7 @@ struct gzipstream : memorystream {
memcpy(pdata, archive.data, psize);
}
inline ~gzipstream() {
~gzipstream() {
if(pdata) delete[] pdata;
}
};

View File

@@ -0,0 +1,49 @@
#ifndef NALL_STREAM_HTTP_HPP
#define NALL_STREAM_HTTP_HPP
#include <nall/http.hpp>
namespace nall {
struct httpstream : stream {
using stream::read;
using stream::write;
bool seekable() const { return true; }
bool readable() const { return true; }
bool writable() const { return true; }
bool randomaccess() const { return true; }
unsigned size() const { return psize; }
unsigned offset() const { return poffset; }
void seek(unsigned offset) const { poffset = offset; }
uint8_t read() const { return pdata[poffset++]; }
void write(uint8_t data) const { pdata[poffset++] = data; }
uint8_t read(unsigned offset) const { return pdata[offset]; }
void write(unsigned offset, uint8_t data) const { pdata[offset] = data; }
httpstream(const string &url, unsigned port) : pdata(nullptr), psize(0), poffset(0) {
string uri = url;
uri.ltrim<1>("http://");
lstring part = uri.split<1>("/");
part[1] = { "/", part[1] };
http connection;
if(connection.connect(part[0], port) == false) return;
connection.download(part[1], pdata, psize);
}
~httpstream() {
if(pdata) delete[] pdata;
}
private:
mutable uint8_t *pdata;
mutable unsigned psize, poffset;
};
}
#endif

View File

@@ -0,0 +1,47 @@
#ifndef NALL_STREAM_MEMORY_HPP
#define NALL_STREAM_MEMORY_HPP
#include <nall/stream/stream.hpp>
namespace nall {
struct memorystream : stream {
using stream::read;
using stream::write;
bool seekable() const { return true; }
bool readable() const { return true; }
bool writable() const { return pwritable; }
bool randomaccess() const { return true; }
uint8_t *data() const { return pdata; }
unsigned size() const { return psize; }
unsigned offset() const { return poffset; }
void seek(unsigned offset) const { poffset = offset; }
uint8_t read() const { return pdata[poffset++]; }
void write(uint8_t data) const { pdata[poffset++] = data; }
uint8_t read(unsigned offset) const { return pdata[offset]; }
void write(unsigned offset, uint8_t data) const { pdata[offset] = data; }
memorystream() : pdata(nullptr), psize(0), poffset(0), pwritable(true) {}
memorystream(uint8_t *data, unsigned size) {
pdata = data, psize = size, poffset = 0;
pwritable = true;
}
memorystream(const uint8_t *data, unsigned size) {
pdata = (uint8_t*)data, psize = size, poffset = 0;
pwritable = false;
}
protected:
mutable uint8_t *pdata;
mutable unsigned psize, poffset, pwritable;
};
}
#endif

View File

@@ -0,0 +1,42 @@
#ifndef NALL_STREAM_MMAP_HPP
#define NALL_STREAM_MMAP_HPP
#include <nall/filemap.hpp>
namespace nall {
struct mmapstream : stream {
using stream::read;
using stream::write;
bool seekable() const { return true; }
bool readable() const { return true; }
bool writable() const { return pwritable; }
bool randomaccess() const { return true; }
unsigned size() const { return pmmap.size(); }
unsigned offset() const { return poffset; }
void seek(unsigned offset) const { poffset = offset; }
uint8_t read() const { return pdata[poffset++]; }
void write(uint8_t data) const { pdata[poffset++] = data; }
uint8_t read(unsigned offset) const { return pdata[offset]; }
void write(unsigned offset, uint8_t data) const { pdata[offset] = data; }
mmapstream(const string &filename) {
pmmap.open(filename, filemap::mode::readwrite);
pwritable = pmmap.open();
if(!pwritable) pmmap.open(filename, filemap::mode::read);
pdata = pmmap.data(), poffset = 0;
}
private:
mutable filemap pmmap;
mutable uint8_t *pdata;
mutable unsigned pwritable, poffset;
};
}
#endif

View File

@@ -9,6 +9,7 @@ struct stream {
virtual bool writable() const = 0;
virtual bool randomaccess() const = 0;
virtual uint8_t* data() const { return nullptr; }
virtual unsigned size() const = 0;
virtual unsigned offset() const = 0;
virtual void seek(unsigned offset) const = 0;
@@ -16,44 +17,45 @@ struct stream {
virtual uint8_t read() const = 0;
virtual void write(uint8_t data) const = 0;
inline virtual uint8_t read(unsigned) const { return 0; }
inline virtual void write(unsigned, uint8_t) const {}
virtual uint8_t read(unsigned) const { return 0; }
virtual void write(unsigned, uint8_t) const {}
inline bool end() const {
operator bool() const {
return size();
}
bool empty() const {
return size() == 0;
}
bool end() const {
return offset() >= size();
}
inline void copy(uint8_t *&data, unsigned &length) const {
seek(0);
length = size();
data = new uint8_t[length];
for(unsigned n = 0; n < length; n++) data[n] = read();
}
inline uintmax_t readl(unsigned length = 1) const {
uintmax_t readl(unsigned length = 1) const {
uintmax_t data = 0, shift = 0;
while(length--) { data |= read() << shift; shift += 8; }
return data;
}
inline uintmax_t readm(unsigned length = 1) const {
uintmax_t readm(unsigned length = 1) const {
uintmax_t data = 0;
while(length--) data = (data << 8) | read();
return data;
}
inline void read(uint8_t *data, unsigned length) const {
void read(uint8_t *data, unsigned length) const {
while(length--) *data++ = read();
}
inline void writel(uintmax_t data, unsigned length = 1) const {
void writel(uintmax_t data, unsigned length = 1) const {
while(length--) {
write(data);
data >>= 8;
}
}
inline void writem(uintmax_t data, unsigned length = 1) const {
void writem(uintmax_t data, unsigned length = 1) const {
uintmax_t shift = 8 * length;
while(length--) {
shift -= 8;
@@ -61,26 +63,26 @@ struct stream {
}
}
inline void write(const uint8_t *data, unsigned length) const {
void write(const uint8_t *data, unsigned length) const {
while(length--) write(*data++);
}
struct byte {
inline operator uint8_t() const { return s.read(offset); }
inline byte& operator=(uint8_t data) { s.write(offset, data); }
inline byte(const stream &s, unsigned offset) : s(s), offset(offset) {}
operator uint8_t() const { return s.read(offset); }
byte& operator=(uint8_t data) { s.write(offset, data); return *this; }
byte(const stream &s, unsigned offset) : s(s), offset(offset) {}
private:
const stream &s;
const unsigned offset;
};
inline byte operator[](unsigned offset) const {
byte operator[](unsigned offset) const {
return byte(*this, offset);
}
inline stream() {}
inline virtual ~stream() {}
stream() {}
virtual ~stream() {}
stream(const stream&) = delete;
stream& operator=(const stream&) = delete;
};

View File

@@ -0,0 +1,39 @@
#ifndef NALL_STREAM_VECTOR_HPP
#define NALL_STREAM_VECTOR_HPP
#include <nall/stream/stream.hpp>
#include <nall/vector.hpp>
namespace nall {
struct vectorstream : stream {
using stream::read;
using stream::write;
bool seekable() const { return true; }
bool readable() const { return true; }
bool writable() const { return pwritable; }
bool randomaccess() const { return true; }
uint8_t* data() const { return memory.data(); }
unsigned size() const { return memory.size(); }
unsigned offset() const { return poffset; }
void seek(unsigned offset) const { poffset = offset; }
uint8_t read() const { return memory[poffset++]; }
void write(uint8_t data) const { memory[poffset++] = data; }
uint8_t read(unsigned offset) const { return memory[offset]; }
void write(unsigned offset, uint8_t data) const { memory[offset] = data; }
vectorstream(vector<uint8_t> &memory) : memory(memory), poffset(0), pwritable(true) {}
vectorstream(const vector<uint8_t> &memory) : memory((vector<uint8_t>&)memory), poffset(0), pwritable(false) {}
protected:
vector<uint8_t> &memory;
mutable unsigned poffset, pwritable;
};
}
#endif

View File

@@ -0,0 +1,38 @@
#ifndef NALL_STREAM_ZIP_HPP
#define NALL_STREAM_ZIP_HPP
#include <nall/unzip.hpp>
namespace nall {
struct zipstream : memorystream {
using stream::read;
using stream::write;
zipstream(const stream &stream, const string &filter = "*") {
unsigned size = stream.size();
uint8_t *data = new uint8_t[size];
stream.read(data, size);
unzip archive;
if(archive.open(data, size) == false) return;
delete[] data;
for(auto &file : archive.file) {
if(file.name.wildcard(filter)) {
auto buffer = archive.extract(file);
psize = buffer.size();
pdata = buffer.move();
return;
}
}
}
~zipstream() {
if(pdata) delete[] pdata;
}
};
}
#endif

53
ananke/nall/string.hpp Normal file
View File

@@ -0,0 +1,53 @@
#ifndef NALL_STRING_HPP
#define NALL_STRING_HPP
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <initializer_list>
#include <nall/atoi.hpp>
#include <nall/function.hpp>
#include <nall/platform.hpp>
#include <nall/sha256.hpp>
#include <nall/stdint.hpp>
#include <nall/utility.hpp>
#include <nall/varint.hpp>
#include <nall/vector.hpp>
#include <nall/windows/utf8.hpp>
#define NALL_STRING_INTERNAL_HPP
#include <nall/string/base.hpp>
#include <nall/string/bsv.hpp>
#include <nall/string/cast.hpp>
#include <nall/string/compare.hpp>
#include <nall/string/convert.hpp>
#include <nall/string/core.hpp>
#include <nall/string/cstring.hpp>
#include <nall/string/datetime.hpp>
#include <nall/string/filename.hpp>
#include <nall/string/math-fixed-point.hpp>
#include <nall/string/math-floating-point.hpp>
#include <nall/string/platform.hpp>
#include <nall/string/strm.hpp>
#include <nall/string/strpos.hpp>
#include <nall/string/trim.hpp>
#include <nall/string/replace.hpp>
#include <nall/string/split.hpp>
#include <nall/string/static.hpp>
#include <nall/string/utf8.hpp>
#include <nall/string/utility.hpp>
#include <nall/string/variadic.hpp>
#include <nall/string/wildcard.hpp>
#include <nall/string/wrapper.hpp>
#include <nall/string/markup/node.hpp>
#include <nall/string/markup/bml.hpp>
#include <nall/string/markup/xml.hpp>
#include <nall/string/markup/document.hpp>
#undef NALL_STRING_INTERNAL_HPP
#endif

View File

@@ -1,24 +1,34 @@
#ifndef NALL_STRING_BASE_HPP
#define NALL_STRING_BASE_HPP
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <nall/concept.hpp>
#include <nall/function.hpp>
#include <nall/stdint.hpp>
#include <nall/vector.hpp>
#include <nall/windows/utf8.hpp>
#ifdef NALL_STRING_INTERNAL_HPP
namespace nall {
class string;
class lstring;
struct cstring;
struct string;
struct lstring;
template<typename T> inline const char* to_string(T);
class string {
public:
struct cstring {
inline operator const char*() const;
inline unsigned length() const;
inline bool operator==(const char*) const;
inline bool operator!=(const char*) const;
inline optional<unsigned> position(const char *key) const;
inline optional<unsigned> iposition(const char *key) const;
inline cstring& operator=(const char *data);
inline cstring(const char *data);
inline cstring();
protected:
const char *data;
};
struct string {
inline static string read(const string &filename);
inline static string date();
inline static string time();
inline static string datetime();
inline void reserve(unsigned);
inline bool empty() const;
template<typename... Args> inline string& assign(Args&&... args);
template<typename... Args> inline string& append(Args&&... args);
@@ -31,6 +41,12 @@ namespace nall {
template<unsigned Limit = 0> inline string& iqreplace(const char*, const char*);
inline unsigned length() const;
inline unsigned capacity() const;
template<unsigned Limit = 0> inline lstring split(const char*) const;
template<unsigned Limit = 0> inline lstring isplit(const char*) const;
template<unsigned Limit = 0> inline lstring qsplit(const char*) const;
template<unsigned Limit = 0> inline lstring iqsplit(const char*) const;
inline bool equals(const char*) const;
inline bool iequals(const char*) const;
@@ -52,6 +68,7 @@ namespace nall {
template<unsigned limit = 0> inline string& ltrim(const char *key = " ");
template<unsigned limit = 0> inline string& rtrim(const char *key = " ");
template<unsigned limit = 0> inline string& trim(const char *key = " ", const char *rkey = 0);
inline string& strip();
inline optional<unsigned> position(const char *key) const;
inline optional<unsigned> iposition(const char *key) const;
@@ -77,6 +94,11 @@ namespace nall {
inline string(string&&);
inline ~string();
inline char* begin() { return &data[0]; }
inline char* end() { return &data[length()]; }
inline const char* begin() const { return &data[0]; }
inline const char* end() const { return &data[length()]; }
//internal functions
inline string& assign_(const char*);
inline string& append_(const char*);
@@ -93,18 +115,29 @@ namespace nall {
#endif
};
class lstring : public linear_vector<string> {
public:
template<typename T> inline lstring& operator<<(T value);
struct lstring : vector<string> {
inline optional<unsigned> find(const char*) const;
inline string concatenate(const char*) const;
inline void append() {}
inline void isort();
template<typename... Args> inline void append(const string&, Args&&...);
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*);
template<unsigned Limit = 0> inline lstring& iqsplit(const char*, const char*);
lstring();
lstring(std::initializer_list<string>);
inline bool operator==(const lstring&) const;
inline bool operator!=(const lstring&) const;
inline lstring& operator=(const lstring&);
inline lstring& operator=(lstring&);
inline lstring& operator=(lstring&&);
template<typename... Args> inline lstring(Args&&... args);
inline lstring(const lstring&);
inline lstring(lstring&);
inline lstring(lstring&&);
protected:
template<unsigned Limit, bool Insensitive, bool Quoted> inline lstring& usplit(const char*, const char*);
@@ -114,8 +147,6 @@ namespace nall {
inline char chrlower(char c);
inline char chrupper(char c);
inline int istrcmp(const char *str1, const char *str2);
inline bool wildcard(const char *str, const char *pattern);
inline bool iwildcard(const char *str, const char *pattern);
inline bool strbegin(const char *str, const char *key);
inline bool istrbegin(const char *str, const char *key);
inline bool strend(const char *str, const char *key);
@@ -127,24 +158,24 @@ namespace nall {
inline char* qstrlower(char *str);
inline char* qstrupper(char *str);
inline char* strtr(char *dest, const char *before, const char *after);
inline uintmax_t hex(const char *str);
inline intmax_t integer(const char *str);
inline uintmax_t decimal(const char *str);
inline uintmax_t binary(const char *str);
inline double fp(const char *str);
//math.hpp
inline bool strint(const char *str, int &result);
inline bool strmath(const char *str, int &result);
//platform.hpp
inline string realpath(const char *name);
inline string activepath();
inline string realpath(const string &name);
inline string userpath();
inline string currentpath();
inline string configpath();
inline string temppath();
//strl.hpp
inline unsigned strlcpy(char *dest, const char *src, unsigned length);
inline unsigned strlcat(char *dest, const char *src, unsigned length);
//strm.hpp
inline unsigned strmcpy(char *target, const char *source, unsigned length);
inline unsigned strmcat(char *target, const char *source, unsigned length);
inline bool strccpy(char *target, const char *source, unsigned length);
inline bool strccat(char *target, const char *source, unsigned length);
inline void strpcpy(char *&target, const char *source, unsigned &length);
//strpos.hpp
inline optional<unsigned> strpos(const char *str, const char *key);
@@ -157,27 +188,33 @@ namespace nall {
template<unsigned limit = 0> inline char* ltrim(char *str, const char *key = " ");
template<unsigned limit = 0> inline char* rtrim(char *str, const char *key = " ");
template<unsigned limit = 0> inline char* trim(char *str, const char *key = " ", const char *rkey = 0);
inline char* strip(char *s);
//utility.hpp
template<bool Insensitive> alwaysinline bool chrequal(char x, char y);
template<bool Quoted, typename T> alwaysinline bool quoteskip(T *&p);
template<bool Quoted, typename T> alwaysinline bool quotecopy(char *&t, T *&p);
inline unsigned strlcpy(string &dest, const char *src, unsigned length);
inline unsigned strlcat(string &dest, const char *src, unsigned length);
inline string substr(const char *src, unsigned start = 0, unsigned length = ~0u);
inline string sha256(const uint8_t *data, unsigned size);
inline char* integer(char *result, intmax_t value);
inline char* decimal(char *result, uintmax_t value);
template<unsigned length = 0, char padding = ' '> inline string integer(intmax_t value);
template<unsigned length = 0, char padding = ' '> inline string linteger(intmax_t value);
template<unsigned length = 0, char padding = ' '> inline string decimal(uintmax_t value);
template<unsigned length = 0, char padding = ' '> inline string ldecimal(uintmax_t value);
template<unsigned length = 0, char padding = '0'> inline string hex(uintmax_t value);
template<unsigned length = 0, char padding = '0'> inline string binary(uintmax_t value);
inline unsigned fp(char *str, double value);
inline string fp(double value);
inline unsigned fp(char *str, long double value);
inline string fp(long double value);
//variadic.hpp
template<typename... Args> inline void print(Args&&... args);
//wildcard.hpp
inline bool wildcard(const char *str, const char *pattern);
inline bool iwildcard(const char *str, const char *pattern);
};
#endif

View File

View File

View File

View File

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