Compare commits

...

182 Commits
v092 ... v100b

Author SHA1 Message Date
Tim Allen
88c79e56a0 Update to v100r01 release.
[This version, with the internal version number changed back to "v100",
replaced the original v100 source archive on byuu.org soon after v100's
release, because it fixes important bugs in that version. --Ed]

byuu says:

Changelog:
- fixed default paths for Sufami Turbo slotted games
- moved WonderSwan orientation controls to the port rather than the device
  - I do like hex_usr's idea here; but that'll need more consideration;
    so this is a temporary fix
- added new debugger interface (see the public topic for more on that)
2016-07-08 22:31:35 +10:00
Tim Allen
07995c05a5 Update to v100 release.
byuu says:

higan has finally reached v100!

I feel it's important to stress right away that this is not "version
1.00", nor is it a major milestone release. Rather than arbitrary version
numbers, all of my software simply bumps version numbers by one for each
official release. As such, higan v100 is simply higan's 100th release.

That said, the primary focus of this release has been code
clean-ups. These are always somewhat dangerous in that regressions are
possible. We've tested through sixteen WIP revisions, one of which was
open to the public, to try and minimize any regressions. But all the same,
please report any regressions if you discover any.

Changelog (since v099):
FC: render during pixels 1-256 instead of 0-255 [hex_usr]
FC: rewrote controller emulation code
SFC: 8% speedup over the previous release thanks to PPU optimizations
SFC: fixed nasty DB address wrapping regression from v099
SFC: USART developer controller removed; superseded by 21fx
SFC: Super Multitap option removed from controller port 1; ports
    renamed 2-5
SFC: hidden option to experiment with 128KB VRAM (strictly for novelty)
higan: audio volume no longer divided by number of audio streams
higan: updated controller polling code to fix possible future mapping
    issues
higan: replaced nall/stream with nall/vfs for file-loading subsystem
tomoko: can now load multi-slotted games via command-line
tomoko: synchronize video removed from UI; still available in the
    settings file
tomoko, icarus: can navigate to root drive selection on Windows
all: major code cleanups and refactoring (~1MB diff against v099)

Note 1: the audio volume change means that SGB and MSU1 games won't
lose half the volume on the SNES sounds anymore. However, if one goes
overboard and drives the sound all the way to max volume with the MSU1,
clamping may occur. The obvious solution is not to drive volume that high
(it will vastly overpower the SNES audio, which usually never exceeds
25% volume.) Another option is to lower the volume in the audio settings
panel to 50%. In general, neither is likely to ever be necessary.

Note 2: the synchronize video option was hidden from the UI because it
is no longer useful. With the advent of compositors, the loss of the
complicated timing settings panel, support for the WonderSwan and its
75hz display, the need to emulate variable refresh rate behaviors in the
Game Boy, the unfortunate latency spike and audio distortion caused by
long Vsync pauses, and the arrival of adaptive sync technology ... it
no longer makes sense to present this option. However, as stated, you
can edit settings.bml to enable this option anyway if you insist and
understand the aforementioned risks.

Changelog (since v099r16 open beta):

- fixed MSU1 audio sign extension
- fixed compilation with SGB support disabled
- icarus can now navigate to root directory
- fixed compilation issues with OS X port
- (hopefully) fixed label height issue with hiro that affected icarus
  import dialog
- (mostly) fixed BS Memory, Sufami Turbo slot loading

Errata:

- forgot to remove the " - Slot A", " - Slot B" suffixes for Sufami
  Turbo slot loading
  - this means you have to navigate up one folder and then into Sufami
    Turbo/ to load games for this system
- moving WonderSwan orientation controls to the device slot is causing
  some nastiness
  - can now select orientation from the main menu, but it doesn't rotate
    the display
2016-07-08 22:04:59 +10:00
Tim Allen
13ad9644a2 Update to v099r16 release (public beta).
byuu says:

Changelog:
- hiro: BrowserDialog can navigate up to drive selection on Windows
- nall: (file,path,dir,base,prefix,suffix)name =>
  Location::(file,path,dir,base,prefix,suffix)
- higan/tomoko: rename audio filter label from "Sinc" to "IIR - Biquad"
- higan/tomoko: allow loading files via icarus on the command-line
  once again
- higan/tomoko: (begrudging) quick hack to fix presentation window focus
  on startup
- higan/audio: don't divide output audio volume by number of streams
- processor/r65816: fix a regression in (read,write)DB; fixes Taz-Mania
- fixed compilation regressions on Windows and Linux

I'm happy with where we are at with code cleanups and stability, so I'd
like to release v100. But even though I'm not assigning any special
significance to this version, we should probably test it more thoroughly
first.
2016-07-04 21:53:24 +10:00
Tim Allen
8d5cc0c35e Update to v099r15 release.
byuu says:

Changelog:
- nall::lstring -> nall::string_vector
- added IntegerBitField<type, lo, hi> -- hopefully it works correctly...
- Multitap 1-4 -> Super Multitap 2-5
- fixed SFC PPU CGRAM read regression
- huge amounts of SFC PPU IO register cleanups -- .bits really is lovely
- re-added the read/write(VRAM,OAM,CGRAM) helpers for the SFC PPU
  - but they're now optimized to the realities of the PPU (16-bit data
    sizes / no address parameter / where appropriate)
  - basically used to get the active-display overrides in a unified place;
    but also reduces duplicate code in (read,write)IO
2016-07-04 21:48:17 +10:00
Tim Allen
82293c95ae Update to v099r14 release.
byuu says:

Changelog:
- (u)int(max,ptr) abbreviations removed; use _t suffix now [didn't feel
  like they were contributing enough to be worth it]
- cleaned up nall::integer,natural,real functionality
  - toInteger, toNatural, toReal for parsing strings to numbers
  - fromInteger, fromNatural, fromReal for creating strings from numbers
  - (string,Markup::Node,SQL-based-classes)::(integer,natural,real)
    left unchanged
  - template<typename T> numeral(T value, long padding, char padchar)
    -> string for print() formatting
    - deduces integer,natural,real based on T ... cast the value if you
      want to override
    - there still exists binary,octal,hex,pointer for explicit print()
      formatting
- lstring -> string_vector [but using lstring = string_vector; is
  declared]
  - would be nice to remove the using lstring eventually ... but that'd
    probably require 10,000 lines of changes >_>
- format -> string_format [no using here; format was too ambiguous]
- using integer = Integer<sizeof(int)*8>; and using natural =
  Natural<sizeof(uint)*8>; declared
  - for consistency with boolean. These three are meant for creating
    zero-initialized values implicitly (various uses)
- R65816::io() -> idle() and SPC700::io() -> idle() [more clear; frees
  up struct IO {} io; naming]
- SFC CPU, PPU, SMP use struct IO {} io; over struct (Status,Registers) {}
  (status,registers); now
  - still some CPU::Status status values ... they didn't really fit into
    IO functionality ... will have to think about this more
- SFC CPU, PPU, SMP now use step() exclusively instead of addClocks()
  calling into step()
- SFC CPU joypad1_bits, joypad2_bits were unused; killed them
- SFC PPU CGRAM moved into PPU::Screen; since nothing else uses it
- SFC PPU OAM moved into PPU::Object; since nothing else uses it
  - the raw uint8[544] array is gone. OAM::read() constructs values from
    the OAM::Object[512] table now
  - this avoids having to determine how we want to sub-divide the two
    OAM memory sections
  - this also eliminates the OAM::synchronize() functionality
- probably more I'm forgetting

The FPS fluctuations are driving me insane. This WIP went from 128fps to
137fps. Settled on 133.5fps for the final build. But nothing I changed
should have affected performance at all. This level of fluctuation makes
it damn near impossible to know whether I'm speeding things up or slowing
things down with changes.
2016-07-01 21:50:32 +10:00
Tim Allen
67457fade4 Update to v099r13 release.
byuu says:

Changelog:
- GB core code cleanup completed
- GBA core code cleanup completed
- some more cleanup on missed processor/arm functions/variables
- fixed FC loading icarus bug
- "Load ROM File" icarus functionality restored
- minor code unification efforts all around (not perfect yet)
  - MMIO->IO
  - mmio.cpp->io.cpp
  - read,write->readIO,writeIO

It's been a very long work in progress ... starting all the way back with
v094r09, but the major part of the higan code cleanup is now completed! Of
course, it's very important to note that this is only for the basic style:

- under_score functions and variables are now camelCase
- return-type function-name() are now auto function-name() -> return-type
- Natural<T>/Integer<T> replace (u)intT_n types where possible
- signed/unsigned are now int/uint
- most of the x==true,x==false tests changed to x,!x

A lot of spot improvements to consistency, simplicity and quality have
gone in along the way, of course. But we'll probably never fully finishing
beautifying every last line of code in the entire codebase. Still,
this is a really great start. Going forward, WIP diffs should start
being smaller and of higher quality once again.

I know the joke is, "until my coding style changes again", but ... this
was way too stressful, way too time consuming, and way too risky. I'm
too old and tired now for extreme upheavel like this again. The only
major change I'm slowly mulling over would be renaming the using
Natural<T>/Integer<T> = (u)intT; shorthand to something that isn't as
easily confused with the (u)int_t types ... but we'll see. I'll definitely
continue to change small things all the time, but for the larger picture,
I need to just accept the style I have and live with it.
2016-06-29 21:10:28 +10:00
Tim Allen
7a68059f78 Update to v099r12 release.
byuu says:

Changelog:
- fixed FC AxROM / VRC7 regression
- BitField split to BooleanBitField/NaturalBitField (in preparation
  for IntegerBitField)
- BitFieldReference removed
- GB CPU cleaned up
- GB Cartridge + Mappers cleaned up
- SFC CGRAM is now emulated as uint15[256] instead of uint[512]
- sfc/ppu/memory.cpp no longer needed; removed
- purged SFC Debugger hooks for now (some of the operator[] calls were
  bypassing them anyway)

Unfortunately, for reasons that defy all semblance of logic, the CGRAM
change caused a slight speed hit. As have the last few changes. We're
now down to around 129.5fps compared to 123.fps for v099 and 134.5fps
at our peak (v099r01-r02).

I really like the style I came up with for the Game Boy mappers to settle
the purpose(ROM,RAM) vs (rom,ram)Purpose naming convention. If I ever get
around to redoing the NES mappers, that's likely the approach I'll take.
2016-06-28 20:43:47 +10:00
Tim Allen
3e807946b8 Update to v099r11 release.
byuu says:

Changelog:
- NES PPU core updated to use BitFields (absolutely massive improvement
  in code readability)
- NES APU core updated to new coding style
- NES cartridge/board and cartridge/chip updated to new coding style
- pushed NES PPU rendering one dot forward (doesn't fix King's Quest V
  yet, sadly)
- fixed SNES PPU BG tilemask for 128KiB VRAM mode (doesn't fix Yoshi's
  Island, though)

So ... I kind of went overboard with the fc/cartridge changes. This WIP
diff is 185KiB >_>
I didn't realize it was going to be as big a task as it was, but once
I started everything broke in a chain reaction, so I had to do it all
at once.

There's a massive chance we've broken a bunch of NES things. Any typos
in this WIP are going to be absolutely insidious to track down =(

But ... supposing I pulled it off, this means the Famicom core is now
fully converted to the new coding style as well. That leaves only the
GB and GBA cores. Once those are finished, then we'll finally be free
of these gigantic hellspawn diffs.
2016-06-27 23:07:57 +10:00
Tim Allen
a816998122 Update to v099r10 release.
byuu says:

Changelog:
- higan/profile/ => higan/systems/ [temporary; unless we can't think of
  a better base folder name]
- god-damn-better-have fixed the input polling bug
- re-added command-line and drag-and-drop loading
  - command-line loading can now load multiple folders at once (SGB+GB
    game; Sufami Turbo+Slot A+Slot B; etc)
  - if you load just the base cart, it'll present you with a dialog to
    optionally load slotted cart(s)
- MSU1 now goes through nall/vfs instead of directly accessing the
  filesystem
- Famicom Cartridge, PPU cores updated to newer programming style
  - there's countless opportunity for BitField and .bits() in the PPU
    ... but I'm worried about breaking things

If anyone has a working MSU1 game and can test the changes out, that'd
be appreciated. I still don't have a test ROM on my dev box.

I wouldn't worry too much about extensively testing the Famicom PPU
changes just yet ... I'm still struggling with what to name the structs
inside the classes between all of my emulators, and the BitField/.bits()
changes will be much more important to test at a later date.

The only use case left for Emulator::Interface::path(uint id) is for
21fx emulation. This peripheral loads a DLL/SO via LoadLibrary/dlopen,
which do not have any official ways to open a file in RAM. I'm
very hesitant to use the portable trick of writing the memory to a
temporary file, loading it, and deleting the temporary file once done
... it's a real waste of disk activity. I might make something like
vfs::file::isVirtual->bool,path()->string to get around this. But even
once I do, the underlying LoadLibrary/dlopen call is still going to be
direct disk access.
2016-06-26 18:54:12 +10:00
Tim Allen
3a9c7c6843 Update to v099r09 release.
byuu says:

Changelog:
- Emulator::Interface::Medium::bootable removed
- Emulator::Interface::load(bool required) argument removed
  [File::Required makes no sense on a folder]
- Super Famicom.sys now has user-configurable properties (CPU,PPU1,PPU2
  version; PPU1 VRAM size, Region override)
- old nall/property removed completely
- volatile flags supported on coprocessor RAM files now (still not in
  icarus, though)
- (hopefully) fixed SNES Multitap support (needs testing)
- fixed an OAM tiledata range clipping limit in 128KiB VRAM mode (doesn't
  fix Yoshi's Island, sadly)
- (hopefully, again) fixed the input polling bug hex_usr reported
- re-added dialog box for when File::Required files are missing
  - really cool: if you're missing a boot ROM, BIOS ROM, or IPL ROM,
    it warns you immediately
  - you don't have to select a game before seeing the error message
    anymore
- fixed cheats.bml load/save location
2016-06-25 18:53:11 +10:00
Tim Allen
f48b332c83 Update to v099r08 release.
byuu says:

Changelog:
- nall/vfs work 100% completed; even SGB games load now
- emulation cores now call load() for the base cartridges as well
- updated port/device handling; portmask is gone; device ID bug should
  be resolved now
- SNES controller port 1 multitap option was removed
- added support for 128KiB SNES PPU VRAM (for now, edit sfc/ppu/ppu.hpp
  VRAM::size=0x10000; to enable)

Overall, nall/vfs was a huge success!! We've substantially reduced
the amount of boilerplate code everywhere, while still allowing (even
easier than before) support for RAM-based game loading/saving. All of
nall/stream is dead and buried.

I am considering removing Emulator::Interface::Medium::id and/or
bootable flag. Or at least, doing something different with it. The
values for the non-bootable GB/BS/ST entries duplicate the ID that is
supposed to be unique. They are for GB/GBC and WS/WSC. Maybe I'll use
this as the hardware revision selection ID, and then gut non-bootable
options. There's really no reason for that to be there. I think at one
point I was using it to generate library tabs for non-bootable systems,
but we don't do that anymore anyway.

Emulator::Interface::load() may not need the required flag anymore ... it
doesn't really do anything right now anyway.

I have a few reasons for having the cores load the base cartridge. Most
importantly, it is going to enable a special mode for the WonderSwan /
WonderSwan Color in the future. If we ever get the IPLROMs dumped ... it's
possible to boot these systems with no games inserted to set user profile
information and such. There are also other systems that may accept being
booted without a cartridge. To reach this state, you would load a game and
then cancel the load dialog. Right now, this results in games not loading.

The second reason is this prevents nasty crashes when loading fails. So
if you're missing a required manifest, the emulator won't die a violent
death anymore. It's able to back out at any point.

The third reason is consistency: loading the base cartridge works the
same as the slot cartridges.

The fourth reason is Emulator::Interface::open(uint pathID)
values. Before, the GB, SB, GBC modes were IDs 1,2,3 respectively. This
complicated things because you had to pass the correct ID. But now
instead, Emulator::Interface::load() returns maybe<uint> that is nothing
when no game is selected, and a pathID for a valid game. And now open()
can take this ID to access this game's folder contents.

The downside, which is temporary, is that command-line loading is
currently broken. But I do intend on restoring it. In fact, I want to do
better than before and allow multi-cart booting from the command-line by
specifying the base cartridge and then slot cartridges. The idea should
be pretty simple: keep a queue of pending filenames that we fill from
the command-line and/or drag-and-drop operations on the main window,
and then empty out the queue or prompt for load dialogs from the UI
when booting a system. This also might be a bit more unorthodox compared
to the traditional emulator design of "loadGame(filename)", but ... oh
well. It's easy enough still.

The port/device changes are fun. We simplified things quite a bit. The
portmask stuff is gone entirely. While ports and devices keep IDs,
this is really just sugar-coating so UIs can use for(auto& port :
emulator->ports) and access port.id; rather than having to use for(auto
n : range(emulator->ports)) { auto& port = emulator->ports[n]; ... };
but they should otherwise generally be identical to the order they appear
in their respective ranges. Still, don't rely on that.

Input::id is gone. There was no point since we also got rid of the nasty
Input::order vector. Since I was in here, I went ahead and caved on the
pedantics and renamed Input::guid to Input::userData.

I removed the SNES controller port 1 multitap option. Basically, the only
game that uses this is N-warp Daisakusen and, no offense to d4s, it's
not really a good game anyway. It's just a quick demo to show 8-players
on the SNES. But in the UI, all it does is confuse people into wasting
time mapping a controller they're never going to use, and they're going
to wonder which port to use. If more compelling use cases for 8-players
comes about, we can reconsider this. I left all the code to support this
in place, so all you have to do is uncomment one line to enable it again.

We now have dsnes emulation! :D
If you change PPU::VRAM::size to 0x10000 (words), then you should now
have 128KiB of VRAM. Even better, it serializes the used-VRAM size,
so your save states shouldn't crash on you if you swap between the two
(though if you try this, you're nuts.)

Note that this option does break commercial software. Yoshi's Island in
particular. This game is setting A15 on some PPU register writes, but
not on others. The end result of this is things break horribly in-game.

Also, this option is causing a very tiny speed hit for obvious reasons
with the variable masking value (I'm even using size-1 for now.) Given
how niche this is, I may just leave it a compile-time constant to avoid
the overhead cost. Otherwise, if we keep the option, then it'll go into
Super Famicom.sys/manifest.bml ... I'll flesh that out in the near-future.

----

Finally, some fun for my OCD ... my monitor suddenly cut out on me
in the middle of working on this WIP, about six hours in of non-stop
work. Had to hit a bunch of ctrl+alt+fN commands (among other things)
and trying to log in headless on another TTY to do issue commands,
trying to recover the display. Finally power cycled the monitor and it
came back up. So all my typing ended up going to who knows where.

Usually this sort of thing terrifies me enough that I scrap a WIP and
start over to ensure I didn't screw anything up during the crashed screen
when hitting keys randomly.

Obviously, everything compiles and appears to work fine. And I know
it's extremely paranoid, but OCD isn't logical, so ... I'm going
to go over every line of the 100KiB r07->r08 diff looking for any
corruption/errors/whatever.

----

Review finished.

r08 diff review notes:
- fc/controller/gamepad/gamepad.cpp:
  use uint device = ID::Device::Gamepad; not id = ...;
- gb/cartridge/cartridge.hpp:
  remove redundant uint _pathID; (in Information::pathID already)
- gb/cartridge/cartridge.hpp:
  pull sha256 inside Information
- sfc/cartridge/load/cpp:
  add " - Slot (A,B)" to interface->load("Sufami Turbo"); to be more
  descriptive
- sfc/controller/gamepad/gamepad.cpp:
  use uint device = ID::Device::Gamepad; not id = ...;
- sfc/interface/interface.cpp:
  remove n variable from the Multitap device input generation loop
  (now unused)
- sfc/interface/interface.hpp:
  put struct Port above struct Device like the other classes
- ui-tomoko:
  cheats.bml is reading from/writing to mediumPaths(0) [system folder
  instead of game folder]
- ui-tomoko:
  instead of mediumPaths(1) - call emulator->metadataPathID() or something
  like that
2016-06-24 22:16:53 +10:00
Tim Allen
ccd8878d75 Update to v099r07 release.
byuu says:

Changelog:
- (hopefully) fixed BS Memory and Sufami Turbo slot loading
- ported GB, GBA, WS cores to use nall/vfs
- completely removed loadRequest, saveRequest functionality from
  Emulator::Interface and ui-tomoko
  - loadRequest(folder) is now load(folder)
- save states now use a shared Emulator::SerializerVersion string
  - whenever this is bumped, all older states will break; but this makes
    bumping state versions way easier
  - also, the version string makes it a lot easier to identify
    compatibility windows for save states
- SNES PPU now uses uint16 vram[32768] for memory accesses [hex_usr]

NOTE: Super Game Boy loading is currently broken, and I'm not entirely
sure how to fix it :/
The file loading handoff was -really- complicated, and so I'm kind of
at a loss ... so for now, don't try it.
Everything else should theoretically work, so please report any bugs
you find.

So, this is pretty much it. I'd be very curious to hear feedback from
people who objected to the old nall/stream design, whether they are
happy with the new file loading system or think it could use further
improvements.

The 16-bit VRAM turned out to be a wash on performance (roughly the same
as before. 1fps slower on Zelda 3, 1fps faster on Yoshi's Island.) The
main reason for this was because Yoshi's Island was breaking horribly
until I changed the vramRead, vramWrite functions to take uint15 instead
of uint16.

I suspect the issue is we're using uint16s in some areas now that need
to be uint15, and this game is setting the VRAM address to 0x8000+,
causing us to go out of bounds on memory accesses.

But ... I want to go ahead and do something cute for fun, and just because
we can ... and this new interface is so incredibly perfect for it!! I
want to support an SNES unit with 128KiB of VRAM. Not out of the box,
but as a fun little tweakable thing. The SNES was clearly designed to
support that, they just didn't use big enough VRAM chips, and left one
of the lines disconnected. So ... let's connect it anyway!

In the end, if we design it right, the only code difference should be
one area where we mask by 15-bits instead of by 16-bits.
2016-06-24 22:09:30 +10:00
Tim Allen
875f031182 Update to v099r06 release.
byuu says:

Changelog:
- Super Famicom core converted to use nall/vfs
  - excludes Super Game Boy; since that's invoked from inside the GB core

This was definitely the major obstacle to test nall/vfs'
applicability. Things worked out pretty great in the end.

We went from 22.0KiB (cartridge) + 18.6KiB (interface) to 24.5KiB
(cartridge) + 11.4KiB (interface). Or 40.7KiB to 36.0KiB. This removes
a very large source of indirection. Before it was: "coprocessor <=>
cartridge <=> interface" for loading and saving data, and now it's just
"coprocessor <=> cartridge". And it may make sense to eventually turn
this into just "cartridge -> coprocessor" by making each coprocessor
class handle its own markup parsing.

It's nice to have all the manifest parsing in one location (well, sans
MSU1); but it's also nice for loading/unloading to be handled by each
coprocessor itself. So I'll have to think longer about that one.

I've also started handling Interface::save() differently. Instead of
keeping track of memory IDs and filenames, and iterating through that
vector of objects ... instead I now have a system that mirrors the markup
parsing on loading, but handles saving instead. This was actually the
reason the code size savings weren't more significant, but I like this
style more. As before, it removes an extra level of indirection.

So ... next up, I need to port over the GB, then GBA, then WS
cores. These shouldn't take too long since they're all very simple with
just ROM+RAM(+RTC) right now. Then get the SGB callbacks using vfs. Then
after that, gut all the old stream stuff from nall and higan. Kill the
(load,save)Request stuff, rename the load(Gamepak)Request to something
simpler, and then we should be good.

Anyway ... these are some huge changes.
2016-06-24 22:01:03 +10:00
Tim Allen
f04d9d58f5 Update to v099r05 release.
byuu says:

Changelog:
- added nall/vfs
- converted Famicom core to use nall/vfs interface instead of nall/stream
  interface
2016-06-20 21:00:32 +10:00
Tim Allen
40abcfc4a5 Update to v099r04 release.
byuu says:

Changelog:
- lots of code cleanups to processor/r6502 (the switch.cpp file is only
  halfway done ...)
- lots of code cleanups to fc/cpu
- removed fc/input
- implemented fc/controller

hex_usr, you may not like this, but I want to keep the controller port
and expansion port interface separate, like I do with the SNES. I realize
the NES' is used more for controllers, and the SNES' more for hardware
expansions, but ... they're not compatible pinouts and you can't really
connect one to the other.

Right now, I've only implemented the controller portion. I'll have to
get to the peripheral portion later.

Also, the gamepad implementation there now may be wrong. It's based off
the Super Famicom version obviously. I'm not sure if the Famicom has
different behavior with latching $4016 writes, or not. But, it works in
Mega Man II, so it's a start.

Everyone, be sure to remap your controls, and then set port 1 -> gamepad
after loading your first Famicom game with the new WIP.
2016-06-18 16:04:32 +10:00
Tim Allen
44a8c5a2b4 Update to v099r03 release.
byuu says:

Changelog:
- finished cleaning up the SFC core to my new coding conventions
- removed sfc/controller/usart (superseded by 21fx)
- hid Synchronize Video option from the menu (still in the configuration
  file)

Pretty much the only minor detail left is some variable names in the
SA-1 core that really won't look good at all if I move to camelCase,
so I'll have to rethink how I handle those. It's probably a good area
to attempt using BitFields, to see how it impacts performance. But I'll
do that in a test branch first.

But for the most part, this should be the end of the gigantic diffs (this
one was 174KiB), at least for the SFC/WS cores. Still have the FC/GB/GBA
cores to clean up more fully. Assuming we don't spot any new regressions,
we should be ~95% out of the woods on code cleanups breaking things.
2016-06-17 23:03:54 +10:00
Tim Allen
f1a80075fa Update to v099r02 release.
byuu says:

Changelog:
- renamed sfc/ppu/sprite (OAM oam;) to sfc/ppu/object (Object obj;) [hex_usr]
- renamed sfc/ppu's memory {vram, oam, cgram} to just vram, oam, cgram
- fixed addr&=~1 regression [hex_usr]
- fixed 8bpp tiledata regression [hex_usr]
2016-06-15 21:32:17 +10:00
Tim Allen
ae5b4c3bb3 Update to v099r01 release.
byuu says:

Changelog:
- massive cleanups and optimizations on the PPU core
- ~9% speedup over v099 official

This is pretty much it for the low-hanging fruit of speeding up higan. Any
more gains from this point will be extremely hard-fought, unfortunately.
2016-06-14 20:51:54 +10:00
Tim Allen
c074c6e064 Update to v099 release.
byuu says:

Time for a new release. There are a few important emulation improvements
and a few new features; but for the most part, this release focuses on
major code refactoring, the details of which I will mostly spare you.

The major change is that, as of v099, the SNES balanced and performance
cores have been removed from higan. Basically, in addition to my five
other emulation cores, these were too much of a burden to maintain. And
they've come along as far as I was able to develop them. If you need to
use these cores, please use these two from the v098 release.

I'm very well aware that ~80% of the people using higan for SNES
emulation were using the two removed profiles. But they simply had
to go. Hopefully in the future, we can compensate for their loss by
increasing the performance of the accuracy core.

Changelog (since v098):

SFC: balanced profile removed
SFC: performance profile removed
SFC: expansion port devices can now be changed during gameplay (atlhough
    you shouldn't)
SFC: fixed bug in SharpRTC leap year calculations
SFC: emulated new research findings for the S-DD1 coprocessor
SFC: fixed CPU emulation-mode wrapping bug with pei, [dp], [dp]+y
    instructions [AWJ]
SFC: fixed Super Game Boy bug that caused the bottom tile-row to flicker
    in games
GB: added MBC1M (multi-cart) mapper; icarus can't detect these so manual
    manifests are needed for now
GB: corrected return value when HuC3 unmapped RAM is read; fixes Robopon
    [endrift]
GB: improved STAT IRQ emulation; fixes Altered Space, etc [endrift,
    gekkio]
GB: partial emulation of DMG STAT write IRQ bug; fixes Legend of Zerd,
    Road Rash, etc
nall: execute() fix, for some Linux platforms that had trouble detecting
    icarus
nall: new BitField class; which allows for simplifying flag/register
    emulation in various cores
ruby: added Windows WASAPI audio driver (experimental)
ruby: remove attempts to call glSwapIntervalEXT (fixes crashing on some
    Linux systems)
ui: timing settings panel removed
video: restored saturation, gamma, luminance settings
video: added new post-emulation sprite system; light gun cursors are
    now higher-resolution
audio: new resampler (6th-order Butterworth biquad IIR); quite a bit
    faster than the old one
audio: added optional basic reverb filter (for fun)
higan: refresh video outside cooperative threads (workaround for shoddy
    code in AMD graphics drivers)
higan: individual emulation cores no longer have unique names
higan: really substantial code refactoring; 43% reduction in binary size

Off the bat, here are the known bugs:

hiro/Windows: focus stealing bug on startup. Needs to be fixed in hiro,
    not with a cheap hack to tomoko.

higan/SFC: some of the coprocessors are saving some volatile memory to
    disk. Completely harmless, but still needs to be fixed.

ruby/WASAPI: some sound cards have a lot of issues with the current driver
    (eg FitzRoy's). We need to find a clean way to fix this before it
    can be made the default driver. Which would be a huge win because
    the latency improvements are substantial, and in exclusive mode,
    WASAPI allows G-sync to work very well.

[From the v099 WIP thread, here's the changelog since v098r19:

- GB: don't force mode 1 during force-blank; fixes v098r16 regression
  with many Game Boy games
- GB: only perform the STAT write IRQ bug during vblank, not hblank
  (still not hardware accurate, though)

-Ed.]
2016-06-11 11:13:18 +10:00
Tim Allen
50420e3dd2 Update to v098r19 release.
byuu says:

Changelog:
- added nall/bit-field.hpp
- updated all CPU cores (sans LR35902 due to some complexities) to use
  BitFields instead of bools
- updated as many CPU cores as I could to use BitFields instead of union {
  struct { uint8_t ... }; }; pairs

The speed changes are mostly a wash for this. In some instances,
I noticed a ~2-3% speedup (eg SNES emulation), and in others a 2-3%
slowdown (eg Famicom emulation.) It's within the margin of error, so
it's safe to say it has no impact.

This does give us a lot of new useful things, however:

- no more manual reconstruction of flag values from lots of left shifts
  and ORs
- no more manual deconstruction of flag values from lots of ANDs
- ability to get completely free aliases to flag groups (eg GSU can
  provide alt2, alt1 and also alt (which is alt2,alt1 combined)
- removes the need for the nasty order_lsbN macro hack (eventually will
  make higan 100% endian independent)
- saves us from insane compilers that try and do nasty things with
  alignment on union-structs
- saves us from insane compilers that try to store bit-field bits in
  reverse order
- will allow some really novel new use cases (I'm planning an
  instant-decode ARM opcode function, for instance.)
- reduces code size (we can serialize flag registers in one line instead
  of one for each flag)

However, I probably won't use it for super critical code that's constantly
reading out register values (eg PPU MMIO registers.) I think there we
would end up with a performance penalty.
2016-06-09 08:26:35 +10:00
Tim Allen
b08449215a Update to v098r18 release.
byuu says:

Changelog:
- hiro: fixed the BrowserDialog column resizing when navigating to new
  folders (prevents clipping of filenames)
  - note: this is kind of a quick-fix; but I have a good idea how to do
    the proper fix now
- nall: added BitField<T, Lo, Hi> class
  - note: not yet working on the SFC CPU class; need to go at it with
    a debugger to find out what's happening
- GB: emulated DMG/SGB STAT IRQ bug; fixes Zerd no Densetsu and Road Rash
  (won't fix anything else; don't get hopes up)
2016-06-07 21:55:03 +10:00
Tim Allen
9b452c9f5f Update to v098r17 release.
byuu says:

Changelog:
- fixed Super Game Boy regression from v096r04 with bottom tile row
  flickering
- fixed GB STAT IRQ regression from previous WIP
  - Altered Space is now playable
  - GBVideoPlayer isn't; but nobody seems to know exactly what weird
    hardware quirk that one relies on to work
- ~3-4% speed improvement in SuperFX games by eliminating function<>
  callback on register assignments
  - most noticeable in Doom in-game; least noticeable on Yoshi's Island
    title screen (darn)
- finished GSU core and SuperFX coprocessor code cleanups
- did some more work cleaning up the LR35902 core and GB CPU code

Just a fair warning: don't get your hopes up on these GB
fixes. Cliffhanger now hangs completely (har har), and none of the
other bugs are fixed. We pretty much did all this work just for Altered
Space. So, I hope you like playing Altered Space.
2016-06-06 08:10:01 +10:00
Tim Allen
3681961ca5 Update to v098r16 release.
byuu says:

Changelog:
- GNUmakefile: reverted $(call unique,) to $(strip)
- processor/r6502: removed templates; reduces object size from 146.5kb
  to 107.6kb
- processor/lr35902: removed templates; reduces object size from 386.2kb
  to 197.4kb
- processor/spc700: merged op macros for switch table declarations
- sfc/coprocessor/sa1: partial cleanups; flattened directory structure
- sfc/coprocessor/superfx: partial cleanups; flattened directory structure
- sfc/coprocessor/icd2: flattened directory structure
- gb/ppu: changed behavior of STAT IRQs

Major caveat! The GB/GBC STAT IRQ changes has a major bug in it somewhere
that's seriously breaking most games. I'm pushing the WIP anyway, because
I believe the changes to be mostly correct. I'd like to get more people
looking at these changes, and also try more heavy-handed hacking and
diff comparison logging between the previous WIP and this one.
2016-06-05 15:03:21 +10:00
Tim Allen
20ac95ee49 Update to v098r15 release.
byuu says:

Changelog:
- removed template usage from processor/spc700; cleaned up many function
  names and the switch table
  - object size: 176.8kb => 127.3kb
  - source code size: 43.5kb => 37.0kb
- fixed processor/r65816 BRK/COP vector regression [hex_usr]
- corrected HuC3 unmapped RAM read value; fixes Robopon [endrift]
- cosmetic: simplified the butterworth constant calculation
  [Wolfram|Alpha]

The SPC700 core changes took forever, about three hours of work.

Only the LR35902 and R6502 still need their template functions
removed. The point of this is that it doesn't cause any speed penalty
to do so, and it results in smaller binary sizes and faster compilation
times.
2016-06-05 14:52:43 +10:00
Tim Allen
fdc41611cf Update to v098r14 release.
byuu says:

Changelog:
- improved attenuation of biquad filter by computing butterworth Q
  coefficients correctly (instead of using the same constant)
- adding 1e-25 to each input sample into the biquad filters to try and
  prevent denormalization
- updated normalization from [0.0 to 1.0] to [-1.0 to +1.0]; volume/reverb
  happen in floating-point mode now
- good amount of work to make the base Emulator::Audio support any number
  of output channels
  - so that we don't have to do separate work on left/right channels;
    and can instead share the code for each channel
- Emulator::Interface::audioSample(int16 left, int16 right); changed to:
  - Emulator::Interface::audioSample(double* samples, uint channels);
  - samples are normalized [-1.0 to +1.0]
  - for now at least, channels will be the value given to
    Emulator::Audio::reset()
- fixed GUI crash on startup when audio driver is set to None

I'm probably going to be updating ruby to accept normalized doubles as
well; but I'm not sure if I will try and support anything other 2-channel
audio output. It'll depend on how easy it is to do so; perhaps it'll be
a per-driver setting.

The denormalization thing is fierce. If that happens, it drops the
emulator framerate from 220fps to about 20fps for Game Boy emulation. And
that happens basically whenever audio output is silent. I'm probably
also going to make a nall/denormal.hpp file at some point with
platform-specific functionality to set the CPU state to "denormals as
zero" where applicable. I'll still add the 1e-25 offset (inaudible)
as another fallback.
2016-06-01 21:23:22 +10:00
Tim Allen
839813d0f1 Update to v098r13 release.
byuu says:

Changelog:
- nall/dsp returns with new iir/biquad.hpp and resampler/cubic.hpp files
- nall/queue.hpp added (simple ring buffer ... nall/vector wouldn't
  cause too many moves with FIFO)
- audio streams now only buffer 20ms; so even if multiple audio streams
  desync, latency can never exceed 20ms
- replaced blackman windwed sinc FIR hermite audio filter with transposed
  direct form II biquadratic sixth-order IIR butterworth filter (better
  attenuation of frequencies above 20KHz, faster, no need for decimation,
  less code)
- put in experimental eight-tap echo filter (a lot better than what I
  had before, but still rather weak)
- substantial cleanups to the SuperFX GSU processor core (slightly
  faster, 479KB->100KB object file, 42.7KB->33.4KB source code size,
  way less code duplication)

We'll definitely want to test the whole SuperFX library (not many games)
just to make sure there's no regressions caused by this one.

Not sure what I want to do with audio processing effects yet. I've always
really wanted lots of fun controls to customize audio, and now finally
with this new biquad filter, I can finally start implementing real
effects. For instance, an equalizer wouldn't be too complicated anymore.

The new reverb effect is still a poor man's version. I need to find human
readable source for implementing a comb-filter properly. I'm pretty sure
I can already treat nall::queue as an all-pass filter since all that
does is phase shift (fancy audio term for "delay audio"). What's really
going to be hard is figuring out how to expose user-friendly settings for
controlling it. It looks like you need a bunch of coprime coefficients,
and I don't think casual users are going to be able to hand-enter coprime
values to get the echo effect they want. I uh ... don't even know how
to calculate coprime values dynamically right now >_> But we're going
to have to, as they are correlated to the output sampling rate.

We'll definitely want to make some audio profiles so that users can
quickly select pre-configured themes that sound nice, but expose the
underlying coefficients so that they can tweak stuff to their liking. This
isn't just about higan, this is about me trying to learn digital signal
processing, so please don't be too upset about feature creep or anything
on this.

Anyway ... I'm having some difficulties with my audio right now. When
the reverb effect is enabled, there's a bunch of static on system
reset for just a moment. But this should not be possible. nall::queue
is initializing all previous reverb sample elements to 0.0. I don't
understand where static is coming in from. Further, we have the same
issue with both the windowed sinc and the biquad filters ... a bit of
a popping sound when starting a game. Any help tracking this down would
be appreciated.

There's also one really annoying issue ... I can't seem to do reverb
or volume adjustments with normalized samples. If I say "volume *= 0.5"
in higan/audio/audio.cpp line 68, it doesn't just halve the volume, it
adds a whole bunch of distortion. This makes absolutely zero sense to
me. The sample values are between 0.0 (mute) and 1.0 (full volume) here,
so multiplying a double by 0.5 shouldn't cause distortion. So right now,
I'm doing these adjustments with less precision after denormalizing back
to int16. Anyone ever see something like that? :/
2016-06-01 08:29:36 +10:00
Tim Allen
7f3cfa17b9 Update to v098r12 release.
byuu says:

Changelog:
- higan/video: added support for Emulator::Sprite
- higan/resource: a new system for accessing embedded binary files
  inside the emulation cores; holds the sprites
- higan/sfc/superscope,justifier: re-enabled display of crosshairs
- higan/sfc/superscope: fixed turbo toggle (also shows different
  crosshair color when in turbo mode)
- higan/sfc/ppu: always outputs at 512x480 resolution now
  - causes a slight speed-hit from ~127fps to ~125fps;
  - but allows high-resolution 32x32 cursors that look way better;
  - also avoids the need to implement sprite scaling logic

Right now, the PPU code to always output at 480-height is a really gross
hack. Don't worry, I'll make that nicer before release.

Also, superscope.cpp and justifier.cpp are built around a 256x240
screen. But since we now have 512x480, we can make the cursor's movement
much smoother by doubling the resolution on both axes. The actual games
won't see any accuracy improvements when firing the light guns, but the
cursors will animate nicer so I think it's still worth it. I'll work on
that before the next release as well.

The current 32x32 cursors are nicer, but we can do better now with full
24-bit color. So feel free to submit alternatives. I'll probably reject
them, but you can always try :D

The sprites don't support alpha blending, just color keying (0x00000000
= transparent; anything else is 0xff......). We can revisit that later
if necessary.

The way I have it designed, the only files that do anything with
Emulator::Sprite at all are the superscope and justifier folders.
I didn't have to add any hooks anywhere else. Rendering the sprite is
a lot cleaner than the old code, too.
2016-05-26 21:20:15 +10:00
Tim Allen
ae5d380d06 Update to v098r11 release.
byuu says:

Changelog:
- fixed nall/path.hpp compilation issue
- fixed ruby/audio/xaudio header declaration compilation issue (again)
- cleaned up xaudio2.hpp file to match my coding syntax (12.5% of the
  file was whitespace overkill)
- added null terminator entry to nall/windows/utf8.hpp argc[] array
- nall/windows/guid.hpp uses the Windows API for generating the GUID
  - this should stop all the bug reports where two nall users were
    generating GUIDs at the exact same second
- fixed hiro/cocoa compilation issue with uint# types
- fixed major higan/sfc Super Game Boy audio latency issue
- fixed higan/sfc CPU core bug with pei, [dp], [dp]+y instructions
- major cleanups to higan/processor/r65816 core
  - merged emulation/native-mode opcodes
  - use camel-case naming on memory.hpp functions
  - simplify address masking code for memory.hpp functions
  - simplify a few opcodes themselves (avoid redundant copies, etc)
  - rename regs.* to r.* to match modern convention of other CPU cores
- removed device.order<> concept from Emulator::Interface
  - cores will now do the translation to make the job of the UI easier
- fixed plurality naming of arrays in Emulator::Interface
  - example: emulator.ports[p].devices[d].inputs[i]
  - example: vector<Medium> media
- probably more surprises

Major show-stoppers to the next official release:
- we need to work on GB core improvements: LY=153/0 case, multiple STAT
  IRQs case, GBC audio output regs, etc.
- we need to re-add software cursors for light guns (Super Scope,
  Justifier)
- after the above, we need to fix the turbo button for the Super Scope

I really have no idea how I want to implement the light guns. Ideally,
we'd want it in higan/video, so we can support the NES Zapper with the
same code. But this isn't going to be easy, because only the SNES knows
when its output is interlaced, and its resolutions can vary as
{256,512}x{224,240,448,480} which requires pixel doubling that was
hard-coded to the SNES-specific behavior, but isn't appropriate to be
exposed in higan/video.
2016-05-25 21:13:02 +10:00
Tim Allen
3ebc77c148 Update to v098r10 release.
byuu says:

Changelog:
- synchronized tomoko, loki, icarus with extensive changes to nall
  (118KiB diff)
2016-05-16 19:51:12 +10:00
Tim Allen
6ae0abe3d3 Update to v098r09 release.
byuu says:

Changelog:
- fixed major nall/vector/prepend bug
- renamed hiro/ListView to hiro/TableView
- added new hiro/ListView control which is a simplified abstraction of
  hiro/TableView
- updated higan's cheat database window and icarus' scan dialog to use
  the new ListView control
- compilation works once again on all platforms (Windows, Cocoa, GTK,
  Qt)
- the loki skeleton compiles once again (removed nall/DSP references;
  updated port/device ID names)

Small catch: need to capture layout resize events internally in Windows
to call resizeColumns. For now, just resize the icarus window to get it
to use the full window width for list view items.
2016-05-04 20:07:13 +10:00
Tim Allen
0955295475 Update to v098r08 release.
byuu says:

Changelog:
- nall/vector rewritten from scratch
- higan/audio uses nall/vector instead of raw pointers
- higan/sfc/coprocessor/sdd1 updated with new research information
- ruby/video/glx and ruby/video/glx2: fuck salt glXSwapIntervalEXT!

The big change here is definitely nall/vector. The Windows, OS X and Qt
ports won't compile until you change some first/last strings to
left/right, but GTK will compile.

I'd be really grateful if anyone could stress-test nall/vector. Pretty
much everything I do relies on this class. If we introduce a bug, the
worst case scenario is my entire SFC game dump database gets corrupted,
or the byuu.org server gets compromised. So it's really critical that we
test the hell out of this right now.

The S-DD1 changes mean you need to update your installation of icarus
again. Also, even though the Lunar FMV never really worked on the
accuracy core anyway (it didn't initialize the PPU properly), it really
won't work now that we emulate the hard-limit of 16MiB for S-DD1 games.
2016-05-02 19:57:04 +10:00
Tim Allen
7cdae5195a Update to v098r07 release.
byuu says:

Changelog:
- GB: support modeSelect and RAM for MBC1M (Momotarou Collection)
- audio: implemented native resampling support into Emulator::Stream
- audio: removed nall::DSP completely

Unfortunately, the new resampler didn't turn out quite as fast as I had
hoped. The final hermite resampling added some overhead; and I had to
bump up the kernel count to 500 from 400 to get the buzzing to go away
on my main PC. I think that's due to it running at 48000hz output
instead of 44100hz output, maybe?

Compared to Ryphecha's:
(NES) Mega Man 2: 167fps -> 166fps
(GB) Mega Man II: 224fps -> 200fps
(WSC) Riviera: 143fps -> 151fps

Odd that the WS/WSC ends up faster while the DMG/CGB ends up slower.

But this knocks 922 lines down to 146 lines. The only files left in all
of higan not written (or rewritten) by me are ruby/xaudio2.h and
libco/ppc.c
2016-04-23 17:55:59 +10:00
Tim Allen
e2ee6689a0 Update to v098r06 release.
byuu says:

Changelog:
- emulation cores now refresh video from host thread instead of
  cothreads (fix AMD crash)
- SFC: fixed another bug with leap year months in SharpRTC emulation
- SFC: cleaned up camelCase on function names for
  armdsp,epsonrtc,hitachidsp,mcc,nss,sharprtc classes
- GB: added MBC1M emulation (requires manually setting mapper=MBC1M in
  manifest.bml for now, sorry)
- audio: implemented Emulator::Audio mixer and effects processor
- audio: implemented Emulator::Stream interface
  - it is now possible to have more than two audio streams: eg SNES
    + SGB + MSU1 + Voicer-Kun (eventually)
- audio: added reverb delay + reverb level settings; exposed balance
  configuration in UI
- video: reworked palette generation to re-enable saturation, gamma,
  luminance adjustments
- higan/emulator.cpp is gone since there was nothing left in it

I know you guys are going to say the color adjust/balance/reverb stuff
is pointless. And indeed it mostly is. But I like the idea of allowing
some fun special effects and configurability that isn't system-wide.

Note: there seems to be some kind of added audio lag in the SGB
emulation now, and I don't really understand why. The code should be
effectively identical to what I had before. The only main thing is that
I'm sampling things to 48000hz instead of 32040hz before mixing. There's
no point where I'm intentionally introducing added latency though. I'm
kind of stumped, so if anyone wouldn't mind taking a look at it, it'd be
much appreciated :/

I don't have an MSU1 test ROM, but the latency issue may affect MSU1 as
well, and that would be very bad.
2016-04-22 23:35:51 +10:00
Tim Allen
55e507d5df Update to v098r05 release.
byuu says:

Changelog:
- WS/WSC: re-added support for screen rotation (code is inside WS core)
- ruby: changed sample(uint16_t left, uint16_t right) to sample(int16_t
  left, int16_t right);
  - requires casting to uint prior to shifting in each driver, but
    I felt it was misleading to use uint16_t just to avoid that
- ruby: WASAPI is now built in by default; has wareya's improvements,
  and now supports latency adjust
- tomoko: audio settings panel has new "Exclusive Mode" checkbox for
  WASAPI driver only
  - note: although the setting *does* take effect in real-time, I'd
    suggest restarting the emulator after changing it
- tomoko: audio latency can now be set to 0ms (which in reality means
  "the minimum supported by the driver")
- all: increased cothread size from 512KiB to 2MiB to see if it fixes
  bullshit AMD driver crashes
  - this appears to cause a slight speed penalty due to cache locality
    going down between threads, though
2016-04-18 20:49:45 +10:00
Tim Allen
a2d3b8ba15 Update to v098r04 release.
byuu says:

Changelog:
- SFC: fixed behavior of 21fx $21fe register when no device is connected
  (must return zero)
- SFC: reduced 21fx buffer size to 1024 bytes in both directions to
  mirror the FT232H we are using
- SFC: eliminated dsp/modulo-array.hpp [1]
- higan: implemented higan/video interface and migrated all cores to it
  [2]

[1] the echo history buffer was 8-bytes, so there was no need for it at
all here. Not sure what I was thinking. The BRR buffer was 12-bytes, and
has very weird behavior ... but there's only a single location in the
code where it actually writes to this buffer. It's much easier to just
write to the buffer three times there instead of implementing an entire
class just to abstract away two lines of code. This change actually
boosted the speed from ~124.5fps to around ~127.5fps, but that's within
the margin of error for GCC. I doubt it's actually faster this way.

The DSP core could really use a ton of work. It comes from a port of
blargg's spc_dsp to my coding style, but he was extremely fond of using
32-bit signed integers everywhere. There's a lot of opportunity to
remove red tape masking by resizing the variables to their actual state
sizes.

I really need to find where I put spc_dsp6.sfc from blargg. It's a great
test to verify if I've made any mistakes in my implementation that would
cause regressions. Don't suppose anyone has it?

[2] so again, the idea is that higan/audio and higan/video are going to
sit between the emulation cores and the user interfaces. The hope is to
output raw encoding data from the emulation cores without having to
worry about the video display format (generally 24-bit RGB) of the host
display. And also to avoid having to repeat myself with eg three
separate implementations of interframe blending, and so on.

Furthermore, the idea is that the user interface can configure its side
of the settings, and the emulation cores can configure their sides.
Thus, neither has to worry about the other end. And now we can spin off
new user interfaces much easier without having to mess with all of these
things.

Right now, I've implemented color emulation, interframe blending and
SNES horizontal color bleed. I did not implement scanlines (and
interlace effects for them) yet, but I probably will at some point.

Further, for right now, the WonderSwan/Color screen rotation is busted
and will only show games in the horizontal orientation. Obviously this
must be fixed before the next official release, but I'll want to think
about how to implement it.

Also, the SNES light gun pointers are missing for now.

Things are a bit messy right now as I've gone through several revisions
of how to handle these things, so a good house cleaning is in order once
everything is feature-complete again. I need to sit down and think
through how and where I want to handle things like light gun cursors,
LCD icons, and maybe even rasterized text messages.

And obviously ... higan/audio is still just nall::DSP's headers. I need
to revamp that whole interface. I want to make it quite powerful with
a true audio mixer so I can handle things like
SNES+SGB+MSU1+Voicer-Kun+SNES-CD (five separate audio streams at once.)

The video system has the concept of "effects" for things like color
bleed and interframe blending. I want to extend on this with useful
other effects, such as NTSC simulation, maybe bringing back my mini-HQ2x
filter, etc. I'd also like to restore the saturation/gamma/luma
adjustment sliders ... I always liked allowing people to compensate for
their displays without having to change settings system-wide. Lastly,
I've always wanted to see some audio effects. Although I doubt we'll
ever get my dream of CoreAudio-style profiles, I'd like to get some
basic equalizer settings and echo/reverb effects in there.
2016-04-12 07:29:56 +10:00
Tim Allen
1929ad47d2 Update to v098r03 release.
byuu says:

It took several hours, but I've rebuilt much of the SNES' bus memory
mapping architecture.

The new design unifies the cartridge string-based mapping
("00-3f,80-bf:8000-ffff") and internal bus.map calls. The map() function
now has an accompanying unmap() function, and instead of a fixed 256
callbacks, it'll scan to find the first available slot. unmap() will
free slots up when zero addresses reference a given slot.

The controllers and expansion port are now both entirely dynamic.
Instead of load/unload/power/reset, they only have the constructor
(power/reset/load) and destructor (unload). What this means is you can
now dynamically change even expansion port devices after the system is
loaded.

Note that this is incredibly dangerous and stupid, but ... oh well. The
whole point of this was for 21fx. There's no way to change the expansion
port device prior to loading a game, but if the 21fx isn't active, then
the reset vector hijack won't work. Now you can load a 21fx game, change
the expansion port device, and simply reset the system to active the
device.

The unification of design between controller port devices and expansion
port devices is nice, and overall this results in a reduction of code
(all of the Mapping stuff in Cartridge is gone, replaced with direct bus
mapping.) And there's always the potential to expand this system more in
the future now.

The big missing feature right now is the ability to push/pop mappings.
So if you look at how the 21fx does the reset vector, you might vomit
a little bit. But ... it works.

Also changed exit(0) to _exit(0) in the POSIX version of nall::execute.

[The _exit(0) thing is an attempt to make higan not crash when it tries
to launch icarus and it's not on $PATH. The theory is that higan forks,
then the child tries to exec icarus and fails, so it exits, all the
unique_ptrs clean up their resources and tell the X server to free
things the parent process is still using. Calling _exit() prevents
destructors from running, and seems to prevent the problem. -Ed.]
2016-04-09 20:21:18 +10:00
Tim Allen
7403e69307 Update to v098r02 release.
byuu says:

Changelog:
- SFC: fixed a regression on auto joypad polling due to missing
  parentheses
- SFC: exported new PPU::vdisp() const -> uint; function [1]
- SFC: merged PPU MMIO functions into the read/write handles (as
  I previously did for the CPU)
- higan: removed individual emulator core names (bnes, bsnes, bgb, bgba,
  bws) [2] Forgot:
- to remove /tomoko from the about dialog

[1] note that technically I was relying on the cached, per-frame
overscan setting when the CPU and light guns were polling the number of
active display scanlines per frame. This was technically incorrect as
you can change this value mid-frame and it'll kick in. I've never seen
any game toggle overscan every frame, we only know about this because
anomie tested this a long time ago. So, nothing should break, but ...
you know how the SNES is. You can't even look at the code without
something breaking, so I figured I'd mention it >_>

[2] I'll probably keep referring to the SNES core as bsnes anyway.
I don't mind if you guys use the b<system> names as shorthand. The
simplification is mostly to make the branding easier.
2016-04-09 15:20:41 +10:00
Tim Allen
19e1d89f00 Update to v098r01 release.
byuu says:

Changelog:
- SFC: balanced profile removed
- SFC: performance profile removed
- SFC: code for handling non-threaded CPU, SMP, DSP, PPU removed
- SFC: Coprocessor, Controller (and expansion port) shared Thread code
  merged to SFC::Cothread
  - Cothread here just means "Thread with CPU affinity" (couldn't think
    of a better name, sorry)
- SFC: CPU now has vector<Thread*> coprocessors, peripherals;
  - this is the beginning of work to allow expansion port devices to be
    dynamically changed at run-time
- ruby: all audio drivers default to 48000hz instead of 22050hz now if
  no frequency is assigned
  - note: the WASAPI driver can default to whatever the native frequency
    is; doesn't have to be 48000hz
- tomoko: removed the ability to change the frequency from the UI (but
  it will display the frequency used)
- tomoko: removed the timing settings panel
  - the goal is to work toward smooth video via adaptive sync
  - the model is broken by not being in control of the audio frequency
    anyway
  - it's further broken by PAL running at 50hz and WSC running at 75hz
  - it was always broken anyway by SNES interlace timing varying from
    progressive timing
- higan: audio/ stub created (for now, it's just nall/dsp/ moved here
  and included as a header)
- higan: video/ stub created
- higan/GNUmakefile: now includes build rules for essential components
  (libco, emulator, audio, video)

The audio changes are in preparation to merge wareya's awesome WASAPI
work without the need for the nall/dsp resampler.
2016-04-09 13:40:12 +10:00
Tim Allen
aff00506c5 Update to v098 hotfix release.
byuu says:

There was a minor SNES input regression spotted very shortly after
release.
2016-04-09 12:43:12 +10:00
Tim Allen
e846c83d47 Update to v098 release.
byuu says:

This release most notably adds WonderSwan and WonderSwan Color
emulation.

It is also the final release that will include the balanced and
performance profiles for bsnes.

Changelog (since v097):
- higan: added WonderSwan and WonderSwan Color emulation
- higan: simplified the coooperative-thread schedulers for all emulation
  cores
- higan: moved from native (u)int[8,16,32,64]_t types to
  Natural<T>/Integer<T> classes
- higan: major cleanups to the Makefiles; including auto-selection of
  processor cores
- loki: very barebones skeleton in place now; does absolutely nothing
  - these allow the removal of huge amounts of manual bit-twiddling with
    more readable alternatives
- FC: fixed PPU OAM reads (mask the correct bits when writing) [hex_usr]
- SFC: fixed expansion port device mapping on game load
- SFC: reworked the way SGB games were loaded -* SFC core can now be
  compiled without GB core (and thus without SGB support)
- SFC: added Super Disc expansion port device (although it's just
  a non-functional skeleton so far)
- SFC: bugfix to SharpRTC emulation regarding leap year extra day counts
  (Dai Kaijuu Monogatari II)
- SFC: major code cleanups to the CPU core and the R65816 processor base
  class
- SFC: added 21fx emulation (not the old 21fx that became MSu1; reusing
  the name for a new idea)
  - basic idea is to move the serial USART to the expansion port along
    with a reset vector hijack
- SFC: emulate reset vector pushing PC onto the stack on system soft
  reset
- GB: pass gekkio's if_ie_registers and boot_hwio-G test ROMs
- GBA: reworked all handling of MMIO functions: removed the get/set
  class functions
- nall: improved edge case return values for
  (basename,pathname,dirname,...)
- ruby: fixed ~AudioXAudio2() typo (now calls destructor on exit)
- ruby: if DirectSoundCreate fails (no sound device present), return
  false from init instead of crashing
- tomoko: added "All" option to filetype dropdown for ROM loading
  - allows loading GBC games in SGB mode
- tomoko: locate() updated to search multiple paths [2]
- tomoko: fixed some oddities when changing the audio frequency/latency
  settings
- icarus: can now work with WonderSwan and WonderSwan Color games

Note 1: 90% of the changelog for this release was related to the
WonderSwan emulation being in development. Doesn't make a lot of sense
to post about fixes since the code didn't exist publicly prior to this
release.
2016-04-09 12:38:50 +10:00
Tim Allen
06d44b4878 Update to v097r32 release.
byuu says:

Changelog:
- bsnes-accuracy emulates reset vector properly[1]
- bsnes-balanced compiles once more
- bsnes-performance compiles once more

The balanced and performance profiles are fixed for the last time. They
will be removed for v098r01.

Please test this WIP as much as you can. I intend to release v098 soon.
I know save states are a little unstable for the WS/WSC, but they work
well enough for a release. If I can't figure it out soon, I'm going to
post v098 anyway.

[1] this one's been a really long time coming, but ... one of the bugs
I found when I translated Tekkaman Blade was that my translation patch
would crash every now and again when you hit the reset button on a real
SNES, but it always worked upon power on.

Turns out that while power-on initializes the stack register to $01ff,
reset does things a little bit differently. Reset actually triggers the
reset interrupt vector after putting the CPU into emulation mode, but it
doesn't initialize the stack pointer. The net effect is that the stack
high byte is set to $01, and the low byte is left as it was. And then
the reset vector runs, which pushes the low 16-bits of the program
counter, plus the processor flags, onto the stack frame. So you can
actually tell where the game was at when the system was reset ... sort
of.

It's a really weird behavior to be sure. But here's the catch: say
you're hacking a game, and so you hook the reset vector with jsl
showMyTranslationCreditsSplashScreen, and inside this new subroutine,
you then perform whatever bytes you hijacked, and then initialize the
stack frame to go about your business drawing the screen, and when
you're done, you return via rtl.

Generally, this works fine. But if S={0100, 0101, or 0102}, then the
stack will wrap due to being in emulation mode at reset. So it will
write to {0100, 01ff, 01fe}. But now in your subroutine, you enable
native mode. So when you return from your subroutine hijack, it reads
the return address from {01ff, 0200, 0201} instead of the expected
{01ff, 0100, 0101}. Thus, you get an invalid address back, and you
"return" to the wrong location, and your program dies.

The odds of this happening depend on how the game handles S, but
generally speaking, it's a ~1:85 chance.

By emulating this behavior, I'll likely expose this bug in many ROM
hacks that do splash screen hooks like this, including my own Tekkaman
Blade translation. And it's also very possible that there are commercial
games that screw this up as well.

But, it's what the real system does. So if any crashes start happening
as of this WIP upon resetting the game, well ... it'd happen on real
hardware, too.
2016-04-03 21:17:20 +10:00
Tim Allen
25eaaa82f4 Update to v097r31 release.
byuu says:

Changelog:
- WS: fixed sprite window clipping (again)
- WS: don't set IRQ status bits of IRQ enable bits are clear
- SFC: signed/unsigned -> int/uint for DSP core
- SFC: removed eBoot
- SFC: added 21fx (not the same as the old precursor to MSU1; just
  reusing the name)

Note: XI Little doesn't seem to be fixed after all ... but the other
three are. So I guess we're at 13 bugs :( And holy shit that music when
you choose a menu option is one of the worst sounds I've ever heard in
my life >_<
2016-03-29 20:15:01 +11:00
Tim Allen
2d83300235 Update to v097r30 release.
byuu says:

Changelog:
- fixed sprite window attribute bit (Final Fantasy, Tekken Card
  Challenge, etc)
- rewrote renderer to support 2bpp color mode (Dark Eyes, Dokodemo
  Hamster, Flash Koibito-kun, etc)
2016-03-29 19:44:03 +11:00
Tim Allen
680d16561e Update to v097r29 release.
byuu says:

Changelog:
- fixed DAS instruction (Judgment Silversword score)
- fixed [VH]TMR_FREQ writes (Judgement Silversword audio after area 20)
- fixed initialization of SP (fixes seven games that were hanging on
  startup)
- added SER_STATUS and SER_DATA stubs (fixes four games that were
  hanging on startup)
- initialized IEEP data (fixes Super Robot Taisen Compact 2 series)
  - note: you'll need to delete your internal.com in WonderSwan
    (Color).sys folders
- fixed CMPS and SCAS termination condition (fixes serious bugs in four
  games)
- set read/writeCompleted flags for EEPROM status (fixes Tetsujin 28
  Gou)
- major code cleanups to SFC/R65816 and SFC/CPU
  - mostly refactored disassembler to output strings instead of using
    char* buffer
  - unrolled all the subfolders on sfc/cpu to a single directory
  - corrected casing for all of sfc/cpu and a large portion of
    processor/r65816

I kind of went overboard on the code cleanup with this WIP. Hopefully
nothing broke. Any testing one can do with the SFC accuracy core would
be greatly appreciated.

There's still an absolutely huge amount of work left to go, but I do
want to eventually refresh the entire codebase to my current coding
style, which is extremely different from stuff that's been in higan
mostly untouched since ~2006 or so. It's dangerous and fickle work, but
if I don't do it, then the code will be a jumbled mess of several
different styles.
2016-03-26 12:56:15 +11:00
Tim Allen
379ab6991f Update to v097r28 release.
byuu says:

Changelog: (all WSC unless otherwise noted)
- fixed LINECMP=0 interrupt case (fixes FF4 world map during airship
  sequence)
- improved CPU timing (fixes Magical Drop flickering and FF1 battle
  music)
- added per-frame OAM caching (fixes sprite glitchiness in Magical Drop,
  Riviera, etc.)
- added RTC emulation (fixes Dicing Knight and Judgement Silversword)
- added save state support
- added cheat code support (untested because I don't know of any cheat
  codes that exist for this system)
- icarus: can now detect games with RTC chips
- SFC: bugfix to SharpRTC emulation (Dai Kaijuu Monogatari II)
  - ( I was adding the extra leap year day to all 12 months instead of
    just February ... >_< )

Note that the RTC emulation is very incomplete. It's not really
documented at all, and the two games I've tried that use it never even
ask you to set the date/time (so they're probably just using it to count
seconds.) I'm not even sure if I've implement the level-sensitive
behavior correctly (actually, now that I think about it, I need to mask
the clear bit in INT_ACK for the level-sensitive interrupts ...)

A bit worried about the RTC alarm, because it seems like it'll fire
continuously for a full minute. Or even if you turn it off after it
fires, then that doesn't seem to be lowering the line until the next
second ticks on the RTC, so that likely needs to happen when changing
the alarm flag.

Also not sure on this RTC's weekday byte. On the SharpRTC, it actually
computes this for you. Because it's not at all an easy thing to
calculate yourself in 65816 or V30MZ assembler. About 40 lines of code
to do it in C. For now, I'm requiring the program to calculate the value
itself.

Also note that there's some gibberish tiles in Judgement Silversword,
sadly. Not sure what's up there, but the game's still fully playable at
least.

Finally, no surprise: Beat-Mania doesn't run :P
2016-03-25 17:19:08 +11:00
Tim Allen
d3413db04a Update to v097r27 release.
byuu says:

Absolutely major improvements to the WS/C emulation today.

Changelog: (all WS/C related)
- fixed channel 3 sweep pitch adjustment
- fixed channel 3 sweep value sign extension
- removed errant channel 5 speed setting (not what's really going on)
- fixed sign extension on channel 5 samples
- improved DAC mixing of all five audio channels
- fixed r26 regression with PPU timing loop
- fixed sprite windowing behavior (sprite attribute flag is window mode;
  not window enable)
- added per-scanline register latching to the PPU
- IRQs should terminate HLT even when the IRQ enable register bits are
  clear
- fixed PALMONO reads
- added blur emulation
- added color emulation (based on GBA, so it heavily desaturates colors;
  not entirely correct, but it helps a lot)
- no longer decimating audio to 24KHz; running at full 3.072MHz through
  the windowed sinc filter [1]
- cleaned up PPU portRead / portWrite functions significantly
- emulated a weird quirk as mentioned by trap15 regarding timer
  frequency writes enabling said timers [2]
- emulated LCD_CTRL sleep bit; screen can now be disabled (always draws
  black in this case for now)
- improved OAM caching; but it's still disabled because it causes huge
  amounts of sprite glitches (unsure why)
- fixed rendering of sprites that wrap around the screen edges back to
  the top/left of the display
- emulated keypad interrupts
- icarus: detect orientation bit in game header
- higan: use orientation setting in manifest to set default screen
  rotation

[1] the 24KHz -> 3.072MHz sound change is huge. Sound is substantially
improved over the previous WIPs. It does come at a pretty major speed
penalty, though. This is the highest frequency of any system in higan
running through an incredibly (amazing, yet) demanding sinc resampler.
Frame rate dropped from around 240fps to 150fps with the sinc filter on.
If you choose a different audio filter, you'll get most of that speed
back, but audio will sound worse again.

[2] we aren't sure if this is correct hardware behavior or not. It seems
to very slightly help Magical Drop, but not much.

The blur emulation is brutal. It's absolutely required for Riviera's
translucency simulation of selected menu items, but it causes serious
headaches due to the WS's ~75hz refresh rate running on ~60hz monitors
without vsync. It's probably best to leave it off and just deal with the
awful flickering on Riviera's menu options.

Overall, WS/C emulation is starting to get quite usable indeed. Couple
of major bugs that I'd really like to get fixed before releasing it,
though. But they're getting harder and harder to fix ...

Major Bugs:
- Final Fantasy battle background music is absent. Sound effects still
  work. Very weird.
- Final Fantasy IV scrolling during airship flight opening sequence is
  horribly broken. Scrolls one screen at a time.
- Magical Drop flickers like crazy in-game. Basically unplayable like
  this.
- Star Hearts character names don't appear in the smaller dialog box
  that pops up.

Minor Bugs:
- Occasional flickering during Riviera opening scenes.
- One-frame flicker of Leda's sprite at the start of the first stage.
2016-03-19 18:35:25 +11:00
Tim Allen
a7f7985581 Update to v097r26 release.
byuu says:

Changelog:
- WS: fixed 8-bit sign-extended imul (fixes Star Hearts completely,
  Final Fantasy world map)
- WS: fixed rcl/rcr carry shifting (fixes Crazy Climber, others)
- WS: added sound DMA emulation (Star Hearts rain sound for one example)
- WS: added OAM caching, but it's forced every line for now because
  otherwise there are too many sprite glitches
- WS: use headphoneEnable bit instead of speakerEnable bit (fixes muted
  audio in games)
- WS: various code cleanups (I/O mapping, audio channel naming, etc)

The hypervoice channel doesn't sound all that great just yet. But I'm
not sure how it's supposed to sound. I need a better example of some
more complex music.

What's left are some unknown register status bits (especially in the
sound area), keypad interrupts, RTC emulation, CPU prefetch emulation.
And then it's all just bugs. Lots and lots of bugs that need to be
fixed.

EDIT: oops, bad typo in the code.

ws/ppu/ppu.cpp line 20: change range(256) to range(224).

Also, delete the r.speed stuff from channel5.cpp to make the rain sound
a lot better in Star Hearts. Apparently that's outdated and not what the
bits really do.
2016-03-17 22:28:15 +11:00
Tim Allen
b586471562 Update to v097r25 release.
byuu says:

Changelog:
- WS: added HblankTimer and VblankTimer IRQs; although they don't appear
  to have any effect on any games that use them :/
- WS: added sound emulation; works perfectly in some games (eg Riviera);
  is completely silent in most games (eg GunPey)

The sound emulation only partially supports the hypervoice (headphone
only) channel. I need to implement the SDMA before it'll actually do
anything useful. I'm a bit confused about how exactly things work. It
looks like the speaker volume shift and clamp only applies to speaker
mode and not headphone mode, which is very weird. Then there's the
software possibility of muting the headphones and/or the speaker.
Preferably, I want to leave the emulator always in headphone mode for
the extra audio channel. If there are games that force-mute the
headphones, but not speakers, then I may need to force headphones back
on but with the hypervoice channel disabled. I guess we'll see how
things go.

Rough guess is probably that the channels default to enabled after the
IPLROM, and games aren't bothering to manually enable them or something.
2016-03-14 22:03:32 +11:00
Tim Allen
c33065fbd1 Update to v097r24 release.
byuu says:

Changelog:
- WS: fixed bug when IRQs triggered during a rep string instruction
- WS: added sprite attribute caching (per-scanline); absolutely massive
  speed-up
- WS: emulated limit of 32 sprites per scanline
- WS: emulated the extended PPU register bit behavior based on the
  DISP_CTRL tile bit-depth setting
- WS: added "Rotate" key binding; can be used to flip the WS display
  between horizontal and vertical in real-time

The prefix emulation may not be 100% hardware-accurate, but the edge
cases should be extreme enough to not come up in the WS library. No way
to get the emulation 100% down without intensive hardware testing.
trap15 pointed me at a workflow diagram for it, but that diagram is
impossible without a magic internal stack frame that grows with every
IRQ, and can thus grow infinitely large.

The rotation thing isn't exactly the most friendly set-up, but oh well.
I'll see about adding a default rotation setting to manifests, so that
games like GunPey can start in the correct orientation. After that, if
the LCD orientation icon turns out to be reliable, then I'll start using
that. But if there are cases where it's not reliable, then I'll leave it
to manual button presses.

Speaking of icons, I'll need a set of icons to render on the screen.
Going to put them to the top right on vertical orientation, and on the
bottom left for horizontal orientation. Just outside of the video
output, of course.

Overall, WS is getting pretty far along, but still some major bugs in
various games. I really need sound emulation, though. Nobody's going to
use this at all without that.
2016-03-13 11:22:15 +11:00
Tim Allen
79e7e6ab9e Update to v097r23 release.
byuu says:

Changelog:
- emulated SuperDisc $21e1 basic interface (NEC 4-bit MCU); all hardware
  tests pass now (but they don't test much)
- WS/V30MZ: fixed inc/dec reg flag calculation
- WS/V30MZ: fixed lds/les instructions

WS/C compatibility should be way up now. SuperDisc BIOS passes all tests
now (but they only test for the presence of the interface, nothing
more.)
2016-03-13 11:22:14 +11:00
Tim Allen
3d3ac8c1db Update to v097r22 release.
byuu says:

Changelog:
- WS: fixed lods, scas instructions
- WS: implemented missing GRP4 instructions
- WS: fixed transparency for screen one
- WSC: added color-mode PPU rendering
- WS+WSC: added packed pixel mode support
- WS+WSC: added dummy sound register reads/writes
- SFC: added threading to SuperDisc (it's hanging for right now; need to
  clear IRQ on $21e2 writes)

SuperDisc Timer and Sound Check were failing before due to not turning
off IRQs on $21e4 clear, so I'm happy that's fixed now.

Riviera starts now, and displays the first intro screen before crashing.
Huge, huge amounts of corrupted graphics, though. This game's really
making me work for it :(

No color games seem fully playable yet, but a lot of monochrome and
color games are now at least showing more intro screen graphics before
dying.

This build defaults to horizontal orientation, but I left the inputs
bound to vertical orientation. Whoops. I still need to implement
a screen flip key binding.
2016-03-13 11:22:14 +11:00
Tim Allen
b0d2f5033e Update to v097r21 release.
byuu says:

Changelog:
- icarus: WS/C detects RAM type/size heuristically now
- icarus: WS/C uses ram type=$type instead of $type
- WS: use back color instead of white for backdrop
- WS: fixed sprite count limit; removes all the garbled sprites from
  GunPey
- WS: hopefully fixed sprite priority with screen 2
- WS: implemented keypad polling; GunPey is now fully playable
- SNES: added Super Disc expansion port device (doesn't do anything,
  just for testing)

Note: WS is hard-coded to vertical orientation right now. But there's
basic code in there for all the horizontal stuff.
2016-03-13 11:22:14 +11:00
Tim Allen
570eb9c5f5 Update to v097r20 release.
byuu says:

Changelog:
- WS: fixed a major CPU bug where I was using the wrong bits for
  ModR/M's memory mode
- WS: added grayscale PPU emulation (exceptionally buggy)

GunPey now runs, as long as you add:

    eeprom name=save.ram size=0x800

to the manifest after importing with icarus.

Right now, you can't control the game due to missing keypad polling.
There's also a lot of glitchiness with the sprites. Seems like they're
not getting properly cleared sometimes or something.

Also, the PPU emulation is totally unrealistic bullshit. I decode and
evaluate every single tile and sprite on every single pixel of output.
No way in hell the hardware could ever come close to that. The speed's
around 500fps without the insane sprite evaluations, and around 90fps
with it. Obviously, I'll fix this in time.

Nothing else seems to run that I've tried. Not even far enough to
display any output whatsoever. Tried Langrisser Millenium, Rockman
& Forte and Riviera. I really need to update icarus to try and encode
eeprom/sram sizes, because that's going to break a lot of stuff if it's
missing.
2016-03-13 11:22:14 +11:00
Tim Allen
7dc62e3a69 Update to v097r19 release.
byuu says:

Changelog:
- fixed nall/windows/guard.hpp
- fixed hiro/(windows,gtk)/header.hpp
- fixed Famicom PPU OAM reads (mask the correct bits when writing)
  [hex_usr]
- removed the need for (system := system) lines from higan/GNUmakefile
- added "All" option to filetype dropdown for ROM loading
  - allows loading GBC games in SGB mode (and technically non-GB(C)
    games, which will obviously fail to do anything)
- loki can load and play game folders now (command-line only) (extremely
  unimpressive; don't waste your time :P)
  - the input is extremely hacked in as a quick placeholder; not sure
    how I'm going to do mapping yet for it
2016-03-13 11:22:14 +11:00
Tim Allen
fc7d5991ce Update to v097r18 release.
byuu says:

Changelog:
- fixed SNES sprite priority regression from r17
- added nall/windows/guard.hpp to guard against global namespace
  pollution (similar to nall/xorg/guard.hpp)
- almost fixed Windows compilation (still accuracy profile only, sorry)
- finished porting all of gba/ppu's registers over to the new .bit,.bits
  format ... all GBA registers.cpp files gone now
- the "processors :=" line in the target-$(ui)/GNUmakefile is no longer
  required
  - processors += added to each emulator core
  - duplicates are removed using the new nall/GNUmakefile's $(unique)
    function
- SFC core can be compiled without the GB core now
  - "-DSFC_SUPERGAMEBOY" is required to build in SGB support now (it's
    set in target-tomoko/GNUmakefile)
- started once again on loki (higan/target-loki/) [as before, loki is
  Linux/BSD only on account of needing hiro::Console]

loki shouldn't be too horrendous ... I hope. I just have the base
skeleton ready for now. But the code from v094r08 should be mostly
copyable over to it. It's just that it's about 50KiB of incredibly
tricky code that has to be just perfect, so it's not going to be quick.
But at least with the skeleton, it'll be a lot easier to pick away at it
as I want.

Windows compilation fix: move hiro/windows/header.hpp line 18 (header
guard) to line 16 instead.
2016-03-13 11:22:14 +11:00
Tim Allen
29be18ce0c Update to v097r17 release.
byuu says:

Changelog:
- ruby: if DirectSoundCreate fails (no sound device present), return
  false from init instead of crashing
- nall: improved edge case return values for
  (basename,pathname,dirname,...)
- nall: renamed file_system_object class to inode
- nall: varuint_t replaced with VariadicNatural; which contains
  .bit,.bits,.byte ala Natural/Integer
- nall: fixed boolean compilation error on Windows
- WS: popa should not restore SP
- GBA: rewrote the CPU/APU cores to use the .bit,.bits functions;
  removed registers.cpp from each

Note that the GBA changes are extremely major. This is about five hours
worth of extremely delicate work. Any slight errors could break
emulation in extremely bad ways. Let's hold off on extensive testing
until the next WIP, after I do the same to the PPU.

So far ... endrift's SOUNDCNT_X I/O test is failing, although that code
didn't change, so clearly I messed up SOUNDCNT_H somehow ...

To compile on Windows:

1. change nall/string/platform.hpp line 47 to

    return slice(result, 0, 3);

2. change ruby/video.wgl.cpp line 72 to

    auto lock(uint32_t*& data, uint& pitch, uint width, uint height) -> bool {

3. add this line to the very top of hiro/windows/header.cpp:

    #define boolean FuckYouMicrosoft
2016-03-13 11:22:14 +11:00
Tim Allen
810cbdafb4 Update to v097r16 release.
byuu says:

Changelog:
- sfc/ppu/sprite updated to use new .bit(s) functions; masked sizes
  better; added valid flags instead of using magic numbers
- ws/ppu updates to use new .bit(s) functions
- ws/ppu: added line compare interrupt support
- added ws/eeprom; emulation of WS/WSC internal EEPROM and cartridge
  EEPROM (1kbit - 16kbit supported)
- added basic read/write handlers for remaining WS/WSC PPU registers

WS EEPROM emulation is basically a direct copy of trap15's code. Still
some unknown areas in there, but hopefully it's enough to get further
into games that depend on EEPROM support. Note that you'll have to
manually add the eeprom line to the manifest for now, as icarus doesn't
know how to detect EEPROM/sizes yet.

I figured the changes to the SNES PPU sprites would slow it down a tad,
but it actually sped it up. Most of the impact from the integer classes
are gone now.
2016-03-13 11:22:10 +11:00
Tim Allen
4b29f4bad7 Update to v097r15 release.
byuu says:

Changelog:
- higan now uses Natural<Size>/Integer<Size> for its internal types
- Super Famicom emulation now uses uint24 instead of uint for bus
  addresses (it's a 24-bit bus)
- cleaned up gb/apu MMIO writes
- cleaned up sfc/coprocessor/msu1 MMIO writes
- ~3% speed penalty

I've wanted to do that 24-bit bus thing for so long, but have always
been afraid of the speed impact. It's probably going to hurt
balanced/performance once they compile again, but it wasn't significant
enough to harm the accuracy core's frame rate, thankfully. Only lost one
frame per second.

The GBA core handlers are clearly going to take a lot more work. The
bit-ranges will make it substantially easier to handle, though. Lots of
32-bit registers where certain values span multiple bytes, but we have
to be able to read/write at byte-granularity.
2016-02-16 20:32:49 +11:00
Tim Allen
ef65bb862a Update to 20160215 release.
byuu says:

Got it. Wow, that didn't hurt nearly as much as I thought it was going
to.

Dropped from 127.5fps to 123.5fps to use Natural/Integer for
(u)int(8,16,32,64).

That's totally worth the cost.
2016-02-16 20:27:55 +11:00
Tim Allen
0d0af39b44 Update to v097r14 release.
byuu says:

This is a few days old, but oh well.

This WIP changes nall,hiro,ruby,icarus back to (u)int(8,16,32,64)_t.

I'm slowly pushing for (u)int(8,16,32,64) to use my custom
Integer<Size>/Natural<Size> classes instead. But it's going to be one
hell of a struggle to get that into higan.
2016-02-16 20:11:58 +11:00
Tim Allen
6c83329cae Update to v097r13 release.
byuu says:

I refactored my schedulers. Added about ten lines to each scheduler, and
removed about 100 lines of calling into internal state in the scheduler
for the FC,SFC cores and about 30-40 lines for the other cores. All of
its state is now private.

Also reworked all of the entry points to static auto Enter() and auto
main(). Where Enter() handles all the synchronization stuff, and main()
doesn't need the while(true); loop forcing another layer of indentation
everywhere.

Took a few hours to do, but totally worth it. I'm surprised I didn't do
this sooner.

Also updated icarus gmake install rule to copy over the database.
2016-02-09 22:51:12 +11:00
Tim Allen
32a95a9761 Update to v097r12 release.
byuu says:

Nothing WS-related this time.

First, I fixed expansion port device mapping. On first load, it was
mapping the expansion port device too late, so it ended up not taking
effect. I had to spin out the logic for that into
Program::connectDevices(). This was proving to be quite annoying while
testing eBoot (SNES-Hook simulation.)

Second, I fixed the audio->set(Frequency, Latency) functions to take
(uint) parameters from the configuration file, so the weird behavior
around changing settings in the audio panel should hopefully be gone
now.

Third, I rewrote the interface->load,unload functions to call into the
(Emulator)::System::load,unload functions. And I have those call out to
Cartridge::load,unload. Before, this was inverted, and Cartridge::load()
was invoking System::load(), which I felt was kind of backward.

The Super Game Boy really didn't like this change, however. And it took
me a few hours to power through it. Before, I had the Game Boy core
dummying out all the interface->(load,save)Request calls, and having the
SNES core make them for it. This is because the folder paths and IDs
will be different between the two cores.

I've redesigned things so that ICD2's Emulator::Interface overloads
loadRequest and saveRequest, and translates the requests into new
requests for the SuperFamicom core. This allows the Game Boy code to do
its own loading for everything without a bunch of Super Game Boy special
casing, and without any awkwardness around powering on with no cartridge
inserted.

This also lets the SNES side of things simply call into higher-level
GameBoy::interface->load,save(id, stream) functions instead of stabbing
at the raw underlying state inside of various Game Boy core emulation
classes. So things are a lot better abstracted now.
2016-02-08 14:17:59 +11:00
Tim Allen
a89a3da77a Update to v097r11 release.
byuu says:

Alright, well interrupts are in. At least Vblank is.

I also fixed a bug in vector() indexing, MoDRM mod!=3&&reg==6 using SS
instead of DS, opcodes a0-a3 allowing segment override, and added the
"irq_disable" stuff to the relevant opcodes to suppress IRQs after
certain instructions.

But unfortunately ... still no go on Riviera. It's not reading any
unmapped ports, and although it enables Vblank IRQs and they set port
$b4's status bit, the game never sets the IE flag, so no interrupts ever
actually fire. The game does indeed appear to be sitting in a rather
huge loop, which is probably dependent upon some RAM variable being set
from the Vblank IRQ, but I don't know how I'm supposed to be triggering
it.

... I'm really quite stumped here >_>
2016-02-05 08:18:06 +11:00
Tim Allen
7a748e093e Update to v097r10 release.
byuu says:

All 256 instructions implemented fully. Fixed a major bug with
instructions that both read and write to ModRM with displacement.
Riviera now runs into an infinite loop ... possibly crashed, possibly
waiting on interrupts or in to return something. Added a bunch of PPU
settings registers, but nothing's actually rendering with them yet.
2016-02-04 21:29:08 +11:00
Tim Allen
d158c8f293 Update to v097r09 release.
244 of 256 opcodes implemented now, although the interrupt triggering
portions are missing from them still. Much better handling of prefixes
now.

I definitely have a newfound hatepreciation for x86 now >_>
2016-02-03 21:24:58 +11:00
Tim Allen
71bda4144a Update to v097r08 release.
byuu says:

Up to 211 opcodes implemented, with the caveat that the four opcodes
that make up group 3 and group 4 don't do anything yet. Both groups seem
to have some "illegal" instructions in them, so that'll be "fun".

I have a new mechanic in place for opcode prefixes, but it could use
some work still. I also only have it working to override ModRM mem
addressing, but of course it does it in a lot of other places like the
string operations.

Making it about 5.5 million instructions into Gunpey now, but of course
that doesn't mean much. Could be going off the rails at any point due to
CPU bugs or unimplemented ports. Riviera's still crashing.
2016-02-03 21:07:50 +11:00
Tim Allen
ad51f1478e Update to v097r07 release.
byuu says:

26 hours in, 173 instructions implemented. Although the four segment
prefix opcodes don't actually do anything yet. There's less than 256
actual instructions on the 80186, not sure of the exact count.

Gunpey gets around ~8,200 instructions in before hitting an unsupported
opcode (loop). Riviera goes off the rails on a retf and ends up
executing an endless stream of bad opcodes in RAM =( Both games hammer
the living shit out of the in/out ports pretty much immediately.
2016-02-02 21:51:17 +11:00
Tim Allen
d0ddd87e9c Update to v097r06 release.
byuu says:

Man, the 80186 is taking a lot longer to implement than I thought it
would. So far I'm 18 hours into this emulator. Whereas I had Super Mario
Bros fully playable (no sound) in 12 hours for the NES >_>

I refactored all the byte/word variant functions to single functions
that take a size parameter. Cuts the amount of code in half.

Also implemented repz/repnz + movsb/movsw, so Riviera now gets 299
instructions in before dying. Nobody really bothers to explain how the
CPU actually implements these instructions, but I think I have it right:
ignore non-string opcodes that follow rep, invoke the string operations
inside the rep opcodes to prevent interrupts from triggering between the
two (which will be even more fun for segment selector overrides ...)

The next opcode needed is 0xC7, which ... throws ModRM on its head. In
this mode, ModRM is only used to determine the target operand (and it
doesn't use the middle bits for that at all), and the source is an
immediate that follows it. Gonna have to waste a few more hours thinking
about how best to handle that.

Also, disabled HiDPI for higan as well on OS X.
2016-01-31 18:59:44 +11:00
Tim Allen
605a8aa3e9 Update to v097r05 release.
byuu says:

More V30MZ implemented, a lot more to go.

icarus now supports importing WS and WSC games. It expects them to have
the correct file extension, same for GB and GBC.

> Ugh, apparently HiDPI icarus doesn't let you press the check boxes.

I set the flag value in the plist to false for now. Forgot to do it for
higan, but hopefully I won't forget before release.
2016-01-30 17:40:35 +11:00
Tim Allen
a8323d0d2b Update to v097r04 release.
byuu says:

Lots of improvements. We're now able to start executing some V30MZ
instructions. 32 of 256 opcodes implemented so far.

I hope this goes without saying, but there's absolutely no point in
loading WS/WSC games right now. You won't see anything until I have the
full CPU and partial PPU implemented.

ROM bank 2 works properly now, the I/O map is 16-bit (address) x 16-bit
(data) as it should be*, and I have a basic disassembler in place
(adding to it as I emulate new opcodes.)

(* I don't know what happens if you access an 8-bit port in 16-bit mode
or vice versa, so for now I'm just treating the handlers as always being
16-bit, and discarding the upper 8-bits when not needed.)
2016-01-28 22:39:49 +11:00
Tim Allen
d7998b23ef Update to v097r03 release.
byuu says:

So, this WIP starts work on something new for higan. Obviously, I can't
keep it a secret until it's ready, because I want to continue daily WIP
releases, and of course, solicit feedback as I go along.
2016-01-27 22:31:39 +11:00
Tim Allen
344e63d928 Update to v097r02 release.
byuu says:

Note: balanced/performance profiles still broken, sorry.

Changelog:
- added nall/GNUmakefile unique() function; used on linking phase of
  higan
- added nall/unique_pointer
- target-tomoko and {System}::Video updated to use
  unique_pointer<ClassName> instead of ClassName* [1]
- locate() updated to search multiple paths [2]
- GB: pass gekkio's if_ie_registers and boot_hwio-G test ROMs
- FC, GB, GBA: merge video/ into the PPU cores
- ruby: fixed ~AudioXAudio2() typo

[1] I expected this to cause new crashes on exit due to changing the
order of destruction of objects (and deleting things that weren't
deleted before), but ... so far, so good. I guess we'll see what crops
up, especially on OS X (which is already crashing for unknown reasons on
exit.)

[2] right now, the search paths are: programpath(), {configpath(),
"higan/"}, {localpath(), "higan/"}; but we can add as many more as we
want, and we can also add platform-specific versions.
2016-01-25 22:27:18 +11:00
Tim Allen
f1ebef2ea8 Update to v097r01 release.
byuu says:

A minor WIP to get us started.

Changelog:
- System::Video merged to PPU::Video
- System::Audio merged to DSP::Audio
- System::Configuration merged to Interface::Settings
- created emulator/emulator.cpp and accompanying object file for shared
  code between all cores

Currently, emulator.cpp just holds a videoColor() function that takes
R16G16B16, performs gamma/saturation/luma adjust, and outputs
(currently) A8R8G8B8. It's basically an internal function call for cores
to use when generating palette entries. This code used to exist inside
ui-tomoko/program/interface.cpp, but we have to move it internal for
software display emulation. But in the future, we could add other useful
cross-core functionality here.
2016-01-23 18:29:34 +11:00
Tim Allen
1fdd0582fc Update to v097 release.
byuu says:

This release features improvements to all emulation cores, but most
substantially for the Game Boy core. All of blargg's test ROMs that pass
in gambatte now either pass in higan, or are off by 1-2 clocks (the
actual behaviors are fully emulated.) I consider the Game Boy core to
now be fairly accurate, but there's still more improvements to be had.

Also, what's sure to be a major feature for some: higan now has full
support for loading and playing ordinary ROM files, whether they have
copier headers, weird extensions, or are inside compressed archives. You
can load these games from the command-line, from the main Library menu
(via Load ROM Image), or via drag-and-drop on the main higan window. Of
course, fans of game folders and the library need not worry: that's
still there as well.

Also new, you can drop the (uncompressed) Game Boy Advance BIOS onto the
higan main window to install it into the correct location with the
correct file name.

Lastly, this release technically restores Mac OS X support. However,
it's still not very stable, so I have decided against releasing binaries
at this time. I'd rather not rush this and leave a bad first impression
for OS X users.

Changelog (since v096):
- higan: project source code hierarchy restructured; icarus directly
  integrated
- higan: added software emulation of color-bleed, LCD-refresh,
  scanlines, interlacing
- icarus: you can now load and import ROM files/archives from the main
  higan menu
- NES: fixed manifest parsing for board mirroring and VRC pinouts
- SNES: fixed manifest for Star Ocean
- SNES: fixed manifest for Rockman X2,X3
- GB: enabling LCD restarts frame
- GB: emulated extra OAM STAT IRQ quirk required for GBVideoPlayer
  (Shonumi)
- GB: VBK, BGPI, OBPI are readable
- GB: OAM DMA happens inside PPU core instead of CPU core
- GB: fixed APU length and sweep operations
- GB: emulated wave RAM quirks when accessing while channel is enabled
- GB: improved timings of several CPU opcodes (gekkio)
- GB: improved timings of OAM DMA refresh (gekkio)
- GB: CPU uses open collector logic; return 0xFF for unmapped memory
  (gekkio)
- GBA: fixed sequencer enable flags; fixes audio in Zelda - Minish Cap
  (Jonas Quinn)
- GBA: fixed disassembler masking error (Lioncash)
- hiro: Cocoa support added; higan can now be compiled on Mac OS X 10.7+
- nall: improved program path detection on Windows
- higan/Windows: moved configuration data from %appdata% to
  %localappdata%
- higan/Linux,BSD: moved configuration data from ~/.config/higan to
  ~/.local/higan
2016-01-17 19:59:25 +11:00
Tim Allen
12df278c5b Update to v096r08 release.
byuu says:

Changelog:
- FC: scanline emulation support added
- SFC: balanced profile compiles again
- SFC: performance profile compiles again
- GB,GBC: more fixes to pass blargg's 07, 08, 11 APU tests
- tomoko: added input loss { pause, allow-input } options
- tomoko: refactored settings video menu options to { Video Scale, Video
  Emulation, Video Shader }
- icarus: connected { About, Preferences, Quit } application menu options
2016-01-15 21:28:51 +11:00
Tim Allen
cec33c1d0f Update to v096r07 release.
byuu says:

Changelog:
- configuration files are now stored in localpath() instead of configpath()
- Video gamma/saturation/luminance sliders are gone now, sorry
- added Video Filter->Blur Emulation [1]
- added Video Filter->Scanline Emulation [2]
- improvements to GBA audio emulation (fixes Minish Cap) [Jonas Quinn]

[1] For the Famicom, this does nothing. For the Super Famicom, this
performs horizontal blending for proper pseudo-hires translucency. For
the Game Boy, Game Boy Color, and Game Boy Advance, this performs
interframe blending (each frame is the average of the current and
previous frame), which is important for things like the GBVideoPlayer.

[2] Right now, this only applies to the Super Famicom, but it'll come to
the Famicom in the future. For the Super Famicom, this option doesn't
just add scanlines, it simulates the phosphor decay that's visible in
interlace mode. If you observe an interlaced game like RPM Racing on
a real SNES, you'll notice that even on perfectly still screens, the
image appears to shake. This option emulates that effect.

Note 1: the buffering right now is a little sub-optimal, so there will
be a slight speed hit with this new support. Since the core is now
generating native ARGB8888 colors, it might as well call out to the
interface to lock/unlock/refresh the video, that way it can render
directly to the screen. Although ... that might not be such a hot idea,
since the GBx interframe blending reads from the target buffer, and that
tends to be a catastrophic option for performance.

Note 2: the balanced and performance profiles for the SNES are
completely busted again. This WIP took 6 1/2 hours, and I'm exhausted.
Very much not looking forward to working on those, since those two have
all kinds of fucked up speedup tricks for non-interlaced and/or
non-hires video modes.

Note 3: if you're on Windows and you saved your system folders somewhere
else, now'd be a good time to move them to %localappdata%/higan
2016-01-15 21:07:57 +11:00
Tim Allen
3414c8c8df Update to v096r06 release.
byuu says:

This WIP finally achieves the vision I've had for icarus.

I also fixed a mapping issue with Cx4 that, oddly enough, only caused
the "2" from the Mega Man X2 title screen to disappear.

[Editor's note - "the vision for icarus" was described in a separate,
public forum post: http://board.byuu.org/phpbb3/viewtopic.php?p=20584
Quoting for posterity:

    icarus is now a full-fledged part of higan, and will be bundled with
    each higan WIP as well. This will ensure that in the future, the
    exact version of icarus you need to run higan will be included right
    along with it. As of this WIP, physical manifest files are now truly
    and entirely optional.

    From now on, you can associate your ROM image files with higan's
    main binary, or drop them directly on top of it, to load and play
    your games.

    Furthermore, there are two new menu options that appear under the
    library menu when icarus is present:

    - "Load ROM File ..." => gives you a single-file selection dialog to
      import (and if possible) run the game
    - "Import ROM Files ..." => gives you a multi-file import dialog
      with checkboxes to pull in multiple games at once

    Finally, as before, icarus can generate manifest.bml files for
    folders that lack them.

    For people who like the game folder and library system, nothing's
    changed. Keep using higan as you have been.

    For people who hate it, you can now use higan like your classic
    emulators. Treat the "Library->{System Name}" entries as your
    "favorites" list: the games you actually play. Treat the
    "Library->Load ROM" as your standard open file dialog in other
    emulators. And finally, treat "Advanced->Game Library" as your save
    data path for cheat codes, save states, save RAM, etc.

]
2016-01-15 21:07:37 +11:00
Tim Allen
82ec876302 Update to v096r05 release.
byuu says:

Changelog:
- GB: re-enabling the LCD resets the display to LY=0,LX=0 [1]
- GB: emulated new findings (as of today!) for a DMG quirk that triggers
  an extra OAM STAT IRQ when Vblank STAT IRQs are off
- GB: made VBK, BGPI, OBPI readable
- GB: fixed APU length operations
- GB: fixed APU sweep operations
- NES: fixed cartridge/ -> board/ manifest lookups for mirroring/pinous
- hiro/Cocoa: added endrift's plist keys

Fixed:
- Astro Rabby is fully playable, even the title screen works correctly
- Bomb Jack is fully playable
- Kirby's Dream Land 2 intro scrolling first scanline of Rick is now fixed
- GBVideoPlayer functions correctly [2]
- Shin Megami Tensei: Devichil series regression fixed

[1] doesn't pass oam_bug-2/1-lcd_sync; because it seems to want
LY=0,LX>0, and I can't step the PPU in a register write as it's not
a state machine; the effect is emulated, it just starts the frame a tiny
bit sooner. blargg's testing is brutal, you can't be even one cycle off
or the test will fail.

[2] note that you will need the GBC Display Emulation shader from
hunterk's repository, or it will look like absolute shit. The
inter-frame blending is absolutely critical here.
2016-01-12 22:08:34 +11:00
Tim Allen
72b6a8b32e Update to v096r04 release.
byuu says:

Changelog:
- fixed S-DD1 RAM writes (Star Ocean audio fixed)
- applied all of the DMG test ROM fixes discussed earlier; passes many
  more test ROMs now
- at least until the GBVideoPlayer is working: for debugging purposes,
  CPU/PPU single-step now instead of sync just-in-time (~30% slower)
- fixed OS X crash on NSTextView (hopefully, would be very odd if not)

Unfortunately passing these test ROMs caused my favorite GB/GBC game to
break all of its graphics =(
Shin Megami Tensei - Devichil - Kuro no Sho (Japan) is all garbled now.
I'm really quite bummed by this ... but I guess I'll go through and
revert r04's fixes one at a time until I find what's causing it.

On the plus side, Astro Rabby is playable now. Still acts weird when
pressing B/A on the first screen, but the start button will start the
game.

EDIT: got it. Shin Megami Tensei - Devichil requires FF4F (VBK) to be
readable. Before, it was always returning 0x00. With my return 0xFF
patch, that broke. But it should be returning the VBK value, which also
fixes it. Also need to handle FF68/FF6A reads. Was really hoping that'd
help GBVideoPlayer too, but nope. It doesn't read any of those three
registers.
2016-01-11 21:31:30 +11:00
Tim Allen
653bb378ee Update to v096r03 release.
byuu says:

Changelog:
- fixed icarus to save settings properly
- fixed higan's full screen toggle on OS X
- increased "Add Codes" button width to avoid text clipping
- implemented cocoa/canvas.cpp
- added 1s delay after mapping inputs before re-enabling the window
  (wasn't actually necessary, but already added it)
- fixed setEnabled(false) on Cocoa's ListView and TextEdit widgets
- updated nall::programpath() to use GetModuleFileName on Windows
- GB: system uses open collector logic, so unmapped reads return 0xFF,
  not 0x00 (passes blargg's cpu_instrs again) [gekkio]
2016-01-08 20:23:46 +11:00
Tim Allen
0b923489dd Update to 20160106 OS X Preview for Developers release.
byuu says:

New update. Most of the work today went into eliminating hiro::Image
from all objects in all ports, replacing with nall::image. That took an
eternity.

Changelog:
- fixed crashing bug when loading games [thanks endrift!!]
- toggling "show status bar" option adjusts window geometry (not
  supposed to recenter the window, though)
- button sizes improved; icon-only button icons no longer being cut off
2016-01-07 19:17:15 +11:00
Tim Allen
4d193d7d94 Update to v096r02 (OS X Preview for Developers) release.
byuu says:

Warning: this is not for the faint of heart. This is a very early,
unpolished, buggy release. But help testing/fixing bugs would be greatly
appreciated for anyone willing.

Requirements:
- Mac OS X 10.7+
- Xcode 7.2+

Installation Commands:

    cd higan
    gmake -j 4
    gmake install
    cd ../icarus
    gmake -j 4
    gmake install

(gmake install is absolutely required, sorry. You'll be missing key
files in key places if you don't run it, and nothing will work.)

(gmake uninstall also exists, or you can just delete the .app bundles
from your Applications folder, and the Dev folder on your desktop.)

If you want to use the GBA emulation, then you need to drop the GBA BIOS
into ~/Emulation/System/Game\ Boy\ Advance.sys\bios.rom

Usage:
You'll now find higan.app and icarus.app in your Applications folders.
First, run icarus.app, navigate to where you keep your game ROMs. Now
click the settings button at the bottom right, and check "Create
Manifests", and click OK. (You'll need to do this every time you run
icarus because there's some sort of bug on OSX saving the settings.) Now
click "Import", and let it bring in your games into ~/Emulation.

Note: "Create Manifests" is required. I don't yet have a pipe
implementation on OS X for higan to invoke icarus yet. If you don't
check this box, it won't create manifest.bml files, and your games won't
run at all.

Now you can run higan.app. The first thing you'll want to do is go to
higan->Preferences... and assign inputs for your gamepads. At the very
least, do it for the default controller for all the systems you want to
emulate.

Now this is very important ... close the application at this point so
that it writes your config file to disk. There's a serious crashing bug,
and if you trigger it, you'll lose your input bindings.

Now the really annoying part ... go to Library->{System} and pick the
game you want to play. Right now, there's a ~50% chance the application
will bomb. It seems the hiro::pListView object is getting destroyed, yet
somehow the internal Cocoa callbacks are being triggered anyway. I don't
know how this is possible, and my attempts to debug with lldb have been
a failure :(

If you're unlucky, the application will crash. Restart and try again. If
it crashes every single time, then you can try launching your game from
the command-line instead. Example:

    open /Applications/higan.app \
	--args ~/Emulation/Super\ Famicom/Zelda3.sfc/

Help wanted:
I could really, really, really use some help with that crashing on game
loading. There's a lot of rough edges, but they're all cosmetic. This
one thing is pretty much the only major show-stopping issue at the
moment, preventing a wider general audience pre-compiled binary preview.
2016-01-07 19:17:15 +11:00
Tim Allen
47d4bd4d81 Update to v096r01 release.
byuu says:

Changelog:

- restructured the project and removed a whole bunch of old/dead
  directives from higan/GNUmakefile
- huge amounts of work on hiro/cocoa (compiles but ~70% of the
  functionality is commented out)
- fixed a masking error in my ARM CPU disassembler [Lioncash]
- SFC: decided to change board cic=(411,413) back to board
  region=(ntsc,pal) ... the former was too obtuse

If you rename Boolean (it's a problem with an include from ruby, not
from hiro) and disable all the ruby drivers, you can compile an
OS X binary, but obviously it's not going to do anything.

It's a boring WIP, I just wanted to push out the project structure
change now at the start of this WIP cycle.
2015-12-30 17:54:59 +11:00
Tim Allen
27660505c8 Update to v096 release.
byuu says:

Changelog (since v095):

- higan: absolutely massive amounts of coding style updates; probably
  150 hours of work here
- higan: manifest format updated for much greater consistency and
  simplicity
- higan: wrote popen() replacement to suppress console flashing when
  loading games via icarus
- icarus: now includes external database with mapping information for
  all verified games
- icarus: added support for importing Campus Challenge '92 and Powerfest
  '94
- icarus: merged settings.bml with higan; changing library path in one
  affects the other now
- SFC: added MSU1 audio resume support
- SFC: added new expansion port device (eBoot); simulation of SNES-Boot
  hardware
- SFC: expansion port device can now be selected from system menu
- SFC: updated handling of open bus (thanks to Exophase for the design
  idea)
- SFC: "BS-X Satellaview" library folder renamed to "BS Memory"
- GBA: fixed 8-bit SRAM reading/writing
- GBA: PRAM is 16-bits wide
- GBA: VRAM OBJ 8-bit writes are ignored
- GBA: BGnCNT unused bits are writable
- GBA: BG(0,1)CNT can't set d13
- GBA: BLDALPHA is readable (fixes many games including Donkey Kong
  Country)
- GBA: DMA masks &~1/Half, &~3/Word
- GBA: fixed many other I/O register reads; gets perfect score on
  endrift's I/O tests
- GBA: fixed caching of r(d) to pass armwrestler tests (Jonas Quinn)
- GBA: blocked DMA to/from BIOS region (Cydrak)
- GBA: fixed sign-extend and rotate on ldrs instructions (Cydrak)
- tomoko: added "Ignore Manifests" option to advanced settings panel
- tomoko: re-added support for ruby/quark video shaders
- tomoko: improved aspect correction behavior
- tomoko: added new tool, "Manifest Viewer" (mostly useful for
  developers)
- ruby: fixed mouse capture clipping on Windows (Cydrak)
- ruby: won't crash when using OpenGL 3.2 Linux driver with only OpenGL
  2.0 available
- ruby: added Linux fallback OpenGL 2.0 driver (not compiled in by
  default)
- ruby: added preliminary WASAPI driver (not compiled in by default, due
  to bugginess)
- hiro: fixed the appearance of Button and ListView::CheckButton on
  Windows classic
- hiro: added missing return values from several functions (fixes
  crashes with Clang)
2015-12-21 23:21:31 +11:00
Tim Allen
702b657e75 Update to v095r18 release.
byuu says:

Changelog:
- replaced popen() with execvp() / CreateProcess()
- suppressed (hid) controllers with no mappable inputs from the input
  settings panel

This gets rid of the window flashing when loading games with
higan+icarus. And hiding of empty devices should be a huge usability
improvement, especially since "None" was appearing at the top of the
list before for the SNES.
2015-12-21 20:16:47 +11:00
Tim Allen
0253db8685 Update to higan and icarus v095r17 release.
byuu says:

higan supports Event mapping again.

Further, icarus can now detect Event ROMs and MSU1 games.

Event ROMs must be named "program.rom", "slot-(1,2,3).rom" MSU1 games
must contain "msu1.rom"; and tracks must be named "track-#.pcm"

When importing the CC'92, PF'94 ROMs, the program.rom and
slot-(1,2,3).rom files must be concatenated. The DSP firmware can
optionally be separate, but I'd recommend you go ahead and merge it all
to one file. Especially since that common "higan DSP pack" floating
around on the web left out the DSP1 ROMs (only has DSP1B) for god knows
what reason.

There is no support for loading "game.sfc+game.msu+game-*.pcm", because
I'm not going to support trying to pull in all of those files through
importing. Games will have to be distributed as game folders to use
MSU1. The MSU1 icarus support is simply so your game folders won't
require an unstable manifest.bml file to be played. So once they're in
there, they are good for life.

Note: the Event sizes in icarus' SFC heuristics are wrong for appended
firmware. Change from 0xXX8000 to 0xXX2000 and it works fine. Will be
fixed in r18.

Added Sintendo's flickering fixes. The window one's a big help for
regular controls, but the ListView double buffering does nothing for me
on Windows 7 :( Fairly sure I know why, but too lazy to try and fix that
now.

Also fixes the mMenu thing.
2015-12-20 13:53:40 +11:00
Tim Allen
2a4eb1cfc8 Update to higan and icarus v095r16 release.
byuu says (in the WIP forum):

    Changelog:
    - satellaviewcartridge/SatellaviewCartridge is now bsmemory/BSMemory
    - Emulation/BS-X Satellaview/ is now Emulation/BS Memory/
    - masking is in for MCC's mcu (awful hack in the code, but that's
      temporary)
    - BS Memory types are now "flash" or "mrom"
    - fixed loading Same Game - Tengai Hen
    - icarus fixed up a lot; can load database entries for any supported
      media type now (only the SFC DB exists currently)

    mMenu::remove() fix will be in the next WIP.

byuu says (in the public beta thread):

    Changelog:
    - GBA emulation accuracy improved quite a bit
    - video shaders are supported once again
    - icarus shares settings.bml with higan; changing library path in
      one now affects the other
    - icarus manifest generation now uses my SNES game dumping database
      for perfect mapping of US games
    - major overhaul to manifest file format. As long as you have
      v095-style folders without manifest.bml, you will be fine
      - if not, go to higan->settings->configuration->advanced and check
	"Ignore Manifests" before loading your first game
    - new "Manifest Viewer" tool (not really meant for regular users;
      more of a developer tool)
    - experimental (but disabled in the binary) WASAPI driver. Help
      stabilizing it would be *greatly* appreciated!
    - lots of other stuff
2015-12-19 21:42:18 +11:00
Tim Allen
bd628de3cf Update to higan and icarus v095r15 release.
r13 and r14 weren't posted as individual releases, but their changelogs
were posted.

byuu says about r13:

    I'm not going to be posting WIPs for r13 and above for a while.

    The reason is that I'm working on the major manifest overhaul I've
    discussed previously on the icarus subforum.

    I'm recreating my boards database from scratch using the map files
    and the new map analyzer. The only games that will load are ones
    I've created board definitions for, and updated
    sfc/cartridge/markup.cpp to parse. Once I've finished all the
    boards, then I'll update the heuristics.

    Then finally, I'll sync the syntax changes over to the fc, gb, gba
    cores.

    Once that's done, I'll start posting WIPs again, along with a new
    build of icarus.

    But I'll still post changelogs as I work through things.

    Changelog (r13):
    - preservation: created new database-builder tool (merges
      region-specific databases with boards)
    - icarus: support new, external database format
      (~/.config/icarus/Database/(Super Famicom.bml, ...)
    - added 1A3B-(10,11,12); 1A3B-20

byuu says about r14:

    r14 work:

    I successfully created mappings for every board used in the US set.

    I also updated icarus' heuristics to use the new mappings, and
    created ones there for the boards that are only in the JP set.

    Then I patched icarus to support pulling games out of the database
    when it's used on a game folder to generate a manifest file.

    Then I updated a lot of code in higan/sfc to support the new mapping
    syntax. sfc/cartridge/markup.cpp is about half the size it used to
    be with the new mappings, and I was able to kill off both map/id and
    map/select entirely.

    Then I updated all four emulated systems (and both subsystems) to
    use "board" as the root node, and harmonized their syntax (made them
    all more consistent with each other.)

    Then I added a manifest viewer to the tools window+menu. It's kind
    of an advanced user feature, but oh well. No reason to coddle people
    when the feature is very useful for developers. The viewer will show
    all manifests in order when you load multi-cart games as well.

    Still not going to call any syntax 100% done right now, but
    thankfully with the new manifest-free folders, nobody will have to
    do anything to use the new format. Just download the new version and
    go.

    The Super Famicom Event stuff is currently broken (CC92/PF94
    boards). That's gonna be fun to support.

byuu says about r15:

    EDIT: small bug in icarus with heuristics. Edit
    core/super-famicom.cpp line 27:

	if(/*auto*/ markup = cartridge.markup) {

    Gotta remove that "auto" so that it returns valid markup.

    Resolved the final concerns I had with the new manifest format.

    Right now there are two things that are definitely broken: MCC (BS-X
    Town cart) and Event (CC '92 and PF'94).
    And there are a few things that are untested: SPC7110, EpsonRTC,
    SharpRTC, SDD1+RAM, SufamiTurbo, BS-X slotted carts.
2015-12-19 20:02:06 +11:00
Tim Allen
2c53d5fbc0 Update to v095r12 release.
byuu says:

Got it. They broke in r05.

Changelog:
- fixed typo in sfc/cpu/timing.cpp that was breaking coprocessor games
  with clocks
- updated sfc/coprocessor/hitachidsp to not access Bus directly
2015-12-15 20:30:26 +11:00
Tim Allen
f2a416aea9 Update to v095r11 release.
byuu says:

Changelog:
- SFC: "uint8 read(uint addr)" -> "uint8 read(uint addr, uint8 data)"
- hiro: mHorizontalLayout::setGeometry() return value
- hiro/GTK: ListView,TreeView::setFocused() does not grab focus of first
  item

Notes:
- nall/windows/utf8.hpp needs using uint = unsigned; at the top to
  compile
- sfc/balanced, sfc/performance won't compile yet

Seems Cx4 games broke a while back. Not from this WIP, either. I'll go
back and find out what's wrong now.
2015-12-14 20:41:06 +11:00
Tim Allen
78d49d3873 Update to v095r10 release.
byuu says:

Changelog:

- int_t<bits> replaced with Integer<bits>
- uint_t<bits> replaced with Natural<bits>
- fixed "Synchronize Audio" menu option that broke recently
- all of sfc/performance ported to "auto function() -> return;" syntax

With this WIP, all of higan is finally ported over to the new function
declaration syntax. Thank the gods.

There's still going to be periodic disruption for diffs from porting
over signed->int, unsigned->uint, and whatever we come up with for the
new Natural<> and Integer<> classes. But the worst of it's behind us
now.
2015-12-07 08:11:41 +11:00
Tim Allen
65a3306ad5 Update to v095r09 release.
byuu says:

Changelog:

- all of fc/ ported to "auto function() -> return;" syntax
  - (includes all of cartridge/board and cartridge/chip as well; even
    though they're all deprecated)
- sfc balanced profile ported to "auto function() -> return;" syntax
- sfc balanced and performance profiles compile again
- Linux always gets -ldl
- removed arch=x86 logic from nall/GNUmakefile, as TDM/GCC64 can't
  produce bug-free 32-bit binaries anyway

The only code that continues to use the old function syntax is the SFC
performance core, obscure parts of nall that higan doesn't use, and the
pieces of code that weren't written by me (blargg's SFC-DSP, Ryphecha's
sinc resampler, and OV2's xaudio2 header file.)

I was too burned out to finish it tonight. The above was about four
hours straight of non-stop typing. Really can't wait to be done with
this once and for all.
2015-12-05 16:44:49 +11:00
Tim Allen
a219f9c121 Update to v095r08 release.
byuu says:

Changelog:
- added preliminary WASAPI driver (it's really terrible, though. Patches
  most welcome.)
- all of processor/ updated to auto fn() -> ret syntax
- all of gb/ updated to auto fn() -> ret syntax

If you want to test the WASAPI driver, then edit ui-tomoko/GNUmakefile,
and replace audio.xaudio2 with audio.wasapi Note that the two drivers
are incompatible and cannot co-exist (yet. We can probably make it work
in the future.)

All that's left for the auto fn() -> ret syntax is the NES core and the
balanced/performance SNES components. This is kind of a big deal because
this syntax change causes diffs between WIPs to go crazy. So the sooner
we get this done and out of the way, the better. It's also nice from
a consistency standpoint, of course.
2015-11-21 18:36:48 +11:00
Tim Allen
6adfe71836 Update to icarus 20151117.
byuu says:

This release adds a settings dialog that lets you control the library
path, optionally generate manifest.bml files, and optionally bypass the
internal games database (so far this is only the US SNES set.)

Also, the settings.bml file can exist in the same folder as the binary
now (portable mode). Plus it can share the same config file as
higan/tomoko itself does. This will allow you to change the library
location in either program and have it affect the other program as well.
It's a bit hackish, but it works >_>

Note: don't use this with higan v095.06 or earlier, or bad things will
happen.
2015-11-19 20:27:56 +11:00
Tim Allen
41c478ac4a Update to v095r07 release.
byuu says:

Changelog:
- entire GBA core ported to auto function() -> return; syntax
- fixed GBA BLDY bug that was causing flickering in a few games
- replaced nall/config usage with nall/string/markup/node
  - this merges all configuration files to a unified settings.bml file
- added "Ignore Manifests" option to the advanced setting tab
  - this lets you keep a manifest.bml for an older version of higan; if
    you want to do regression testing

Be sure to remap your controller/hotkey inputs, and for SNES, choose
"Gamepad" from "Controller Port 1" in the system menu. Otherwise you
won't get any input. No need to blow away your old config files, unless
you want to.
2015-11-16 19:38:05 +11:00
Tim Allen
40f4b91000 Update to v095r06 release.
byuu says:

Changelog:
- fixed I/O register reads; perfect score on endrift's I/O tests now
- fixed mouse capture clipping on Windows [Cydrak]
- several hours of code maintenance work done on the SFC core

All higan/sfc files should now use the auto fn() -> ret; syntax. Haven't
converted all unsigned->uint yet. Also, probably won't do sfc/alt as
that's mostly just speed hack stuff.

Errata:
- forgot auto& instead of just auto on SuperFamicom::Video::draw_cursor,
  which makes Super Scope / Justifier crash. Will be fixed in the next
  WIP.
2015-11-14 11:52:51 +11:00
Tim Allen
6d9f43a37b Update to v095r05 release.
byuu says:

Changelog:
- GBA: lots of emulation improvements
- PPU PRAM is 16-bits wide
- DMA masks &~1/Half, &~3/Word
- VRAM OBJ 8-bit writes are ignored
- OAM 8-bit writes are ignored
- BGnCNT unused bits are writable*
- BG(0,1)CNT can't set the d13
- BLDALPHA is readable (fixes Donkey Kong Country, etc)
- SNES: lots of code cleanups
- sfc/chip => sfc/coprocessor
- UI: save most recent controller selection

GBA test scores: 1552/1552, 37/38, 1020/1260

(* forgot to add the value to the read function, so endrift's I/O tests
for them will fail. Fixed locally.)

Note: SNES is the only system with multiple controller/expansion port
options, and as such is the only one with a "None" option. Because it's
shared by the controller and expansion port, it ends up sorted first in
the list. This means that on your first run, you'll need to go to Super
Famicom->Controller Port 1 and select "Gamepad", otherwise input won't
work.

Also note that changing the expansion port device requires loading a new
cart. Unlike controllers, you aren't meant to hotplug expansion port
devices.
2015-11-12 21:15:03 +11:00
Tim Allen
d1ffd59c29 Update to v095r04 release.
Changelog:
- S-SMP core code style updated
- S-SMP loads reset vector from IPLROM ($fffe-ffff)
- sfc/base => sfc/expansion
- system/input => system/device
- added expansion/eBoot (simulation of defparam's SNES-Boot device)
- expansion port device can now be selected from Super Famicom menu
  option
- improved GBA MROM/SRAM reading

endrift's memory test is up to 1388/1552.

Note: I added the expansion port devices to the same group as controller
ports. I also had to move "None" to the top of the list. Before v096,
I am going to have to add caching of port selections to the
configuration file, check the proper default item in the system menu,
and remove the items with no mappings from the input configuration
window. Lots of work >_>
2015-11-10 22:11:29 +11:00
Tim Allen
0fe55e3f5b Update to v095r03 release and icarus 20151107.
byuu says:

Note: you will need the new icarus (and please use the "no manifest"
system) to run GBA games with this WIP.

Changelog:
- fixed caching of r(d) to pass armwrestler tests [Jonas Quinn]
- DMA to/from GBA BIOS should fail [Cydrak]
- fixed sign-extend and rotate on ldrs instructions [Cydrak]
- fixed 8-bit SRAM reading/writing [byuu]
- refactored GBA/cartridge
  - cartridge/rom,ram.type is now cartridge/mrom,sram,eeprom,flash
  - things won't crash horribly if you specify a RAM size larger than
    the largest legal size in the manifest
  - specialized MROM / SRAM classes replace all the shared read/write
    functions that didn't work right anyway
- there's a new ruby/video.glx2 driver, which is not enabled by default
  - use this if you are running Linux/BSD, but don't have OpenGL 3.2 yet
  - I'm not going to support OpenGL2 on Windows/OS X, because these OSes
    don't ship ancient video card drivers
- probably more. What am I, clairvoyant? :P

For endrift's tests, this gets us to 1348/1552 memory and 1016/1260
timing. Overall, this puts us back in second place. Only no$ is ahead
on memory, but bgba is even more ahead on timing.
2015-11-10 22:11:29 +11:00
Tim Allen
b42ab2fcb3 Update to v095r02 release.
byuu says:

Aspect correction is fixed now. Works way better than in v095 official.

It's still force-enabled in fullscreen mode. The idea of disabling it is
that it looks bad at 2x scale. But when you're fullscreen with a minimum
of 4x scale, there's no reason not to enable it.

It won't turn on at all for GB/C/A anymore. And I dropped the cute
attempt at making the aspect prettier on 2560x1600 monitors, so it'll be
the stock 8:7 across the board now for S/NES.

Also, the aspect correction will affect the window even when a game's
not loaded now, so the size won't bounce around as you change games in
windowed mode between GB/C/A and S/NES.

...

I also enhanced the ruby/glx driver. It won't crash if OpenGL 3.2 isn't
available anymore (fails safely ... had to capture the Xlib error
handler to suppress that), and it defaults to the MESA glXSwapInterval
before the SGI version. Because apparently the MESA version defines the
SGI version, but makes it a no-op. What. The. Fuck. right? But whatever,
reordering the enumerations fixes the ability to toggle Vsync on AMD
GPUs now.

...

Video shaders are back again. If you are using the OpenGL driver, you'll
see a "Video Shaders" menu beneath the "Video Filters" menu (couldn't
merge it with the filters due to hiro now constructing menu ordering
inside the header files. This works fine though.)

You want either "higan.exe" + "Video Shaders/" or "~/.local/bin/tomoko"
+ "~/.local/tomoko/Video Shaders/"
2015-11-10 22:07:34 +11:00
Tim Allen
8476a12deb Update to v095r01 release (open beta).
byuu says:

Changelog:
- added MSU1 resume support
- updated sfc/dsp, sfc/controller to match my coding style
- fixed hiro/Windows Button and ListView::CheckButton in Windows Classic
  mode
2015-10-10 13:16:12 +11:00
Tim Allen
b0e862613b Update to v095 release.
byuu says:

After 20 months of development, higan v095 is released at long last!

The most notable feature is vastly improved Game Boy Advance emulation.
With many thanks to endrift, Cydrak, Jonas Quinn and jchadwick, this
release contains substantially improved CPU timings and many bugfixes.
Being one of only two GBA emulators to offer ROM prefetch emulation,
higan is very near mGBA in terms of accuracy, and far ahead of all
others. As a result of these fixes, compatibility is also much higher
than in v094.

There are also several improvements to SNES emulation. Most
significantly is support for mid-scanline changes to the background mode
in the accuracy profile.

Due to substantial changes to the user interface library used by higan,
this release features yet again a brand-new UI. With the exception of
video shaders and NSS DIP switch selection, it is at feature-parity with
the previous UI. It also offers some new features that v094 lacked.

The cheat code database has also been updated to the latest version by
mightymo.
2015-10-08 22:04:42 +11:00
Tim Allen
b113ecb5a3 Deleting ananke and shaders.
ananke has been superseded by icarus.

The new tomoko UI does not support shaders, and if it ever does it will
probably use another format, so not much point keeping the old files
around.
2015-10-08 22:02:22 +11:00
Tim Allen
bc5ad4a1cd Update to icarus_20151002.
byuu says:

- fixes checkboxes (-again- again [*again*])
- won't check folders with select all / unselect all
- won't crash anymore if the SNES ROM image is too small (Saturday Night
  Slam Masters was crashing it before due to DB size error)
- corrected heuristics for Sufami Turbo base cart (mirrors the
  absurdities of the real cart precisely, since it's one of a kind)
- corrected a few DB issues (BS-X name + PSRAM (again [*again*]), SNSM,
  LAH) (_again_)
  - these are temporary. Monkey patched in the generated .hpp source
    rather than the actual DB
  - not going to fix the SFT sizes because I want to verify what
    happened there first
2015-10-03 16:25:39 +10:00
Tim Allen
1a90e206e0 Update to v094r44b release (open beta).
byuu says:

With any luck, this will be the final WIP before v095. If all looks
good, this will be identical to v095. But if we hit some major issues,
I'll try and fix those first.

The most notable part of this release is probably Jonas Quinn's fix for
the unmapped regions of the GBA memory map. This allows games like Mario
& Luigi and Zelda: Minish Cap to (hopefully) be fully playable now.

icarus now supports my game database, so all games I've dumped will be
emulated with bit-perfect memory maps and native-language game titles.
2015-10-01 20:04:30 +10:00
Tim Allen
483fc81356 Update to v094r44 release.
byuu says:

Changelog:
- return open bus instead of mirroring addresses on the bus (fixes
  Mario&Luigi, Minish Cap, etc) [Jonas Quinn]
- add boolean flag to load requests for slotted game carts (fixes slot
  load prompts)
- rename BS-X Town cart from psram to ram
- icarus: add support for game database

Note: I didn't rename "bsx" to "mcc" in the database for icarus before
uploading that. But I just fixed it locally, so it'll be in the next
WIP. For now, make it create the manifest for you and then rename it
yourself. I did fix the PSRAM size to 256kbit.
2015-10-01 20:00:28 +10:00
Tim Allen
0c87bdabed Update to v094r43 release.
byuu says:

Updated to compile with all of the new hiro changes. My next step is to
write up hiro API documentation, and move the API from alpha (constantly
changing) to beta (rarely changing), in preparation for the first stable
release (backward-compatible changes only.)

Added "--fullscreen" command-line option. I like this over
a configuration file option. Lets you use the emulator in both modes
without having to modify the config file each time.

Also enhanced the command-line game loading. You can now use any of
these methods:

    higan /path/to/game-folder.sfc
    higan /path/to/game-folder.sfc/
    higan /path/to/game-folder.sfc/program.rom

The idea is to support launchers that insist on loading files only.

Technically, the file can be any name (manifest.bml also works); the
only criteria is that the file actually exists and is a file, and not
a directory. This is a requirement to support the first version (a
directory lacking the trailing / identifier), because I don't want my
nall::string class to query the file system to determine if the string
is an actual existing file or directory for its pathname() / dirname()
functions.

Anyway, every game folder I've made so far has program.rom, and that's
very unlikely to change, so this should be fine.

Now, of course, if you drop a regular "game.sfc" file on the emulator,
it won't even try to load it, unless it's in a folder that ends in .fc,
.sfc, etc. In which case, it'll bail out immediately by being unable to
produce a manifest for what is obviously not really a game folder.
2015-08-30 12:08:26 +10:00
Tim Allen
c45633550e Update to v094r42 release.
byuu says:

I imagine you guys will like this WIP very much.

Changelog:
- ListView check boxes on Windows
- ListView removal of columns on reset (changing input dropdowns)
- DirectSound audio duplication on latency change
- DirectSound crash on 20ms latency
- Fullscreen window sizing in multi-monitor setups
- Allow joypad bindings of hotkeys
- Allow triggers to be mapped (Xbox 360 / XInput / Windows only)
- Support joypad rumble for Game Boy Player
- Video scale settings modified from {1x,2x,3x} to {2x,3x,4x}
- System menu now renames to active emulation core
- Added fast forward hotkey

Not changing for v095:
- not adding input focus settings yet
- not adding shaders yet

Not changing at all:
- not implementing maximize
2015-08-24 19:42:11 +10:00
Tim Allen
7081f46e45 Added icarus 20150821. 2015-08-21 21:29:53 +10:00
Tim Allen
213879771e Update to v094r41 release (open beta).
byuu says:

Changelog (since the last open beta):
- icarus is now included. icarus is used to import game files/archives
  into game paks (folders)
- SNES: mid-scanline BGMODE changes now emulated correctly (used only by
  atx2.smc Anthrox Demo)
- GBA: fixed a CPU bug that was causing dozens of games to have
  distorted audio
- GBA: fixed default FlashROM ID; should allow much higher compatibility
- GBA: now using Cydrak's new, much improved, GBA color emulation filter
  (still a work-in-progress)
- re-added command-line loading support for game paks (not for game
  files/archives, sorry!)
- Qt port now compiles and runs again (may be a little buggy;
  Windows/GTK+ ports preferred)
- SNES performance profile now compiles and runs again
- much more
2015-08-21 20:57:03 +10:00
Tim Allen
4344b916b6 Update to v094r40 release.
byuu says:

Changelog:
- updated to newest hiro API
- SFC performance profile builds once again
- hiro: Qt port completed

Errata 1: the hiro/Qt target won't run tomoko just yet. Starts by
crashing inside InputSettings because hiro/Qt isn't forcefully selecting
the first item added to a ComboButton just yet. Even with a monkey patch
to get around that, the UI is incredibly unstable. Lots of geometry
calculation bugs, and a crash when you try and access certain folders in
the browser dialog. Lots of work left to be done there, sadly.

Errata 2: the hiro/Windows port has black backgrounds on all ListView
items. It's because I need to test for unassigned colors and grab the
default Windows brush colors in those cases.

Note: alternating row colors on multi-column ListView widgets is gone
now. Not a bug. May add it back later, but I'm not sure. It doesn't
interact nicely with per-cell background colors.

Things left to do:

First, I have to fix the Windows and Qt target bugs.

Next, I need to go through and revise the hiro API even more (nothing
too major.)

Next, I need to update icarus to use the new hiro API, and add support
for the SFC games database.

Next, I have to rewrite my TSV->BML cheat code tool.

Next, I need to post a final WIP of higan+icarus publicly and wait a few
days.

Next, I need to fix any bugs reported from the final WIP that I can.

Finally, I should be able to release v095.
2015-08-18 20:18:00 +10:00
Tim Allen
0271d6a12b Update to v094r39 release.
byuu says:

Changelog:
- SNES mid-scanline BGMODE fixes finally merged (can run
  atx2.zip{mode7.smc}+mtest(2).sfc properly now)
- Makefile now discards all built-in rules and variables
- switch on bool warning disabled for GCC now as well (was already
  disabled for Clang)
- when loading a game, if any required files are missing, display
  a warning message box (manifest.bml, program.rom, bios.rom, etc)
- when loading a game (or a game slot), if manifest.bml is missing, it
  will invoke icarus to try and generate it
  - if that fails (icarus is missing or the folder is bad), you will get
    a warning telling you that the manifest can't be loaded

The warning prompt on missing files work for both games and the .sys
folders and their files. For some reason, failing to load the DMG/CGB
BIOS is causing a crash before I can display the modal dialog. I have no
idea why, and the stack frame backtrace is junk.

I also can't seem to abort the failed loading process. If I call
Program::unloadMedia(), I get a nasty segfault. Again with a really
nasty stack trace. So for now, it'll just end up sitting there emulating
an empty ROM (solid black screen.) In time, I'd like to fix that too.

Lastly, I need a better method than popen for Windows. popen is kind of
ugly and flashes a console window for a brief second even if the
application launched is linked with -mwindows. Not sure if there even is
one (I need to read the stdout result, so CreateProcess may not work
unless I do something nasty like "> %tmp%/temp") I'm also using the
regular popen instead of _wpopen, so for this WIP, it won't work if your
game folder has non-English letters in the path.
2015-08-04 19:02:04 +10:00
Tim Allen
1b0b54a690 Update to v094r38 release.
byuu says:

I'll post more detailed changes later, but basically:
- fixed Baldur's Gate bug
- guess if no flash ROM ID present (fixes Magical Vacation, many many
  others)
- nall cleanups
- sfc/cartridge major cleanups
- bsxcartridge/"bsx" renamed to mcc/"mcc" after the logic chip it uses
  (consistency with SGB/ICD2)
- ... and more!
2015-08-04 19:01:59 +10:00
Tim Allen
092cac9073 Update to v094r37 release.
byuu says:

Changelog:
- synchronizes lots of nall changes
- changes displayed program title from tomoko to higan(*)
- browser dialog sort is case-insensitive
- .sys folders look at user-selected library path; no longer hard-coded

Tried to get rid of the file modes from the Windows browser dialog, but
it was being a bitch so I left it on for now.

- The storage locations and binary still use tomoko. I'm not really sure
  what to do here. The idea is there may be more than one "higan" UI in
  the future, but I don't want people to go around calling the entire
  program by the UI name. For official Windows releases, I can rename
  the binaries to "higan-{profile}.exe", and by putting the config files
  with the binary, they won't ever see the tomoko folder. Linux is of
  course trickier.

Note: Windows users will need to edit hiro/components.hpp and comment
out these lines:

 #define Hiro_Console
 #define Hiro_IconView
 #define Hiro_SourceView
 #define Hiro_TreeView

I forgot to do that, and too lazy to upload another WIP.
2015-07-14 19:32:43 +10:00
Tim Allen
ecb35cac33 Update to v094r36 release (open beta).
byuu says:

Changelog:
- GBA emulation accuracy has been substantially improved [Cydrak]
- GBA ldm bug fixed [jchadwick]
- SNES SuperFX timing has been improved [AWJ, ARM9, qwertymodo]
- SNES accuracy profile is now ~8% faster than before
- you no longer need to copy the .sys profile folders to
  ~/Emulation/System
    - you still need to put bios.rom (GBA BIOS) into Game Boy
      Advance.sys to use GBA emulation!!
- you no longer need to pre-configure inputs before first use
- loading games / changing window size won't recenter window
- checkboxes in cheat editor update correctly
- can't type into state manager description textbox on an empty slot
- typing in state manager description box works correctly; and updates
  list view correctly
- won't show files that match game extensions anymore (only game folders
  show up)
- libco Win64 port fixes with FPU^H^H^H XMM registers
- libco ARM port now available; so you too can play at 15fps on an RPi2!
  [jessedog3, Cydrak]
- controller selection will check the default item in the menu now on
  game load
- as usual, a whole lot of other stuff I'm forgetting

Known issues:
- type-ahead find does not work in list views (eg game selection
  dialog); I don't know how to fix this
- there's no game file importer yet
- there's no shader support yet
- there's no profiler available for the timing panel, you need to adjust
  values manually for now
2015-07-02 20:24:56 +10:00
Tim Allen
28a14198cb Update to v094r35 release.
byuu says:

GBA timings are *almost* perfect now. Off by 1-3 cycles on each test,
sans a few DMA ones that seem to not run at all according to the numbers
(crazy.)
2015-07-01 20:58:42 +10:00
Tim Allen
7ff7f64482 Update to v094r34 release.
byuu says:

Fixes SuperFX fmult, lmult timings; rambr, bramr and clsr assignment
masking. Implements true GBA ROM prefetch (buggy, lower test score, but
runs Mario & Luigi without crashing on battles anymore.)
2015-06-28 18:44:56 +10:00
Tim Allen
4c9266d18f Update to v094r33 release.
byuu says:

Small WIP, just fixes the timings for GSU multiply.

However, the actual product may still be wrong when CLSR and MS0 are
both set. Since I wasn't 'corrupting' the value in said case before,
then this behavior can only be better than before.

Turned the (cache,memory)_access_timing into functions that compute the
values; and pulled "clockspeed" into GSU.

Also, I'm thinking it might be kind of pointless to have clockspeed at
all. Supposedly even the Mario Chip can run at 21.48MHz anyway.
Enforcing 10.74MHz mode seems kind of silly. If we change it to just be
a "default value for CLSR", then we can just inline the memory access
tests without the need for the access_timing functions (literally just
clsr?2:1 then)

Slight compilation bug: go to processor/gsu/registers.hpp:33 and add

    reg16_t() = default;

I missed it due to a partial recompile. Too lazy to upload another WIP
just for that.

Probably not worth doing much SuperFX testing just yet, as it looks like
they're doing some other tests at the moment on NESdev.
2015-06-27 12:38:47 +10:00
Tim Allen
169e400437 Update to v094r32 release.
byuu says:

Lots more timing improvements to GBA emulation. We're now ahead of
everything but mGBA.

Mario & Luigi is still hanging in battles, so I guess my prefetch
simulation isn't as good as Cydrak's previous attempt, no surprise.
2015-06-27 12:38:08 +10:00
Tim Allen
ea02f1e36a Update to v094r31 release.
byuu says:

This WIP scores 448/920 tests passed.

Gave a shot at ROM prefetch that failed miserably (ranged from 409 to
494 tests passed. Nowhere near where it would be if it were implemented
correctly.)

Three remaining issues:
- ROM prefetch
- DMA timing
- timers (I suspect it's a 3-clock delay in starting, not a 3-clock into
  the future affair)

Probably only going to be able to get the timers working without heroic
amounts of effort.

MUL timing is fixed to use idle cycles.
STMIA is fixed to set sequential at the right moments.
DMA priority support is added, so DMA 0 can interrupt DMA 1 mid-transfer.

In other news ...

I'm calling gtk_widget_destroy on the GtkWindow now, so hopefully those
Window_configure issues go away.

I realize I was leaking Display* handles in the X-video driver while
I was looking at it, so I fixed those.

I added DT_NOPREFIX so the Windows ListView will show & characters
correctly now.
2015-06-25 19:52:32 +10:00
Tim Allen
310ff4fa3b Update to v094r30 release.
byuu says:

This WIP does substantially better on endrift's GBA timing tests. Still
not perfect, though. But hopefully enough to get me out of dead last
place. I also finally fixed the THUMB-mode ldmia bug that jchadwick
reported.

So, GBA emulation should be improved quite a bit, hopefully.
2015-06-24 23:21:24 +10:00
Tim Allen
83f684c66c Update to v094r29 release.
byuu says:

Note: for Windows users, please go to nall/intrinsics.hpp line 60 and
correct the typo from "DISPLAY_WINDOW" to "DISPLAY_WINDOWS" before
compiling, otherwise things won't work at all.

This will be a really major WIP for the core SNES emulation, so please
test as thoroughly as possible.

I rewrote the 65816 CPU core's dispatcher from a jump table to a switch
table. This was so that I could pass class variables as parameters to
opcodes without crazy theatrics.

With that, I killed the regs.r[N] stuff, the flag_t operator|=, &=, ^=
stuff, and all of the template versions of opcodes.

I also removed some stupid pointless flag tests in xcn and pflag that
would always be true.

I sure hope that AWJ is happy with this; because this change was so that
my flag assignments and branch tests won't need to build regs.P into
a full 8-bit variable anymore.

It does of course incur a slight performance hit when you pass in
variables by-value to functions, but it should help with binary size
(and thus cache) by reducing a lot of extra functions. (I know I could
have used template parameters for some things even with a switch table,
but chose not to for the aforementioned reasons.)

Overall, it's about a ~1% speedup from the previous build. The CPU core
instructions were never a bottleneck, but I did want to fix the P flag
building stuff because that really was a dumb mistake v_v'
2015-06-22 23:31:49 +10:00
Tim Allen
e0815b55b9 Update to v094r28 release.
byuu says:

This WIP substantially restructures the ruby API for the first time
since that project started.

It is my hope that with this restructuring, destruction of the ruby
objects should now be deterministic, which should fix the crashing on
closing the emulator on Linux. We'll see I guess ... either way, it
removed two layers of wrappers from ruby, so it's a pretty nice code
cleanup.

It won't compile on Windows due to a few issues I didn't see until
uploading the WIP, too lazy to upload another. But I fixed all the
compilation issues locally, so it'll work on Windows again with the next
WIP (unless I break something else.)

(Kind of annoying that Linux defines glActiveTexture but Windows
doesn't.)
2015-06-20 15:44:05 +10:00
Tim Allen
20cc6148cb Update to v094r27 release.
byuu says:

Added AWJ's fixes for alt/cpu (Tetris Attack framelines issue) and
alt/dsp (Thread::clock reset)

Added fix so that the taskbar entry appears when the application first
starts on Windows.

Fixed checkbox toggling inside of list views on Windows.

Updated nall/image to properly protect variables that should not be
written externally.

New Object syntax for hiro is in.

Fixed the backwards-typing on Windows with the state manager.
NOTE: the list view isn't redrawing when you change the description
text. It does so on the cheat editor because of the resizeColumns call;
but that shouldn't be necessary. I'll try and fix this for the next WIP.
2015-06-18 20:48:53 +10:00
Tim Allen
a21ff570ee Update to v094r26 release (open beta).
byuu says:

Obviously, this is a fairly major WIP. It's the first public release in
17 months. The entire UI has been rewritten (for the 74th time), and is
now internally called tomoko. The official releases will be named higan
(both the binaries and title bar.)

Missing features from v094:

- ananke is missing (this means you will need v094 to create game
  folders to be loaded)
- key assignments are limited to one physical button = one mapping (no
  multi-mapping)
- shader support is missing
- audio/video profiling is missing
- DIP switch window is missing (used by NSS Actraiser with a special
  manifest; that's about it)
- alternate paths for game system folders and configuration BML files

There's some new stuff, but not much. This isn't going to be an exciting
WIP in terms of features. It's more about being a brand new release with
the brand new hiro port and its shared memory model. The goal is to get
these WIPs stable, get v095 out, and then finally start improving the
actual emulation again after that.
2015-06-16 20:30:04 +10:00
Tim Allen
bb3c69a30d Update to v094r25 release.
byuu says:

Windows port should run mostly well now, although exiting fullscreen
breaks the application in a really bizarre way. (clicking on the window
makes it sink to background rather than come to the foreground o_O)

I also need to add the doModalChange => audio.clear() thing for the
accursed menu stuttering with DirectSound.

I also finished porting all of the ruby drivers over to the newer API
changes from nall.

Since I can't compile the Linux or OS X drivers, I have no idea if there
are any typos that will result in compilation errors. If so, please let
me know where they're at and I'll try and fix them. If they're simple,
please try and fix them on your end to test further if you can.

I'm hopeful the udev crash will be gone now that nall::string checks for
null char* values passed to its stringify function. Of course, it's
a problem it's getting a null value in the first place, so it may not
work at all.

If you can compile on Linux (or by some miracle, OS X), please test each
video/audio/input driver if you don't mind, to make sure there's no
"compiles okay but still typos exist" bugs.
2015-06-16 20:30:04 +10:00
Tim Allen
f0c17ffc0d Update to v094r24 release.
byuu says:

Finally!! Compilation works once again on Windows.

However, it's pretty buggy. Modality isn't really working right, you can
still poke at other windows, but when you select ListView items, they
redraw as empty boxes (need to process WM_DRAWITEM before checking
modality.)

The program crashes when you close it (probably a ruby driver's term()
function, that's what it usually is.)

The Layout::setEnabled(false) call isn't working right, so you get that
annoying chiming sound and cursor movement when mapping keyboard keys to
game inputs.

The column sizing seems off a bit on first display for the Hotkeys tab.

And probably lots more.
2015-06-16 20:30:04 +10:00
Tim Allen
314aee8c5c Update to v094r23 release.
byuu says:

The library window is gone, and replaced with
hiro::BrowserWindow::openFolder(). This gives navigation capabilities to
game loading, and it also completes our slotted cart selection code. As
an added bonus, it's less code this way, too.

I also set the window size to consistent sizes between all emulated
systems, so that switching between SFC and GB don't cause the window
size to keep changing, and so that the scaling size is consistent (eg at
normal scale, GB @ 3x is closer to SNES @ 2x.) This means black borders
in GB/GBA mode, but it doesn't look that bad, and it's not like many
people ever use these modes anyway.

Finally, added the placeholder tabs for video, audio and timing. I don't
intend to add the timing calculator code to v095 (it might be better as
a separate tool), but I'll add the ability to set video/audio rates, at
least.

Glitch 1: despite selecting the first item in the BrowserDialog list, if
you press enter when the window appears, it doesn't activate the item
until you press an arrow key first.

Glitch 2: in Game Boy mode, if you set the 4x window size, it's not
honoring the full requested height because the viewport is smaller than
the window. 8+ years of trying to get GTK+ and Qt to simply set the god
damned window size I ask for, and I still can't get them to do it
reliably.

Remaining issues:
- finish configuration panels (video, audio, timing)
- fix ruby driver compilation on Windows
- add DIP switch selection window (NSS) [I may end up punting this one
  to v096]
2015-06-16 20:29:47 +10:00
Tim Allen
7bf4cff946 Update to v094r22 release.
byuu says:

I fixed the hiro layout enable bug, so when you go to assign joypad
input, the window disables itself so your input doesn't mess with the
controls.

I added "reset" to the hotkeys, in case you feel like clearing all of
them at once.

I added device selection support and the ability to disable audio
synchronization (run > 60fps) to the ruby/OSS driver. This is exposed in
tomoko's configuration file.

I added checks to stringify so that assigning null char* strings to
nall::string won't cause crashes anymore (technically the crash was in
strlen(), which doesn't check for null strings, but whatever ... I'll do
the check myself.)

I hooked up BrowserDialog::folderSelect() to loading slotted media for
now. Tested it by loading a Game Boy game successfully through the Super
Game Boy. Definitely want to write a custom window for this though, that
looks more like the library dialog.

Remaining issues:
- finish slotted cart loader (SGB, BSX, ST)
- add DIP switch selection window (NSS) [I may end up punting this one
  to v096]
- add more configuration panels (video, audio, timing)
2015-05-30 21:40:07 +10:00
Tim Allen
99b2b4b57c Update to v094r21 release.
byuu says:

This updates ruby to return shared_pointer<HID::Device> objects instead
of HID::Device* objects. It also fixes an ID bug where joypads were
starting at ID# 2+, but mice were also set to ID# 2. I also revised
nall/hid a lot, with getters and setters instead of stabbing at internal
state. I didn't yet patch nall::string to safely consume nullptr const
char* values, though.
2015-05-24 19:44:28 +10:00
Tim Allen
4e0223d590 Update to v094r20 release.
byuu says:

Main reason for this WIP was because of all the added lines to hiro for
selective component disabling. May as well get all the diff-noise apart
from code changes.

It also merges something I've been talking to Cydrak about ... making
nall::string::(integer,decimal) do built-in binary,octal,hex decoding
instead of just failing on those. This will have fun little side effects
all over the place, like being able to view a topic on my forum via
"forum.byuu.org/topic/0b10010110", heh.

There are two small changes to higan itself, though. First up, I fixed
the resampler ratio when loading non-SNES games. Tested and I can play
Game Boy games fine now. Second, I hooked up menu option hiding for
reset and controller selection. Right now, this works like higan v094,
but I'm thinking I might want to show the "Device -> Controller" even if
that's all that's there. It kind of jives nicer with the input settings
window to see the labels there, I think. And if we ever do add more
stuff, it'll be nice that people already always expect that menu there.

Remaining issues:
* add slotted cart loader (SGB, BSX, ST)
* add DIP switch selection window (NSS)
* add timing configuration (video/audio sync)
2015-05-23 15:37:08 +10:00
Tim Allen
458775a481 Update to v094r19 release.
byuu says:

The input port menu was hooked up.

Alternate input support was added, although I wasn't able to test rumble
support because SDL doesn't support that, and I don't have XInput or
udev drivers on FreeBSD. This one's going to be tricky. Maybe I can test
via cross-compiling on Windows/GTK.

Added mouse capture hotkey, and auto capture/release on toggling
fullscreen (as a bonus it hides the mouse cursor.)

Added all possible video and input drivers to ruby for BSD systems.

Remaining issues before we can release v095:
- add slotted cart loader (SGB, BSX, ST)
- add DIP switch selection window (NSS)
- add timing configuration (video/audio sync)
- hide inapplicable options from system menu (eg controller ports and
  reset button from handheld systems)
2015-05-23 15:29:18 +10:00
Tim Allen
fc8eba133d Update to v094r18 release.
byuu says:

Okay yeah, lots of SNES coprocessor games were horribly broken. They
should be fixed now with the below changes:

Old syntax:

    auto programROM = root["rom[0]/name"].text();
    auto dataROM = root["rom[1]/name"].text();
    load_memory(root["ram[0]"]);

New syntax:

    auto rom = root.find("rom");
    auto ram = root.find("ram");
    auto programROM = rom(0)["name"].text();
    auto dataROM = rom(1)["name"].text();
    load_memory(ram(0));

Since I'm now relying on the XShm driver, which is multi-threaded, I'm
now compiling higan with -fopenmp. On FreeBSD, this requires linking
with -Wl,-rpath=/usr/local/lib -Wl,-rpath=/usr/local/lib/gcc49 to get
the right version of GOMP.

This gives a pretty nice speed boost for XShm, I go from around 101fps
to 111fps at 4x scale on the accuracy profile. The combination of
inlining the accuracy-PPU and parallelizing the XShm renderer about
evenly compensates now for the ~20% CPU overclock I gave up a while ago.

The WIP also has some other niceties from the newer version of nall.
Most noticeably, cheat code database searching is now instantaneous. No
more 3-second stall.
2015-05-16 17:37:13 +10:00
Tim Allen
39ca8a2fab Update to v094r17 release.
byuu says:

This updates higan to use the new Markup::Node changes. This is a really
big change, and one slight typo anywhere could break certain classes of
games from playing.

I don't have ananke hooked up again yet, so I don't have the ability to
test this much. If anyone with some v094 game folders wouldn't mind
testing, I'd help out a great deal.

I'm most concerned about testing one of each SNES special chip game.
Most notably, systems like the SA-1, HitachiDSP and NEC-DSP were using
the fancier lookups, eg node["rom[0]/name"], which I had to convert to
a rather ugly node["rom"].at(0)["name"], which I'm fairly confident
won't work. I'm going to blame that on the fumes from the shelves I just
stained >.> Might work with node.find("rom[0]/name")(0) though ...? But
so ugly ... ugh.

That aside, this WIP adds the accuracy-PPU inlining, so the accuracy
profile should run around 7.5% faster than before.
2015-05-16 17:36:22 +10:00
Tim Allen
c335ee9d80 Update to v094r16 release.
byuu says:

Finished the cheat code system, it'll now load and save cheats.bml to
disk.

Also hooked up overscan masking. But for now you can only configure the
amount it clips via the configuration file, since I don't have a video
settings dialog anymore.

And that's the last of the low-hanging fruit. The remaining items are
all going to be a pain in the ass for one reason or another.

Short-term:
- add input port changing support
- add other input types (mouse-based, etc)

Long-term:
- add slotted cart loader (SGB, BSX, ST)
- add DIP switch selection window (NSS)
- add timing configuration (video/audio sync)

Not planned:
- video color adjustments (will allow emulated color vs raw color; but
  no more sliders)
- pixel shaders
- ananke integration (will need to make a command-line version to get my
  games in)
- fancy audio adjustment controls (resampler, latency, volume)
- input focus settings
- localization support (not enough users)
- window geometry memory
- anything else not in higan v094
2015-04-21 21:58:59 +10:00
Tim Allen
2eb50fd70b Update to v094r15 release.
byuu says:

Implemented the cheat database dialog, and most of the cheat editor
dialog. I still have to handle loading and saving the cheats.bml file
for each game. I wanted to finish it today, but I burned out. It's a ton
of really annoying work to support cheat codes. There's also some issue
with the width calculation for the "code(s)" column in hiro/GTK.

Short-term:
- add input port changing support
- add other input types (mouse-based, etc)
- finish cheat codes

Long-term:
- add slotted cart loader (SGB, BSX, ST)
- add DIP switch selection window (NSS)
- add overscan masking
- add timing configuration (video/audio sync)

Not planned:
- video color adjustments (will allow emulated color vs raw color; but
  no more sliders)
- pixel shaders
- ananke integration (will need to make a command-line version to get my
  games in)
- fancy audio adjustment controls (resampler, latency, volume)
- input focus settings
- localization support (not enough users)
- window geometry memory
- anything else not in higan v094
2015-04-21 21:54:07 +10:00
Tim Allen
89d578bc7f Update to v094r14 release.
byuu says:

Man, over five weeks have passed without so much as touching the
codebase ... time is advancing so fast it's positively frightening. Oh
well, little by little, and we'll get there eventually.

Changelog:
- added save state slots (1-5 in the menu)
- added hotkeys settings dialog + mapping system
- added fullscreen toggle (with a cute aspect correction trick)

About three hours of work here.

Short-term:
- add input port changing support
- add other input types (mouse-based, etc)
- add cheat codes
- add timing configuration (video/audio sync)

Long-term:
- add slotted cart loader (SGB, BSX, ST)
- add DIP switch selection window (NSS)
- add cheat code database
- add state manager
- add overscan masking

Not planned:
- video color adjustments (will allow emulated color vs raw color; but
  no more sliders)
- pixel shaders
- ananke integration (will need to make a command-line version to get my
  games in)
- fancy audio adjustment controls (resampler, latency, volume)
- input focus settings
- relocating game library (not hard, just don't feel like it)
- localization support (not enough users)
- window geometry memory
- anything else not in higan v094
2015-04-13 21:16:33 +10:00
Tim Allen
b4ba95242f Update to v094r13 release.
byuu says:

This version polishes up the input dialogue (reset, erase, disable
button when item not focused, split device ID from mapping name), adds
color emulation toggle, and add dummy menu items for remaining features
(to be filled in later.)

Also, it now compiles cleanly on Windows with GTK.

I didn't test with TDM-GCC-32, because for god knows what reason, the
32-bit version ships with headers from Windows 95 OSR2 only. So I built
with TDM-GCC-64 with arch=x86.

And uh, apparently, moving or resizing a window causes a Visual C++
runtime exception in the GTK+ DLLs. This doesn't happen with trance or
renshuu built with TDM-GCC-32. So, yeah, like I said, don't use -m32.
2015-03-07 21:21:47 +11:00
Tim Allen
a1b2fb0124 Update to v094r12 release.
byuu says:

Changelog:
* added driver selection
* added video scale + aspect correction settings
* added A/V sync + audio mute settings
* added configuration file
* fixed compilation bugs under Windows and Linux
* fixed window sizing
* removed HSU1
* the system menu stays as "System", because "Game Boy Advance" was too
  long a string for the smallest scale size
* some more stuff

You guys probably won't be ecstatic about the video sizing options, but
it's basically your choice of 1x, 2x or 4x scale with optional aspect
correction. 3x was intentionally skipped because it looks horrible on
hires SNES games. The window is resized and recentered upon loading
games. The window doesn't resize otherwise. I never really liked the way
v094 always left you with black screen areas and left you with
off-centered window positions.

I might go ahead and add the pseudo-fullscreen toggle that will jump
into 4x mode (respecting your aspect setting.)

Short-term:
* add input port changing support
* add other input types (mouse-based, etc)
* add save states
* add cheat codes
* add timing configuration (video/audio sync)
* add hotkeys (single state)

We can probably do a new release once the short-term items are
completed.

Long-term:
* add slotted cart loader (SGB, BSX, ST)
* add DIP switch selection window (NSS)
* add cheat code database
* add state manager
* add overscan masking

Not planned:
* video color adjustments (will allow emulated color vs raw color; but
  no more sliders)
* pixel shaders
* ananke integration (will need to make a command-line version to get my
  games in)
* fancy audio adjustment controls (resampler, latency, volume)
* input focus settings
* relocating game library (not hard, just don't feel like it)
* localization support (not enough users)
* window geometry memory
* anything else not in higan v094
2015-03-03 21:26:44 +11:00
Tim Allen
4a069761f9 Update to v094r11 release.
byuu says:

I've hooked up the input subsystem, and the input manager to assign
hotkeys.

So far I only have digital buttons working (keyboard only), and I'm not
planning on supporting input groups again (mapping multiple physical
buttons to one emulated button), but it's progress. As with the rest of
tomoko, the code's a lot more compact. The nice thing about redoing code
so many times is that each time you get a little bit better at it.

The input configuration is saved to ~/.config/tomoko/settings.bml (just
realized that I'm an idiot and need to rename it to input.bml)

Also hooked up game saves and cartridge unloading. Active controller
changing isn't hooked up yet, and I'll probably do it differently.

Oh, and I declared the ruby lines for other platforms.

Still need to add Cydrak's Windows compilation fixes. I am nothing if
not lazy :P
2015-03-03 21:26:44 +11:00
Tim Allen
80c1c9c2ef Update to v094r10 release.
byuu says:

This starts the tomoko UI. So far I have basic library loading and
video+audio output. Basically just enough to take the below screenshot.
(aside from Library, the menus are empty stubs.)

The .sys (system) game folders are now going under ~/Emulation/System,
to avoid needing root privileges to stick them into /usr/share. The game
library now shows all bootable media types, and the drop-down subtype is
gone. I'm going to display a separate modal dialog for loading slotted
games this time around. Much cleaner this way, less clutter.

tomoko's starting off a lot cleaner than ethos was, and I'm scaling back
the number of abstracted classes. What was Utility, Interface, etc are
now being merged all into Program. Of course, the real hell is the input
system. That has so many layers of bullshit that there's really no sane
way to write it.
2015-03-03 21:26:44 +11:00
Tim Allen
a512d14628 Update to v094r09 release.
byuu says:

This will easily be the biggest diff in the history of higan. And not in
a good way.

* target-higan and target-loki have been blown away completely
* nall and ruby massively updated
* phoenix replaced with hiro (pretty near a total rewrite)
* target-higan restarted using hiro (just a window for now)
* all emulation cores updated to compile again
* installation changed to not require root privileges (installs locally)

For the foreseeable future (maybe even permanently?), the new higan UI
will only build under Linux/BSD with GTK+ 2.20+. Probably the most
likely route for Windows/OS X will be to try and figure out how to build
hiro/GTK on those platforms, as awful as that would be. The other
alternative would be to produce new UIs for those platforms ... which
would actually be a good opportunity to make something much more user
friendly.

Being that I just started on this a few hours ago, that means that for
at least a few weeks, don't expect to be able to actually play any
games. Right now, you can pretty much just compile the binary and that's
it. It's quite possible that some nall changes didn't produce
compilation errors, but will produce runtime errors. So until the UI can
actually load games, we won't know if anything is broken. But we should
mostly be okay. It was mostly just trim<1> -> trim changes, moving to
Hash::SHA256 (much cleaner), and patching some reckless memory copy
functions enough to compile.

Progress isn't going to be like it was before: I'm now dividing my time
much thinner between studying and other hobbies.

My aim this time is not to produce a binary for everyone to play games
on. Rather, it's to keep the emulator alive. I want to be able to apply
critical patches again. And I would also like the base of the emulator
to live on, for use in other emulator frontends that utilize higan.
2015-02-28 12:52:53 +11:00
Tim Allen
1a7bc6bb87 Update to v094r08 release.
byuu says:

Lots of changes this time around. FreeBSD stability and compilation is
still a work in progress.

FreeBSD 10 + Clang 3.3 = 108fps
FreeBSD 10 + GCC 4.7 = 130fps

Errata 1: I've been fighting that god-damned endian.h header for the
past nine WIPs now. The above WIP isn't building now because FreeBSD
isn't including headers before using certain types, and you end up with
a trillion error messages. So just delete all the endian.h includes from
nall/intrinsics.hpp to build.

Errata 2: I was trying to match g++ and g++47, so I used $(findstring
g++,$(compiler)), which ends up also matching clang++. Oops. Easy fix,
put Clang first and then else if g++ next. Not ideal, but oh well. All
it's doing for now is declaring -fwrapv twice, so you don't have to fix
it just yet. Probably just going to alias g++="g++47" and do exact
matching instead.

Errata 3: both OpenGL::term and VideoGLX::term are causing a core dump
on BSD. No idea why. The resources are initialized and valid, but
releasing them crashes the application.

Changelog:
- nall/Makefile is more flexible with overriding $(compiler), so you can
  build with GCC or Clang on BSD (defaults to GCC now)
- PLATFORM_X was renamed to PLATFORM_XORG, and it's also declared with
  PLATFORM_LINUX or PLATFORM_BSD
  - PLATFORM_XORG probably isn't the best name ... still thinking about
    what best to call LINUX|BSD|SOLARIS or ^(WINDOWS|MACOSX)
- fixed a few legitimate Clang warning messages in nall
- Compiler::VisualCPP is ugly as hell, renamed to Compiler::CL
- nall/platform includes nall/intrinsics first. Trying to move away from
  testing for _WIN32, etc directly in all files. Work in progress.
- nall turns off Clang warnings that I won't "fix", because they aren't
  broken. It's much less noisy to compile with warnings on now.
- phoenix gains the ability to set background and foreground colors on
  various text container widgets (GTK only for now.)
- rewrote a lot of the MSU1 code to try and simplify it. Really hope
  I didn't break anything ... I don't have any MSU1 test ROMs handy
- SNES coprocessor audio is now mixed as sclamp<16>(system_sample
  + coprocessor_sample) instead of sclamp<16>((sys + cop) / 2)
  - allows for greater chance of aliasing (still low, SNES audio is
    quiet), but doesn't cut base system volume in half anymore
- fixed Super Scope and Justifier cursor colors
- use input.xlib instead of input.x ... allows Xlib input driver to be
  visible on Linux and BSD once again
- make install and make uninstall must be run as root again; no longer
  using install but cp instead for BSD compatibility
- killed $(DESTDIR) ... use make prefix=$DESTDIR$prefix instead
- you can now set text/background colors for the loki console via (eg):
 - settings.terminal.background-color 0x000000
 - settings.terminal.foreground-color 0xffffff
2014-02-24 20:39:09 +11:00
Tim Allen
ecc651c88b Update to v094r07 release.
byuu says:

Changelog for loki:
- added command aliases (match with * [sorry, regex lib isn't available
  everywhere yet], replace with {1}+)
- added command hotkeys
- added window geometry saving
- added save state support
- added power/reset commands
- added an input manager, so you can remap keys (limiting it to the
  keyboard for now though)

The combination of aliases and hotkeys really makes things shine. Save
states will temporarily disable your breakpoints (run/step are
technically temporary breakpoints) so as to ensure the state is captured
at a good time. In practice, this should pose about as much of a problem
as higan desyncing and breaking when capturing states ... should be
exceedingly rare to ever even notice this behavior at all, with 99.9% of
state captures happening in half an instruction boundary. But still,
keep it in mind, as you might see the CPU step one instruction ahead.
Tracing and usage map functionality is still enabled during state
synchronization.

So at this point, I have 100% of the essential stuff in. All that's left
now is to add polish / wishlist features like bass and mosaic
integration.
2014-02-09 17:05:58 +11:00
Tim Allen
3016e595f0 Update to v094r06 release.
byuu says:

New terminal is in. Much nicer to use now. Command history makes a major
difference in usability.

The SMP is now fully traceable and debuggable. Basically they act as
separate entities, you can trace both at the same time, but for the most
part running and stepping is performed on the chip you select.

I'm going to put off CPU+SMP interleave support for a while. I don't
actually think it'll be too hard. Will get trickier if/when we support
coprocessor debugging.

Remaining tasks:
- aliases
- hotkeys
- save states
- window geometry

Basically, the debugger's done. Just have to add the UI fluff.

I also removed tracing/memory export from higan. It was always meant to
be temporary until the debugger was remade.
2014-02-09 17:05:58 +11:00
Tim Allen
423a6c6bf8 Update to v094r05 release.
byuu says:

Commands can be prefixed with: (cpu|smp|ppu|dsp|apu|vram|oam|cgram)/ to
set their source. Eg "vram/hex 0800" or "smp/breakpoints.append execute
ffc0"; default is cpu.

These overlap a little bit in odd ways, but that's just the way the SNES
works: it's not a very orthogonal system. CPU is both a processor and
the main bus (ROM, RAM, WRAM, etc), APU is the shared memory by the
SMP+DSP (eg use it to catch writes from either chip); PPU probably won't
ever be used since it's broken down into three separate buses (VRAM,
OAM, CGRAM), but DSP could be useful for tracking bugs like we found in
Koushien 2 with the DSP echo buffer corrupting SMP opcodes. Technically
the PPU memory pools are only ever tripped by the CPU poking at them, as
the PPU doesn't ever write.

I now have run.for, run.to, step.for, step.to. The difference is that
run only prints the next instruction after running, whereas step prints
all of the instructions along the way as well. run.to acts the same as
"step over" here. Although it's not quite as nice, since you have to
specify the address of the next instruction.

Logging the Field/Vcounter/Hcounter on instruction listings now, good
for timing information.

Added in the tracer mask, as well as memory export, as well as
VRAM/OAM/CGRAM/SMP read/write/execute breakpoints, as well as an APU
usage map (it tracks DSP reads/writes separately, although I don't
currently have debugger callbacks on DSP accesses just yet.)

Have not hooked up actual SMP debugging just yet, but I plan to soon.
Still thinking about how I want to allow / block interleaving of
instructions (terminal output and tracing.)

So ... remaining tasks at this point:
- full SMP debugging
- CPU+SMP interleave support
- aliases
- hotkeys
- save states (will be kind of tricky ... will have to suppress
  breakpoints during synchronization, or abort a save in a break event.)
- keep track of window geometry between runs
2014-02-09 17:05:58 +11:00
Tim Allen
10e2a6d497 Update to v094r04 release.
byuu says:

Changelog:
- target-ethos/ is now target-higan/ (will unfortunately screw up diffs
  pretty badly at this point.)
- had a serious bug in nall::optional<T>::operator=, which is now fixed.
- added tracer (no masking just yet, I need to write a nall::bitvector
  class because I don't want to hard-code those anymore.)
- added usage logging (keep track of RWX/EP states for all bus
  addresses.)
- added read/write to poke at memory (hex also works for reading, but
  this one can poke at MMIO regs and is for one address only.)
- added both run.for (# of instructions) and run.to (program counter
  address.)
- added read/write/execute breakpoints with counters for a given
  address, and with an optional compare byte (for read/write modes.)

About the only major things left now for loki is support for trace
masking, memory export, and VRAM/OAM/CGRAM access.
For phoenix/Console, I really need to add a history to up+down arrows,
and I should support left/right insert-at.
2014-02-09 17:05:58 +11:00
Tim Allen
187ba0eec6 Update to v094r02 release.
byuu says:

Changelog:
- ethos: use nall::programpath() instead of realpath(argv[0]) to get
  executable path
- loki: add presentation window
- loki: add terminal window
- loki: add interface to emulation core
- loki: add ruby
- loki: add enough support to run games and save data on exit
    - load game folders via command-line (or drop folder onto binary),
      use "r" to start, "p" to pause ... temporary command names

I'll probably have to say this several times, but for now, loki is only
available on Linux/GTK+, due to the use of the Console widget. Support
for other platforms can come later easily enough.
2014-02-09 17:05:58 +11:00
Tim Allen
c54be74832 Ignore loki binary too. 2014-02-09 17:05:57 +11:00
Tim Allen
04986d2bf7 Update to v094r01 release.
byuu says:

Changelog:
- port: various compilation fixes for OS X [kode54]
- nall: added programpath() function to return path to process binary
  [todo: need to have ethos use this function]
- ruby: XAudio2 will select default game sound device instead of first
  sound device
- ruby: DirectInput device IDs are no longer ambiguous when VID+PID are
  identical
- ruby: OpenGL won't try and terminate if it hasn't been initialized
- gb: D-pad up+down/left+right not masked in SGB mode
- sfc: rewrote ICD2 video rendering to output in real-time, work with
  cycle-based Game Boy renderer
- sfc: rewrote Bus::reduce(), reduces game loading time by about 500ms
- ethos: store save states in {game}/higan/* instead of {game}/bsnes/*
- loki: added target-loki/ (blank stub for now)
- Makefile: purge out/* on make clean
2014-01-28 21:04:58 +11:00
Tim Allen
10464b8c54 Update to v094 release.
byuu says:

This release adds support for game libraries, and substantially improves
Game Boy and Game Boy Color emulation with cycle-based renderers. Many
other changes are also present.

It's very important to note that this release now defaults to optimal
drivers rather than safe drivers. This is particularly important if you
do not have strong OpenGL 3.2 drivers. If performance is bad, go to
Settings -> Configuration -> Advanced, change the video driver, and
restart higan. In the rare case that you have trouble opening higan, you
can edit settings.bml directly and change the setting there. The Windows
safe driver is Direct3D, and the Linux safe driver is XShm.

Also note that although display emulation shaders are now supported,
they have not been included in this release as they are not ready yet.
The support has been built-in anyway, so that they can be tested by
everyone. Once refined, future releases of higan will come with built-in
shaders for each emulated system that simulates the unique display
characteristics of each.

Changelog (since v093):
- sfc: added SA-1 MDR support (fixes SD Gundam G-Next bug)
- sfc: remove random/ and config/, merge to system/ with better
  randomization
- gb: improved color emulation palette contrast
- gbc: do not sort sprites by X-priority
- gbc: allow transparency on BG priority pixels
- gbc: VRAM DMA timing and register fixes
- gbc: block invalid VRAM DMA transfer source and target addresses
- gba: added LCD color emulation (without it, colors are grossly
  over-saturated)
- gba: removed internal frame blending (use shaders to simulate motion
  blur if desired)
- gba: added Game Boy Player support (adds joypad rumble support to
  supported games)
- gba: SOUND_CTL_H is readable
- gb/gbc: PPU renderer is now cycle-based (major accuracy improvement)
- gb/gbc: OAM DMA runs in parallel with the CPU
- gb/gbc: only HRAM can be accessed during OAM DMA
- gb/gbc: fixed serialization of games with SRAM
- gb/gbc: disallow up+down or left+right at the same time
- gb/gbc: added weak hipass filter to remove DC bias
- gb/gbc: STAT OAM+Hblank IRQs only trigger during active display
- gb/gbc: fixed underflow in window clamping
- gb/gbc/gba: audio mixes internally at 2MHz now instead of 4MHz (does
  not affect accuracy)
- gb/gbc/gba: audio volume reduced for consistency with other systems
- fc/sfc/gb/gbc/gba: cheat codes are now stored in universal, decrypted
  format
- ethos: replaced file loader with a proper game library
- ethos: added display emulation shader support
- ethos: added color emulation option to video settings
- ethos: program icon upgraded from 48x48 to 512x512
- ethos: settings and tools windows now use tab frames (less wasted
  screen space)
- ethos: default to optimal (video, audio, input) drivers instead of
  safest drivers
- ethos: input mapping system completely rewritten to support
  hotplugging and unique device mappings
- ruby: added fixes for OpenGL 3.2 on AMD graphics cards
- ruby: quark shaders now support user settings inside of manifest
- ruby: quark shaders can use integral textures (allows display
  emulation shaders to work with raw colors)
- ruby: add joypad rumble support
- ruby: XInput (Xbox 360) controllers now support hotplugging
- ruby: added Linux udev joypad driver with hotplug support
- phoenix: fixed a rare null pointer dereference issue on Windows
- port: target -std=c++11 instead of -std=gnu++11 (do not rely on GNU
  C++ extensions)
- port: added out-of-the-box compilation support for BSD/Clang 3.3+
- port: applied a few Debian upstream patches
- cheats: updated to mightymo's 2014-01-02 release; decrypted all Game
  Genie codes
2014-01-20 19:55:17 +11:00
Tim Allen
fe85679321 Update to v093r13 release.
byuu says:

This WIP removes nall/input.hpp entirely, and implements the new
universal cheat format for FC/SFC/GB/GBC/SGB.

GBA is going to be tricky since there's some consternation around
byte/word/dword overrides.

It's also not immediately obvious to me how to implement the code search
in logarithmic time, due to the optional compare value.

Lastly, the cheat values inside cheats.bml seem to be broken for the
SFC. Likely there's a bug somewhere in the conversion process. Obviously
I'll have to fix that before v094.

I received no feedback on the universal cheat format. If nobody adds
anything before v094, then I don't want to hear any complaining about
the formatting :P
2014-01-13 20:35:46 +11:00
Tim Allen
2b81b630cb Update to v093r12a release.
byuu says:

Not an official WIP (a WIP WIP? A meta-WIP?), just throwing in the new
fullscreen code, and I noticed that OpenGL colors in 30-bit mode are all
fucked up now for some strange reason. So I'm just using this snapshot
to debug the issue.
2014-01-05 20:59:17 +11:00
Tim Allen
3ce1d19f7a Update to v093r12 release.
byuu says:

I've completely redone the ethos InputManager and ruby to work on
HID::Device objects instead of one giant scancode pool.

Currently only the udev driver supports the changes to ruby, so only
Linux users will be able to compile and run this WIP build.

The nice thing about the new system is that it's now possible to
uniquely identify controllers, so if you swap out gamepads, you won't
end up with it working but with all the mappings all screwed up. Since
higan lets you map multiple physical inputs to one emulated input, you
can now configure your keyboard and multiple gamepads to the same
emulated input, and then just use whatever controller you want.

Because USB gamepad makers failed to provide unique serial#s with each
controller, we have to limit the mapping to specific USB ports.
Otherwise, we couldn't distinguish two otherwise identical gamepads. So
basically your computer USB ports act like real game console input port
numbers. Which is kind of neat, I guess.

And the really nice thing about the new system is that we now have the
capability to support hotplugging input devices. I haven't yet added
this to any drivers, but I'm definitely going to add it to udev for v094
official.

Finally, with the device ID (vendor ID + product ID) exposed, we gain
one last really cool feature that we may be able to develop more in the
future. Say we created a joypad.bml file to include with higan. In it,
we'd store the Xbox 360 controller, and pre-defined button mappings for
each emulated system. So if higan detects you have an Xbox 360
controller, you can just plug it in and use it. Even better, we can
clearly specify the difference between triggers and analog axes, and
name each individual input. So you'd see "Xbox 360 Gamepad #1: Left
Trigger" instead of higan v093's "JP0::Axis2.Hi"

Note: for right now, ethos' input manager isn't filtering the device IDs
to look pretty. So you're going to see a 64-bit hex value for a device
ID right now instead of something like Joypad#N for now.
2013-12-23 22:43:51 +11:00
Tim Allen
73be2e729c Update to v093r11 release.
byuu says:

Changelog:
- GBA: SOUND_CTL_H is readable, fixes sound effects in Mario&Luigi
  Superstar Saga [Cydrak] (note: game is still unplayable due to other
  bugs)
- phoenix/Windows: workaround for Win32 API ListView bug, fixes slot
  loading behavior
- ruby: added udev driver for Linux with rumble support, and added
  rumble support to existing RawInput driver for XInput and DirectInput
- ethos: added new "Rumble" mapping to GBA input assignment, use it to
  tell higan which controller to rumble (clear it to disable rumble)
- GBA: Game Boy Player rumble is now fully emulated
- core: added new normalized raw-color palette mode for Display
  Emulation shaders

The way rumble was added to ethos was somewhat hackish. The support
doesn't really exist in nall.

I need to redesign the entire input system, but that's not a change
I want to make so close to a release.
2013-12-21 21:45:58 +11:00
Tim Allen
84fab07756 Update to v093r10 release.
byuu says:

Changelog:
- Game Boy (Color): STAT OAM+HBlank IRQs only trigger during LY=0-143
  with display enabled
  - fixes backgrounds and text in Wacky Races
- Game Boy (Color): fixed underflow in window clamping
  - fixes Wacky Races, Prehistorik Man, Alleyway, etc
- Game Boy (Color): LCD OAM DMA was running too slow
  - fixes Shin Megami Tensei - Devichil - Kuro no Sho
- Game Boy Advance: removed built-in frame blending; display emulation
  shaders will handle this going forward
- Game Boy Advance: added Game Boy Player emulation
  - currently the screen is tinted red during rumble, no actual gamepad
    rumble support yet
  - this is going to be slow, as we have to hash the frame to detect the
    GBP logo, it'll be optional later on
- Emulator::Interface::Palette can now output a raw palette (for Display
  Emulation shaders only)
  - color channels are not yet split up, it's just the raw packed value
2013-12-20 22:40:39 +11:00
Tim Allen
926a39d701 Update to v093r09 release.
byuu says:

Changelog:
- GB/C OAM DMA now runs in parallel with the CPU
- CPU can only access HRAM during OAM DMA
- fixed SGB mode again
- brand new config files will default to the optimal drivers now
  (OpenGL, etc) instead of the safest
- hopefully fixed remaining Windows UI issues
2013-12-14 17:25:12 +11:00
Tim Allen
1361820dd8 Update to v093r08 release.
byuu says:

Changelog:
- Game Boy and Game Boy Color now have a weak hipass filter to remove DC
  bias (or whatever)
- Game Boy and Game Boy Color now have cycle-based PPU renderers instead
  of scanline-based renderers
- improved Game Boy color emulation palette contrast
- fixed GTK+ ListView selection bug
- fixed a typo when saving states (should say "Saved to slot N", not
  "Save to slot N")
2013-12-11 22:19:17 +11:00
Tim Allen
0f78acffd7 Update to v093r07 release.
byuu says:

Changelog:
- importing a game won't show message box on success
- importing a game will select the game that was imported in the list
  - caveat: GTK+ port doesn't seem to be removing focus from item 0 even
    though the selection is on item 2
- Game Boy audio reduced in volume by 50%
- Game Boy Advance audio reduced in volume by 50%
- Game Boy internally mixes audio at 2MHz now
- Game Boy Advance's Game Boy audio hardware internally mixes audio at
  2MHz now
- Game Boy Color doesn't sort sprites by X-coordinate
- Game Boy Color allows transparency on BGpriority pixels
  - caveat: this seems to allow sprites to appear on top of windows
- Game Boy Color VRAM DMA transfers 16 bytes in 8 clocks (or 16 clocks
  in double speed mode)
- Game Boy Color VRAM DMA masks low 4-bits of source and destination
  address
- Game Boy Color VRAM DMA only allows reads from ROM or RAM
- Game Boy Color VRAM DMA only allows writes to VRAM
- fixed a bug in dereferencing a nullptr from pObject::find(), should
  fix crash when pressing enter key on blank windows
- fixed Windows RadioItem selection
- Game Boy Advance color emulation code added
2013-12-10 23:12:54 +11:00
Tim Allen
35f1605829 Update to v093r06 release.
byuu says:

Changelog:
- Windows port should compile out-of-the-box
- InputManager::scancode[] initialized at startup
- Library menu shows item for each bootable media type (notably Game Boy
  Color)
- Display Emulation menu selection fix
- LibraryManager load button works now
- Added hotkey to show library manager (defaults to L)
- Added color emulation to video settings (missing on GBA for now)
- SFC loading SGB without GB cartridge no longer segfaults
- GB/GBC system.load() after cartridge.load()
- GB/GBC BG-over-OAM fix
- GB/GBC disallow up+down and left+right
2013-12-07 20:12:37 +11:00
Tim Allen
ed4e87f65e Update to v093r05 release.
byuu says:

Library concept has been refined as per the general forum discussion.
2013-12-03 21:01:59 +11:00
Tim Allen
b4f18c3b47 Update to v093r04 release.
byuu says:

This version replaces the old folder-browser with a proper game library.
2013-11-28 21:32:53 +11:00
Tim Allen
68eaf53691 Update to v093r03 release.
byuu says:

Updated to support latest phoenix changes.
Converted Settings and Tools to TabFrame views.

Errata:
- phoenix/Windows ComboButton wasn't calling parent
  pWidget::setGeometry() [fixed locally]
- TRACKBAR_CLASS draws COLOR_3DFACE for the background even when its
  parent is a WC_TABCONTROL
2013-11-28 21:29:01 +11:00
Tim Allen
8c0b0fa4ad Update to v093r02 release.
byuu says:

Changelog:
- nall: fixed major memory leak in string class
- ruby: video shaders support #define-based settings now
- phoenix/GTK+: support > 256x256 icons for window / task bar / alt-tab
- sfc: remove random/ and config/, merge into system/
- ethos: delete higan.png (48x48), replace with higan512.png (512x512)
  as new higan.png
- ethos: default gamma to 100% (no color adjustment)
- ethos: use "Video Shaders/Display Emulation/" instead of "Video
  Shaders/Emulation/"
- use g++ instead of g++-4.7 (g++ -v must be >= 4.7)
- use -std=c++11 instead of -std=gnu++11
- applied a few patches from Debian upstream to make their packaging job
  easier

So because colors are normalized in GLSL, I won't be able to offer video
shaders absolute color literals. We will have to perform basic color
conversion inside the core.

As such, the current plan is to create some sort of Emulator::Settings
interface. With that, I'll connect an option for color correction, which
will be on by default. For FC/SFC, that will mean gamma correction
(darker / stronger colors), and for GB/GBC/GBA, it will mean simulating
the weird brightness levels of the displays. I am undecided on whether
to use pea soup green for the GB or not. By not doing so, it'll be
easier for the display emulation shader to do it.
2013-11-09 22:45:54 +11:00
Tim Allen
66f136718e Update to v093r01 release.
byuu says:

Changelog:
- added SA-1 MDR; fixes bug in SD Gundam G-Next where the main
  battleship was unable to fire
- added out-of-the-box support for any BSD running Clang 3.3+ (FreeBSD
  10+, notably)
- added new video shader, "Display Emulation", which changes the shader
  based on the emulated system
- fixed the home button to go to your default library path
- phoenix: Windows port won't send onActivate unless an item is selected
  (prevents crashing on pressing enter in file dialog)
- ruby: removed vec4 position from out Vertex {} (helps AMD cards)
- shaders: updated all shaders to use texture() instead of texture2D()
  (helps AMD cards)

The "Display Emulation" option works like this: when selected, it tries
to load "<path>/Video Shaders/Emulation/<systemName>.shader/"; otherwise
it falls back to the blur shader. <path> is the usual (next to binary,
then in <config>/higan, then in /usr/share/higan, etc); and <systemName>
is "Famicom", "Super Famicom", "Game Boy", "Game Boy Color", "Game Boy
Advance"

To support BSD, I had to modify the $(platform) variable to
differentiate between Linux and BSD.
As such, the new $(platform) values are:
win -> windows
osx -> macosx
x -> linux or bsd

I am also checking uname -s instead of uname -a now. No reason to
potentially match the hostname to the wrong OS type.
2013-10-21 22:45:39 +11:00
Tim Allen
4e2eb23835 Update to v093 release.
byuu says:

Changelog:
- added Cocoa target: higan can now be compiled for OS X Lion
  [Cydrak, byuu]
- SNES/accuracy profile hires color blending improvements - fixes
  Marvelous text [AWJ]
- fixed a slight bug in SNES/SA-1 VBR support caused by a typo
- added support for multi-pass shaders that can load external textures
  (requires OpenGL 3.2+)
- added game library path (used by ananke->Import Game) to
  Settings->Advanced
- system profiles, shaders and cheats database can be stored in "all
  users" shared folders now (eg /usr/share on Linux)
- all configuration files are in BML format now, instead of XML (much
  easier to read and edit this way)
- main window supports drag-and-drop of game folders (but not game files
  / ZIP archives)
- audio buffer clears when entering a modal loop on Windows (prevents
  audio repetition with DirectSound driver)
- a substantial amount of code clean-up (probably the biggest
  refactoring to date)

One highly desired target for this release was to default to the optimal
drivers instead of the safest drivers, but because AMD drivers don't
seem to like my OpenGL 3.2 driver, I've decided to postpone that. AMD
has too big a market share. Hopefully with v093 officially released, we
can get some public input on what AMD doesn't like.
2013-08-18 13:21:14 +10:00
Tim Allen
c74865e171 Update to v092r10 release.
byuu says:

Changelog:
- you can now drop game folders (not game files, sorry) onto higan's
  main window to load them
- audio buffer will clear on Windows when entering modal loop (entering
  menu, moving or resizing window)
  - this prevents DirectSound driver's audio repetition
- ruby defaults to the optimal driver for each platform, rather than the
  safest driver, now
- added Cydrak's gl_Position.zw change to ruby
- added fixes for all the changes to nall, ruby, phoenix over the past
  three months
2013-07-29 19:42:45 +10:00
Tim Allen
29ea5bd599 Update to v092r09 release.
byuu says:

This will be another massive diff from the previous version.

All of higan was updated to use the new foo& bar syntax, and I also
updated switch statements to be consistent as well (but not in the
disassemblers, was starting to get an RSI just from what I already did.)

phoenix/{windows, cocoa, qt} need to be updated to use "string foo"
instead of "const string& foo", and after that, the major diffs should
be finished.

This archive is the first time I'm posting my copy-on-write,
size+capacity nall::string class, so any feedback on that is welcome as
well.
2013-05-05 19:21:30 +10:00
Tim Allen
75dab443b4 Update to v092r08 release.
byuu says:

Changelog:
- fixed cartridge load window focus on Windows
- lots of updates to nall, ruby and phoenix
- ethos and Emulator::Interface updated from "foo &bar" to "foo& bar"
  syntax (work-in-progress)

Before I had mixed the two ways to declare variables/arguments all over
the place, so the goal is to unify them all for consistency. So the
changelog for this release will be massive (750KB >.>) due to the syntax
change. Yeah, that's what I spent the last three days working on ...
2013-05-02 21:25:45 +10:00
Tim Allen
177e222ca7 Update to v092r07 release.
byuu says:

- OpenGL should work on OS X now; it uses VAOs and VBOs, and is fully
  OpenGL 3.2 core compliant
- all configuration files are now stored in BML format, instead of CFG
  format (half the size, much more readable)
- some old nall libraries that were never used have been removed
- make install works with or without root now (copies core files to
  /usr/share/higan [non-configurable])
- make install also works on OS X (copies to /Library/Application
  Support/higan)
2013-04-14 18:52:47 +10:00
Tim Allen
0d75524791 Update to v092r06 release.
byuu says:

Changelog:
- added support for ruby shader folders (place in "Video Shaders/")
- higan now also looks in your shared folder for configuration files and
  system media folders
- added CFBundleExecutable key to OS X Info.plist

Shared folder locations:
- Windows XP: C:\Documents and Settings\All Users\Application Data\higan
- Windows 7: C:\ProgramData\higan
- OS X: /Library/Application Support/higan
- Linux: /etc/higan

Evaluation order:
- look for item in binary folder: if found, use this folder
- look for item in user folder: if found, use this folder
- look for item in shared folder: if found, use this folder
- create item in user folder

For people repackaging higan for other distros: you should chmod 777
/etc/higan. Failure to do so could result in higan breaking. No, I will
not copy the files from the shared path to the user path.
2013-04-09 23:31:46 +10:00
Tim Allen
5b4bbf5045 Update to v092r05 release.
byuu says:

This release should be polished enough for a general release.

This release should be polished enough for a general release.

Anyone with a real, clean Mac up for posting compiled binaries?
Preferably compile with "make profile=balanced" In fact, I'd like it if
someone were willing to host a "higan for Mac" page, with binaries of
each of the latest releases. Only really needed for major official
releases, but it'd be preferable to have the builds updated as soon as
possible after I post new builds.

Changelog:
- no more keyboard chimes when pressing keys
- status bar added, fully functional
- Label::minimumSize() takes frame into account (but note a few places
  hard-code raw Font::size(), so a few text labels are still clipped)
- resizing the main window looks smooth regardless of whether a game is
  running or not
  - currently, resizing the window pauses the emulation. Allowing it to
    run the main loop was lagging out the window resize process too much
    to be worth it

Additional OS X integration enhancements:
- closing the main window unloads the current game, but does not quit
  the application (quit via the main menu or the dock menu)
- clicking the icon in the dock will (re)display the main menu
2013-03-21 23:59:01 +11:00
Tim Allen
fdd3ea490e Update to v092r04 release.
byuu says:

This is the first release with full support for OS X, although it's
certainly still very buggy.

Known issues:
- window status bars are still unsupported (they just don't show up)
- you get the bad keypress chime when you use the keyboard
- window geometry and font metrics aren't perfect (bit of clipping here
  and there)
- list view headers that aren't auto-sized are sometimes too short (file
  browser)
- input assignment is really rough (assigning a key also moves around in
  the list or beeps at you)

Custom OS X integration support so far:
- 512x512 ICNS application icon: will look razor-sharp even on a retina
  display
- basic Info.plist added to application bundle
- program menu about, preferences, quit all connected
- Settings->Configuration removed (use higan->Preferences instead)
- global menubar

To compile and use this, you'll need:
- Xz Utils (to extract .tar.xz)
- Xcode 4.6
- Lion 10.7.4 or newer

    mkdir higan_v092r04
    tar -xJf higan_v092r04.tar.xz -C higan_v092r04
    cd higan_v092r04
    make -j 2

ananke is missing, and I haven't updated purify yet, so you'll have to
move game folders from Windows or Linux over, or make them by hand (a
not so enjoyable experience, to say the least.)
2013-03-19 19:48:50 +11:00
Tim Allen
b7c212de7e Update to v092r03 release.
byuu says:

This release adds the phoenix/Cocoa port, and rewrites a lot of the
higan user interface to work with all of the new changes (like blocking
in the main run loop and in modal windows.)

It doesn't yet modify the compilation flags to actually build on OS
X yet, and even then, we don't really have ruby drivers, so there'd be
no video, audio or input.

Two months between a single WIP point release ... for the first six
years, I never went more than a month without a full official release.
I guess I should be happy that it's become so refined, but I sure do
miss those halcyon days of exciting progress.
2013-03-16 00:11:33 +11:00
Tim Allen
d9400084c2 Update to v092r02 release.
byuu says:

Changelog:
- merged AWJ's hires color blending improvements (most notably: fixes
  Marvelous' text)
- created sfc/base/ to store base unit (expansion port device) emulation
- synchronized the markup of Satellaview and Sufami Turbo cartridge
  slots in the board markup
- fixed "Initializing ..." typo in timing settings

If at all possible, I'd really like to have heavy testing of games that
use hires graphics to check for any regressions.
I trust AWJ's code, and all of the test ROMs I have thrown at it all
appear to work great. But better safe than sorry. Same deal for any core
changes, it's a lot better to catch it now than after v093 is released.
2013-01-23 19:28:35 +11:00
Tim Allen
bbc33fe05f Update to higan v092r01, ananke v02r01 and purify v03r01 releases.
byuu says:

higan changelog:
- compiler is set to g++-4.7, subst(cc,++) rule is gone, C files compile
  with $(compiler) -x c
- make throws an error when you specify an invalid profile or compile on
  an unsupported platform (instead of hanging forever)
- added unverified.png to resources (causes too big of a speed hit to
  actually check for folder/unverified file ... so disabled for now)
- fixed default browser paths for Game Boy, Sufami Turbo and BS-X
  Satellaview (have to delete paths.cfg to see this)
- browser home button seeks to configpath()/higan/library.cfg
- settings->driver is now settings->advanced, and it adds game library
  path setting and profile information
- emulation cores now load manifest files internally, manifest.bml is
  not required for a game folder to be recognized by higan as such
- BS-X Satellaview and Sufami Turbo slot cartridge handling moved out of
  sfc/chip and into sfc/slot
- Video::StartFullScreen only sets fullscreen when a game is specified
  on the command-line

purify and ananke changelog:
- library output path shown in purify window
- added button to change library path
- squelch firmware warning windows to prevent multi-threading crash, but
  only via purify (they show up in higan still)
2013-01-21 23:27:15 +11:00
Tim Allen
65c4011bec Update to purify v03 release.
byuu says:

This release has an updated version of ananke. If you replace the higan
v092 ananke.dll with this new one, it will fix the SGB+TG3000+ToP+DKJM2
loading issues.
2013-01-21 19:57:04 +11:00
Tim Allen
a7c35a65b4 Update to ananke v01r01 release.
This version fixes a problem where ananke would leave out the
'information' section (that is, the game name) when converting a game to
a game folder, resulting in a folder named " (!)".

It also includes the latest version of nall.
2013-01-19 22:23:42 +11:00
Tim Allen
ba660600ad Update to purify v02r01 release.
Because byuu's Win32 compiler does not yet support the C++11 std::thread
API, he wrote his own portable wrapper library, so now the new purify
works on Windows too.
2013-01-19 22:20:25 +11:00
Tim Allen
b6575ca02a Update to purify v02 release.
byuu says:

purify has been rewritten. It now resembles the older snespurify, and
lets you import multiple game files+archives and regenerate manifests
for multiple game folders. It is also recursive.

So you can now import all of your games for all systems at once, or you
can update all of your bsnes v091 game folders to the new higan v092
format at once.

Caveats:

First, I am now using std::thread, so that the GUI doesn't freeze.
Instead, you get a nice progress bar. Unfortunately, I was mislead and
TDM/GCC 4.7 still does not have std::thread support. So ... sorry, but
I can't compile purify for Windows. I am sick and tired of not being
able to write multi-threaded code, so fuck it. If anyone can get it to
build on Windows, whether that be by using Windows threads, hacking in
std::thread support, skipping threading all together, whatever ...
that'll be great. Otherwise, sorry, purify is Linux only until MinGW can
get its god damned shit together and offers threading support.

Second, there's no way to regenerate Famicom (NES) manifests, because we
discard the iNES header. We are going to need a database for that. So,
all I can suggest is that if you use bsnes/higan, keep all your iNES
images around to re-import as new releases come out.

Third, when you purify game folders, it will back up the ROM and RAM
files only. Your save states, cheat codes, debug logs, etc will be wiped
out. There's a whole lot of reasons for this, the most pertinent is that
it's meant to clean up the folder to a pristine state. It also fixes the
game folder name, etc. So ... sorry, but this is how it works. New
releases rarely if ever allow old save states to work anyway.

Lastly, I am not going to have purify contain infinite backward
compatibility for updating manifests. You will want to keep up with
purifying the collection, otherwise you'll have to grab older purify
copies and convert your way along. Although hopefully the format won't
be so volatile and this won't be necessary very often.
2013-01-17 22:21:00 +11:00
Tim Allen
8d88337e28 Update to ananke v01 release.
byuu says:

This updated anake fixes all of the reported game issues thus far.
2013-01-17 22:20:53 +11:00
Tim Allen
6ac67c260b Update to v092 hotfix release.
byuu says:

For higan:
- I fixed the data ROM/RAM initialization for the Cx4, which would
  periodically cause a crash.
- I also moved the Satellaview MaskROM vs FlashROM detection into the
  Satellaview manifests, so Same Game - Character Data works now.
- I also re-added the driver filter to the video shaders, so the D3D
  driver won't show OGL shaders and vice versa.

For ananke:
- You can now generate the other SGB images by putting sgb.rom in the
  same folder as the BIOS images.
- I fixed the markup in the database and via heuristics for 5MB+ games
  (DKJM2, ToP)
- Sufami Turbo and BS-X Satellaview generate BML now instead of XML when
  using heuristics.
2013-01-15 21:51:49 +11:00
2994 changed files with 147915 additions and 214141 deletions

7
.gitignore vendored
View File

@@ -1,5 +1,2 @@
purify/*.o
purify/purify
purify/analyze-gba
ananke/ananke.o
ananke/libananke.so
higan/profile/WonderSwan.sys/internal.ram
higan/profile/WonderSwan Color.sys/internal.ram

View File

@@ -1,51 +0,0 @@
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:

View File

@@ -1,143 +0,0 @@
#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);
}

View File

@@ -1,29 +0,0 @@
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

@@ -1,60 +0,0 @@
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);
}

View File

@@ -1,13 +0,0 @@
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

@@ -1,19 +0,0 @@
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

@@ -1,162 +0,0 @@
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

View File

@@ -1,32 +0,0 @@
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);
}

View File

@@ -1,125 +0,0 @@
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

@@ -1,37 +0,0 @@
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);
}

View File

@@ -1,39 +0,0 @@
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

@@ -1,173 +0,0 @@
#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

@@ -1,63 +0,0 @@
#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,120 +0,0 @@
#ifndef NALL_EMULATION_GAME_BOY_HPP
#define NALL_EMULATION_GAME_BOY_HPP
#include <nall/sha256.hpp>
#include <nall/string.hpp>
namespace nall {
struct GameBoyCartridge {
string markup;
inline GameBoyCartridge(uint8_t *data, unsigned size);
//private:
struct Information {
string mapper;
bool ram;
bool battery;
bool rtc;
bool rumble;
unsigned romsize;
unsigned ramsize;
bool cgb;
bool cgbonly;
} info;
};
GameBoyCartridge::GameBoyCartridge(uint8_t *romdata, unsigned romsize) {
markup = "";
if(romsize < 0x4000) return;
info.mapper = "unknown";
info.ram = false;
info.battery = false;
info.rtc = false;
info.rumble = false;
info.romsize = 0;
info.ramsize = 0;
unsigned base = romsize - 0x8000;
if(romdata[base + 0x0104] == 0xce && romdata[base + 0x0105] == 0xed
&& romdata[base + 0x0106] == 0x66 && romdata[base + 0x0107] == 0x66
&& romdata[base + 0x0108] == 0xcc && romdata[base + 0x0109] == 0x0d
&& romdata[base + 0x0147] >= 0x0b && romdata[base + 0x0147] <= 0x0d
) {
//MMM01 stores header at bottom of image
//flip this around for consistency with all other mappers
uint8_t header[0x8000];
memcpy(header, romdata + base, 0x8000);
memmove(romdata + 0x8000, romdata, romsize - 0x8000);
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;
case 0x02: info.mapper = "MBC1"; info.ram = true; break;
case 0x03: info.mapper = "MBC1"; info.ram = true; info.battery = true; break;
case 0x05: info.mapper = "MBC2"; info.ram = true; break;
case 0x06: info.mapper = "MBC2"; info.ram = true; info.battery = true; break;
case 0x08: info.mapper = "none"; info.ram = true; break;
case 0x09: info.mapper = "MBC0"; info.ram = true; info.battery = true; break;
case 0x0b: info.mapper = "MMM01"; break;
case 0x0c: info.mapper = "MMM01"; info.ram = true; break;
case 0x0d: info.mapper = "MMM01"; info.ram = true; info.battery = true; break;
case 0x0f: info.mapper = "MBC3"; info.rtc = true; info.battery = true; break;
case 0x10: info.mapper = "MBC3"; info.rtc = true; info.ram = true; info.battery = true; break;
case 0x11: info.mapper = "MBC3"; break;
case 0x12: info.mapper = "MBC3"; info.ram = true; break;
case 0x13: info.mapper = "MBC3"; info.ram = true; info.battery = true; break;
case 0x19: info.mapper = "MBC5"; break;
case 0x1a: info.mapper = "MBC5"; info.ram = true; break;
case 0x1b: info.mapper = "MBC5"; info.ram = true; info.battery = true; break;
case 0x1c: info.mapper = "MBC5"; info.rumble = true; break;
case 0x1d: info.mapper = "MBC5"; info.rumble = true; info.ram = true; break;
case 0x1e: info.mapper = "MBC5"; info.rumble = true; info.ram = true; info.battery = true; break;
case 0xfc: break; //Pocket Camera
case 0xfd: break; //Bandai TAMA5
case 0xfe: info.mapper = "HuC3"; break;
case 0xff: info.mapper = "HuC1"; info.ram = true; info.battery = true; break;
}
switch(romdata[0x0148]) { default:
case 0x00: info.romsize = 2 * 16 * 1024; break;
case 0x01: info.romsize = 4 * 16 * 1024; break;
case 0x02: info.romsize = 8 * 16 * 1024; break;
case 0x03: info.romsize = 16 * 16 * 1024; break;
case 0x04: info.romsize = 32 * 16 * 1024; break;
case 0x05: info.romsize = 64 * 16 * 1024; break;
case 0x06: info.romsize = 128 * 16 * 1024; break;
case 0x07: info.romsize = 256 * 16 * 1024; break;
case 0x52: info.romsize = 72 * 16 * 1024; break;
case 0x53: info.romsize = 80 * 16 * 1024; break;
case 0x54: info.romsize = 96 * 16 * 1024; break;
}
switch(romdata[0x0149]) { default:
case 0x00: info.ramsize = 0 * 1024; break;
case 0x01: info.ramsize = 2 * 1024; break;
case 0x02: info.ramsize = 8 * 1024; break;
case 0x03: info.ramsize = 32 * 1024; break;
}
if(info.mapper == "MBC2") info.ramsize = 512; //512 x 4-bit
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");
}
}
#endif

View File

@@ -1,26 +0,0 @@
#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

@@ -1,33 +0,0 @@
#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

@@ -1,814 +0,0 @@
#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

View File

@@ -1,118 +0,0 @@
# Makefile
# author: byuu
# license: public domain
[A-Z] = A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
[a-z] = a b c d e f g h i j k l m n o p q r s t u v w x y z
[0-9] = 0 1 2 3 4 5 6 7 8 9
[markup] = ` ~ ! @ \# $$ % ^ & * ( ) - _ = + [ { ] } \ | ; : ' " , < . > / ?
[all] = $([A-Z]) $([a-z]) $([0-9]) $([markup])
[space] :=
[space] +=
#####
# platform detection
#####
ifeq ($(platform),)
uname := $(shell uname -a)
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)
else ifneq ($(findstring Darwin,$(uname)),)
platform := osx
delete = rm -f $1
else
platform := x
delete = rm -f $1
endif
endif
ifeq ($(compiler),)
ifeq ($(platform),win)
compiler := gcc
else ifeq ($(platform),osx)
compiler := gcc-mp-4.7
else
compiler := gcc-4.7
endif
endif
c := $(compiler) -std=gnu99
cpp := $(subst cc,++,$(compiler)) -std=gnu++0x
ifeq ($(prefix),)
prefix := /usr/local
endif
#####
# function rwildcard(directory, pattern)
#####
rwildcard = \
$(strip \
$(filter $(if $2,$2,%), \
$(foreach f, \
$(wildcard $1*), \
$(eval t = $(call rwildcard,$f/)) \
$(if $t,$t,$f) \
) \
) \
)
#####
# function strtr(source, from, to)
#####
strtr = \
$(eval __temp := $1) \
$(strip \
$(foreach c, \
$(join $(addsuffix :,$2),$3), \
$(eval __temp := \
$(subst $(word 1,$(subst :, ,$c)),$(word 2,$(subst :, ,$c)),$(__temp)) \
) \
) \
$(__temp) \
)
#####
# function strupper(source)
#####
strupper = $(call strtr,$1,$([a-z]),$([A-Z]))
#####
# function strlower(source)
#####
strlower = $(call strtr,$1,$([A-Z]),$([a-z]))
#####
# function strlen(source)
#####
strlen = \
$(eval __temp := $(subst $([space]),_,$1)) \
$(words \
$(strip \
$(foreach c, \
$([all]), \
$(eval __temp := \
$(subst $c,$c ,$(__temp)) \
) \
) \
$(__temp) \
) \
)
#####
# function streq(source)
#####
streq = $(if $(filter-out xx,x$(subst $1,,$2)$(subst $2,,$1)x),,1)
#####
# function strne(source)
#####
strne = $(if $(filter-out xx,x$(subst $1,,$2)$(subst $2,,$1)x),1,)

View File

@@ -1,17 +0,0 @@
#ifndef NALL_ALGORITHM_HPP
#define NALL_ALGORITHM_HPP
#undef min
#undef max
namespace nall {
template<typename T, typename U> T min(const T &t, const U &u) {
return t < u ? t : u;
}
template<typename T, typename U> T max(const T &t, const U &u) {
return t > u ? t : u;
}
}
#endif

View File

@@ -1,73 +0,0 @@
#ifndef NALL_ANY_HPP
#define NALL_ANY_HPP
#include <typeinfo>
#include <nall/traits.hpp>
namespace nall {
struct any {
bool empty() const { return container; }
const std::type_info& type() const { return container ? container->type() : typeid(void); }
template<typename T> any& operator=(const T& value_) {
typedef typename type_if<
std::is_array<T>::value,
typename std::remove_extent<typename std::add_const<T>::type>::type*,
T
>::type auto_t;
if(type() == typeid(auto_t)) {
static_cast<holder<auto_t>*>(container)->value = (auto_t)value_;
} else {
if(container) delete container;
container = new holder<auto_t>((auto_t)value_);
}
return *this;
}
any() : container(nullptr) {}
~any() { if(container) delete container; }
template<typename T> any(const T& value_) : container(nullptr) { operator=(value_); }
private:
struct placeholder {
virtual const std::type_info& type() const = 0;
} *container;
template<typename T> struct holder : placeholder {
T value;
const std::type_info& type() const { return typeid(T); }
holder(const T& value_) : value(value_) {}
};
template<typename T> friend T any_cast(any&);
template<typename T> friend T any_cast(const any&);
template<typename T> friend T* any_cast(any*);
template<typename T> friend const T* any_cast(const any*);
};
template<typename T> T any_cast(any &value) {
typedef typename std::remove_reference<T>::type nonref;
if(value.type() != typeid(nonref)) throw;
return static_cast<any::holder<nonref>*>(value.container)->value;
}
template<typename T> T any_cast(const any &value) {
typedef const typename std::remove_reference<T>::type nonref;
if(value.type() != typeid(nonref)) throw;
return static_cast<any::holder<nonref>*>(value.container)->value;
}
template<typename T> T* any_cast(any *value) {
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 nullptr;
return &static_cast<any::holder<T>*>(value->container)->value;
}
}
#endif

View File

@@ -1,103 +0,0 @@
#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;
//once they are supported by GCC. eg:
//unsigned operator "" b(const char *s) { return binary(s); }
//-> signed data = 1001b;
//(0b1001 is nicer, but is not part of the C++ standard)
constexpr inline uintmax_t binary_(const char *s, uintmax_t sum = 0) {
return (
*s == '0' || *s == '1' ? binary_(s + 1, (sum << 1) | *s - '0') :
sum
);
}
constexpr inline uintmax_t octal_(const char *s, uintmax_t sum = 0) {
return (
*s >= '0' && *s <= '7' ? octal_(s + 1, (sum << 3) | *s - '0') :
sum
);
}
constexpr inline uintmax_t decimal_(const char *s, uintmax_t sum = 0) {
return (
*s >= '0' && *s <= '9' ? decimal_(s + 1, (sum * 10) + *s - '0') :
sum
);
}
constexpr inline uintmax_t hex_(const char *s, uintmax_t sum = 0) {
return (
*s >= 'A' && *s <= 'F' ? hex_(s + 1, (sum << 4) | *s - 'A' + 10) :
*s >= 'a' && *s <= 'f' ? hex_(s + 1, (sum << 4) | *s - 'a' + 10) :
*s >= '0' && *s <= '9' ? hex_(s + 1, (sum << 4) | *s - '0') :
sum
);
}
//
constexpr inline uintmax_t binary(const char *s) {
return (
*s == '0' && *(s + 1) == 'B' ? binary_(s + 2) :
*s == '0' && *(s + 1) == 'b' ? binary_(s + 2) :
*s == '%' ? binary_(s + 1) :
binary_(s)
);
}
constexpr inline uintmax_t octal(const char *s) {
return (
octal_(s)
);
}
constexpr inline intmax_t integer(const char *s) {
return (
*s == '+' ? +decimal_(s + 1) :
*s == '-' ? -decimal_(s + 1) :
decimal_(s)
);
}
constexpr inline uintmax_t decimal(const char *s) {
return (
decimal_(s)
);
}
constexpr inline uintmax_t hex(const char *s) {
return (
*s == '0' && *(s + 1) == 'X' ? hex_(s + 2) :
*s == '0' && *(s + 1) == 'x' ? hex_(s + 2) :
*s == '$' ? hex_(s + 1) :
hex_(s)
);
}
constexpr inline intmax_t numeral(const char *s) {
return (
*s == '0' && *(s + 1) == 'X' ? hex_(s + 2) :
*s == '0' && *(s + 1) == 'x' ? hex_(s + 2) :
*s == '0' && *(s + 1) == 'B' ? binary_(s + 2) :
*s == '0' && *(s + 1) == 'b' ? binary_(s + 2) :
*s == '0' ? octal_(s + 1) :
*s == '+' ? +decimal_(s + 1) :
*s == '-' ? -decimal_(s + 1) :
decimal_(s)
);
}
inline double fp(const char *s) {
return atof(s);
}
}
#endif

View File

@@ -1,118 +0,0 @@
#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

@@ -1,84 +0,0 @@
#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

View File

@@ -1,92 +0,0 @@
#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,214 +0,0 @@
#ifndef NALL_BEAT_DELTA_HPP
#define NALL_BEAT_DELTA_HPP
#include <nall/crc32.hpp>
#include <nall/file.hpp>
#include <nall/filemap.hpp>
#include <nall/stdint.hpp>
#include <nall/string.hpp>
namespace nall {
struct bpsdelta {
inline void source(const uint8_t *data, unsigned size);
inline void target(const uint8_t *data, unsigned size);
inline bool source(const string &filename);
inline bool target(const string &filename);
inline bool create(const string &filename, const string &metadata = "");
protected:
enum : unsigned { SourceRead, TargetRead, SourceCopy, TargetCopy };
enum : unsigned { Granularity = 1 };
struct Node {
unsigned offset;
Node *next;
inline Node() : offset(0), next(nullptr) {}
inline ~Node() { if(next) delete next; }
};
filemap sourceFile;
const uint8_t *sourceData;
unsigned sourceSize;
filemap targetFile;
const uint8_t *targetData;
unsigned targetSize;
};
void bpsdelta::source(const uint8_t *data, unsigned size) {
sourceData = data;
sourceSize = size;
}
void bpsdelta::target(const uint8_t *data, unsigned size) {
targetData = data;
targetSize = size;
}
bool bpsdelta::source(const string &filename) {
if(sourceFile.open(filename, filemap::mode::read) == false) return false;
source(sourceFile.data(), sourceFile.size());
return true;
}
bool bpsdelta::target(const string &filename) {
if(targetFile.open(filename, filemap::mode::read) == false) return false;
target(targetFile.data(), targetFile.size());
return true;
}
bool bpsdelta::create(const string &filename, const string &metadata) {
file modifyFile;
if(modifyFile.open(filename, file::mode::write) == false) return false;
uint32_t sourceChecksum = ~0, modifyChecksum = ~0;
unsigned sourceRelativeOffset = 0, targetRelativeOffset = 0, outputOffset = 0;
auto write = [&](uint8_t data) {
modifyFile.write(data);
modifyChecksum = crc32_adjust(modifyChecksum, data);
};
auto encode = [&](uint64_t data) {
while(true) {
uint64_t x = data & 0x7f;
data >>= 7;
if(data == 0) {
write(0x80 | x);
break;
}
write(x);
data--;
}
};
write('B');
write('P');
write('S');
write('1');
encode(sourceSize);
encode(targetSize);
unsigned markupSize = metadata.length();
encode(markupSize);
for(unsigned n = 0; n < markupSize; n++) write(metadata[n]);
Node *sourceTree[65536], *targetTree[65536];
for(unsigned n = 0; n < 65536; n++) sourceTree[n] = 0, targetTree[n] = 0;
//source tree creation
for(unsigned offset = 0; offset < sourceSize; offset++) {
uint16_t symbol = sourceData[offset + 0];
sourceChecksum = crc32_adjust(sourceChecksum, symbol);
if(offset < sourceSize - 1) symbol |= sourceData[offset + 1] << 8;
Node *node = new Node;
node->offset = offset;
node->next = sourceTree[symbol];
sourceTree[symbol] = node;
}
unsigned targetReadLength = 0;
auto targetReadFlush = [&]() {
if(targetReadLength) {
encode(TargetRead | ((targetReadLength - 1) << 2));
unsigned offset = outputOffset - targetReadLength;
while(targetReadLength) write(targetData[offset++]), targetReadLength--;
}
};
while(outputOffset < targetSize) {
unsigned maxLength = 0, maxOffset = 0, mode = TargetRead;
uint16_t symbol = targetData[outputOffset + 0];
if(outputOffset < targetSize - 1) symbol |= targetData[outputOffset + 1] << 8;
{ //source read
unsigned length = 0, offset = outputOffset;
while(offset < sourceSize && offset < targetSize && sourceData[offset] == targetData[offset]) {
length++;
offset++;
}
if(length > maxLength) maxLength = length, mode = SourceRead;
}
{ //source copy
Node *node = sourceTree[symbol];
while(node) {
unsigned length = 0, x = node->offset, y = outputOffset;
while(x < sourceSize && y < targetSize && sourceData[x++] == targetData[y++]) length++;
if(length > maxLength) maxLength = length, maxOffset = node->offset, mode = SourceCopy;
node = node->next;
}
}
{ //target copy
Node *node = targetTree[symbol];
while(node) {
unsigned length = 0, x = node->offset, y = outputOffset;
while(y < targetSize && targetData[x++] == targetData[y++]) length++;
if(length > maxLength) maxLength = length, maxOffset = node->offset, mode = TargetCopy;
node = node->next;
}
//target tree append
node = new Node;
node->offset = outputOffset;
node->next = targetTree[symbol];
targetTree[symbol] = node;
}
{ //target read
if(maxLength < 4) {
maxLength = min((unsigned)Granularity, targetSize - outputOffset);
mode = TargetRead;
}
}
if(mode != TargetRead) targetReadFlush();
switch(mode) {
case SourceRead:
encode(SourceRead | ((maxLength - 1) << 2));
break;
case TargetRead:
//delay write to group sequential TargetRead commands into one
targetReadLength += maxLength;
break;
case SourceCopy:
case TargetCopy:
encode(mode | ((maxLength - 1) << 2));
signed relativeOffset;
if(mode == SourceCopy) {
relativeOffset = maxOffset - sourceRelativeOffset;
sourceRelativeOffset = maxOffset + maxLength;
} else {
relativeOffset = maxOffset - targetRelativeOffset;
targetRelativeOffset = maxOffset + maxLength;
}
encode((relativeOffset < 0) | (abs(relativeOffset) << 1));
break;
}
outputOffset += maxLength;
}
targetReadFlush();
sourceChecksum = ~sourceChecksum;
for(unsigned n = 0; n < 32; n += 8) write(sourceChecksum >> n);
uint32_t targetChecksum = crc32_calculate(targetData, targetSize);
for(unsigned n = 0; n < 32; n += 8) write(targetChecksum >> n);
uint32_t outputChecksum = ~modifyChecksum;
for(unsigned n = 0; n < 32; n += 8) write(outputChecksum >> n);
modifyFile.close();
return true;
}
}
#endif

View File

@@ -1,152 +0,0 @@
#ifndef NALL_BEAT_LINEAR_HPP
#define NALL_BEAT_LINEAR_HPP
#include <nall/crc32.hpp>
#include <nall/file.hpp>
#include <nall/filemap.hpp>
#include <nall/stdint.hpp>
#include <nall/string.hpp>
namespace nall {
struct bpslinear {
inline void source(const uint8_t *data, unsigned size);
inline void target(const uint8_t *data, unsigned size);
inline bool source(const string &filename);
inline bool target(const string &filename);
inline bool create(const string &filename, const string &metadata = "");
protected:
enum : unsigned { SourceRead, TargetRead, SourceCopy, TargetCopy };
enum : unsigned { Granularity = 1 };
filemap sourceFile;
const uint8_t *sourceData;
unsigned sourceSize;
filemap targetFile;
const uint8_t *targetData;
unsigned targetSize;
};
void bpslinear::source(const uint8_t *data, unsigned size) {
sourceData = data;
sourceSize = size;
}
void bpslinear::target(const uint8_t *data, unsigned size) {
targetData = data;
targetSize = size;
}
bool bpslinear::source(const string &filename) {
if(sourceFile.open(filename, filemap::mode::read) == false) return false;
source(sourceFile.data(), sourceFile.size());
return true;
}
bool bpslinear::target(const string &filename) {
if(targetFile.open(filename, filemap::mode::read) == false) return false;
target(targetFile.data(), targetFile.size());
return true;
}
bool bpslinear::create(const string &filename, const string &metadata) {
file modifyFile;
if(modifyFile.open(filename, file::mode::write) == false) return false;
uint32_t modifyChecksum = ~0;
unsigned targetRelativeOffset = 0, outputOffset = 0;
auto write = [&](uint8_t data) {
modifyFile.write(data);
modifyChecksum = crc32_adjust(modifyChecksum, data);
};
auto encode = [&](uint64_t data) {
while(true) {
uint64_t x = data & 0x7f;
data >>= 7;
if(data == 0) {
write(0x80 | x);
break;
}
write(x);
data--;
}
};
unsigned targetReadLength = 0;
auto targetReadFlush = [&]() {
if(targetReadLength) {
encode(TargetRead | ((targetReadLength - 1) << 2));
unsigned offset = outputOffset - targetReadLength;
while(targetReadLength) write(targetData[offset++]), targetReadLength--;
}
};
write('B');
write('P');
write('S');
write('1');
encode(sourceSize);
encode(targetSize);
unsigned markupSize = metadata.length();
encode(markupSize);
for(unsigned n = 0; n < markupSize; n++) write(metadata[n]);
while(outputOffset < targetSize) {
unsigned sourceLength = 0;
for(unsigned n = 0; outputOffset + n < min(sourceSize, targetSize); n++) {
if(sourceData[outputOffset + n] != targetData[outputOffset + n]) break;
sourceLength++;
}
unsigned rleLength = 0;
for(unsigned n = 1; outputOffset + n < targetSize; n++) {
if(targetData[outputOffset] != targetData[outputOffset + n]) break;
rleLength++;
}
if(rleLength >= 4) {
//write byte to repeat
targetReadLength++;
outputOffset++;
targetReadFlush();
//copy starting from repetition byte
encode(TargetCopy | ((rleLength - 1) << 2));
unsigned relativeOffset = (outputOffset - 1) - targetRelativeOffset;
encode(relativeOffset << 1);
outputOffset += rleLength;
targetRelativeOffset = outputOffset - 1;
} else if(sourceLength >= 4) {
targetReadFlush();
encode(SourceRead | ((sourceLength - 1) << 2));
outputOffset += sourceLength;
} else {
targetReadLength += Granularity;
outputOffset += Granularity;
}
}
targetReadFlush();
uint32_t sourceChecksum = crc32_calculate(sourceData, sourceSize);
for(unsigned n = 0; n < 32; n += 8) write(sourceChecksum >> n);
uint32_t targetChecksum = crc32_calculate(targetData, targetSize);
for(unsigned n = 0; n < 32; n += 8) write(targetChecksum >> n);
uint32_t outputChecksum = ~modifyChecksum;
for(unsigned n = 0; n < 32; n += 8) write(outputChecksum >> n);
modifyFile.close();
return true;
}
}
#endif

View File

@@ -1,121 +0,0 @@
#ifndef NALL_BEAT_METADATA_HPP
#define NALL_BEAT_METADATA_HPP
#include <nall/crc32.hpp>
#include <nall/file.hpp>
#include <nall/filemap.hpp>
#include <nall/stdint.hpp>
#include <nall/string.hpp>
namespace nall {
struct bpsmetadata {
inline bool load(const string &filename);
inline bool save(const string &filename, const string &metadata);
inline string metadata() const;
protected:
file sourceFile;
string metadataString;
};
bool bpsmetadata::load(const string &filename) {
if(sourceFile.open(filename, file::mode::read) == false) return false;
auto read = [&]() -> uint8_t {
return sourceFile.read();
};
auto decode = [&]() -> uint64_t {
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;
};
if(read() != 'B') return false;
if(read() != 'P') return false;
if(read() != 'S') return false;
if(read() != '1') return false;
decode();
decode();
unsigned metadataSize = decode();
char data[metadataSize + 1];
for(unsigned n = 0; n < metadataSize; n++) data[n] = read();
data[metadataSize] = 0;
metadataString = (const char*)data;
return true;
}
bool bpsmetadata::save(const string &filename, const string &metadata) {
file targetFile;
if(targetFile.open(filename, file::mode::write) == false) return false;
if(sourceFile.open() == false) return false;
sourceFile.seek(0);
auto read = [&]() -> uint8_t {
return sourceFile.read();
};
auto decode = [&]() -> uint64_t {
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;
};
uint32_t checksum = ~0;
auto write = [&](uint8_t data) {
targetFile.write(data);
checksum = crc32_adjust(checksum, data);
};
auto encode = [&](uint64_t data) {
while(true) {
uint64_t x = data & 0x7f;
data >>= 7;
if(data == 0) {
write(0x80 | x);
break;
}
write(x);
data--;
}
};
for(unsigned n = 0; n < 4; n++) write(read());
encode(decode());
encode(decode());
unsigned sourceLength = decode();
unsigned targetLength = metadata.length();
encode(targetLength);
sourceFile.seek(sourceLength, file::index::relative);
for(unsigned n = 0; n < targetLength; n++) write(metadata[n]);
unsigned length = sourceFile.size() - sourceFile.offset() - 4;
for(unsigned n = 0; n < length; n++) write(read());
uint32_t outputChecksum = ~checksum;
for(unsigned n = 0; n < 32; n += 8) write(outputChecksum >> n);
targetFile.close();
return true;
}
string bpsmetadata::metadata() const {
return metadataString;
}
}
#endif

View File

@@ -1,242 +0,0 @@
#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,219 +0,0 @@
#ifndef NALL_BEAT_PATCH_HPP
#define NALL_BEAT_PATCH_HPP
#include <nall/crc32.hpp>
#include <nall/file.hpp>
#include <nall/filemap.hpp>
#include <nall/stdint.hpp>
#include <nall/string.hpp>
namespace nall {
struct bpspatch {
inline bool modify(const uint8_t *data, unsigned size);
inline void source(const uint8_t *data, unsigned size);
inline void target(uint8_t *data, unsigned size);
inline bool modify(const string &filename);
inline bool source(const string &filename);
inline bool target(const string &filename);
inline string metadata() const;
inline unsigned size() const;
enum result : unsigned {
unknown,
success,
patch_too_small,
patch_invalid_header,
source_too_small,
target_too_small,
source_checksum_invalid,
target_checksum_invalid,
patch_checksum_invalid,
};
inline result apply();
protected:
enum : unsigned { SourceRead, TargetRead, SourceCopy, TargetCopy };
filemap modifyFile;
const uint8_t *modifyData;
unsigned modifySize;
filemap sourceFile;
const uint8_t *sourceData;
unsigned sourceSize;
filemap targetFile;
uint8_t *targetData;
unsigned targetSize;
unsigned modifySourceSize;
unsigned modifyTargetSize;
unsigned modifyMarkupSize;
string metadataString;
};
bool bpspatch::modify(const uint8_t *data, unsigned size) {
if(size < 19) return false;
modifyData = data;
modifySize = size;
unsigned offset = 4;
auto decode = [&]() -> uint64_t {
uint64_t data = 0, shift = 1;
while(true) {
uint8_t x = modifyData[offset++];
data += (x & 0x7f) * shift;
if(x & 0x80) break;
shift <<= 7;
data += shift;
}
return data;
};
modifySourceSize = decode();
modifyTargetSize = decode();
modifyMarkupSize = decode();
char buffer[modifyMarkupSize + 1];
for(unsigned n = 0; n < modifyMarkupSize; n++) buffer[n] = modifyData[offset++];
buffer[modifyMarkupSize] = 0;
metadataString = (const char*)buffer;
return true;
}
void bpspatch::source(const uint8_t *data, unsigned size) {
sourceData = data;
sourceSize = size;
}
void bpspatch::target(uint8_t *data, unsigned size) {
targetData = data;
targetSize = size;
}
bool bpspatch::modify(const string &filename) {
if(modifyFile.open(filename, filemap::mode::read) == false) return false;
return modify(modifyFile.data(), modifyFile.size());
}
bool bpspatch::source(const string &filename) {
if(sourceFile.open(filename, filemap::mode::read) == false) return false;
source(sourceFile.data(), sourceFile.size());
return true;
}
bool bpspatch::target(const string &filename) {
file fp;
if(fp.open(filename, file::mode::write) == false) return false;
fp.truncate(modifyTargetSize);
fp.close();
if(targetFile.open(filename, filemap::mode::readwrite) == false) return false;
target(targetFile.data(), targetFile.size());
return true;
}
string bpspatch::metadata() const {
return metadataString;
}
unsigned bpspatch::size() const {
return modifyTargetSize;
}
bpspatch::result bpspatch::apply() {
if(modifySize < 19) return result::patch_too_small;
uint32_t modifyChecksum = ~0, targetChecksum = ~0;
unsigned modifyOffset = 0, sourceRelativeOffset = 0, targetRelativeOffset = 0, outputOffset = 0;
auto read = [&]() -> uint8_t {
uint8_t data = modifyData[modifyOffset++];
modifyChecksum = crc32_adjust(modifyChecksum, data);
return data;
};
auto decode = [&]() -> uint64_t {
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;
};
auto write = [&](uint8_t data) {
targetData[outputOffset++] = data;
targetChecksum = crc32_adjust(targetChecksum, data);
};
if(read() != 'B') return result::patch_invalid_header;
if(read() != 'P') return result::patch_invalid_header;
if(read() != 'S') return result::patch_invalid_header;
if(read() != '1') return result::patch_invalid_header;
modifySourceSize = decode();
modifyTargetSize = decode();
modifyMarkupSize = decode();
for(unsigned n = 0; n < modifyMarkupSize; n++) read();
if(modifySourceSize > sourceSize) return result::source_too_small;
if(modifyTargetSize > targetSize) return result::target_too_small;
while(modifyOffset < modifySize - 12) {
unsigned length = decode();
unsigned mode = length & 3;
length = (length >> 2) + 1;
switch(mode) {
case SourceRead:
while(length--) write(sourceData[outputOffset]);
break;
case TargetRead:
while(length--) write(read());
break;
case SourceCopy:
case TargetCopy:
signed offset = decode();
bool negative = offset & 1;
offset >>= 1;
if(negative) offset = -offset;
if(mode == SourceCopy) {
sourceRelativeOffset += offset;
while(length--) write(sourceData[sourceRelativeOffset++]);
} else {
targetRelativeOffset += offset;
while(length--) write(targetData[targetRelativeOffset++]);
}
break;
}
}
uint32_t modifySourceChecksum = 0, modifyTargetChecksum = 0, modifyModifyChecksum = 0;
for(unsigned n = 0; n < 32; n += 8) modifySourceChecksum |= read() << n;
for(unsigned n = 0; n < 32; n += 8) modifyTargetChecksum |= read() << n;
uint32_t checksum = ~modifyChecksum;
for(unsigned n = 0; n < 32; n += 8) modifyModifyChecksum |= read() << n;
uint32_t sourceChecksum = crc32_calculate(sourceData, modifySourceSize);
targetChecksum = ~targetChecksum;
if(sourceChecksum != modifySourceChecksum) return result::source_checksum_invalid;
if(targetChecksum != modifyTargetChecksum) return result::target_checksum_invalid;
if(checksum != modifyModifyChecksum) return result::patch_checksum_invalid;
return result::success;
}
}
#endif

View File

@@ -1,82 +0,0 @@
#ifndef NALL_BIT_HPP
#define NALL_BIT_HPP
#include <nall/stdint.hpp>
namespace nall {
template<unsigned bits>
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>
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>
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>
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;
}
//clear_lowest(0b1110) == 0b1100
constexpr inline uintmax_t clear_lowest(const uintmax_t x) {
return x & (x - 1);
}
//set_lowest(0b0101) == 0b0111
constexpr inline uintmax_t set_lowest(const uintmax_t x) {
return x | (x + 1);
}
//count number of bits set in a byte
inline unsigned count(uintmax_t x) {
unsigned count = 0;
do count += x & 1; while(x >>= 1);
return count;
}
//round up to next highest single bit:
//round(15) == 16, round(16) == 16, round(17) == 32
inline uintmax_t round(uintmax_t x) {
if((x & (x - 1)) == 0) return x;
while(x & (x - 1)) x &= x - 1;
return x << 1;
}
}
}
#endif

View File

@@ -1,101 +0,0 @@
#ifndef NALL_BMP_HPP
#define NALL_BMP_HPP
#include <nall/file.hpp>
//BMP reader / writer
//author: byuu
//note: only 24-bit RGB and 32-bit ARGB uncompressed images supported
namespace nall {
struct bmp {
inline static bool read(const string &filename, uint32_t *&data, unsigned &width, unsigned &height);
inline static bool write(const string &filename, const uint32_t *data, unsigned width, unsigned height, unsigned pitch, bool alpha = false);
};
bool bmp::read(const string &filename, uint32_t *&data, unsigned &width, unsigned &height) {
file fp;
if(fp.open(filename, file::mode::read) == false) return false;
if(fp.size() < 0x36) return false;
if(fp.readm(2) != 0x424d) return false;
fp.seek(0x000a);
unsigned offset = fp.readl(4);
unsigned dibsize = fp.readl(4);
if(dibsize != 40) return false;
signed headerWidth = fp.readl(4);
if(headerWidth < 0) return false;
signed headerHeight = fp.readl(4);
fp.readl(2);
unsigned bitsPerPixel = fp.readl(2);
if(bitsPerPixel != 24 && bitsPerPixel != 32) return false;
unsigned compression = fp.readl(4);
if(compression != 0) return false;
fp.seek(offset);
bool noFlip = headerHeight < 0;
width = headerWidth, height = abs(headerHeight);
data = new uint32_t[width * height];
unsigned bytesPerPixel = bitsPerPixel / 8;
unsigned alignedWidth = width * bytesPerPixel;
unsigned paddingLength = 0;
while(alignedWidth % 4) alignedWidth++, paddingLength++;
for(unsigned y = 0; y < height; y++) {
uint32_t *p = noFlip ? data + y * width : data + (height - 1 - y) * width;
for(unsigned x = 0; x < width; x++, p++) {
*p = fp.readl(bytesPerPixel);
if(bytesPerPixel == 3) *p |= 255 << 24;
}
if(paddingLength) fp.readl(paddingLength);
}
fp.close();
return true;
}
bool bmp::write(const string &filename, const uint32_t *data, unsigned width, unsigned height, unsigned pitch, bool alpha) {
file fp;
if(fp.open(filename, file::mode::write) == false) return false;
unsigned bitsPerPixel = alpha ? 32 : 24;
unsigned bytesPerPixel = bitsPerPixel / 8;
unsigned alignedWidth = width * bytesPerPixel;
unsigned paddingLength = 0;
unsigned imageSize = alignedWidth * height;
unsigned fileSize = 0x36 + imageSize;
while(alignedWidth % 4) alignedWidth++, paddingLength++;
fp.writem(0x424d, 2); //signature
fp.writel(fileSize, 4); //file size
fp.writel(0, 2); //reserved
fp.writel(0, 2); //reserved
fp.writel(0x36, 4); //offset
fp.writel(40, 4); //DIB size
fp.writel(width, 4); //width
fp.writel(-height, 4); //height
fp.writel(1, 2); //color planes
fp.writel(bitsPerPixel, 2); //bits per pixel
fp.writel(0, 4); //compression method (BI_RGB)
fp.writel(imageSize, 4); //image data size
fp.writel(3780, 4); //horizontal resolution
fp.writel(3780, 4); //vertical resolution
fp.writel(0, 4); //palette size
fp.writel(0, 4); //important color count
for(unsigned y = 0; y < height; y++) {
const uint32_t *p = (const uint32_t*)((const uint8_t*)data + y * pitch);
for(unsigned x = 0; x < width; x++) fp.writel(*p++, bytesPerPixel);
if(paddingLength) fp.writel(0, paddingLength);
}
fp.close();
return true;
}
}
#endif

View File

@@ -1,152 +0,0 @@
#ifndef NALL_COMPOSITOR_HPP
#define NALL_COMPOSITOR_HPP
#include <nall/intrinsics.hpp>
namespace nall {
struct compositor {
inline static bool enabled();
inline static bool enable(bool status);
#if defined(PLATFORM_X)
enum class Compositor : unsigned { Unknown, Metacity, Xfwm4 };
inline static Compositor detect();
inline static bool enabled_metacity();
inline static bool enable_metacity(bool status);
inline static bool enabled_xfwm4();
inline static bool enable_xfwm4(bool status);
#endif
};
#if defined(PLATFORM_X)
//Metacity
bool compositor::enabled_metacity() {
FILE *fp = popen("gconftool-2 --get /apps/metacity/general/compositing_manager", "r");
if(fp == 0) return false;
char buffer[512];
if(fgets(buffer, sizeof buffer, fp) == 0) return false;
if(!memcmp(buffer, "true", 4)) return true;
return false;
}
bool compositor::enable_metacity(bool status) {
FILE *fp;
if(status) {
fp = popen("gconftool-2 --set --type bool /apps/metacity/general/compositing_manager true", "r");
} else {
fp = popen("gconftool-2 --set --type bool /apps/metacity/general/compositing_manager false", "r");
}
if(fp == 0) return false;
pclose(fp);
return true;
}
//Xfwm4
bool compositor::enabled_xfwm4() {
FILE *fp = popen("xfconf-query -c xfwm4 -p '/general/use_compositing'", "r");
if(fp == 0) return false;
char buffer[512];
if(fgets(buffer, sizeof buffer, fp) == 0) return false;
if(!memcmp(buffer, "true", 4)) return true;
return false;
}
bool compositor::enable_xfwm4(bool status) {
FILE *fp;
if(status) {
fp = popen("xfconf-query -c xfwm4 -p '/general/use_compositing' -t 'bool' -s 'true'", "r");
} else {
fp = popen("xfconf-query -c xfwm4 -p '/general/use_compositing' -t 'bool' -s 'false'", "r");
}
if(fp == 0) return false;
pclose(fp);
return true;
}
//General
compositor::Compositor compositor::detect() {
Compositor result = Compositor::Unknown;
FILE *fp;
char buffer[512];
fp = popen("pidof metacity", "r");
if(fp && fgets(buffer, sizeof buffer, fp)) result = Compositor::Metacity;
pclose(fp);
fp = popen("pidof xfwm4", "r");
if(fp && fgets(buffer, sizeof buffer, fp)) result = Compositor::Xfwm4;
pclose(fp);
return result;
}
bool compositor::enabled() {
switch(detect()) {
case Compositor::Metacity: return enabled_metacity();
case Compositor::Xfwm4: return enabled_xfwm4();
default: return false;
}
}
bool compositor::enable(bool status) {
switch(detect()) {
case Compositor::Metacity: return enable_metacity(status);
case Compositor::Xfwm4: return enable_xfwm4(status);
default: return false;
}
}
#elif defined(PLATFORM_WINDOWS)
bool compositor::enabled() {
HMODULE module = GetModuleHandleW(L"dwmapi");
if(module == 0) module = LoadLibraryW(L"dwmapi");
if(module == 0) return false;
auto pDwmIsCompositionEnabled = (HRESULT (WINAPI*)(BOOL*))GetProcAddress(module, "DwmIsCompositionEnabled");
if(pDwmIsCompositionEnabled == 0) return false;
BOOL result;
if(pDwmIsCompositionEnabled(&result) != S_OK) return false;
return result;
}
bool compositor::enable(bool status) {
HMODULE module = GetModuleHandleW(L"dwmapi");
if(module == 0) module = LoadLibraryW(L"dwmapi");
if(module == 0) return false;
auto pDwmEnableComposition = (HRESULT (WINAPI*)(UINT))GetProcAddress(module, "DwmEnableComposition");
if(pDwmEnableComposition == 0) return false;
if(pDwmEnableComposition(status) != S_OK) return false;
return true;
}
#else
bool compositor::enabled() {
return false;
}
bool compositor::enable(bool) {
return false;
}
#endif
}
#endif

View File

@@ -1,126 +0,0 @@
#ifndef NALL_CONFIG_HPP
#define NALL_CONFIG_HPP
#include <nall/file.hpp>
#include <nall/string.hpp>
#include <nall/vector.hpp>
namespace nall {
namespace configuration_traits {
template<typename T> struct is_boolean { enum { value = false }; };
template<> struct is_boolean<bool> { enum { value = true }; };
template<typename T> struct is_signed { enum { value = false }; };
template<> struct is_signed<signed> { enum { value = true }; };
template<typename T> struct is_unsigned { enum { value = false }; };
template<> struct is_unsigned<unsigned> { enum { value = true }; };
template<typename T> struct is_double { enum { value = false }; };
template<> struct is_double<double> { enum { value = true }; };
template<typename T> struct is_string { enum { value = false }; };
template<> struct is_string<string> { enum { value = true }; };
}
class configuration {
public:
enum type_t { boolean_t, signed_t, unsigned_t, double_t, string_t, unknown_t };
struct item_t {
uintptr_t data;
string name;
string desc;
type_t type;
inline string get() const {
switch(type) {
case boolean_t: return { *(bool*)data };
case signed_t: return { *(signed*)data };
case unsigned_t: return { *(unsigned*)data };
case double_t: return { *(double*)data };
case string_t: return { "\"", *(string*)data, "\"" };
}
return "???";
}
inline void set(string s) {
switch(type) {
case boolean_t: *(bool*)data = (s == "true"); break;
case signed_t: *(signed*)data = integer(s); break;
case unsigned_t: *(unsigned*)data = decimal(s); break;
case double_t: *(double*)data = fp(s); break;
case string_t: s.trim("\""); *(string*)data = s; break;
}
}
};
vector<item_t> list;
template<typename T>
inline void append(T &data, const char *name, const char *desc = "") {
item_t item = { (uintptr_t)&data, name, desc };
if(configuration_traits::is_boolean<T>::value) item.type = boolean_t;
else if(configuration_traits::is_signed<T>::value) item.type = signed_t;
else if(configuration_traits::is_unsigned<T>::value) item.type = unsigned_t;
else if(configuration_traits::is_double<T>::value) item.type = double_t;
else if(configuration_traits::is_string<T>::value) item.type = string_t;
else item.type = unknown_t;
list.append(item);
}
//deprecated
template<typename T>
inline void attach(T &data, const char *name, const char *desc = "") {
append(data, name, desc);
}
inline virtual bool load(const string &filename) {
string data;
if(data.readfile(filename) == true) {
data.replace("\r", "");
lstring line;
line.split("\n", data);
for(unsigned i = 0; i < line.size(); i++) {
if(auto position = qstrpos(line[i], "#")) line[i][position()] = 0;
if(!qstrpos(line[i], " = ")) continue;
lstring part;
part.qsplit(" = ", line[i]);
part[0].trim();
part[1].trim();
for(unsigned n = 0; n < list.size(); n++) {
if(part[0] == list[n].name) {
list[n].set(part[1]);
break;
}
}
}
return true;
} else {
return false;
}
}
inline virtual bool save(const string &filename) const {
file fp;
if(fp.open(filename, file::mode::write)) {
for(unsigned i = 0; i < list.size(); i++) {
string output;
output.append(list[i].name, " = ", list[i].get());
if(list[i].desc != "") output.append(" # ", list[i].desc);
output.append("\r\n");
fp.print(output);
}
fp.close();
return true;
} else {
return false;
}
}
};
}
#endif

View File

@@ -1,25 +0,0 @@
#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

View File

@@ -1,66 +0,0 @@
#ifndef NALL_CRC32_HPP
#define NALL_CRC32_HPP
#include <nall/stdint.hpp>
namespace nall {
const uint32_t crc32_table[256] = {
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
};
inline uint32_t crc32_adjust(uint32_t crc32, uint8_t input) {
return ((crc32 >> 8) & 0x00ffffff) ^ crc32_table[(crc32 ^ input) & 0xff];
}
inline uint32_t crc32_calculate(const uint8_t *data, unsigned length) {
uint32_t crc32 = ~0;
for(unsigned i = 0; i < length; i++) {
crc32 = crc32_adjust(crc32, data[i]);
}
return ~crc32;
}
}
#endif

View File

@@ -1,224 +0,0 @@
#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

View File

@@ -1,115 +0,0 @@
#ifndef NALL_DL_HPP
#define NALL_DL_HPP
//dynamic linking support
#include <nall/intrinsics.hpp>
#include <nall/stdint.hpp>
#include <nall/string.hpp>
#include <nall/utility.hpp>
#if defined(PLATFORM_X) || defined(PLATFORM_OSX)
#include <dlfcn.h>
#elif defined(PLATFORM_WINDOWS)
#include <windows.h>
#include <nall/windows/utf8.hpp>
#endif
namespace nall {
struct library {
bool opened() const { return handle; }
bool open(const char*, const char* = "");
bool open_absolute(const char*);
void* sym(const char*);
void close();
library() : handle(0) {}
~library() { close(); }
library& operator=(const library&) = delete;
library(const library&) = delete;
private:
uintptr_t handle;
};
#if defined(PLATFORM_X)
inline bool library::open(const char *name, const char *path) {
if(handle) close();
handle = (uintptr_t)dlopen(string(path, *path && !strend(path, "/") ? "/" : "", "lib", name, ".so"), RTLD_LAZY);
if(!handle) handle = (uintptr_t)dlopen(string("/usr/local/lib/lib", name, ".so"), RTLD_LAZY);
return handle;
}
inline bool library::open_absolute(const char *name) {
if(handle) close();
handle = (uintptr_t)dlopen(name, RTLD_LAZY);
return handle;
}
inline void* library::sym(const char *name) {
if(!handle) return 0;
return dlsym((void*)handle, name);
}
inline void library::close() {
if(!handle) return;
dlclose((void*)handle);
handle = 0;
}
#elif defined(PLATFORM_OSX)
inline bool library::open(const char *name, const char *path) {
if(handle) close();
handle = (uintptr_t)dlopen(string(path, *path && !strend(path, "/") ? "/" : "", "lib", name, ".dylib"), RTLD_LAZY);
if(!handle) handle = (uintptr_t)dlopen(string("/usr/local/lib/lib", name, ".dylib"), RTLD_LAZY);
return handle;
}
inline bool library::open_absolute(const char *name) {
if(handle) close();
handle = (uintptr_t)dlopen(name, RTLD_LAZY);
return handle;
}
inline void* library::sym(const char *name) {
if(!handle) return 0;
return dlsym((void*)handle, name);
}
inline void library::close() {
if(!handle) return;
dlclose((void*)handle);
handle = 0;
}
#elif defined(PLATFORM_WINDOWS)
inline bool library::open(const char *name, const char *path) {
if(handle) close();
string filepath(path, *path && !strend(path, "/") && !strend(path, "\\") ? "\\" : "", name, ".dll");
handle = (uintptr_t)LoadLibraryW(utf16_t(filepath));
return handle;
}
inline bool library::open_absolute(const char *name) {
if(handle) close();
handle = (uintptr_t)LoadLibraryW(utf16_t(name));
return handle;
}
inline void* library::sym(const char *name) {
if(!handle) return 0;
return (void*)GetProcAddress((HMODULE)handle, name);
}
inline void library::close() {
if(!handle) return;
FreeLibrary((HMODULE)handle);
handle = 0;
}
#else
inline bool library::open(const char*, const char*) { return false; }
inline void* library::sym(const char*) { return 0; }
inline void library::close() {}
#endif
};
#endif

View File

@@ -1,13 +0,0 @@
#ifndef NALL_DSP_HPP
#define NALL_DSP_HPP
#include <algorithm>
#ifdef __SSE__
#include <xmmintrin.h>
#endif
#define NALL_DSP_INTERNAL_HPP
#include <nall/dsp/core.hpp>
#undef NALL_DSP_INTERNAL_HPP
#endif

View File

@@ -1,51 +0,0 @@
#ifdef NALL_DSP_INTERNAL_HPP
struct Buffer {
double **sample;
uint16_t rdoffset;
uint16_t wroffset;
unsigned channels;
void setChannels(unsigned channels) {
for(unsigned c = 0; c < this->channels; c++) {
if(sample[c]) delete[] sample[c];
}
if(sample) delete[] sample;
this->channels = channels;
if(channels == 0) return;
sample = new double*[channels];
for(unsigned c = 0; c < channels; c++) {
sample[c] = new double[65536]();
}
}
inline double& read(unsigned channel, signed offset = 0) {
return sample[channel][(uint16_t)(rdoffset + offset)];
}
inline double& write(unsigned channel, signed offset = 0) {
return sample[channel][(uint16_t)(wroffset + offset)];
}
inline void clear() {
for(unsigned c = 0; c < channels; c++) {
for(unsigned n = 0; n < 65536; n++) {
sample[c][n] = 0;
}
}
rdoffset = 0;
wroffset = 0;
}
Buffer() {
channels = 0;
}
~Buffer() {
setChannels(0);
}
};
#endif

View File

@@ -1,167 +0,0 @@
#ifdef NALL_DSP_INTERNAL_HPP
#include <math.h>
#include <nall/stdint.hpp>
namespace nall {
//precision: can be float, double or long double
#define real float
struct DSP;
struct Resampler {
DSP &dsp;
real frequency;
virtual void setFrequency() = 0;
virtual void clear() = 0;
virtual void sample() = 0;
Resampler(DSP &dsp) : dsp(dsp) {}
};
struct DSP {
enum class ResampleEngine : unsigned {
Nearest,
Linear,
Cosine,
Cubic,
Hermite,
Average,
Sinc,
};
inline void setChannels(unsigned channels);
inline void setPrecision(unsigned precision);
inline void setFrequency(real frequency); //inputFrequency
inline void setVolume(real volume);
inline void setBalance(real balance);
inline void setResampler(ResampleEngine resamplingEngine);
inline void setResamplerFrequency(real frequency); //outputFrequency
inline void sample(signed channel[]);
inline bool pending();
inline void read(signed channel[]);
inline void clear();
inline DSP();
inline ~DSP();
protected:
friend class ResampleNearest;
friend class ResampleLinear;
friend class ResampleCosine;
friend class ResampleCubic;
friend class ResampleAverage;
friend class ResampleHermite;
friend class ResampleSinc;
struct Settings {
unsigned channels;
unsigned precision;
real frequency;
real volume;
real balance;
//internal
real intensity;
real intensityInverse;
} settings;
Resampler *resampler;
inline void write(real channel[]);
#include "buffer.hpp"
Buffer buffer;
Buffer output;
inline void adjustVolume();
inline void adjustBalance();
inline signed clamp(const unsigned bits, const signed x);
};
#include "resample/nearest.hpp"
#include "resample/linear.hpp"
#include "resample/cosine.hpp"
#include "resample/cubic.hpp"
#include "resample/hermite.hpp"
#include "resample/average.hpp"
#include "resample/sinc.hpp"
#include "settings.hpp"
void DSP::sample(signed channel[]) {
for(unsigned c = 0; c < settings.channels; c++) {
buffer.write(c) = (real)channel[c] * settings.intensityInverse;
}
buffer.wroffset++;
resampler->sample();
}
bool DSP::pending() {
return output.rdoffset != output.wroffset;
}
void DSP::read(signed channel[]) {
adjustVolume();
adjustBalance();
for(unsigned c = 0; c < settings.channels; c++) {
channel[c] = clamp(settings.precision, output.read(c) * settings.intensity);
}
output.rdoffset++;
}
void DSP::write(real channel[]) {
for(unsigned c = 0; c < settings.channels; c++) {
output.write(c) = channel[c];
}
output.wroffset++;
}
void DSP::adjustVolume() {
for(unsigned c = 0; c < settings.channels; c++) {
output.read(c) *= settings.volume;
}
}
void DSP::adjustBalance() {
if(settings.channels != 2) return; //TODO: support > 2 channels
if(settings.balance < 0.0) output.read(1) *= 1.0 + settings.balance;
if(settings.balance > 0.0) output.read(0) *= 1.0 - settings.balance;
}
signed DSP::clamp(const unsigned bits, const signed x) {
const signed b = 1U << (bits - 1);
const signed m = (1U << (bits - 1)) - 1;
return (x > m) ? m : (x < -b) ? -b : x;
}
void DSP::clear() {
buffer.clear();
output.clear();
resampler->clear();
}
DSP::DSP() {
setResampler(ResampleEngine::Hermite);
setResamplerFrequency(44100.0);
setChannels(2);
setPrecision(16);
setFrequency(44100.0);
setVolume(1.0);
setBalance(0.0);
clear();
}
DSP::~DSP() {
if(resampler) delete resampler;
}
#undef real
}
#endif

View File

@@ -1,72 +0,0 @@
#ifdef NALL_DSP_INTERNAL_HPP
struct ResampleAverage : Resampler {
inline void setFrequency();
inline void clear();
inline void sample();
inline void sampleLinear();
ResampleAverage(DSP &dsp) : Resampler(dsp) {}
real fraction;
real step;
};
void ResampleAverage::setFrequency() {
fraction = 0.0;
step = dsp.settings.frequency / frequency;
}
void ResampleAverage::clear() {
fraction = 0.0;
}
void ResampleAverage::sample() {
//can only average if input frequency >= output frequency
if(step < 1.0) return sampleLinear();
fraction += 1.0;
real scalar = 1.0;
if(fraction > step) scalar = 1.0 - (fraction - step);
for(unsigned c = 0; c < dsp.settings.channels; c++) {
dsp.output.write(c) += dsp.buffer.read(c) * scalar;
}
if(fraction >= step) {
for(unsigned c = 0; c < dsp.settings.channels; c++) {
dsp.output.write(c) /= step;
}
dsp.output.wroffset++;
fraction -= step;
for(unsigned c = 0; c < dsp.settings.channels; c++) {
dsp.output.write(c) = dsp.buffer.read(c) * fraction;
}
}
dsp.buffer.rdoffset++;
}
void ResampleAverage::sampleLinear() {
while(fraction <= 1.0) {
real channel[dsp.settings.channels];
for(unsigned n = 0; n < dsp.settings.channels; n++) {
real a = dsp.buffer.read(n, -1);
real b = dsp.buffer.read(n, -0);
real mu = fraction;
channel[n] = a * (1.0 - mu) + b * mu;
}
dsp.write(channel);
fraction += step;
}
dsp.buffer.rdoffset++;
fraction -= 1.0;
}
#endif

View File

@@ -1,44 +0,0 @@
#ifdef NALL_DSP_INTERNAL_HPP
struct ResampleCosine : Resampler {
inline void setFrequency();
inline void clear();
inline void sample();
ResampleCosine(DSP &dsp) : Resampler(dsp) {}
real fraction;
real step;
};
void ResampleCosine::setFrequency() {
fraction = 0.0;
step = dsp.settings.frequency / frequency;
}
void ResampleCosine::clear() {
fraction = 0.0;
}
void ResampleCosine::sample() {
while(fraction <= 1.0) {
real channel[dsp.settings.channels];
for(unsigned n = 0; n < dsp.settings.channels; n++) {
real a = dsp.buffer.read(n, -1);
real b = dsp.buffer.read(n, -0);
real mu = fraction;
mu = (1.0 - cos(mu * 3.14159265)) / 2.0;
channel[n] = a * (1.0 - mu) + b * mu;
}
dsp.write(channel);
fraction += step;
}
dsp.buffer.rdoffset++;
fraction -= 1.0;
}
#endif

View File

@@ -1,50 +0,0 @@
#ifdef NALL_DSP_INTERNAL_HPP
struct ResampleCubic : Resampler {
inline void setFrequency();
inline void clear();
inline void sample();
ResampleCubic(DSP &dsp) : Resampler(dsp) {}
real fraction;
real step;
};
void ResampleCubic::setFrequency() {
fraction = 0.0;
step = dsp.settings.frequency / frequency;
}
void ResampleCubic::clear() {
fraction = 0.0;
}
void ResampleCubic::sample() {
while(fraction <= 1.0) {
real channel[dsp.settings.channels];
for(unsigned n = 0; n < dsp.settings.channels; n++) {
real a = dsp.buffer.read(n, -3);
real b = dsp.buffer.read(n, -2);
real c = dsp.buffer.read(n, -1);
real d = dsp.buffer.read(n, -0);
real mu = fraction;
real A = d - c - a + b;
real B = a - b - A;
real C = c - a;
real D = b;
channel[n] = A * (mu * 3) + B * (mu * 2) + C * mu + D;
}
dsp.write(channel);
fraction += step;
}
dsp.buffer.rdoffset++;
fraction -= 1.0;
}
#endif

View File

@@ -1,62 +0,0 @@
#ifdef NALL_DSP_INTERNAL_HPP
struct ResampleHermite : Resampler {
inline void setFrequency();
inline void clear();
inline void sample();
ResampleHermite(DSP &dsp) : Resampler(dsp) {}
real fraction;
real step;
};
void ResampleHermite::setFrequency() {
fraction = 0.0;
step = dsp.settings.frequency / frequency;
}
void ResampleHermite::clear() {
fraction = 0.0;
}
void ResampleHermite::sample() {
while(fraction <= 1.0) {
real channel[dsp.settings.channels];
for(unsigned n = 0; n < dsp.settings.channels; n++) {
real a = dsp.buffer.read(n, -3);
real b = dsp.buffer.read(n, -2);
real c = dsp.buffer.read(n, -1);
real d = dsp.buffer.read(n, -0);
const real tension = 0.0; //-1 = low, 0 = normal, +1 = high
const real bias = 0.0; //-1 = left, 0 = even, +1 = right
real mu1, mu2, mu3, m0, m1, a0, a1, a2, a3;
mu1 = fraction;
mu2 = mu1 * mu1;
mu3 = mu2 * mu1;
m0 = (b - a) * (1.0 + bias) * (1.0 - tension) / 2.0;
m0 += (c - b) * (1.0 - bias) * (1.0 - tension) / 2.0;
m1 = (c - b) * (1.0 + bias) * (1.0 - tension) / 2.0;
m1 += (d - c) * (1.0 - bias) * (1.0 - tension) / 2.0;
a0 = +2 * mu3 - 3 * mu2 + 1;
a1 = mu3 - 2 * mu2 + mu1;
a2 = mu3 - mu2;
a3 = -2 * mu3 + 3 * mu2;
channel[n] = (a0 * b) + (a1 * m0) + (a2 * m1) + (a3 * c);
}
dsp.write(channel);
fraction += step;
}
dsp.buffer.rdoffset++;
fraction -= 1.0;
}
#endif

View File

@@ -1,600 +0,0 @@
// If these types are changed to anything other than "float", you should comment out the SSE detection directives below
// so that the SSE code is not used.
typedef float resample_coeff_t; // note: sizeof(resample_coeff_t) must be == to a power of 2, and not larger than 16
typedef float resample_samp_t;
// ...but don't comment this single RESAMPLE_SSEREGPARM define out when disabling SSE.
#define RESAMPLE_SSEREGPARM
#if defined(__SSE__)
#define SINCRESAMPLE_USE_SSE 1
#ifndef __x86_64__
#undef RESAMPLE_SSEREGPARM
#define RESAMPLE_SSEREGPARM __attribute__((sseregparm))
#endif
#else
// TODO: altivec here
#endif
namespace ResampleUtility
{
inline void kaiser_window(double* io, int count, double beta);
inline void gen_sinc(double* out, int size, double cutoff, double kaiser);
inline void gen_sinc_os(double* out, int size, double cutoff, double kaiser);
inline void normalize(double* io, int size, double gain = 1.0);
inline void* make_aligned(void* ptr, unsigned boundary); // boundary must be a power of 2
}
class SincResampleHR
{
private:
inline void Init(unsigned ratio_arg, double desired_bandwidth, double beta, double d);
inline void write(resample_samp_t sample) RESAMPLE_SSEREGPARM;
inline resample_samp_t read(void) RESAMPLE_SSEREGPARM;
inline bool output_avail(void);
private:
inline resample_samp_t mac(const resample_samp_t *wave, const resample_coeff_t *coeff, unsigned count);
unsigned ratio;
unsigned num_convolutions;
resample_coeff_t *coeffs;
std::vector<unsigned char> coeffs_mem;
// second half of ringbuffer should be copy of first half.
resample_samp_t *rb;
std::vector<unsigned char> rb_mem;
signed rb_readpos;
signed rb_writepos;
signed rb_in;
signed rb_eff_size;
friend class SincResample;
};
class SincResample
{
public:
enum
{
QUALITY_LOW = 0,
QUALITY_MEDIUM = 2,
QUALITY_HIGH = 4
};
inline SincResample(double input_rate, double output_rate, double desired_bandwidth, unsigned quality = QUALITY_HIGH);
inline void write(resample_samp_t sample) RESAMPLE_SSEREGPARM;
inline resample_samp_t read(void) RESAMPLE_SSEREGPARM;
inline bool output_avail(void);
private:
inline void Init(double input_rate, double output_rate, double desired_bandwidth, double beta, double d, unsigned pn_nume, unsigned phases_min);
inline resample_samp_t mac(const resample_samp_t *wave, const resample_coeff_t *coeffs_a, const resample_coeff_t *coeffs_b, const double ffract, unsigned count) RESAMPLE_SSEREGPARM;
unsigned num_convolutions;
unsigned num_phases;
unsigned step_int;
double step_fract;
double input_pos_fract;
std::vector<resample_coeff_t *> coeffs; // Pointers into coeff_mem.
std::vector<unsigned char> coeff_mem;
std::vector<resample_samp_t> rb; // second half should be copy of first half.
signed rb_readpos;
signed rb_writepos;
signed rb_in;
bool hr_used;
SincResampleHR hr;
};
//
// Code:
//
//#include "resample.hpp"
#if 0
namespace bit
{
inline unsigned round(unsigned x) {
if((x & (x - 1)) == 0) return x;
while(x & (x - 1)) x &= x - 1;
return x << 1;
}
}
#endif
void SincResampleHR::Init(unsigned ratio_arg, double desired_bandwidth, double beta, double d)
{
const unsigned align_boundary = 16;
std::vector<double> coeffs_tmp;
double cutoff; // 1.0 = f/2
ratio = ratio_arg;
//num_convolutions = ((unsigned)ceil(d / ((1.0 - desired_bandwidth) / ratio)) + 1) &~ 1; // round up to be even
num_convolutions = ((unsigned)ceil(d / ((1.0 - desired_bandwidth) / ratio)) | 1);
cutoff = (1.0 / ratio) - (d / num_convolutions);
//printf("%d %d %.20f\n", ratio, num_convolutions, cutoff);
assert(num_convolutions > ratio);
// Generate windowed sinc of POWER
coeffs_tmp.resize(num_convolutions);
//ResampleUtility::gen_sinc(&coeffs_tmp[0], num_convolutions, cutoff, beta);
ResampleUtility::gen_sinc_os(&coeffs_tmp[0], num_convolutions, cutoff, beta);
ResampleUtility::normalize(&coeffs_tmp[0], num_convolutions);
// Copy from coeffs_tmp to coeffs~
// We multiply many coefficients at a time in the mac loop, so make sure the last few that don't really
// exist are allocated, zero'd mem.
coeffs_mem.resize(((num_convolutions + 7) &~ 7) * sizeof(resample_coeff_t) + (align_boundary - 1));
coeffs = (resample_coeff_t *)ResampleUtility::make_aligned(&coeffs_mem[0], align_boundary);
for(unsigned i = 0; i < num_convolutions; i++)
coeffs[i] = coeffs_tmp[i];
rb_eff_size = nall::bit::round(num_convolutions * 2) >> 1;
rb_readpos = 0;
rb_writepos = 0;
rb_in = 0;
rb_mem.resize(rb_eff_size * 2 * sizeof(resample_samp_t) + (align_boundary - 1));
rb = (resample_samp_t *)ResampleUtility::make_aligned(&rb_mem[0], align_boundary);
}
inline bool SincResampleHR::output_avail(void)
{
return(rb_in >= (signed)num_convolutions);
}
inline void SincResampleHR::write(resample_samp_t sample)
{
assert(!output_avail());
rb[rb_writepos] = sample;
rb[rb_writepos + rb_eff_size] = sample;
rb_writepos = (rb_writepos + 1) & (rb_eff_size - 1);
rb_in++;
}
resample_samp_t SincResampleHR::mac(const resample_samp_t *wave, const resample_coeff_t *coeff, unsigned count)
{
#if SINCRESAMPLE_USE_SSE
__m128 accum_veca[2] = { _mm_set1_ps(0), _mm_set1_ps(0) };
resample_samp_t accum;
for(unsigned c = 0; c < count; c += 8)
{
for(unsigned i = 0; i < 2; i++)
{
__m128 co[2];
__m128 w[2];
co[i] = _mm_load_ps(&coeff[c + i * 4]);
w[i] = _mm_load_ps(&wave[c + i * 4]);
w[i] = _mm_mul_ps(w[i], co[i]);
accum_veca[i] = _mm_add_ps(w[i], accum_veca[i]);
}
}
__m128 accum_vec = _mm_add_ps(accum_veca[0], accum_veca[1]); //_mm_add_ps(_mm_add_ps(accum_veca[0], accum_veca[1]), _mm_add_ps(accum_veca[2], accum_veca[3]));
accum_vec = _mm_add_ps(accum_vec, _mm_shuffle_ps(accum_vec, accum_vec, (3 << 0) | (2 << 2) | (1 << 4) | (0 << 6)));
accum_vec = _mm_add_ps(accum_vec, _mm_shuffle_ps(accum_vec, accum_vec, (1 << 0) | (0 << 2) | (1 << 4) | (0 << 6)));
_mm_store_ss(&accum, accum_vec);
return accum;
#else
resample_samp_t accum[4] = { 0, 0, 0, 0 };
for(unsigned c = 0; c < count; c+= 4)
{
accum[0] += wave[c + 0] * coeff[c + 0];
accum[1] += wave[c + 1] * coeff[c + 1];
accum[2] += wave[c + 2] * coeff[c + 2];
accum[3] += wave[c + 3] * coeff[c + 3];
}
return (accum[0] + accum[1]) + (accum[2] + accum[3]); // don't mess with parentheses(assuming compiler doesn't already, which it may...
#endif
}
resample_samp_t SincResampleHR::read(void)
{
assert(output_avail());
resample_samp_t ret;
ret = mac(&rb[rb_readpos], &coeffs[0], num_convolutions);
rb_readpos = (rb_readpos + ratio) & (rb_eff_size - 1);
rb_in -= ratio;
return ret;
}
SincResample::SincResample(double input_rate, double output_rate, double desired_bandwidth, unsigned quality)
{
const struct
{
double beta;
double d;
unsigned pn_nume;
unsigned phases_min;
} qtab[5] =
{
{ 5.658, 3.62, 4096, 4 },
{ 6.764, 4.32, 8192, 4 },
{ 7.865, 5.0, 16384, 8 },
{ 8.960, 5.7, 32768, 16 },
{ 10.056, 6.4, 65536, 32 }
};
// Sanity checks
assert(ceil(input_rate) > 0);
assert(ceil(output_rate) > 0);
assert(ceil(input_rate / output_rate) <= 1024);
assert(ceil(output_rate / input_rate) <= 1024);
// The simplistic number-of-phases calculation code doesn't work well enough for when desired_bandwidth is close to 1.0 and when
// upsampling.
assert(desired_bandwidth >= 0.25 && desired_bandwidth < 0.96);
assert(quality >= 0 && quality <= 4);
hr_used = false;
#if 1
// Round down to the nearest multiple of 4(so wave buffer remains aligned)
// It also adjusts the effective intermediate sampling rate up slightly, so that the upper frequencies below f/2
// aren't overly attenuated so much. In the future, we might want to do an FFT or something to choose the intermediate rate more accurately
// to virtually eliminate over-attenuation.
unsigned ioratio_rd = (unsigned)floor(input_rate / (output_rate * (1.0 + (1.0 - desired_bandwidth) / 2) )) & ~3;
if(ioratio_rd >= 8)
{
hr.Init(ioratio_rd, desired_bandwidth, qtab[quality].beta, qtab[quality].d); //10.056, 6.4);
hr_used = true;
input_rate /= ioratio_rd;
}
#endif
Init(input_rate, output_rate, desired_bandwidth, qtab[quality].beta, qtab[quality].d, qtab[quality].pn_nume, qtab[quality].phases_min);
}
void SincResample::Init(double input_rate, double output_rate, double desired_bandwidth, double beta, double d, unsigned pn_nume, unsigned phases_min)
{
const unsigned max_mult_atatime = 8; // multiply "granularity". must be power of 2.
const unsigned max_mult_minus1 = (max_mult_atatime - 1);
const unsigned conv_alignment_bytes = 16; // must be power of 2
const double input_to_output_ratio = input_rate / output_rate;
const double output_to_input_ratio = output_rate / input_rate;
double cutoff; // 1.0 = input_rate / 2
std::vector<double> coeff_init_buffer;
// Round up num_convolutions to be even.
if(output_rate > input_rate)
num_convolutions = ((unsigned)ceil(d / (1.0 - desired_bandwidth)) + 1) & ~1;
else
num_convolutions = ((unsigned)ceil(d / (output_to_input_ratio * (1.0 - desired_bandwidth))) + 1) & ~1;
if(output_rate > input_rate) // Upsampling
cutoff = desired_bandwidth;
else // Downsampling
cutoff = output_to_input_ratio * desired_bandwidth;
// Round up to be even.
num_phases = (std::max<unsigned>(pn_nume / num_convolutions, phases_min) + 1) &~1;
// Adjust cutoff to account for the multiple phases.
cutoff = cutoff / num_phases;
assert((num_convolutions & 1) == 0);
assert((num_phases & 1) == 0);
// fprintf(stderr, "num_convolutions=%u, num_phases=%u, total expected coeff byte size=%lu\n", num_convolutions, num_phases,
// (long)((num_phases + 2) * ((num_convolutions + max_mult_minus1) & ~max_mult_minus1) * sizeof(float) + conv_alignment_bytes));
coeff_init_buffer.resize(num_phases * num_convolutions);
coeffs.resize(num_phases + 1 + 1);
coeff_mem.resize((num_phases + 1 + 1) * ((num_convolutions + max_mult_minus1) &~ max_mult_minus1) * sizeof(resample_coeff_t) + conv_alignment_bytes);
// Assign aligned pointers into coeff_mem
{
resample_coeff_t *base_ptr = (resample_coeff_t *)ResampleUtility::make_aligned(&coeff_mem[0], conv_alignment_bytes);
for(unsigned phase = 0; phase < (num_phases + 1 + 1); phase++)
{
coeffs[phase] = base_ptr + (((num_convolutions + max_mult_minus1) & ~max_mult_minus1) * phase);
}
}
ResampleUtility::gen_sinc(&coeff_init_buffer[0], num_phases * num_convolutions, cutoff, beta);
ResampleUtility::normalize(&coeff_init_buffer[0], num_phases * num_convolutions, num_phases);
// Reorder coefficients to allow for more efficient convolution.
for(int phase = -1; phase < ((int)num_phases + 1); phase++)
{
for(int conv = 0; conv < (int)num_convolutions; conv++)
{
double coeff;
if(phase == -1 && conv == 0)
coeff = 0;
else if(phase == (int)num_phases && conv == ((int)num_convolutions - 1))
coeff = 0;
else
coeff = coeff_init_buffer[conv * num_phases + phase];
coeffs[phase + 1][conv] = coeff;
}
}
// Free a bit of mem
coeff_init_buffer.resize(0);
step_int = floor(input_to_output_ratio);
step_fract = input_to_output_ratio - step_int;
input_pos_fract = 0;
// Do NOT use rb.size() later in the code, since it'll include the padding.
// We should only need one "max_mult_minus1" here, not two, since it won't matter if it over-reads(due to doing "max_mult_atatime" multiplications at a time
// rather than just 1, in which case this over-read wouldn't happen), from the first half into the duplicated half,
// since those corresponding coefficients will be zero anyway; this is just to handle the case of reading off the end of the duplicated half to
// prevent illegal memory accesses.
rb.resize(num_convolutions * 2 + max_mult_minus1);
rb_readpos = 0;
rb_writepos = 0;
rb_in = 0;
}
resample_samp_t SincResample::mac(const resample_samp_t *wave, const resample_coeff_t *coeffs_a, const resample_coeff_t *coeffs_b, const double ffract, unsigned count)
{
resample_samp_t accum = 0;
#if SINCRESAMPLE_USE_SSE
__m128 accum_vec_a[2] = { _mm_set1_ps(0), _mm_set1_ps(0) };
__m128 accum_vec_b[2] = { _mm_set1_ps(0), _mm_set1_ps(0) };
for(unsigned c = 0; c < count; c += 8) //8) //4)
{
__m128 coeff_a[2];
__m128 coeff_b[2];
__m128 w[2];
__m128 result_a[2], result_b[2];
for(unsigned i = 0; i < 2; i++)
{
coeff_a[i] = _mm_load_ps(&coeffs_a[c + (i * 4)]);
coeff_b[i] = _mm_load_ps(&coeffs_b[c + (i * 4)]);
w[i] = _mm_loadu_ps(&wave[c + (i * 4)]);
result_a[i] = _mm_mul_ps(coeff_a[i], w[i]);
result_b[i] = _mm_mul_ps(coeff_b[i], w[i]);
accum_vec_a[i] = _mm_add_ps(result_a[i], accum_vec_a[i]);
accum_vec_b[i] = _mm_add_ps(result_b[i], accum_vec_b[i]);
}
}
__m128 accum_vec, av_a, av_b;
__m128 mult_a_vec = _mm_set1_ps(1.0 - ffract);
__m128 mult_b_vec = _mm_set1_ps(ffract);
av_a = _mm_mul_ps(mult_a_vec, /*accum_vec_a[0]);*/ _mm_add_ps(accum_vec_a[0], accum_vec_a[1]));
av_b = _mm_mul_ps(mult_b_vec, /*accum_vec_b[0]);*/ _mm_add_ps(accum_vec_b[0], accum_vec_b[1]));
accum_vec = _mm_add_ps(av_a, av_b);
accum_vec = _mm_add_ps(accum_vec, _mm_shuffle_ps(accum_vec, accum_vec, (3 << 0) | (2 << 2) | (1 << 4) | (0 << 6)));
accum_vec = _mm_add_ps(accum_vec, _mm_shuffle_ps(accum_vec, accum_vec, (1 << 0) | (0 << 2) | (1 << 4) | (0 << 6)));
_mm_store_ss(&accum, accum_vec);
#else
resample_coeff_t mult_a = 1.0 - ffract;
resample_coeff_t mult_b = ffract;
for(unsigned c = 0; c < count; c += 4)
{
accum += wave[c + 0] * (coeffs_a[c + 0] * mult_a + coeffs_b[c + 0] * mult_b);
accum += wave[c + 1] * (coeffs_a[c + 1] * mult_a + coeffs_b[c + 1] * mult_b);
accum += wave[c + 2] * (coeffs_a[c + 2] * mult_a + coeffs_b[c + 2] * mult_b);
accum += wave[c + 3] * (coeffs_a[c + 3] * mult_a + coeffs_b[c + 3] * mult_b);
}
#endif
return accum;
}
inline bool SincResample::output_avail(void)
{
return(rb_in >= (int)num_convolutions);
}
resample_samp_t SincResample::read(void)
{
assert(output_avail());
double phase = input_pos_fract * num_phases - 0.5;
signed phase_int = (signed)floor(phase);
double phase_fract = phase - phase_int;
unsigned phase_a = num_phases - 1 - phase_int;
unsigned phase_b = phase_a - 1;
resample_samp_t ret;
ret = mac(&rb[rb_readpos], &coeffs[phase_a + 1][0], &coeffs[phase_b + 1][0], phase_fract, num_convolutions);
unsigned int_increment = step_int;
input_pos_fract += step_fract;
int_increment += floor(input_pos_fract);
input_pos_fract -= floor(input_pos_fract);
rb_readpos = (rb_readpos + int_increment) % num_convolutions;
rb_in -= int_increment;
return ret;
}
inline void SincResample::write(resample_samp_t sample)
{
assert(!output_avail());
if(hr_used)
{
hr.write(sample);
if(hr.output_avail())
{
sample = hr.read();
}
else
{
return;
}
}
rb[rb_writepos + 0 * num_convolutions] = sample;
rb[rb_writepos + 1 * num_convolutions] = sample;
rb_writepos = (rb_writepos + 1) % num_convolutions;
rb_in++;
}
void ResampleUtility::kaiser_window( double* io, int count, double beta)
{
int const accuracy = 24; //16; //12;
double* end = io + count;
double beta2 = beta * beta * (double) -0.25;
double to_fract = beta2 / ((double) count * count);
double i = 0;
double rescale = 0; // Doesn't need an initializer, to shut up gcc
for ( ; io < end; ++io, i += 1 )
{
double x = i * i * to_fract - beta2;
double u = x;
double k = x + 1;
double n = 2;
do
{
u *= x / (n * n);
n += 1;
k += u;
}
while ( k <= u * (1 << accuracy) );
if ( !i )
rescale = 1 / k; // otherwise values get large
*io *= k * rescale;
}
}
void ResampleUtility::gen_sinc(double* out, int size, double cutoff, double kaiser)
{
assert( size % 2 == 0 ); // size must be even
int const half_size = size / 2;
double* const mid = &out [half_size];
// Generate right half of sinc
for ( int i = 0; i < half_size; i++ )
{
double angle = (i * 2 + 1) * (M_PI / 2);
mid [i] = sin( angle * cutoff ) / angle;
}
kaiser_window( mid, half_size, kaiser );
// Mirror for left half
for ( int i = 0; i < half_size; i++ )
out [i] = mid [half_size - 1 - i];
}
void ResampleUtility::gen_sinc_os(double* out, int size, double cutoff, double kaiser)
{
assert( size % 2 == 1); // size must be odd
for(int i = 0; i < size; i++)
{
if(i == (size / 2))
out[i] = 2 * M_PI * (cutoff / 2); //0.078478; //1.0; //sin(2 * M_PI * (cutoff / 2) * (i - size / 2)) / (i - (size / 2));
else
out[i] = sin(2 * M_PI * (cutoff / 2) * (i - size / 2)) / (i - (size / 2));
// out[i] *= 0.3635819 - 0.4891775 * cos(2 * M_PI * i / (size - 1)) + 0.1365995 * cos(4 * M_PI * i / (size - 1)) - 0.0106411 * cos(6 * M_PI * i / (size - 1));
//0.42 - 0.5 * cos(2 * M_PI * i / (size - 1)) + 0.08 * cos(4 * M_PI * i / (size - 1));
// printf("%d %f\n", i, out[i]);
}
kaiser_window(&out[size / 2], size / 2 + 1, kaiser);
// Mirror for left half
for ( int i = 0; i < size / 2; i++ )
out [i] = out [size - 1 - i];
}
void ResampleUtility::normalize(double* io, int size, double gain)
{
double sum = 0;
for ( int i = 0; i < size; i++ )
sum += io [i];
double scale = gain / sum;
for ( int i = 0; i < size; i++ )
io [i] *= scale;
}
void* ResampleUtility::make_aligned(void* ptr, unsigned boundary)
{
unsigned char* null_ptr = (unsigned char *)NULL;
unsigned char* uc_ptr = (unsigned char *)ptr;
uc_ptr += (boundary - ((uc_ptr - null_ptr) & (boundary - 1))) & (boundary - 1);
//while((uc_ptr - null_ptr) & (boundary - 1))
// uc_ptr++;
//printf("%16llx %16llx\n", (unsigned long long)ptr, (unsigned long long)uc_ptr);
assert((uc_ptr - (unsigned char *)ptr) < boundary && (uc_ptr >= (unsigned char *)ptr));
return uc_ptr;
}

View File

@@ -1,43 +0,0 @@
#ifdef NALL_DSP_INTERNAL_HPP
struct ResampleLinear : Resampler {
inline void setFrequency();
inline void clear();
inline void sample();
ResampleLinear(DSP &dsp) : Resampler(dsp) {}
real fraction;
real step;
};
void ResampleLinear::setFrequency() {
fraction = 0.0;
step = dsp.settings.frequency / frequency;
}
void ResampleLinear::clear() {
fraction = 0.0;
}
void ResampleLinear::sample() {
while(fraction <= 1.0) {
real channel[dsp.settings.channels];
for(unsigned n = 0; n < dsp.settings.channels; n++) {
real a = dsp.buffer.read(n, -1);
real b = dsp.buffer.read(n, -0);
real mu = fraction;
channel[n] = a * (1.0 - mu) + b * mu;
}
dsp.write(channel);
fraction += step;
}
dsp.buffer.rdoffset++;
fraction -= 1.0;
}
#endif

View File

@@ -1,43 +0,0 @@
#ifdef NALL_DSP_INTERNAL_HPP
struct ResampleNearest : Resampler {
inline void setFrequency();
inline void clear();
inline void sample();
ResampleNearest(DSP &dsp) : Resampler(dsp) {}
real fraction;
real step;
};
void ResampleNearest::setFrequency() {
fraction = 0.0;
step = dsp.settings.frequency / frequency;
}
void ResampleNearest::clear() {
fraction = 0.0;
}
void ResampleNearest::sample() {
while(fraction <= 1.0) {
real channel[dsp.settings.channels];
for(unsigned n = 0; n < dsp.settings.channels; n++) {
real a = dsp.buffer.read(n, -1);
real b = dsp.buffer.read(n, -0);
real mu = fraction;
channel[n] = mu < 0.5 ? a : b;
}
dsp.write(channel);
fraction += step;
}
dsp.buffer.rdoffset++;
fraction -= 1.0;
}
#endif

View File

@@ -1,54 +0,0 @@
#ifdef NALL_DSP_INTERNAL_HPP
#include "lib/sinc.hpp"
struct ResampleSinc : Resampler {
inline void setFrequency();
inline void clear();
inline void sample();
inline ResampleSinc(DSP &dsp);
private:
inline void remakeSinc();
SincResample *sinc_resampler[8];
};
void ResampleSinc::setFrequency() {
remakeSinc();
}
void ResampleSinc::clear() {
remakeSinc();
}
void ResampleSinc::sample() {
for(unsigned c = 0; c < dsp.settings.channels; c++) {
sinc_resampler[c]->write(dsp.buffer.read(c));
}
if(sinc_resampler[0]->output_avail()) {
do {
for(unsigned c = 0; c < dsp.settings.channels; c++) {
dsp.output.write(c) = sinc_resampler[c]->read();
}
dsp.output.wroffset++;
} while(sinc_resampler[0]->output_avail());
}
dsp.buffer.rdoffset++;
}
ResampleSinc::ResampleSinc(DSP &dsp) : Resampler(dsp) {
for(unsigned n = 0; n < 8; n++) sinc_resampler[n] = 0;
}
void ResampleSinc::remakeSinc() {
assert(dsp.settings.channels < 8);
for(unsigned c = 0; c < dsp.settings.channels; c++) {
if(sinc_resampler[c]) delete sinc_resampler[c];
sinc_resampler[c] = new SincResample(dsp.settings.frequency, frequency, 0.85, SincResample::QUALITY_HIGH);
}
}
#endif

View File

@@ -1,50 +0,0 @@
#ifdef NALL_DSP_INTERNAL_HPP
void DSP::setChannels(unsigned channels) {
assert(channels > 0);
buffer.setChannels(channels);
output.setChannels(channels);
settings.channels = channels;
}
void DSP::setPrecision(unsigned precision) {
settings.precision = precision;
settings.intensity = 1 << (settings.precision - 1);
settings.intensityInverse = 1.0 / settings.intensity;
}
void DSP::setFrequency(real frequency) {
settings.frequency = frequency;
resampler->setFrequency();
}
void DSP::setVolume(real volume) {
settings.volume = volume;
}
void DSP::setBalance(real balance) {
settings.balance = balance;
}
void DSP::setResampler(ResampleEngine engine) {
if(resampler) delete resampler;
switch(engine) {
case ResampleEngine::Nearest: resampler = new ResampleNearest(*this); return;
case ResampleEngine::Linear: resampler = new ResampleLinear (*this); return;
case ResampleEngine::Cosine: resampler = new ResampleCosine (*this); return;
case ResampleEngine::Cubic: resampler = new ResampleCubic (*this); return;
case ResampleEngine::Hermite: resampler = new ResampleHermite(*this); return;
case ResampleEngine::Average: resampler = new ResampleAverage(*this); return;
case ResampleEngine::Sinc: resampler = new ResampleSinc (*this); return;
}
throw;
}
void DSP::setResamplerFrequency(real frequency) {
resampler->frequency = frequency;
resampler->setFrequency();
}
#endif

View File

@@ -1,103 +0,0 @@
#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(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;
}
//
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];
signed length = usart.read((uint8_t*)&buffer, 1);
if(length > 0) return buffer[0];
}
}
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) {
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_quit, usarthw_usleep, usarthw_readable, usarthw_read, usarthw_writable, usarthw_write);
usart_main(argc, argv);
usart.close();
return 0;
}
#endif

View File

@@ -1,42 +0,0 @@
#ifndef NALL_ENDIAN_HPP
#define NALL_ENDIAN_HPP
#include <nall/intrinsics.hpp>
#if defined(ENDIAN_LSB)
//little-endian: uint8_t[] { 0x01, 0x02, 0x03, 0x04 } == 0x04030201
#define order_lsb2(a,b) a,b
#define order_lsb3(a,b,c) a,b,c
#define order_lsb4(a,b,c,d) a,b,c,d
#define order_lsb5(a,b,c,d,e) a,b,c,d,e
#define order_lsb6(a,b,c,d,e,f) a,b,c,d,e,f
#define order_lsb7(a,b,c,d,e,f,g) a,b,c,d,e,f,g
#define order_lsb8(a,b,c,d,e,f,g,h) a,b,c,d,e,f,g,h
#define order_msb2(a,b) b,a
#define order_msb3(a,b,c) c,b,a
#define order_msb4(a,b,c,d) d,c,b,a
#define order_msb5(a,b,c,d,e) e,d,c,b,a
#define order_msb6(a,b,c,d,e,f) f,e,d,c,b,a
#define order_msb7(a,b,c,d,e,f,g) g,f,e,d,c,b,a
#define order_msb8(a,b,c,d,e,f,g,h) h,g,f,e,d,c,b,a
#elif defined(ENDIAN_MSB)
//big-endian: uint8_t[] { 0x01, 0x02, 0x03, 0x04 } == 0x01020304
#define order_lsb2(a,b) b,a
#define order_lsb3(a,b,c) c,b,a
#define order_lsb4(a,b,c,d) d,c,b,a
#define order_lsb5(a,b,c,d,e) e,d,c,b,a
#define order_lsb6(a,b,c,d,e,f) f,e,d,c,b,a
#define order_lsb7(a,b,c,d,e,f,g) g,f,e,d,c,b,a
#define order_lsb8(a,b,c,d,e,f,g,h) h,g,f,e,d,c,b,a
#define order_msb2(a,b) a,b
#define order_msb3(a,b,c) a,b,c
#define order_msb4(a,b,c,d) a,b,c,d
#define order_msb5(a,b,c,d,e) a,b,c,d,e
#define order_msb6(a,b,c,d,e,f) a,b,c,d,e,f
#define order_msb7(a,b,c,d,e,f,g) a,b,c,d,e,f,g
#define order_msb8(a,b,c,d,e,f,g,h) a,b,c,d,e,f,g,h
#else
#error "Unknown endian. Please specify in nall/intrinsics.hpp"
#endif
#endif

View File

@@ -1,343 +0,0 @@
#ifndef NALL_FILE_HPP
#define NALL_FILE_HPP
#include <nall/platform.hpp>
#include <nall/stdint.hpp>
#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) {
#if !defined(_WIN32)
return fopen(utf8_filename, mode);
#else
return _wfopen(utf16_t(utf8_filename), utf16_t(mode));
#endif
}
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 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;
fp.read(data, size);
fp.close();
return true;
}
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) {
file fp;
if(fp.open(filename, mode::write) == false) return false;
fp.write(data, size);
fp.close();
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
if(file_offset >= file_size) return 0xff; //cannot read past end of file
buffer_sync();
return buffer[(file_offset++) & buffer_mask];
}
uintmax_t readl(unsigned length = 1) {
uintmax_t data = 0;
for(int i = 0; i < length; i++) {
data |= (uintmax_t)read() << (i << 3);
}
return data;
}
uintmax_t readm(unsigned length = 1) {
uintmax_t data = 0;
while(length--) {
data <<= 8;
data |= read();
}
return data;
}
void read(uint8_t *buffer, unsigned length) {
while(length--) *buffer++ = read();
}
void write(uint8_t data) {
if(!fp) return; //file not open
if(file_mode == mode::read) return; //writes not permitted
buffer_sync();
buffer[(file_offset++) & buffer_mask] = data;
buffer_dirty = true;
if(file_offset > file_size) file_size = file_offset;
}
void writel(uintmax_t data, unsigned length = 1) {
while(length--) {
write(data);
data >>= 8;
}
}
void writem(uintmax_t data, unsigned length = 1) {
for(int i = length - 1; i >= 0; i--) {
write(data >> (i << 3));
}
}
void write(const uint8_t *buffer, unsigned length) {
while(length--) write(*buffer++);
}
template<typename... Args> void print(Args... args) {
string data(args...);
const char *p = data;
while(*p) write(*p++);
}
void flush() {
buffer_flush();
fflush(fp);
}
void seek(int offset, index index_ = index::absolute) {
if(!fp) return; //file not open
buffer_flush();
uintmax_t req_offset = file_offset;
switch(index_) {
case index::absolute: req_offset = offset; break;
case index::relative: req_offset += offset; break;
}
if(req_offset < 0) req_offset = 0; //cannot seek before start of file
if(req_offset > file_size) {
if(file_mode == mode::read) { //cannot seek past end of file
req_offset = file_size;
} else { //pad file to requested location
file_offset = file_size;
while(file_size < req_offset) write(0x00);
}
}
file_offset = req_offset;
}
unsigned offset() const {
if(!fp) return 0; //file not open
return file_offset;
}
unsigned size() const {
if(!fp) return 0; //file not open
return file_size;
}
bool truncate(unsigned size) {
if(!fp) return false; //file not open
#if !defined(_WIN32)
return ftruncate(fileno(fp), size) == 0;
#else
return _chsize(fileno(fp), size) == 0;
#endif
}
bool end() {
if(!fp) return true; //file not open
return file_offset >= file_size;
}
static bool exists(const string &filename) {
#if !defined(_WIN32)
struct stat64 data;
return stat64(filename, &data) == 0;
#else
struct __stat64 data;
return _wstat64(utf16_t(filename), &data) == 0;
#endif
}
static uintmax_t size(const string &filename) {
#if !defined(_WIN32)
struct stat64 data;
stat64(filename, &data);
#else
struct __stat64 data;
_wstat64(utf16_t(filename), &data);
#endif
return S_ISREG(data.st_mode) ? data.st_size : 0u;
}
static time_t timestamp(const string &filename, file::time mode = file::time::create) {
#if !defined(_WIN32)
struct stat64 data;
stat64(filename, &data);
#else
struct __stat64 data;
_wstat64(utf16_t(filename), &data);
#endif
switch(mode) { default:
case file::time::create: return data.st_ctime;
case file::time::modify: return data.st_mtime;
case file::time::access: return data.st_atime;
}
}
bool open() const {
return fp;
}
bool open(const string &filename, mode mode_) {
if(fp) return false;
switch(file_mode = mode_) {
#if !defined(_WIN32)
case mode::read: fp = fopen(filename, "rb" ); break;
case mode::write: fp = fopen(filename, "wb+"); break; //need read permission for buffering
case mode::readwrite: fp = fopen(filename, "rb+"); break;
case mode::writeread: fp = fopen(filename, "wb+"); break;
#else
case mode::read: fp = _wfopen(utf16_t(filename), L"rb" ); break;
case mode::write: fp = _wfopen(utf16_t(filename), L"wb+"); break;
case mode::readwrite: fp = _wfopen(utf16_t(filename), L"rb+"); break;
case mode::writeread: fp = _wfopen(utf16_t(filename), L"wb+"); break;
#endif
}
if(!fp) return false;
buffer_offset = -1; //invalidate buffer
file_offset = 0;
fseek(fp, 0, SEEK_END);
file_size = ftell(fp);
fseek(fp, 0, SEEK_SET);
return true;
}
void close() {
if(!fp) return;
buffer_flush();
fclose(fp);
fp = 0;
}
file() {
memset(buffer, 0, sizeof buffer);
buffer_offset = -1; //invalidate buffer
buffer_dirty = false;
fp = 0;
file_offset = 0;
file_size = 0;
file_mode = mode::read;
}
~file() {
close();
}
file& operator=(const file&) = delete;
file(const file&) = delete;
private:
enum { buffer_size = 1 << 12, buffer_mask = buffer_size - 1 };
char buffer[buffer_size];
int buffer_offset;
bool buffer_dirty;
FILE *fp;
unsigned file_offset;
unsigned file_size;
mode file_mode;
void buffer_sync() {
if(!fp) return; //file not open
if(buffer_offset != (file_offset & ~buffer_mask)) {
buffer_flush();
buffer_offset = file_offset & ~buffer_mask;
fseek(fp, buffer_offset, SEEK_SET);
unsigned length = (buffer_offset + buffer_size) <= file_size ? buffer_size : (file_size & buffer_mask);
if(length) unsigned unused = fread(buffer, 1, length, fp);
}
}
void buffer_flush() {
if(!fp) return; //file not open
if(file_mode == mode::read) return; //buffer cannot be written to
if(buffer_offset < 0) return; //buffer unused
if(buffer_dirty == false) return; //buffer unmodified since read
fseek(fp, buffer_offset, SEEK_SET);
unsigned length = (buffer_offset + buffer_size) <= file_size ? buffer_size : (file_size & buffer_mask);
if(length) unsigned unused = fwrite(buffer, 1, length, fp);
buffer_offset = -1; //invalidate buffer
buffer_dirty = false;
}
};
}
#endif

View File

@@ -1,213 +0,0 @@
#ifndef NALL_FILEMAP_HPP
#define NALL_FILEMAP_HPP
#include <nall/file.hpp>
#include <nall/stdint.hpp>
#include <nall/windows/utf8.hpp>
#include <stdio.h>
#include <stdlib.h>
#if defined(_WIN32)
#include <windows.h>
#else
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#endif
namespace nall {
class filemap {
public:
enum class mode : unsigned { read, write, readwrite, writeread };
bool open() const { return p_open(); }
bool open(const char *filename, mode mode_) { return p_open(filename, mode_); }
void close() { return p_close(); }
unsigned size() const { return p_size; }
uint8_t* data() { return p_handle; }
const uint8_t* data() const { return p_handle; }
filemap() : p_size(0), p_handle(0) { p_ctor(); }
filemap(const char *filename, mode mode_) : p_size(0), p_handle(0) { p_ctor(); p_open(filename, mode_); }
~filemap() { p_dtor(); }
private:
unsigned p_size;
uint8_t *p_handle;
#if defined(_WIN32)
//=============
//MapViewOfFile
//=============
HANDLE p_filehandle, p_maphandle;
bool p_open() const {
return p_handle;
}
bool p_open(const char *filename, mode mode_) {
if(file::exists(filename) && file::size(filename) == 0) {
p_handle = 0;
p_size = 0;
return true;
}
int desired_access, creation_disposition, flprotect, map_access;
switch(mode_) {
default: return false;
case mode::read:
desired_access = GENERIC_READ;
creation_disposition = OPEN_EXISTING;
flprotect = PAGE_READONLY;
map_access = FILE_MAP_READ;
break;
case mode::write:
//write access requires read access
desired_access = GENERIC_WRITE;
creation_disposition = CREATE_ALWAYS;
flprotect = PAGE_READWRITE;
map_access = FILE_MAP_ALL_ACCESS;
break;
case mode::readwrite:
desired_access = GENERIC_READ | GENERIC_WRITE;
creation_disposition = OPEN_EXISTING;
flprotect = PAGE_READWRITE;
map_access = FILE_MAP_ALL_ACCESS;
break;
case mode::writeread:
desired_access = GENERIC_READ | GENERIC_WRITE;
creation_disposition = CREATE_NEW;
flprotect = PAGE_READWRITE;
map_access = FILE_MAP_ALL_ACCESS;
break;
}
p_filehandle = CreateFileW(utf16_t(filename), desired_access, FILE_SHARE_READ, NULL,
creation_disposition, FILE_ATTRIBUTE_NORMAL, NULL);
if(p_filehandle == INVALID_HANDLE_VALUE) return false;
p_size = GetFileSize(p_filehandle, NULL);
p_maphandle = CreateFileMapping(p_filehandle, NULL, flprotect, 0, p_size, NULL);
if(p_maphandle == INVALID_HANDLE_VALUE) {
CloseHandle(p_filehandle);
p_filehandle = INVALID_HANDLE_VALUE;
return false;
}
p_handle = (uint8_t*)MapViewOfFile(p_maphandle, map_access, 0, 0, p_size);
return p_handle;
}
void p_close() {
if(p_handle) {
UnmapViewOfFile(p_handle);
p_handle = 0;
}
if(p_maphandle != INVALID_HANDLE_VALUE) {
CloseHandle(p_maphandle);
p_maphandle = INVALID_HANDLE_VALUE;
}
if(p_filehandle != INVALID_HANDLE_VALUE) {
CloseHandle(p_filehandle);
p_filehandle = INVALID_HANDLE_VALUE;
}
}
void p_ctor() {
p_filehandle = INVALID_HANDLE_VALUE;
p_maphandle = INVALID_HANDLE_VALUE;
}
void p_dtor() {
close();
}
#else
//====
//mmap
//====
int p_fd;
bool p_open() const {
return p_handle;
}
bool p_open(const char *filename, mode mode_) {
if(file::exists(filename) && file::size(filename) == 0) {
p_handle = 0;
p_size = 0;
return true;
}
int open_flags, mmap_flags;
switch(mode_) {
default: return false;
case mode::read:
open_flags = O_RDONLY;
mmap_flags = PROT_READ;
break;
case mode::write:
open_flags = O_RDWR | O_CREAT; //mmap() requires read access
mmap_flags = PROT_WRITE;
break;
case mode::readwrite:
open_flags = O_RDWR;
mmap_flags = PROT_READ | PROT_WRITE;
break;
case mode::writeread:
open_flags = O_RDWR | O_CREAT;
mmap_flags = PROT_READ | PROT_WRITE;
break;
}
p_fd = ::open(filename, open_flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
if(p_fd < 0) return false;
struct stat p_stat;
fstat(p_fd, &p_stat);
p_size = p_stat.st_size;
p_handle = (uint8_t*)mmap(0, p_size, mmap_flags, MAP_SHARED, p_fd, 0);
if(p_handle == MAP_FAILED) {
p_handle = 0;
::close(p_fd);
p_fd = -1;
return false;
}
return p_handle;
}
void p_close() {
if(p_handle) {
munmap(p_handle, p_size);
p_handle = 0;
}
if(p_fd >= 0) {
::close(p_fd);
p_fd = -1;
}
}
void p_ctor() {
p_fd = -1;
}
void p_dtor() {
p_close();
}
#endif
};
}
#endif

View File

@@ -1,60 +0,0 @@
#ifndef NALL_FUNCTION_HPP
#define NALL_FUNCTION_HPP
namespace nall {
template<typename T> class function;
template<typename R, typename... P> class function<R (P...)> {
struct container {
virtual R operator()(P... p) const = 0;
virtual container* copy() const = 0;
virtual ~container() {}
} *callback;
struct global : container {
R (*function)(P...);
R operator()(P... p) const { return function(std::forward<P>(p)...); }
container* copy() const { return new global(function); }
global(R (*function)(P...)) : function(function) {}
};
template<typename C> struct member : container {
R (C::*function)(P...);
C *object;
R operator()(P... p) const { return (object->*function)(std::forward<P>(p)...); }
container* copy() const { return new member(function, object); }
member(R (C::*function)(P...), C *object) : function(function), object(object) {}
};
template<typename L> struct lambda : container {
mutable L object;
R operator()(P... p) const { return object(std::forward<P>(p)...); }
container* copy() const { return new lambda(object); }
lambda(const L& object) : object(object) {}
};
public:
operator bool() const { return callback; }
R operator()(P... p) const { return (*callback)(std::forward<P>(p)...); }
void reset() { if(callback) { delete callback; callback = nullptr; } }
function& operator=(const function &source) {
if(this != &source) {
if(callback) { delete callback; callback = nullptr; }
if(source.callback) callback = source.callback->copy();
}
return *this;
}
function(const function &source) : callback(nullptr) { operator=(source); }
function() : callback(nullptr) {}
function(void *function) : callback(nullptr) { if(function) callback = new global((R (*)(P...))function); }
function(R (*function)(P...)) { callback = new global(function); }
template<typename C> function(R (C::*function)(P...), C *object) { callback = new member<C>(function, object); }
template<typename C> function(R (C::*function)(P...) const, C *object) { callback = new member<C>((R (C::*)(P...))function, object); }
template<typename L> function(const L& object) { callback = new lambda<L>(object); }
~function() { if(callback) delete callback; }
};
}
#endif

View File

@@ -1,85 +0,0 @@
#ifndef NALL_GZIP_HPP
#define NALL_GZIP_HPP
#include <nall/file.hpp>
#include <nall/inflate.hpp>
namespace nall {
struct gzip {
string filename;
uint8_t *data;
unsigned size;
inline bool decompress(const string &filename);
inline bool decompress(const uint8_t *data, unsigned size);
inline gzip();
inline ~gzip();
};
bool gzip::decompress(const string &filename) {
if(auto memory = file::read(filename)) {
return decompress(memory.data(), memory.size());
}
return false;
}
bool gzip::decompress(const uint8_t *data, unsigned size) {
if(size < 18) return false;
if(data[0] != 0x1f) return false;
if(data[1] != 0x8b) return false;
unsigned cm = data[2];
unsigned flg = data[3];
unsigned mtime = data[4];
mtime |= data[5] << 8;
mtime |= data[6] << 16;
mtime |= data[7] << 24;
unsigned xfl = data[8];
unsigned os = data[9];
unsigned p = 10;
unsigned isize = data[size - 4];
isize |= data[size - 3] << 8;
isize |= data[size - 2] << 16;
isize |= data[size - 1] << 24;
filename = "";
if(flg & 0x04) { //FEXTRA
unsigned xlen = data[p + 0];
xlen |= data[p + 1] << 8;
p += 2 + xlen;
}
if(flg & 0x08) { //FNAME
char buffer[PATH_MAX];
for(unsigned n = 0; n < PATH_MAX; n++, p++) {
buffer[n] = data[p];
if(data[p] == 0) break;
}
if(data[p++]) return false;
filename = buffer;
}
if(flg & 0x10) { //FCOMMENT
while(data[p++]);
}
if(flg & 0x02) { //FHCRC
p += 2;
}
this->size = isize;
this->data = new uint8_t[this->size];
return inflate(this->data, this->size, data + p, size - p - 8);
}
gzip::gzip() : data(nullptr) {
}
gzip::~gzip() {
if(data) delete[] data;
}
}
#endif

View File

@@ -1,176 +0,0 @@
#ifndef NALL_HTTP_HPP
#define NALL_HTTP_HPP
#if !defined(_WIN32)
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#else
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
#endif
#include <nall/platform.hpp>
#include <nall/string.hpp>
namespace nall {
struct http {
string hostname;
addrinfo *serverinfo;
int serversocket;
string header;
inline void download(const string &path, uint8_t *&data, unsigned &size) {
data = 0;
size = 0;
send({
"GET ", path, " HTTP/1.1\r\n"
"Host: ", hostname, "\r\n"
"Connection: close\r\n"
"\r\n"
});
header = downloadHeader();
downloadContent(data, size);
}
inline bool connect(string host, unsigned port) {
hostname = host;
addrinfo hints;
memset(&hints, 0, sizeof(addrinfo));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
int status = getaddrinfo(hostname, string(port), &hints, &serverinfo);
if(status != 0) return false;
serversocket = socket(serverinfo->ai_family, serverinfo->ai_socktype, serverinfo->ai_protocol);
if(serversocket == -1) return false;
int result = ::connect(serversocket, serverinfo->ai_addr, serverinfo->ai_addrlen);
if(result == -1) return false;
return true;
}
inline bool send(const string &data) {
return send((const uint8_t*)(const char*)data, data.length());
}
inline bool send(const uint8_t *data, unsigned size) {
while(size) {
int length = ::send(serversocket, (const char*)data, size, 0);
if(length == -1) return false;
data += length;
size -= length;
}
return true;
}
inline string downloadHeader() {
string output;
do {
char buffer[2];
int length = recv(serversocket, buffer, 1, 0);
if(length <= 0) return output;
buffer[1] = 0;
output.append(buffer);
} while(output.endswith("\r\n\r\n") == false);
return output;
}
inline string downloadChunkLength() {
string output;
do {
char buffer[2];
int length = recv(serversocket, buffer, 1, 0);
if(length <= 0) return output;
buffer[1] = 0;
output.append(buffer);
} while(output.endswith("\r\n") == false);
return output;
}
inline void downloadContent(uint8_t *&data, unsigned &size) {
unsigned capacity = 0;
if(header.iposition("\r\nTransfer-Encoding: chunked\r\n")) {
while(true) {
unsigned length = hex(downloadChunkLength());
if(length == 0) break;
capacity += length;
data = (uint8_t*)realloc(data, capacity);
char buffer[length];
while(length) {
int packetlength = recv(serversocket, buffer, length, 0);
if(packetlength <= 0) break;
memcpy(data + size, buffer, packetlength);
size += packetlength;
length -= packetlength;
}
}
} else if(auto position = header.iposition("\r\nContent-Length: ")) {
unsigned length = decimal((const char*)header + position() + 18);
while(length) {
char buffer[256];
int packetlength = recv(serversocket, buffer, min(256, length), 0);
if(packetlength <= 0) break;
capacity += packetlength;
data = (uint8_t*)realloc(data, capacity);
memcpy(data + size, buffer, packetlength);
size += packetlength;
length -= packetlength;
}
} else {
while(true) {
char buffer[256];
int packetlength = recv(serversocket, buffer, 256, 0);
if(packetlength <= 0) break;
capacity += packetlength;
data = (uint8_t*)realloc(data, capacity);
memcpy(data + size, buffer, packetlength);
size += packetlength;
}
}
data = (uint8_t*)realloc(data, capacity + 1);
data[capacity] = 0;
}
inline void disconnect() {
close(serversocket);
freeaddrinfo(serverinfo);
serverinfo = 0;
serversocket = -1;
}
#ifdef _WIN32
inline int close(int sock) {
return closesocket(sock);
}
inline http() {
int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(sock == INVALID_SOCKET && WSAGetLastError() == WSANOTINITIALISED) {
WSADATA wsaData;
if(WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
WSACleanup();
return;
}
} else {
close(sock);
}
}
#endif
};
}
#endif

View File

@@ -1,539 +0,0 @@
#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

View File

@@ -1,358 +0,0 @@
#ifndef NALL_INFLATE_HPP
#define NALL_INFLATE_HPP
#include <setjmp.h>
namespace nall {
namespace puff {
inline int puff(
unsigned char *dest, unsigned long *destlen,
unsigned char *source, unsigned long *sourcelen
);
}
inline bool inflate(
uint8_t *target, unsigned targetLength,
const uint8_t *source, unsigned sourceLength
) {
unsigned long tl = targetLength, sl = sourceLength;
int result = puff::puff((unsigned char*)target, &tl, (unsigned char*)source, &sl);
return result == 0;
}
namespace puff {
//zlib/contrib/puff.c
//version 2.1*
//author: Mark Adler
//license: zlib
//ported by: byuu
//* I have corrected a bug in fixed(), where it was accessing uninitialized
// memory: calling construct() with lencode prior to initializing lencode.count
enum {
MAXBITS = 15,
MAXLCODES = 286,
MAXDCODES = 30,
FIXLCODES = 288,
MAXCODES = MAXLCODES + MAXDCODES,
};
struct state {
unsigned char *out;
unsigned long outlen;
unsigned long outcnt;
unsigned char *in;
unsigned long inlen;
unsigned long incnt;
int bitbuf;
int bitcnt;
jmp_buf env;
};
struct huffman {
short *count;
short *symbol;
};
inline int bits(state *s, int need) {
long val;
val = s->bitbuf;
while(s->bitcnt < need) {
if(s->incnt == s->inlen) longjmp(s->env, 1);
val |= (long)(s->in[s->incnt++]) << s->bitcnt;
s->bitcnt += 8;
}
s->bitbuf = (int)(val >> need);
s->bitcnt -= need;
return (int)(val & ((1L << need) - 1));
}
inline int stored(state *s) {
unsigned len;
s->bitbuf = 0;
s->bitcnt = 0;
if(s->incnt + 4 > s->inlen) return 2;
len = s->in[s->incnt++];
len |= s->in[s->incnt++] << 8;
if(s->in[s->incnt++] != (~len & 0xff) ||
s->in[s->incnt++] != ((~len >> 8) & 0xff)
) return 2;
if(s->incnt + len > s->inlen) return 2;
if(s->out != 0) {
if(s->outcnt + len > s->outlen) return 1;
while(len--) s->out[s->outcnt++] = s->in[s->incnt++];
} else {
s->outcnt += len;
s->incnt += len;
}
return 0;
}
inline int decode(state *s, huffman *h) {
int len, code, first, count, index, bitbuf, left;
short *next;
bitbuf = s->bitbuf;
left = s->bitcnt;
code = first = index = 0;
len = 1;
next = h->count + 1;
while(true) {
while(left--) {
code |= bitbuf & 1;
bitbuf >>= 1;
count = *next++;
if(code - count < first) {
s->bitbuf = bitbuf;
s->bitcnt = (s->bitcnt - len) & 7;
return h->symbol[index + (code - first)];
}
index += count;
first += count;
first <<= 1;
code <<= 1;
len++;
}
left = (MAXBITS + 1) - len;
if(left == 0) break;
if(s->incnt == s->inlen) longjmp(s->env, 1);
bitbuf = s->in[s->incnt++];
if(left > 8) left = 8;
}
return -10;
}
inline int construct(huffman *h, short *length, int n) {
int symbol, len, left;
short offs[MAXBITS + 1];
for(len = 0; len <= MAXBITS; len++) h->count[len] = 0;
for(symbol = 0; symbol < n; symbol++) h->count[length[symbol]]++;
if(h->count[0] == n) return 0;
left = 1;
for(len = 1; len <= MAXBITS; len++) {
left <<= 1;
left -= h->count[len];
if(left < 0) return left;
}
offs[1] = 0;
for(len = 1; len < MAXBITS; len++) offs[len + 1] = offs[len] + h->count[len];
for(symbol = 0; symbol < n; symbol++) {
if(length[symbol] != 0) h->symbol[offs[length[symbol]]++] = symbol;
}
return left;
}
inline int codes(state *s, huffman *lencode, huffman *distcode) {
int symbol, len;
unsigned dist;
static const short lens[29] = {
3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258
};
static const short lext[29] = {
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0
};
static const short dists[30] = {
1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
8193, 12289, 16385, 24577
};
static const short dext[30] = {
0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
7, 7, 8, 8, 9, 9, 10, 10, 11, 11,
12, 12, 13, 13
};
do {
symbol = decode(s, lencode);
if(symbol < 0) return symbol;
if(symbol < 256) {
if(s->out != 0) {
if(s->outcnt == s->outlen) return 1;
s->out[s->outcnt] = symbol;
}
s->outcnt++;
} else if(symbol > 256) {
symbol -= 257;
if(symbol >= 29) return -10;
len = lens[symbol] + bits(s, lext[symbol]);
symbol = decode(s, distcode);
if(symbol < 0) return symbol;
dist = dists[symbol] + bits(s, dext[symbol]);
#ifndef INFLATE_ALLOW_INVALID_DISTANCE_TOO_FAR
if(dist > s->outcnt) return -11;
#endif
if(s->out != 0) {
if(s->outcnt + len > s->outlen) return 1;
while(len--) {
s->out[s->outcnt] =
#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOO_FAR
dist > s->outcnt ? 0 :
#endif
s->out[s->outcnt - dist];
s->outcnt++;
}
} else {
s->outcnt += len;
}
}
} while(symbol != 256);
return 0;
}
inline int fixed(state *s) {
static int virgin = 1;
static short lencnt[MAXBITS + 1], lensym[FIXLCODES];
static short distcnt[MAXBITS + 1], distsym[MAXDCODES];
static huffman lencode, distcode;
if(virgin) {
int symbol = 0;
short lengths[FIXLCODES];
lencode.count = lencnt;
lencode.symbol = lensym;
distcode.count = distcnt;
distcode.symbol = distsym;
for(; symbol < 144; symbol++) lengths[symbol] = 8;
for(; symbol < 256; symbol++) lengths[symbol] = 9;
for(; symbol < 280; symbol++) lengths[symbol] = 7;
for(; symbol < FIXLCODES; symbol++) lengths[symbol] = 8;
construct(&lencode, lengths, FIXLCODES);
for(symbol = 0; symbol < MAXDCODES; symbol++) lengths[symbol] = 5;
construct(&distcode, lengths, MAXDCODES);
virgin = 0;
}
return codes(s, &lencode, &distcode);
}
inline int dynamic(state *s) {
int nlen, ndist, ncode, index, err;
short lengths[MAXCODES];
short lencnt[MAXBITS + 1], lensym[MAXLCODES];
short distcnt[MAXBITS + 1], distsym[MAXDCODES];
huffman lencode, distcode;
static const short order[19] = {
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
};
lencode.count = lencnt;
lencode.symbol = lensym;
distcode.count = distcnt;
distcode.symbol = distsym;
nlen = bits(s, 5) + 257;
ndist = bits(s, 5) + 1;
ncode = bits(s, 4) + 4;
if(nlen > MAXLCODES || ndist > MAXDCODES) return -3;
for(index = 0; index < ncode; index++) lengths[order[index]] = bits(s, 3);
for(; index < 19; index++) lengths[order[index]] = 0;
err = construct(&lencode, lengths, 19);
if(err != 0) return -4;
index = 0;
while(index < nlen + ndist) {
int symbol, len;
symbol = decode(s, &lencode);
if(symbol < 16) {
lengths[index++] = symbol;
} else {
len = 0;
if(symbol == 16) {
if(index == 0) return -5;
len = lengths[index - 1];
symbol = 3 + bits(s, 2);
} else if(symbol == 17) {
symbol = 3 + bits(s, 3);
} else {
symbol = 11 + bits(s, 7);
}
if(index + symbol > nlen + ndist) return -6;
while(symbol--) lengths[index++] = len;
}
}
if(lengths[256] == 0) return -9;
err = construct(&lencode, lengths, nlen);
if(err < 0 || (err > 0 && nlen - lencode.count[0] != 1)) return -7;
err = construct(&distcode, lengths + nlen, ndist);
if(err < 0 || (err > 0 && ndist - distcode.count[0] != 1)) return -8;
return codes(s, &lencode, &distcode);
}
inline int puff(
unsigned char *dest, unsigned long *destlen,
unsigned char *source, unsigned long *sourcelen
) {
state s;
int last, type, err;
s.out = dest;
s.outlen = *destlen;
s.outcnt = 0;
s.in = source;
s.inlen = *sourcelen;
s.incnt = 0;
s.bitbuf = 0;
s.bitcnt = 0;
if(setjmp(s.env) != 0) {
err = 2;
} else {
do {
last = bits(&s, 1);
type = bits(&s, 2);
err = type == 0 ? stored(&s)
: type == 1 ? fixed(&s)
: type == 2 ? dynamic(&s)
: -1;
if(err != 0) break;
} while(!last);
}
if(err <= 0) {
*destlen = s.outcnt;
*sourcelen = s.incnt;
}
return err;
}
}
}
#endif

View File

@@ -1,386 +0,0 @@
#ifndef NALL_INPUT_HPP
#define NALL_INPUT_HPP
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <nall/stdint.hpp>
#include <nall/string.hpp>
namespace nall {
struct Keyboard;
Keyboard& keyboard(unsigned = 0);
static const char KeyboardScancodeName[][64] = {
"Escape", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12",
"PrintScreen", "ScrollLock", "Pause", "Tilde",
"Num1", "Num2", "Num3", "Num4", "Num5", "Num6", "Num7", "Num8", "Num9", "Num0",
"Dash", "Equal", "Backspace",
"Insert", "Delete", "Home", "End", "PageUp", "PageDown",
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
"N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
"LeftBracket", "RightBracket", "Backslash", "Semicolon", "Apostrophe", "Comma", "Period", "Slash",
"Keypad1", "Keypad2", "Keypad3", "Keypad4", "Keypad5", "Keypad6", "Keypad7", "Keypad8", "Keypad9", "Keypad0",
"Point", "Enter", "Add", "Subtract", "Multiply", "Divide",
"NumLock", "CapsLock",
"Up", "Down", "Left", "Right",
"Tab", "Return", "Spacebar", "Menu",
"Shift", "Control", "Alt", "Super",
};
struct Keyboard {
const unsigned ID;
enum { Base = 1 };
enum { Count = 8, Size = 128 };
enum Scancode {
Escape, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12,
PrintScreen, ScrollLock, Pause, Tilde,
Num1, Num2, Num3, Num4, Num5, Num6, Num7, Num8, Num9, Num0,
Dash, Equal, Backspace,
Insert, Delete, Home, End, PageUp, PageDown,
A, B, C, D, E, F, G, H, I, J, K, L, M,
N, O, P, Q, R, S, T, U, V, W, X, Y, Z,
LeftBracket, RightBracket, Backslash, Semicolon, Apostrophe, Comma, Period, Slash,
Keypad1, Keypad2, Keypad3, Keypad4, Keypad5, Keypad6, Keypad7, Keypad8, Keypad9, Keypad0,
Point, Enter, Add, Subtract, Multiply, Divide,
NumLock, CapsLock,
Up, Down, Left, Right,
Tab, Return, Spacebar, Menu,
Shift, Control, Alt, Super,
Limit,
};
static signed numberDecode(uint16_t scancode) {
for(unsigned i = 0; i < Count; i++) {
if(keyboard(i).belongsTo(scancode)) return i;
}
return -1;
}
static signed keyDecode(uint16_t scancode) {
for(unsigned i = 0; i < Count; i++) {
if(keyboard(i).isKey(scancode)) return scancode - keyboard(i).key(Escape);
}
return -1;
}
static signed modifierDecode(uint16_t scancode) {
for(unsigned i = 0; i < Count; i++) {
if(keyboard(i).isModifier(scancode)) return scancode - keyboard(i).key(Shift);
}
return -1;
}
static bool isAnyKey(uint16_t scancode) {
for(unsigned i = 0; i < Count; i++) {
if(keyboard(i).isKey(scancode)) return true;
}
return false;
}
static bool isAnyModifier(uint16_t scancode) {
for(unsigned i = 0; i < Count; i++) {
if(keyboard(i).isModifier(scancode)) return true;
}
return false;
}
static uint16_t decode(const char *name) {
string s(name);
if(!strbegin(name, "KB")) return 0;
s.ltrim("KB");
unsigned id = decimal(s);
auto pos = strpos(s, "::");
if(!pos) return 0;
s = substr(s, pos() + 2);
for(unsigned i = 0; i < Limit; i++) {
if(s == KeyboardScancodeName[i]) return Base + Size * id + i;
}
return 0;
}
string encode(uint16_t code) const {
unsigned index = 0;
for(unsigned i = 0; i < Count; i++) {
if(code >= Base + Size * i && code < Base + Size * (i + 1)) {
index = code - (Base + Size * i);
break;
}
}
return { "KB", ID, "::", KeyboardScancodeName[index] };
}
uint16_t operator[](Scancode code) const { return Base + ID * Size + code; }
uint16_t key(unsigned id) const { return Base + Size * ID + id; }
bool isKey(unsigned id) const { return id >= key(Escape) && id <= key(Menu); }
bool isModifier(unsigned id) const { return id >= key(Shift) && id <= key(Super); }
bool belongsTo(uint16_t scancode) const { return isKey(scancode) || isModifier(scancode); }
Keyboard(unsigned ID_) : ID(ID_) {}
};
inline Keyboard& keyboard(unsigned id) {
static Keyboard kb0(0), kb1(1), kb2(2), kb3(3), kb4(4), kb5(5), kb6(6), kb7(7);
switch(id) { default:
case 0: return kb0; case 1: return kb1; case 2: return kb2; case 3: return kb3;
case 4: return kb4; case 5: return kb5; case 6: return kb6; case 7: return kb7;
}
}
static const char MouseScancodeName[][64] = {
"Xaxis", "Yaxis", "Zaxis",
"Button0", "Button1", "Button2", "Button3", "Button4", "Button5", "Button6", "Button7",
};
struct Mouse;
Mouse& mouse(unsigned = 0);
struct Mouse {
const unsigned ID;
enum { Base = Keyboard::Base + Keyboard::Size * Keyboard::Count };
enum { Count = 8, Size = 16 };
enum { Axes = 3, Buttons = 8 };
enum Scancode {
Xaxis, Yaxis, Zaxis,
Button0, Button1, Button2, Button3, Button4, Button5, Button6, Button7,
Limit,
};
static signed numberDecode(uint16_t scancode) {
for(unsigned i = 0; i < Count; i++) {
if(mouse(i).belongsTo(scancode)) return i;
}
return -1;
}
static signed axisDecode(uint16_t scancode) {
for(unsigned i = 0; i < Count; i++) {
if(mouse(i).isAxis(scancode)) return scancode - mouse(i).axis(0);
}
return -1;
}
static signed buttonDecode(uint16_t scancode) {
for(unsigned i = 0; i < Count; i++) {
if(mouse(i).isButton(scancode)) return scancode - mouse(i).button(0);
}
return -1;
}
static bool isAnyAxis(uint16_t scancode) {
for(unsigned i = 0; i < Count; i++) {
if(mouse(i).isAxis(scancode)) return true;
}
return false;
}
static bool isAnyButton(uint16_t scancode) {
for(unsigned i = 0; i < Count; i++) {
if(mouse(i).isButton(scancode)) return true;
}
return false;
}
static uint16_t decode(const char *name) {
string s(name);
if(!strbegin(name, "MS")) return 0;
s.ltrim("MS");
unsigned id = decimal(s);
auto pos = strpos(s, "::");
if(!pos) return 0;
s = substr(s, pos() + 2);
for(unsigned i = 0; i < Limit; i++) {
if(s == MouseScancodeName[i]) return Base + Size * id + i;
}
return 0;
}
string encode(uint16_t code) const {
unsigned index = 0;
for(unsigned i = 0; i < Count; i++) {
if(code >= Base + Size * i && code < Base + Size * (i + 1)) {
index = code - (Base + Size * i);
break;
}
}
return { "MS", ID, "::", MouseScancodeName[index] };
}
uint16_t operator[](Scancode code) const { return Base + ID * Size + code; }
uint16_t axis(unsigned id) const { return Base + Size * ID + Xaxis + id; }
uint16_t button(unsigned id) const { return Base + Size * ID + Button0 + id; }
bool isAxis(unsigned id) const { return id >= axis(0) && id <= axis(2); }
bool isButton(unsigned id) const { return id >= button(0) && id <= button(7); }
bool belongsTo(uint16_t scancode) const { return isAxis(scancode) || isButton(scancode); }
Mouse(unsigned ID_) : ID(ID_) {}
};
inline Mouse& mouse(unsigned id) {
static Mouse ms0(0), ms1(1), ms2(2), ms3(3), ms4(4), ms5(5), ms6(6), ms7(7);
switch(id) { default:
case 0: return ms0; case 1: return ms1; case 2: return ms2; case 3: return ms3;
case 4: return ms4; case 5: return ms5; case 6: return ms6; case 7: return ms7;
}
}
static const char JoypadScancodeName[][64] = {
"Hat0", "Hat1", "Hat2", "Hat3", "Hat4", "Hat5", "Hat6", "Hat7",
"Axis0", "Axis1", "Axis2", "Axis3", "Axis4", "Axis5", "Axis6", "Axis7",
"Axis8", "Axis9", "Axis10", "Axis11", "Axis12", "Axis13", "Axis14", "Axis15",
"Button0", "Button1", "Button2", "Button3", "Button4", "Button5", "Button6", "Button7",
"Button8", "Button9", "Button10", "Button11", "Button12", "Button13", "Button14", "Button15",
"Button16", "Button17", "Button18", "Button19", "Button20", "Button21", "Button22", "Button23",
"Button24", "Button25", "Button26", "Button27", "Button28", "Button29", "Button30", "Button31",
};
struct Joypad;
Joypad& joypad(unsigned = 0);
struct Joypad {
const unsigned ID;
enum { Base = Mouse::Base + Mouse::Size * Mouse::Count };
enum { Count = 8, Size = 64 };
enum { Hats = 8, Axes = 16, Buttons = 32 };
enum Scancode {
Hat0, Hat1, Hat2, Hat3, Hat4, Hat5, Hat6, Hat7,
Axis0, Axis1, Axis2, Axis3, Axis4, Axis5, Axis6, Axis7,
Axis8, Axis9, Axis10, Axis11, Axis12, Axis13, Axis14, Axis15,
Button0, Button1, Button2, Button3, Button4, Button5, Button6, Button7,
Button8, Button9, Button10, Button11, Button12, Button13, Button14, Button15,
Button16, Button17, Button18, Button19, Button20, Button21, Button22, Button23,
Button24, Button25, Button26, Button27, Button28, Button29, Button30, Button31,
Limit,
};
enum Hat { HatCenter = 0, HatUp = 1, HatRight = 2, HatDown = 4, HatLeft = 8 };
static signed numberDecode(uint16_t scancode) {
for(unsigned i = 0; i < Count; i++) {
if(joypad(i).belongsTo(scancode)) return i;
}
return -1;
}
static signed hatDecode(uint16_t scancode) {
for(unsigned i = 0; i < Count; i++) {
if(joypad(i).isHat(scancode)) return scancode - joypad(i).hat(0);
}
return -1;
}
static signed axisDecode(uint16_t scancode) {
for(unsigned i = 0; i < Count; i++) {
if(joypad(i).isAxis(scancode)) return scancode - joypad(i).axis(0);
}
return -1;
}
static signed buttonDecode(uint16_t scancode) {
for(unsigned i = 0; i < Count; i++) {
if(joypad(i).isButton(scancode)) return scancode - joypad(i).button(0);
}
return -1;
}
static bool isAnyHat(uint16_t scancode) {
for(unsigned i = 0; i < Count; i++) {
if(joypad(i).isHat(scancode)) return true;
}
return false;
}
static bool isAnyAxis(uint16_t scancode) {
for(unsigned i = 0; i < Count; i++) {
if(joypad(i).isAxis(scancode)) return true;
}
return false;
}
static bool isAnyButton(uint16_t scancode) {
for(unsigned i = 0; i < Count; i++) {
if(joypad(i).isButton(scancode)) return true;
}
return false;
}
static uint16_t decode(const char *name) {
string s(name);
if(!strbegin(name, "JP")) return 0;
s.ltrim("JP");
unsigned id = decimal(s);
auto pos = strpos(s, "::");
if(!pos) return 0;
s = substr(s, pos() + 2);
for(unsigned i = 0; i < Limit; i++) {
if(s == JoypadScancodeName[i]) return Base + Size * id + i;
}
return 0;
}
string encode(uint16_t code) const {
unsigned index = 0;
for(unsigned i = 0; i < Count; i++) {
if(code >= Base + Size * i && code < Base + Size * (i + 1)) {
index = code - (Base + Size * i);
}
}
return { "JP", ID, "::", JoypadScancodeName[index] };
}
uint16_t operator[](Scancode code) const { return Base + ID * Size + code; }
uint16_t hat(unsigned id) const { return Base + Size * ID + Hat0 + id; }
uint16_t axis(unsigned id) const { return Base + Size * ID + Axis0 + id; }
uint16_t button(unsigned id) const { return Base + Size * ID + Button0 + id; }
bool isHat(unsigned id) const { return id >= hat(0) && id <= hat(7); }
bool isAxis(unsigned id) const { return id >= axis(0) && id <= axis(15); }
bool isButton(unsigned id) const { return id >= button(0) && id <= button(31); }
bool belongsTo(uint16_t scancode) const { return isHat(scancode) || isAxis(scancode) || isButton(scancode); }
Joypad(unsigned ID_) : ID(ID_) {}
};
inline Joypad& joypad(unsigned id) {
static Joypad jp0(0), jp1(1), jp2(2), jp3(3), jp4(4), jp5(5), jp6(6), jp7(7);
switch(id) { default:
case 0: return jp0; case 1: return jp1; case 2: return jp2; case 3: return jp3;
case 4: return jp4; case 5: return jp5; case 6: return jp6; case 7: return jp7;
}
}
struct Scancode {
enum { None = 0, Limit = Joypad::Base + Joypad::Size * Joypad::Count };
static uint16_t decode(const char *name) {
uint16_t code;
code = Keyboard::decode(name);
if(code) return code;
code = Mouse::decode(name);
if(code) return code;
code = Joypad::decode(name);
if(code) return code;
return None;
}
static string encode(uint16_t code) {
for(unsigned i = 0; i < Keyboard::Count; i++) {
if(keyboard(i).belongsTo(code)) return keyboard(i).encode(code);
}
for(unsigned i = 0; i < Mouse::Count; i++) {
if(mouse(i).belongsTo(code)) return mouse(i).encode(code);
}
for(unsigned i = 0; i < Joypad::Count; i++) {
if(joypad(i).belongsTo(code)) return joypad(i).encode(code);
}
return "None";
}
};
}
#endif

View File

@@ -1,59 +0,0 @@
#ifndef NALL_INTERPOLATION_HPP
#define NALL_INTERPOLATION_HPP
namespace nall {
struct Interpolation {
static inline double Nearest(double mu, double a, double b, double c, double d) {
return (mu <= 0.5 ? b : c);
}
static inline double Sublinear(double mu, double a, double b, double c, double d) {
mu = ((mu - 0.5) * 2.0) + 0.5;
if(mu < 0) mu = 0;
if(mu > 1) mu = 1;
return b * (1.0 - mu) + c * mu;
}
static inline double Linear(double mu, double a, double b, double c, double d) {
return b * (1.0 - mu) + c * mu;
}
static inline double Cosine(double mu, double a, double b, double c, double d) {
mu = (1.0 - cos(mu * 3.14159265)) / 2.0;
return b * (1.0 - mu) + c * mu;
}
static inline double Cubic(double mu, double a, double b, double c, double d) {
double A = d - c - a + b;
double B = a - b - A;
double C = c - a;
double D = b;
return A * (mu * mu * mu) + B * (mu * mu) + C * mu + D;
}
static inline double Hermite(double mu1, double a, double b, double c, double d) {
const double tension = 0.0; //-1 = low, 0 = normal, +1 = high
const double bias = 0.0; //-1 = left, 0 = even, +1 = right
double mu2, mu3, m0, m1, a0, a1, a2, a3;
mu2 = mu1 * mu1;
mu3 = mu2 * mu1;
m0 = (b - a) * (1.0 + bias) * (1.0 - tension) / 2.0;
m0 += (c - b) * (1.0 - bias) * (1.0 - tension) / 2.0;
m1 = (c - b) * (1.0 + bias) * (1.0 - tension) / 2.0;
m1 += (d - c) * (1.0 - bias) * (1.0 - tension) / 2.0;
a0 = +2 * mu3 - 3 * mu2 + 1;
a1 = mu3 - 2 * mu2 + mu1;
a2 = mu3 - mu2;
a3 = -2 * mu3 + 3 * mu2;
return (a0 * b) + (a1 * m0) + (a2 * m1) + (a3 * c);
}
};
}
#endif

View File

@@ -1,63 +0,0 @@
#ifndef NALL_INTRINSICS_HPP
#define NALL_INTRINSICS_HPP
struct Intrinsics {
enum class Compiler : unsigned { GCC, VisualC, Unknown };
enum class Platform : unsigned { X, OSX, Windows, Unknown };
enum class Endian : unsigned { LSB, MSB, Unknown };
static inline Compiler compiler();
static inline Platform platform();
static inline Endian endian();
};
/* Compiler detection */
#if defined(__GNUC__)
#define COMPILER_GCC
Intrinsics::Compiler Intrinsics::compiler() { return Intrinsics::Compiler::GCC; }
#elif defined(_MSC_VER)
#define COMPILER_VISUALC
Intrinsics::Compiler Intrinsics::compiler() { return Intrinsics::Compiler::VisualC; }
#else
#warning "unable to detect compiler"
#define COMPILER_UNKNOWN
Intrinsics::Compiler Intrinsics::compiler() { return Intrinsics::Compiler::Unknown; }
#endif
/* Platform detection */
#if defined(linux) || defined(__sun__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__)
#define PLATFORM_X
Intrinsics::Platform Intrinsics::platform() { return Intrinsics::Platform::X; }
#elif defined(__APPLE__)
#define PLATFORM_OSX
Intrinsics::Platform Intrinsics::platform() { return Intrinsics::Platform::OSX; }
#elif defined(_WIN32)
#define PLATFORM_WINDOWS
#define PLATFORM_WIN
Intrinsics::Platform Intrinsics::platform() { return Intrinsics::Platform::Windows; }
#else
#warning "unable to detect platform"
#define PLATFORM_UNKNOWN
Intrinsics::Platform Intrinsics::platform() { return Intrinsics::Platform::Unknown; }
#endif
/* Endian detection */
#if defined(__i386__) || defined(__amd64__) || defined(_M_IX86) || defined(_M_AMD64)
#define ENDIAN_LSB
#define ARCH_LSB
Intrinsics::Endian Intrinsics::endian() { return Intrinsics::Endian::LSB; }
#elif defined(__powerpc__) || defined(_M_PPC) || defined(__BIG_ENDIAN__)
#define ENDIAN_MSB
#define ARCH_MSB
Intrinsics::Endian Intrinsics::endian() { return Intrinsics::Endian::MSB; }
#else
#warning "unable to detect endian"
#define ENDIAN_UNKNOWN
#define ARCH_UNKNOWN
Intrinsics::Endian Intrinsics::endian() { return Intrinsics::Endian::Unknown; }
#endif
#endif

View File

@@ -1,52 +0,0 @@
#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

View File

@@ -1,100 +0,0 @@
#ifndef NALL_IPS_HPP
#define NALL_IPS_HPP
#include <nall/file.hpp>
#include <nall/stdint.hpp>
#include <nall/string.hpp>
namespace nall {
struct ips {
inline bool apply();
inline void source(const uint8_t *data, unsigned size);
inline void modify(const uint8_t *data, unsigned size);
inline ips();
inline ~ips();
uint8_t *data;
unsigned size;
const uint8_t *sourceData;
unsigned sourceSize;
const uint8_t *modifyData;
unsigned modifySize;
};
bool ips::apply() {
if(modifySize < 8) return false;
if(modifyData[0] != 'P') return false;
if(modifyData[1] != 'A') return false;
if(modifyData[2] != 'T') return false;
if(modifyData[3] != 'C') return false;
if(modifyData[4] != 'H') return false;
if(data) delete[] data;
data = new uint8_t[16 * 1024 * 1024 + 65536](); //maximum size of IPS patch + single-tag padding
size = sourceSize;
memcpy(data, sourceData, sourceSize);
unsigned offset = 5;
while(true) {
unsigned address, length;
if(offset > modifySize - 3) break;
address = modifyData[offset++] << 16;
address |= modifyData[offset++] << 8;
address |= modifyData[offset++] << 0;
if(address == 0x454f46) { //EOF
if(offset == modifySize) return true;
if(offset == modifySize - 3) {
size = modifyData[offset++] << 16;
size |= modifyData[offset++] << 8;
size |= modifyData[offset++] << 0;
return true;
}
}
if(offset > modifySize - 2) break;
length = modifyData[offset++] << 8;
length |= modifyData[offset++] << 0;
if(length) { //Copy
if(offset > modifySize - length) break;
while(length--) data[address++] = modifyData[offset++];
} else { //RLE
if(offset > modifySize - 3) break;
length = modifyData[offset++] << 8;
length |= modifyData[offset++] << 0;
if(length == 0) break; //illegal
while(length--) data[address++] = modifyData[offset];
offset++;
}
size = max(size, address);
}
delete[] data;
data = nullptr;
return false;
}
void ips::source(const uint8_t *data, unsigned size) {
sourceData = data, sourceSize = size;
}
void ips::modify(const uint8_t *data, unsigned size) {
modifyData = data, modifySize = size;
}
ips::ips() : data(nullptr), sourceData(nullptr), modifyData(nullptr) {
}
ips::~ips() {
if(data) delete[] data;
if(sourceData) delete[] sourceData;
if(modifyData) delete[] modifyData;
}
}
#endif

View File

@@ -1,165 +0,0 @@
#ifndef NALL_LZSS_HPP
#define NALL_LZSS_HPP
#include <nall/file.hpp>
#include <nall/filemap.hpp>
#include <nall/stdint.hpp>
#include <nall/string.hpp>
namespace nall {
//19:5 pulldown
//8:1 marker: d7-d0
//length: { 4 - 35 }, offset: { 1 - 0x80000 }
//4-byte file size header
//little-endian encoding
struct lzss {
inline void source(const uint8_t *data, unsigned size);
inline bool source(const string &filename);
inline unsigned size() const;
inline bool compress(const string &filename);
inline bool decompress(uint8_t *targetData, unsigned targetSize);
inline bool decompress(const string &filename);
protected:
struct Node {
unsigned offset;
Node *next;
inline Node() : offset(0), next(nullptr) {}
inline ~Node() { if(next) delete next; }
} *tree[65536];
filemap sourceFile;
const uint8_t *sourceData;
unsigned sourceSize;
public:
inline lzss() : sourceData(nullptr), sourceSize(0) {}
};
void lzss::source(const uint8_t *data, unsigned size) {
sourceData = data;
sourceSize = size;
}
bool lzss::source(const string &filename) {
if(sourceFile.open(filename, filemap::mode::read) == false) return false;
sourceData = sourceFile.data();
sourceSize = sourceFile.size();
return true;
}
unsigned lzss::size() const {
unsigned size = 0;
if(sourceSize < 4) return size;
for(unsigned n = 0; n < 32; n += 8) size |= sourceData[n >> 3] << n;
return size;
}
bool lzss::compress(const string &filename) {
file targetFile;
if(targetFile.open(filename, file::mode::write) == false) return false;
for(unsigned n = 0; n < 32; n += 8) targetFile.write(sourceSize >> n);
for(unsigned n = 0; n < 65536; n++) tree[n] = 0;
uint8_t buffer[25];
unsigned sourceOffset = 0;
while(sourceOffset < sourceSize) {
uint8_t mask = 0x00;
unsigned bufferOffset = 1;
for(unsigned iteration = 0; iteration < 8; iteration++) {
if(sourceOffset >= sourceSize) break;
uint16_t symbol = sourceData[sourceOffset + 0];
if(sourceOffset < sourceSize - 1) symbol |= sourceData[sourceOffset + 1] << 8;
Node *node = tree[symbol];
unsigned maxLength = 0, maxOffset = 0;
while(node) {
if(node->offset < sourceOffset - 0x80000) {
//out-of-range: all subsequent nodes will also be, so free up their memory
if(node->next) { delete node->next; node->next = 0; }
break;
}
unsigned length = 0, x = sourceOffset, y = node->offset;
while(length < 35 && x < sourceSize && sourceData[x++] == sourceData[y++]) length++;
if(length > maxLength) maxLength = length, maxOffset = node->offset;
if(length == 35) break;
node = node->next;
}
//attach current symbol to top of tree for subsequent searches
node = new Node;
node->offset = sourceOffset;
node->next = tree[symbol];
tree[symbol] = node;
if(maxLength < 4) {
buffer[bufferOffset++] = sourceData[sourceOffset++];
} else {
unsigned output = ((maxLength - 4) << 19) | (sourceOffset - 1 - maxOffset);
for(unsigned n = 0; n < 24; n += 8) buffer[bufferOffset++] = output >> n;
mask |= 0x80 >> iteration;
sourceOffset += maxLength;
}
}
buffer[0] = mask;
targetFile.write(buffer, bufferOffset);
}
sourceFile.close();
targetFile.close();
return true;
}
bool lzss::decompress(uint8_t *targetData, unsigned targetSize) {
if(targetSize < size()) return false;
unsigned sourceOffset = 4, targetOffset = 0;
while(sourceOffset < sourceSize) {
uint8_t mask = sourceData[sourceOffset++];
for(unsigned iteration = 0; iteration < 8; iteration++) {
if(sourceOffset >= sourceSize) break;
if((mask & (0x80 >> iteration)) == 0) {
targetData[targetOffset++] = sourceData[sourceOffset++];
} else {
unsigned code = 0;
for(unsigned n = 0; n < 24; n += 8) code |= sourceData[sourceOffset++] << n;
unsigned length = (code >> 19) + 4;
unsigned offset = targetOffset - 1 - (code & 0x7ffff);
while(length--) targetData[targetOffset++] = targetData[offset++];
}
}
}
}
bool lzss::decompress(const string &filename) {
if(sourceSize < 4) return false;
unsigned targetSize = size();
file fp;
if(fp.open(filename, file::mode::write) == false) return false;
fp.truncate(targetSize);
fp.close();
filemap targetFile;
if(targetFile.open(filename, filemap::mode::readwrite) == false) return false;
uint8_t *targetData = targetFile.data();
bool result = decompress(targetData, targetSize);
sourceFile.close();
targetFile.close();
return result;
}
}
#endif

View File

@@ -1,117 +0,0 @@
#ifndef NALL_MAP_HPP
#define NALL_MAP_HPP
#include <nall/vector.hpp>
namespace nall {
template<typename LHS, typename RHS>
struct map {
struct pair {
LHS name;
RHS data;
};
inline void reset() {
list.reset();
}
inline unsigned size() const {
return list.size();
}
//O(log n) find
inline optional<unsigned> find(const LHS &name) const {
signed first = 0, last = size() - 1;
while(first <= last) {
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, (unsigned)middle }; //match found
}
return { false, 0u };
}
//O(n) insert + O(log n) find
inline RHS& insert(const LHS &name, const RHS &data) {
if(auto position = find(name)) {
list[position()].data = data;
return list[position()].data;
}
signed offset = size();
for(unsigned n = 0; n < size(); n++) {
if(name < list[n].name) { offset = n; break; }
}
list.insert(offset, { name, data });
return list[offset].data;
}
//O(log n) find
inline void modify(const LHS &name, const RHS &data) {
if(auto position = find(name)) list[position()].data = data;
}
//O(n) remove + O(log n) find
inline void remove(const LHS &name) {
if(auto position = find(name)) list.remove(position());
}
//O(log n) find
inline RHS& operator[](const LHS &name) {
if(auto position = find(name)) return list[position()].data;
throw;
}
inline const RHS& operator[](const LHS &name) const {
if(auto position = find(name)) return list[position()].data;
throw;
}
inline RHS& operator()(const LHS &name) {
if(auto position = find(name)) return list[position()].data;
return insert(name, RHS());
}
inline const RHS& operator()(const LHS &name, const RHS &data) const {
if(auto position = find(name)) return list[position()].data;
return data;
}
inline pair* begin() { return list.begin(); }
inline pair* end() { return list.end(); }
inline const pair* begin() const { return list.begin(); }
inline const pair* end() const { return list.end(); }
protected:
vector<pair> list;
};
template<typename LHS, typename RHS>
struct bidirectional_map {
const map<LHS, RHS> &lhs;
const map<RHS, LHS> &rhs;
inline void reset() {
llist.reset();
rlist.reset();
}
inline unsigned size() const {
return llist.size();
}
inline void insert(const LHS &ldata, const RHS &rdata) {
llist.insert(ldata, rdata);
rlist.insert(rdata, ldata);
}
inline bidirectional_map() : lhs(llist), rhs(rlist) {}
protected:
map<LHS, RHS> llist;
map<RHS, LHS> rlist;
};
}
#endif

View File

@@ -1,10 +0,0 @@
#ifndef NALL_MOSAIC_HPP
#define NALL_MOSAIC_HPP
#define NALL_MOSAIC_INTERNAL_HPP
#include <nall/mosaic/bitstream.hpp>
#include <nall/mosaic/context.hpp>
#include <nall/mosaic/parser.hpp>
#undef NALL_MOSAIC_INTERNAL_HPP
#endif

View File

@@ -1,55 +0,0 @@
#ifdef NALL_MOSAIC_INTERNAL_HPP
namespace nall {
namespace mosaic {
struct bitstream {
filemap fp;
uint8_t *data;
unsigned size;
bool readonly;
bool endian;
inline bool read(uint64_t addr) const {
if(data == nullptr || (addr >> 3) >= size) return 0;
unsigned mask = endian == 0 ? (0x01 << (addr & 7)) : (0x80 >> (addr & 7));
return data[addr >> 3] & mask;
}
inline void write(uint64_t addr, bool value) {
if(data == nullptr || readonly == true || (addr >> 3) >= size) return;
unsigned mask = endian == 0 ? (0x01 << (addr & 7)) : (0x80 >> (addr & 7));
if(value == 0) data[addr >> 3] &= ~mask;
if(value == 1) data[addr >> 3] |= mask;
}
inline bool open(const string &filename) {
readonly = false;
if(fp.open(filename, filemap::mode::readwrite) == false) {
readonly = true;
if(fp.open(filename, filemap::mode::read) == false) {
return false;
}
}
data = fp.data();
size = fp.size();
return true;
}
inline void close() {
fp.close();
data = nullptr;
}
inline bitstream() : data(nullptr), endian(1) {
}
inline ~bitstream() {
close();
}
};
}
}
#endif

View File

@@ -1,224 +0,0 @@
#ifdef NALL_MOSAIC_INTERNAL_HPP
namespace nall {
namespace mosaic {
struct context {
unsigned offset;
unsigned width;
unsigned height;
unsigned count;
bool endian; //0 = lsb, 1 = msb
bool order; //0 = linear, 1 = planar
unsigned depth; //1 - 24bpp
unsigned blockWidth;
unsigned blockHeight;
unsigned blockStride;
unsigned blockOffset;
vector<unsigned> block;
unsigned tileWidth;
unsigned tileHeight;
unsigned tileStride;
unsigned tileOffset;
vector<unsigned> tile;
unsigned mosaicWidth;
unsigned mosaicHeight;
unsigned mosaicStride;
unsigned mosaicOffset;
vector<unsigned> mosaic;
unsigned paddingWidth;
unsigned paddingHeight;
unsigned paddingColor;
vector<unsigned> palette;
inline unsigned objectWidth() const { return blockWidth * tileWidth * mosaicWidth + paddingWidth; }
inline unsigned objectHeight() const { return blockHeight * tileHeight * mosaicHeight + paddingHeight; }
inline unsigned objectSize() const {
unsigned size = blockStride * tileWidth * tileHeight * mosaicWidth * mosaicHeight
+ blockOffset * tileHeight * mosaicWidth * mosaicHeight
+ tileStride * mosaicWidth * mosaicHeight
+ tileOffset * mosaicHeight;
return max(1u, size);
}
inline unsigned eval(const string &expression) {
intmax_t result;
if(fixedpoint::eval(expression, result) == false) return 0u;
return result;
}
inline void eval(vector<unsigned> &buffer, const string &expression_) {
string expression = expression_;
bool function = false;
for(auto &c : expression) {
if(c == '(') function = true;
if(c == ')') function = false;
if(c == ',' && function == true) c = ';';
}
lstring list = expression.split(",");
for(auto &item : list) {
item.trim();
if(item.wildcard("f(?*) ?*")) {
item.ltrim<1>("f(");
lstring part = item.split<1>(") ");
lstring args = part[0].split<3>(";");
for(auto &item : args) item.trim();
unsigned length = eval(args(0, "0"));
unsigned offset = eval(args(1, "0"));
unsigned stride = eval(args(2, "0"));
if(args.size() < 2) offset = buffer.size();
if(args.size() < 3) stride = 1;
for(unsigned n = 0; n < length; n++) {
string fn = part[1];
fn.replace("n", decimal(n));
fn.replace("o", decimal(offset));
fn.replace("p", decimal(buffer.size()));
buffer.resize(offset + 1);
buffer[offset] = eval(fn);
offset += stride;
}
} else if(item.wildcard("base64*")) {
unsigned offset = 0;
item.ltrim<1>("base64");
if(item.wildcard("(?*) *")) {
item.ltrim<1>("(");
lstring part = item.split<1>(") ");
offset = eval(part[0]);
item = part(1, "");
}
item.trim();
for(auto &c : item) {
if(c >= 'A' && c <= 'Z') buffer.append(offset + c - 'A' + 0);
if(c >= 'a' && c <= 'z') buffer.append(offset + c - 'a' + 26);
if(c >= '0' && c <= '9') buffer.append(offset + c - '0' + 52);
if(c == '-') buffer.append(offset + 62);
if(c == '_') buffer.append(offset + 63);
}
} else if(item.wildcard("file *")) {
item.ltrim<1>("file ");
item.trim();
//...
} else if(item.empty() == false) {
buffer.append(eval(item));
}
}
}
inline void parse(const string &data) {
reset();
lstring lines = data.split("\n");
for(auto &line : lines) {
lstring part = line.split<1>(":");
if(part.size() != 2) continue;
part[0].trim();
part[1].trim();
if(part[0] == "offset") offset = eval(part[1]);
if(part[0] == "width") width = eval(part[1]);
if(part[0] == "height") height = eval(part[1]);
if(part[0] == "count") count = eval(part[1]);
if(part[0] == "endian") endian = eval(part[1]);
if(part[0] == "order") order = eval(part[1]);
if(part[0] == "depth") depth = eval(part[1]);
if(part[0] == "blockWidth") blockWidth = eval(part[1]);
if(part[0] == "blockHeight") blockHeight = eval(part[1]);
if(part[0] == "blockStride") blockStride = eval(part[1]);
if(part[0] == "blockOffset") blockOffset = eval(part[1]);
if(part[0] == "block") eval(block, part[1]);
if(part[0] == "tileWidth") tileWidth = eval(part[1]);
if(part[0] == "tileHeight") tileHeight = eval(part[1]);
if(part[0] == "tileStride") tileStride = eval(part[1]);
if(part[0] == "tileOffset") tileOffset = eval(part[1]);
if(part[0] == "tile") eval(tile, part[1]);
if(part[0] == "mosaicWidth") mosaicWidth = eval(part[1]);
if(part[0] == "mosaicHeight") mosaicHeight = eval(part[1]);
if(part[0] == "mosaicStride") mosaicStride = eval(part[1]);
if(part[0] == "mosaicOffset") mosaicOffset = eval(part[1]);
if(part[0] == "mosaic") eval(mosaic, part[1]);
if(part[0] == "paddingWidth") paddingWidth = eval(part[1]);
if(part[0] == "paddingHeight") paddingHeight = eval(part[1]);
if(part[0] == "paddingColor") paddingColor = eval(part[1]);
if(part[0] == "palette") eval(palette, part[1]);
}
sanitize();
}
inline bool load(const string &filename) {
string filedata;
if(filedata.readfile(filename) == false) return false;
parse(filedata);
return true;
}
inline void sanitize() {
if(depth < 1) depth = 1;
if(depth > 24) depth = 24;
if(blockWidth < 1) blockWidth = 1;
if(blockHeight < 1) blockHeight = 1;
if(tileWidth < 1) tileWidth = 1;
if(tileHeight < 1) tileHeight = 1;
if(mosaicWidth < 1) mosaicWidth = 1;
if(mosaicHeight < 1) mosaicHeight = 1;
}
inline void reset() {
offset = 0;
width = 0;
height = 0;
count = 0;
endian = 1;
order = 0;
depth = 1;
blockWidth = 1;
blockHeight = 1;
blockStride = 0;
blockOffset = 0;
block.reset();
tileWidth = 1;
tileHeight = 1;
tileStride = 0;
tileOffset = 0;
tile.reset();
mosaicWidth = 1;
mosaicHeight = 1;
mosaicStride = 0;
mosaicOffset = 0;
mosaic.reset();
paddingWidth = 0;
paddingHeight = 0;
paddingColor = 0x000000;
palette.reset();
}
inline context() {
reset();
}
};
}
}
#endif

View File

@@ -1,126 +0,0 @@
#ifdef NALL_MOSAIC_INTERNAL_HPP
namespace nall {
namespace mosaic {
struct parser {
image canvas;
//export from bitstream to canvas
inline void load(bitstream &stream, uint64_t offset, context &ctx, unsigned width, unsigned height) {
canvas.allocate(width, height);
canvas.clear(ctx.paddingColor);
parse(1, stream, offset, ctx, width, height);
}
//import from canvas to bitstream
inline bool save(bitstream &stream, uint64_t offset, context &ctx) {
if(stream.readonly) return false;
parse(0, stream, offset, ctx, canvas.width, canvas.height);
return true;
}
inline parser() : canvas(0, 32, 0u, 255u << 16, 255u << 8, 255u << 0) {
}
private:
inline uint32_t read(unsigned x, unsigned y) const {
unsigned addr = y * canvas.width + x;
if(addr >= canvas.width * canvas.height) return 0u;
uint32_t *buffer = (uint32_t*)canvas.data;
return buffer[addr];
}
inline void write(unsigned x, unsigned y, uint32_t data) {
unsigned addr = y * canvas.width + x;
if(addr >= canvas.width * canvas.height) return;
uint32_t *buffer = (uint32_t*)canvas.data;
buffer[addr] = data;
}
inline void parse(bool load, bitstream &stream, uint64_t offset, context &ctx, unsigned width, unsigned height) {
stream.endian = ctx.endian;
unsigned canvasWidth = width / (ctx.mosaicWidth * ctx.tileWidth * ctx.blockWidth + ctx.paddingWidth);
unsigned canvasHeight = height / (ctx.mosaicHeight * ctx.tileHeight * ctx.blockHeight + ctx.paddingHeight);
unsigned bitsPerBlock = ctx.depth * ctx.blockWidth * ctx.blockHeight;
unsigned objectOffset = 0;
for(unsigned objectY = 0; objectY < canvasHeight; objectY++) {
for(unsigned objectX = 0; objectX < canvasWidth; objectX++) {
if(objectOffset >= ctx.count && ctx.count > 0) break;
unsigned objectIX = objectX * ctx.objectWidth();
unsigned objectIY = objectY * ctx.objectHeight();
objectOffset++;
unsigned mosaicOffset = 0;
for(unsigned mosaicY = 0; mosaicY < ctx.mosaicHeight; mosaicY++) {
for(unsigned mosaicX = 0; mosaicX < ctx.mosaicWidth; mosaicX++) {
unsigned mosaicData = ctx.mosaic(mosaicOffset, mosaicOffset);
unsigned mosaicIX = (mosaicData % ctx.mosaicWidth) * (ctx.tileWidth * ctx.blockWidth);
unsigned mosaicIY = (mosaicData / ctx.mosaicWidth) * (ctx.tileHeight * ctx.blockHeight);
mosaicOffset++;
unsigned tileOffset = 0;
for(unsigned tileY = 0; tileY < ctx.tileHeight; tileY++) {
for(unsigned tileX = 0; tileX < ctx.tileWidth; tileX++) {
unsigned tileData = ctx.tile(tileOffset, tileOffset);
unsigned tileIX = (tileData % ctx.tileWidth) * ctx.blockWidth;
unsigned tileIY = (tileData / ctx.tileWidth) * ctx.blockHeight;
tileOffset++;
unsigned blockOffset = 0;
for(unsigned blockY = 0; blockY < ctx.blockHeight; blockY++) {
for(unsigned blockX = 0; blockX < ctx.blockWidth; blockX++) {
if(load) {
unsigned palette = 0;
for(unsigned n = 0; n < ctx.depth; n++) {
unsigned index = blockOffset++;
if(ctx.order == 1) index = (index % ctx.depth) * ctx.blockWidth * ctx.blockHeight + (index / ctx.depth);
palette |= stream.read(offset + ctx.block(index, index)) << n;
}
write(
objectIX + mosaicIX + tileIX + blockX,
objectIY + mosaicIY + tileIY + blockY,
ctx.palette(palette, palette)
);
} else /* save */ {
uint32_t palette = read(
objectIX + mosaicIX + tileIX + blockX,
objectIY + mosaicIY + tileIY + blockY
);
for(unsigned n = 0; n < ctx.depth; n++) {
unsigned index = blockOffset++;
if(ctx.order == 1) index = (index % ctx.depth) * ctx.blockWidth * ctx.blockHeight + (index / ctx.depth);
stream.write(offset + ctx.block(index, index), palette & 1);
palette >>= 1;
}
}
} //blockX
} //blockY
offset += ctx.blockStride;
} //tileX
offset += ctx.blockOffset;
} //tileY
offset += ctx.tileStride;
} //mosaicX
offset += ctx.tileOffset;
} //mosaicY
offset += ctx.mosaicStride;
} //objectX
offset += ctx.mosaicOffset;
} //objectY
}
};
}
}
#endif

View File

@@ -1,58 +0,0 @@
#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

@@ -1,86 +0,0 @@
#ifndef NALL_PLATFORM_HPP
#define NALL_PLATFORM_HPP
#if defined(_WIN32)
//minimum version needed for _wstat64, etc
#undef __MSVCRT_VERSION__
#define __MSVCRT_VERSION__ 0x0601
#include <nall/windows/utf8.hpp>
#endif
//=========================
//standard platform headers
//=========================
#include <limits>
#include <assert.h>
#include <limits.h>
#include <math.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#if defined(_WIN32)
#include <io.h>
#include <direct.h>
#include <shlobj.h>
#include <wchar.h>
#undef interface
#define dllexport __declspec(dllexport)
#else
#include <unistd.h>
#include <pwd.h>
#define dllexport
#endif
//==================
//warning supression
//==================
//Visual C++
#if defined(_MSC_VER)
//disable libc "deprecation" warnings
#pragma warning(disable:4996)
#endif
//================
//POSIX compliance
//================
#if defined(_MSC_VER)
#define PATH_MAX _MAX_PATH
#define va_copy(dest, src) ((dest) = (src))
#endif
#if defined(_WIN32)
#define getcwd _getcwd
#define putenv _putenv
#define vsnprintf _vsnprintf
inline void usleep(unsigned milliseconds) { Sleep(milliseconds / 1000); }
#endif
//================
//inline expansion
//================
#if defined(__GNUC__)
#define noinline __attribute__((noinline))
#define inline inline
#define alwaysinline inline __attribute__((always_inline))
#elif defined(_MSC_VER)
#define noinline __declspec(noinline)
#define inline inline
#define alwaysinline inline __forceinline
#else
#define noinline
#define inline inline
#define alwaysinline inline
#endif
#endif

View File

@@ -1,337 +0,0 @@
#ifndef NALL_PNG_HPP
#define NALL_PNG_HPP
//PNG image decoder
//author: byuu
#include <nall/inflate.hpp>
#include <nall/string.hpp>
namespace nall {
struct png {
//colorType:
//0 = L
//2 = R,G,B
//3 = P
//4 = L,A
//6 = R,G,B,A
struct Info {
unsigned width;
unsigned height;
unsigned bitDepth;
unsigned colorType;
unsigned compressionMethod;
unsigned filterType;
unsigned interlaceMethod;
unsigned bytesPerPixel;
unsigned pitch;
uint8_t palette[256][3];
} info;
uint8_t *data;
unsigned size;
inline bool decode(const string &filename);
inline bool decode(const uint8_t *sourceData, unsigned sourceSize);
inline unsigned readbits(const uint8_t *&data);
unsigned bitpos;
inline png();
inline ~png();
protected:
enum class FourCC : unsigned {
IHDR = 0x49484452,
PLTE = 0x504c5445,
IDAT = 0x49444154,
IEND = 0x49454e44,
};
inline unsigned interlace(unsigned pass, unsigned index);
inline unsigned inflateSize();
inline bool deinterlace(const uint8_t *&inputData, unsigned pass);
inline bool filter(uint8_t *outputData, const uint8_t *inputData, unsigned width, unsigned height);
inline unsigned read(const uint8_t *data, unsigned length);
};
bool png::decode(const string &filename) {
if(auto memory = file::read(filename)) {
return decode(memory.data(), memory.size());
}
return false;
}
bool png::decode(const uint8_t *sourceData, unsigned sourceSize) {
if(sourceSize < 8) return false;
if(read(sourceData + 0, 4) != 0x89504e47) return false;
if(read(sourceData + 4, 4) != 0x0d0a1a0a) return false;
uint8_t *compressedData = 0;
unsigned compressedSize = 0;
unsigned offset = 8;
while(offset < sourceSize) {
unsigned length = read(sourceData + offset + 0, 4);
unsigned fourCC = read(sourceData + offset + 4, 4);
unsigned checksum = read(sourceData + offset + 8 + length, 4);
if(fourCC == (unsigned)FourCC::IHDR) {
info.width = read(sourceData + offset + 8, 4);
info.height = read(sourceData + offset + 12, 4);
info.bitDepth = read(sourceData + offset + 16, 1);
info.colorType = read(sourceData + offset + 17, 1);
info.compressionMethod = read(sourceData + offset + 18, 1);
info.filterType = read(sourceData + offset + 19, 1);
info.interlaceMethod = read(sourceData + offset + 20, 1);
if(info.bitDepth == 0 || info.bitDepth > 16) return false;
if(info.bitDepth & (info.bitDepth - 1)) return false; //not a power of two
if(info.compressionMethod != 0) return false;
if(info.filterType != 0) return false;
if(info.interlaceMethod != 0 && info.interlaceMethod != 1) return false;
switch(info.colorType) {
case 0: info.bytesPerPixel = info.bitDepth * 1; break; //L
case 2: info.bytesPerPixel = info.bitDepth * 3; break; //R,G,B
case 3: info.bytesPerPixel = info.bitDepth * 1; break; //P
case 4: info.bytesPerPixel = info.bitDepth * 2; break; //L,A
case 6: info.bytesPerPixel = info.bitDepth * 4; break; //R,G,B,A
default: return false;
}
if(info.colorType == 2 || info.colorType == 4 || info.colorType == 6)
if(info.bitDepth != 8 && info.bitDepth != 16) return false;
if(info.colorType == 3 && info.bitDepth == 16) return false;
info.bytesPerPixel = (info.bytesPerPixel + 7) / 8;
info.pitch = (int)info.width * info.bytesPerPixel;
}
if(fourCC == (unsigned)FourCC::PLTE) {
if(length % 3) return false;
for(unsigned n = 0, p = offset + 8; n < length / 3; n++) {
info.palette[n][0] = sourceData[p++];
info.palette[n][1] = sourceData[p++];
info.palette[n][2] = sourceData[p++];
}
}
if(fourCC == (unsigned)FourCC::IDAT) {
compressedData = (uint8_t*)realloc(compressedData, compressedSize + length);
memcpy(compressedData + compressedSize, sourceData + offset + 8, length);
compressedSize += length;
}
if(fourCC == (unsigned)FourCC::IEND) {
break;
}
offset += 4 + 4 + length + 4;
}
unsigned interlacedSize = inflateSize();
uint8_t *interlacedData = new uint8_t[interlacedSize];
bool result = inflate(interlacedData, interlacedSize, compressedData + 2, compressedSize - 6);
delete[] compressedData;
if(result == false) {
delete[] interlacedData;
return false;
}
size = info.width * info.height * info.bytesPerPixel;
data = new uint8_t[size];
if(info.interlaceMethod == 0) {
if(filter(data, interlacedData, info.width, info.height) == false) {
delete[] interlacedData;
delete[] data;
data = 0;
return false;
}
} else {
const uint8_t *passData = interlacedData;
for(unsigned pass = 0; pass < 7; pass++) {
if(deinterlace(passData, pass) == false) {
delete[] interlacedData;
delete[] data;
data = 0;
return false;
}
}
}
delete[] interlacedData;
return true;
}
unsigned png::interlace(unsigned pass, unsigned index) {
static const unsigned data[7][4] = {
//x-distance, y-distance, x-origin, y-origin
{ 8, 8, 0, 0 },
{ 8, 8, 4, 0 },
{ 4, 8, 0, 4 },
{ 4, 4, 2, 0 },
{ 2, 4, 0, 2 },
{ 2, 2, 1, 0 },
{ 1, 2, 0, 1 },
};
return data[pass][index];
}
unsigned png::inflateSize() {
if(info.interlaceMethod == 0) {
return info.width * info.height * info.bytesPerPixel + info.height;
}
unsigned size = 0;
for(unsigned pass = 0; pass < 7; pass++) {
unsigned xd = interlace(pass, 0), yd = interlace(pass, 1);
unsigned xo = interlace(pass, 2), yo = interlace(pass, 3);
unsigned width = (info.width + (xd - xo - 1)) / xd;
unsigned height = (info.height + (yd - yo - 1)) / yd;
if(width == 0 || height == 0) continue;
size += width * height * info.bytesPerPixel + height;
}
return size;
}
bool png::deinterlace(const uint8_t *&inputData, unsigned pass) {
unsigned xd = interlace(pass, 0), yd = interlace(pass, 1);
unsigned xo = interlace(pass, 2), yo = interlace(pass, 3);
unsigned width = (info.width + (xd - xo - 1)) / xd;
unsigned height = (info.height + (yd - yo - 1)) / yd;
if(width == 0 || height == 0) return true;
unsigned outputSize = width * height * info.bytesPerPixel;
uint8_t *outputData = new uint8_t[outputSize];
bool result = filter(outputData, inputData, width, height);
const uint8_t *rd = outputData;
for(unsigned y = yo; y < info.height; y += yd) {
uint8_t *wr = data + y * info.pitch;
for(unsigned x = xo; x < info.width; x += xd) {
for(unsigned b = 0; b < info.bytesPerPixel; b++) {
wr[x * info.bytesPerPixel + b] = *rd++;
}
}
}
inputData += outputSize + height;
delete[] outputData;
return result;
}
bool png::filter(uint8_t *outputData, const uint8_t *inputData, unsigned width, unsigned height) {
uint8_t *wr = outputData;
const uint8_t *rd = inputData;
int bpp = info.bytesPerPixel, pitch = width * bpp;
for(int y = 0; y < height; y++) {
uint8_t filter = *rd++;
switch(filter) {
case 0x00: //None
for(int x = 0; x < pitch; x++) {
wr[x] = rd[x];
}
break;
case 0x01: //Subtract
for(int x = 0; x < pitch; x++) {
wr[x] = rd[x] + (x - bpp < 0 ? 0 : wr[x - bpp]);
}
break;
case 0x02: //Above
for(int x = 0; x < pitch; x++) {
wr[x] = rd[x] + (y - 1 < 0 ? 0 : wr[x - pitch]);
}
break;
case 0x03: //Average
for(int x = 0; x < pitch; x++) {
short a = x - bpp < 0 ? 0 : wr[x - bpp];
short b = y - 1 < 0 ? 0 : wr[x - pitch];
wr[x] = rd[x] + (uint8_t)((a + b) / 2);
}
break;
case 0x04: //Paeth
for(int x = 0; x < pitch; x++) {
short a = x - bpp < 0 ? 0 : wr[x - bpp];
short b = y - 1 < 0 ? 0 : wr[x - pitch];
short c = x - bpp < 0 || y - 1 < 0 ? 0 : wr[x - pitch - bpp];
short p = a + b - c;
short pa = p > a ? p - a : a - p;
short pb = p > b ? p - b : b - p;
short pc = p > c ? p - c : c - p;
uint8_t paeth = (uint8_t)((pa <= pb && pa <= pc) ? a : (pb <= pc) ? b : c);
wr[x] = rd[x] + paeth;
}
break;
default: //Invalid
return false;
}
rd += pitch;
wr += pitch;
}
return true;
}
unsigned png::read(const uint8_t *data, unsigned length) {
unsigned result = 0;
while(length--) result = (result << 8) | (*data++);
return result;
}
unsigned png::readbits(const uint8_t *&data) {
unsigned result = 0;
switch(info.bitDepth) {
case 1:
result = (*data >> bitpos) & 1;
bitpos++;
if(bitpos == 8) { data++; bitpos = 0; }
break;
case 2:
result = (*data >> bitpos) & 3;
bitpos += 2;
if(bitpos == 8) { data++; bitpos = 0; }
break;
case 4:
result = (*data >> bitpos) & 15;
bitpos += 4;
if(bitpos == 8) { data++; bitpos = 0; }
break;
case 8:
result = *data++;
break;
case 16:
result = (data[0] << 8) | (data[1] << 0);
data += 2;
break;
}
return result;
}
png::png() : data(nullptr) {
bitpos = 0;
}
png::~png() {
if(data) delete[] data;
}
}
#endif

View File

@@ -1,109 +0,0 @@
#ifndef NALL_PRIORITY_QUEUE_HPP
#define NALL_PRIORITY_QUEUE_HPP
#include <limits>
#include <nall/function.hpp>
#include <nall/serializer.hpp>
#include <nall/utility.hpp>
namespace nall {
template<typename type_t> void priority_queue_nocallback(type_t) {}
//priority queue implementation using binary min-heap array;
//does not require normalize() function.
//O(1) find (tick)
//O(log n) append (enqueue)
//O(log n) remove (dequeue)
template<typename type_t> class priority_queue {
public:
inline void tick(unsigned ticks) {
basecounter += ticks;
while(heapsize && gte(basecounter, heap[0].counter)) callback(dequeue());
}
//counter is relative to current time (eg enqueue(64, ...) fires in 64 ticks);
//counter cannot exceed std::numeric_limits<unsigned>::max() >> 1.
void enqueue(unsigned counter, type_t event) {
unsigned child = heapsize++;
counter += basecounter;
while(child) {
unsigned parent = (child - 1) >> 1;
if(gte(counter, heap[parent].counter)) break;
heap[child].counter = heap[parent].counter;
heap[child].event = heap[parent].event;
child = parent;
}
heap[child].counter = counter;
heap[child].event = event;
}
type_t dequeue() {
type_t event(heap[0].event);
unsigned parent = 0;
unsigned counter = heap[--heapsize].counter;
while(true) {
unsigned child = (parent << 1) + 1;
if(child >= heapsize) break;
if(child + 1 < heapsize && gte(heap[child].counter, heap[child + 1].counter)) child++;
if(gte(heap[child].counter, counter)) break;
heap[parent].counter = heap[child].counter;
heap[parent].event = heap[child].event;
parent = child;
}
heap[parent].counter = counter;
heap[parent].event = heap[heapsize].event;
return event;
}
void reset() {
basecounter = 0;
heapsize = 0;
}
void serialize(serializer &s) {
s.integer(basecounter);
s.integer(heapsize);
for(unsigned n = 0; n < heapcapacity; n++) {
s.integer(heap[n].counter);
s.integer(heap[n].event);
}
}
priority_queue(unsigned size, function<void (type_t)> callback_ = &priority_queue_nocallback<type_t>)
: callback(callback_) {
heap = new heap_t[size];
heapcapacity = size;
reset();
}
~priority_queue() {
delete[] heap;
}
priority_queue& operator=(const priority_queue&) = delete;
priority_queue(const priority_queue&) = delete;
private:
function<void (type_t)> callback;
unsigned basecounter;
unsigned heapsize;
unsigned heapcapacity;
struct heap_t {
unsigned counter;
type_t event;
} *heap;
//return true if x is greater than or equal to y
inline bool gte(unsigned x, unsigned y) {
return x - y < (std::numeric_limits<unsigned>::max() >> 1);
}
};
}
#endif

View File

@@ -1,91 +0,0 @@
#ifndef NALL_PROPERTY_HPP
#define NALL_PROPERTY_HPP
//nall::property implements ownership semantics into container classes
//example: property<owner>::readonly<type> implies that only owner has full
//access to type; and all other code has readonly access.
//
//this code relies on extended friend semantics from C++0x to work, as it
//declares a friend class via a template paramter. it also exploits a bug in
//G++ 4.x to work even in C++98 mode.
//
//if compiling elsewhere, simply remove the friend class and private semantics
//property can be used either of two ways:
//struct foo {
// property<foo>::readonly<bool> x;
// property<foo>::readwrite<int> y;
//};
//-or-
//struct foo : property<foo> {
// readonly<bool> x;
// readwrite<int> y;
//};
//return types are const T& (byref) instead of T (byval) to avoid major speed
//penalties for objects with expensive copy constructors
//operator-> provides access to underlying object type:
//readonly<Object> foo;
//foo->bar();
//... will call Object::bar();
//operator='s reference is constant so as to avoid leaking a reference handle
//that could bypass access restrictions
//both constant and non-constant operators are provided, though it may be
//necessary to cast first, for instance:
//struct foo : property<foo> { readonly<int> bar; } object;
//int main() { int value = const_cast<const foo&>(object); }
//writeonly is useful for objects that have non-const reads, but const writes.
//however, to avoid leaking handles, the interface is very restricted. the only
//way to write is via operator=, which requires conversion via eg copy
//constructor. example:
//struct foo {
// foo(bool value) { ... }
//};
//writeonly<foo> bar;
//bar = true;
namespace nall {
template<typename C> struct property {
template<typename T> struct traits { typedef T type; };
template<typename T> struct readonly {
const T* operator->() const { return &value; }
const T& operator()() const { return value; }
operator const T&() const { return value; }
private:
T* operator->() { return &value; }
operator T&() { return value; }
const T& operator=(const T& value_) { return value = value_; }
T value;
friend class traits<C>::type;
};
template<typename T> struct writeonly {
void operator=(const T& value_) { value = value_; }
private:
const T* operator->() const { return &value; }
const T& operator()() const { return value; }
operator const T&() const { return value; }
T* operator->() { return &value; }
operator T&() { return value; }
T value;
friend class traits<C>::type;
};
template<typename T> struct readwrite {
const T* operator->() const { return &value; }
const T& operator()() const { return value; }
operator const T&() const { return value; }
T* operator->() { return &value; }
operator T&() { return value; }
const T& operator=(const T& value_) { return value = value_; }
T value;
};
};
}
#endif

View File

@@ -1,32 +0,0 @@
#ifndef NALL_PUBLIC_CAST_HPP
#define NALL_PUBLIC_CAST_HPP
//this is a proof-of-concept-*only* C++ access-privilege elevation exploit.
//this code is 100% legal C++, per C++98 section 14.7.2 paragraph 8:
//"access checking rules do not apply to names in explicit instantiations."
//usage example:
//struct N { typedef void (Class::*)(); };
//template class public_cast<N, &Class::Reference>;
//(class.*public_cast<N>::value);
//Class::Reference may be public, protected or private
//Class::Reference may be a function, object or variable
namespace nall {
template<typename T, typename T::type... P> struct public_cast;
template<typename T> struct public_cast<T> {
static typename T::type value;
};
template<typename T> typename T::type public_cast<T>::value;
template<typename T, typename T::type P> struct public_cast<T, P> {
static typename T::type value;
};
template<typename T, typename T::type P> typename T::type public_cast<T, P>::value = public_cast<T>::value = P;
}
#endif

View File

@@ -1,28 +0,0 @@
#ifndef NALL_RANDOM_HPP
#define NALL_RANDOM_HPP
namespace nall {
//pseudo-random number generator
inline unsigned prng() {
static unsigned n = 0;
return n = (n >> 1) ^ (((n & 1) - 1) & 0xedb88320);
}
struct random_lfsr {
inline void seed(unsigned seed__) {
seed_ = seed__;
}
inline unsigned operator()() {
return seed_ = (seed_ >> 1) ^ (((seed_ & 1) - 1) & 0xedb88320);
}
random_lfsr() : seed_(0) {
}
private:
unsigned seed_;
};
}
#endif

View File

@@ -1,110 +0,0 @@
#ifndef NALL_SERIAL_HPP
#define NALL_SERIAL_HPP
#include <sys/ioctl.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <nall/stdint.hpp>
namespace nall {
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;
return ::write(port, (void*)data, length);
}
bool open(const char *portname, unsigned rate, bool flowcontrol) {
close();
port = ::open(portname, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK);
if(port == -1) return false;
if(ioctl(port, TIOCEXCL) == -1) { close(); return false; }
if(fcntl(port, F_SETFL, 0) == -1) { close(); return false; }
if(tcgetattr(port, &original_attr) == -1) { close(); return false; }
termios attr = original_attr;
cfmakeraw(&attr);
cfsetspeed(&attr, rate);
attr.c_lflag &=~ (ECHO | ECHONL | ISIG | ICANON | IEXTEN);
attr.c_iflag &=~ (BRKINT | PARMRK | INPCK | ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXOFF | IXANY);
attr.c_iflag |= (IGNBRK | IGNPAR);
attr.c_oflag &=~ (OPOST);
attr.c_cflag &=~ (CSIZE | CSTOPB | PARENB | CLOCAL);
attr.c_cflag |= (CS8 | CREAD);
if(flowcontrol == false) {
attr.c_cflag &= ~CRTSCTS;
} else {
attr.c_cflag |= CRTSCTS;
}
attr.c_cc[VTIME] = attr.c_cc[VMIN] = 0;
if(tcsetattr(port, TCSANOW, &attr) == -1) { close(); return false; }
return port_open = true;
}
void close() {
if(port != -1) {
tcdrain(port);
if(port_open == true) {
tcsetattr(port, TCSANOW, &original_attr);
port_open = false;
}
::close(port);
port = -1;
}
}
serial() {
port = -1;
port_open = false;
}
~serial() {
close();
}
private:
int port;
bool port_open;
termios original_attr;
};
}
#endif

View File

@@ -1,146 +0,0 @@
#ifndef NALL_SERIALIZER_HPP
#define NALL_SERIALIZER_HPP
#include <type_traits>
#include <utility>
#include <nall/stdint.hpp>
#include <nall/utility.hpp>
namespace nall {
//serializer: a class designed to save and restore the state of classes.
//
//benefits:
//- data() will be portable in size (it is not necessary to specify type sizes.)
//- data() will be portable in endianness (always stored internally as little-endian.)
//- one serialize function can both save and restore class states.
//
//caveats:
//- only plain-old-data can be stored. complex classes must provide serialize(serializer&);
//- floating-point usage is not portable across platforms
class serializer {
public:
enum mode_t { Load, Save, Size };
mode_t mode() const {
return imode;
}
const uint8_t* data() const {
return idata;
}
unsigned size() const {
return isize;
}
unsigned capacity() const {
return icapacity;
}
template<typename T> void floatingpoint(T &value) {
enum { size = sizeof(T) };
//this is rather dangerous, and not cross-platform safe;
//but there is no standardized way to export FP-values
uint8_t *p = (uint8_t*)&value;
if(imode == Save) {
for(unsigned n = 0; n < size; n++) idata[isize++] = p[n];
} else if(imode == Load) {
for(unsigned n = 0; n < size; n++) p[n] = idata[isize++];
} else {
isize += size;
}
}
template<typename T> void integer(T &value) {
enum { size = std::is_same<bool, T>::value ? 1 : sizeof(T) };
if(imode == Save) {
for(unsigned n = 0; n < size; n++) idata[isize++] = (uintmax_t)value >> (n << 3);
} else if(imode == Load) {
value = 0;
for(unsigned n = 0; n < size; n++) value |= (uintmax_t)idata[isize++] << (n << 3);
} else if(imode == Size) {
isize += size;
}
}
template<typename T> void array(T &array) {
enum { size = sizeof(T) / sizeof(typename std::remove_extent<T>::type) };
for(unsigned n = 0; n < size; n++) integer(array[n]);
}
template<typename T> void array(T array, unsigned size) {
for(unsigned n = 0; n < size; n++) integer(array[n]);
}
//copy
serializer& operator=(const serializer &s) {
if(idata) delete[] idata;
imode = s.imode;
idata = new uint8_t[s.icapacity];
isize = s.isize;
icapacity = s.icapacity;
memcpy(idata, s.idata, s.icapacity);
return *this;
}
serializer(const serializer &s) : idata(0) {
operator=(s);
}
//move
serializer& operator=(serializer &&s) {
if(idata) delete[] idata;
imode = s.imode;
idata = s.idata;
isize = s.isize;
icapacity = s.icapacity;
s.idata = 0;
return *this;
}
serializer(serializer &&s) {
operator=(std::move(s));
}
//construction
serializer() {
imode = Size;
idata = 0;
isize = 0;
icapacity = 0;
}
serializer(unsigned capacity) {
imode = Save;
idata = new uint8_t[capacity]();
isize = 0;
icapacity = capacity;
}
serializer(const uint8_t *data, unsigned capacity) {
imode = Load;
idata = new uint8_t[capacity];
isize = 0;
icapacity = capacity;
memcpy(idata, data, capacity);
}
~serializer() {
if(idata) delete[] idata;
}
private:
mode_t imode;
uint8_t *idata;
unsigned isize;
unsigned icapacity;
};
};
#endif

View File

@@ -1,158 +0,0 @@
#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

View File

@@ -1,145 +0,0 @@
#ifndef NALL_SHA256_HPP
#define NALL_SHA256_HPP
//author: vladitx
#include <nall/stdint.hpp>
namespace nall {
#define PTR(t, a) ((t*)(a))
#define SWAP32(x) ((uint32_t)( \
(((uint32_t)(x) & 0x000000ff) << 24) | \
(((uint32_t)(x) & 0x0000ff00) << 8) | \
(((uint32_t)(x) & 0x00ff0000) >> 8) | \
(((uint32_t)(x) & 0xff000000) >> 24) \
))
#define ST32(a, d) *PTR(uint32_t, a) = (d)
#define ST32BE(a, d) ST32(a, SWAP32(d))
#define LD32(a) *PTR(uint32_t, a)
#define LD32BE(a) SWAP32(LD32(a))
#define LSL32(x, n) ((uint32_t)(x) << (n))
#define LSR32(x, n) ((uint32_t)(x) >> (n))
#define ROR32(x, n) (LSR32(x, n) | LSL32(x, 32 - (n)))
//first 32 bits of the fractional parts of the square roots of the first 8 primes 2..19
static const uint32_t T_H[8] = {
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
};
//first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311
static const uint32_t T_K[64] = {
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
};
struct sha256_ctx {
uint8_t in[64];
unsigned inlen;
uint32_t w[64];
uint32_t h[8];
uint64_t len;
};
inline void sha256_init(sha256_ctx *p) {
memset(p, 0, sizeof(sha256_ctx));
memcpy(p->h, T_H, sizeof(T_H));
}
static void sha256_block(sha256_ctx *p) {
unsigned i;
uint32_t s0, s1;
uint32_t a, b, c, d, e, f, g, h;
uint32_t t1, t2, maj, ch;
for(i = 0; i < 16; i++) p->w[i] = LD32BE(p->in + i * 4);
for(i = 16; i < 64; i++) {
s0 = ROR32(p->w[i - 15], 7) ^ ROR32(p->w[i - 15], 18) ^ LSR32(p->w[i - 15], 3);
s1 = ROR32(p->w[i - 2], 17) ^ ROR32(p->w[i - 2], 19) ^ LSR32(p->w[i - 2], 10);
p->w[i] = p->w[i - 16] + s0 + p->w[i - 7] + s1;
}
a = p->h[0]; b = p->h[1]; c = p->h[2]; d = p->h[3];
e = p->h[4]; f = p->h[5]; g = p->h[6]; h = p->h[7];
for(i = 0; i < 64; i++) {
s0 = ROR32(a, 2) ^ ROR32(a, 13) ^ ROR32(a, 22);
maj = (a & b) ^ (a & c) ^ (b & c);
t2 = s0 + maj;
s1 = ROR32(e, 6) ^ ROR32(e, 11) ^ ROR32(e, 25);
ch = (e & f) ^ (~e & g);
t1 = h + s1 + ch + T_K[i] + p->w[i];
h = g; g = f; f = e; e = d + t1;
d = c; c = b; b = a; a = t1 + t2;
}
p->h[0] += a; p->h[1] += b; p->h[2] += c; p->h[3] += d;
p->h[4] += e; p->h[5] += f; p->h[6] += g; p->h[7] += h;
//next block
p->inlen = 0;
}
inline void sha256_chunk(sha256_ctx *p, const uint8_t *s, unsigned len) {
unsigned l;
p->len += len;
while(len) {
l = 64 - p->inlen;
l = (len < l) ? len : l;
memcpy(p->in + p->inlen, s, l);
s += l;
p->inlen += l;
len -= l;
if(p->inlen == 64) sha256_block(p);
}
}
inline void sha256_final(sha256_ctx *p) {
uint64_t len;
p->in[p->inlen++] = 0x80;
if(p->inlen > 56) {
memset(p->in + p->inlen, 0, 64 - p->inlen);
sha256_block(p);
}
memset(p->in + p->inlen, 0, 56 - p->inlen);
len = p->len << 3;
ST32BE(p->in + 56, len >> 32);
ST32BE(p->in + 60, len);
sha256_block(p);
}
inline void sha256_hash(sha256_ctx *p, uint8_t *s) {
uint32_t *t = (uint32_t*)s;
for(unsigned i = 0; i < 8; i++) ST32BE(t++, p->h[i]);
}
#undef PTR
#undef SWAP32
#undef ST32
#undef ST32BE
#undef LD32
#undef LD32BE
#undef LSL32
#undef LSR32
#undef ROR32
}
#endif

View File

@@ -1,77 +0,0 @@
#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

View File

@@ -1,42 +0,0 @@
#ifndef NALL_STDINT_HPP
#define NALL_STDINT_HPP
#if defined(_MSC_VER)
typedef signed char int8_t;
typedef signed short int16_t;
typedef signed int int32_t;
typedef signed long long int64_t;
typedef int64_t intmax_t;
#if defined(_WIN64)
typedef int64_t intptr_t;
#else
typedef int32_t intptr_t;
#endif
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
typedef unsigned long long uint64_t;
typedef uint64_t uintmax_t;
#if defined(_WIN64)
typedef uint64_t uintptr_t;
#else
typedef uint32_t uintptr_t;
#endif
#else
#include <stdint.h>
#endif
namespace nall {
static_assert(sizeof(int8_t) == 1, "int8_t is not of the correct size" );
static_assert(sizeof(int16_t) == 2, "int16_t is not of the correct size");
static_assert(sizeof(int32_t) == 4, "int32_t is not of the correct size");
static_assert(sizeof(int64_t) == 8, "int64_t is not of the correct size");
static_assert(sizeof(uint8_t) == 1, "int8_t is not of the correct size" );
static_assert(sizeof(uint16_t) == 2, "int16_t is not of the correct size");
static_assert(sizeof(uint32_t) == 4, "int32_t is not of the correct size");
static_assert(sizeof(uint64_t) == 8, "int64_t is not of the correct size");
}
#endif

View File

@@ -1,26 +0,0 @@
#ifndef NALL_STREAM_HPP
#define NALL_STREAM_HPP
#include <algorithm>
#include <memory>
#include <nall/file.hpp>
#include <nall/filemap.hpp>
#include <nall/gzip.hpp>
#include <nall/http.hpp>
#include <nall/stdint.hpp>
#include <nall/string.hpp>
#include <nall/zip.hpp>
#define NALL_STREAM_INTERNAL_HPP
#include <nall/stream/stream.hpp>
#include <nall/stream/memory.hpp>
#include <nall/stream/mmap.hpp>
#include <nall/stream/file.hpp>
#include <nall/stream/http.hpp>
#include <nall/stream/gzip.hpp>
#include <nall/stream/zip.hpp>
#include <nall/stream/auto.hpp>
#undef NALL_STREAM_INTERNAL_HPP
#endif

View File

@@ -1,25 +0,0 @@
#ifndef NALL_STREAM_AUTO_HPP
#define NALL_STREAM_AUTO_HPP
namespace nall {
#define autostream(...) (*makestream(__VA_ARGS__))
inline std::unique_ptr<stream> makestream(const string &path) {
if(path.ibeginswith("http://")) return std::unique_ptr<stream>(new httpstream(path, 80));
if(path.iendswith(".gz")) return std::unique_ptr<stream>(new gzipstream(filestream{path}));
if(path.iendswith(".zip")) return std::unique_ptr<stream>(new zipstream(filestream{path}));
return std::unique_ptr<stream>(new mmapstream(path));
}
inline std::unique_ptr<stream> makestream(uint8_t *data, unsigned size) {
return std::unique_ptr<stream>(new memorystream(data, size));
}
inline std::unique_ptr<stream> makestream(const uint8_t *data, unsigned size) {
return std::unique_ptr<stream>(new memorystream(data, size));
}
}
#endif

View File

@@ -1,42 +0,0 @@
#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,34 +0,0 @@
#ifndef NALL_STREAM_GZIP_HPP
#define NALL_STREAM_GZIP_HPP
#include <nall/gzip.hpp>
namespace nall {
struct gzipstream : memorystream {
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);
gzip archive;
bool result = archive.decompress(data, size);
delete[] data;
if(result == false) return;
psize = archive.size;
pdata = new uint8_t[psize];
memcpy(pdata, archive.data, psize);
}
~gzipstream() {
if(pdata) delete[] pdata;
}
};
}
#endif

View File

@@ -1,49 +0,0 @@
#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

@@ -1,47 +0,0 @@
#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

@@ -1,42 +0,0 @@
#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

@@ -1,92 +0,0 @@
#ifndef NALL_STREAM_STREAM_HPP
#define NALL_STREAM_STREAM_HPP
namespace nall {
struct stream {
virtual bool seekable() const = 0;
virtual bool readable() const = 0;
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;
virtual uint8_t read() const = 0;
virtual void write(uint8_t data) const = 0;
virtual uint8_t read(unsigned) const { return 0; }
virtual void write(unsigned, uint8_t) const {}
operator bool() const {
return size();
}
bool empty() const {
return size() == 0;
}
bool end() const {
return offset() >= size();
}
uintmax_t readl(unsigned length = 1) const {
uintmax_t data = 0, shift = 0;
while(length--) { data |= read() << shift; shift += 8; }
return data;
}
uintmax_t readm(unsigned length = 1) const {
uintmax_t data = 0;
while(length--) data = (data << 8) | read();
return data;
}
void read(uint8_t *data, unsigned length) const {
while(length--) *data++ = read();
}
void writel(uintmax_t data, unsigned length = 1) const {
while(length--) {
write(data);
data >>= 8;
}
}
void writem(uintmax_t data, unsigned length = 1) const {
uintmax_t shift = 8 * length;
while(length--) {
shift -= 8;
write(data >> shift);
}
}
void write(const uint8_t *data, unsigned length) const {
while(length--) write(*data++);
}
struct byte {
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;
};
byte operator[](unsigned offset) const {
return byte(*this, offset);
}
stream() {}
virtual ~stream() {}
stream(const stream&) = delete;
stream& operator=(const stream&) = delete;
};
}
#endif

View File

@@ -1,39 +0,0 @@
#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

@@ -1,38 +0,0 @@
#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

View File

@@ -1,53 +0,0 @@
#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,220 +0,0 @@
#ifdef NALL_STRING_INTERNAL_HPP
namespace nall {
struct cstring;
struct string;
struct lstring;
template<typename T> inline const char* to_string(T);
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);
inline bool readfile(const string&);
template<unsigned Limit = 0> inline string& replace(const char*, const char*);
template<unsigned Limit = 0> inline string& ireplace(const char*, const char*);
template<unsigned Limit = 0> inline string& qreplace(const char*, const char*);
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;
inline bool wildcard(const char*) const;
inline bool iwildcard(const char*) const;
inline bool beginswith(const char*) const;
inline bool ibeginswith(const char*) const;
inline bool endswith(const char*) const;
inline bool iendswith(const char*) const;
inline string& lower();
inline string& upper();
inline string& qlower();
inline string& qupper();
inline string& transform(const char *before, const char *after);
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;
inline optional<unsigned> qposition(const char *key) const;
inline optional<unsigned> iqposition(const char *key) const;
inline operator const char*() const;
inline char* operator()();
inline char& operator[](int);
inline bool operator==(const char*) const;
inline bool operator!=(const char*) const;
inline bool operator< (const char*) const;
inline bool operator<=(const char*) const;
inline bool operator> (const char*) const;
inline bool operator>=(const char*) const;
inline string& operator=(const string&);
inline string& operator=(string&&);
template<typename... Args> inline string(Args&&... args);
inline string(const string&);
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*);
protected:
char *data;
unsigned size;
template<unsigned Limit, bool Insensitive, bool Quoted> inline string& ureplace(const char*, const char*);
#if defined(QSTRING_H)
public:
inline operator QString() const;
#endif
};
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*);
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*);
};
//compare.hpp
inline char chrlower(char c);
inline char chrupper(char c);
inline int istrcmp(const char *str1, const char *str2);
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);
inline bool istrend(const char *str, const char *key);
//convert.hpp
inline char* strlower(char *str);
inline char* strupper(char *str);
inline char* qstrlower(char *str);
inline char* qstrupper(char *str);
inline char* strtr(char *dest, const char *before, const char *after);
//math.hpp
inline bool strint(const char *str, int &result);
inline bool strmath(const char *str, int &result);
//platform.hpp
inline string activepath();
inline string realpath(const string &name);
inline string userpath();
inline string configpath();
inline string temppath();
//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);
inline optional<unsigned> istrpos(const char *str, const char *key);
inline optional<unsigned> qstrpos(const char *str, const char *key);
inline optional<unsigned> iqstrpos(const char *str, const char *key);
template<bool Insensitive = false, bool Quoted = false> inline optional<unsigned> ustrpos(const char *str, const char *key);
//trim.hpp
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 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, 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

@@ -1,76 +0,0 @@
#ifdef NALL_STRING_INTERNAL_HPP
//BSV v1.0 parser
//revision 0.02
namespace nall {
struct BSV {
static inline string decode(const char *input) {
string output;
unsigned offset = 0;
while(*input) {
//illegal characters
if(*input == '}' ) return "";
if(*input == '\r') return "";
if(*input == '\n') return "";
//normal characters
if(*input != '{') { output[offset++] = *input++; continue; }
//entities
if(strbegin(input, "{lf}")) { output[offset++] = '\n'; input += 4; continue; }
if(strbegin(input, "{lb}")) { output[offset++] = '{'; input += 4; continue; }
if(strbegin(input, "{rb}")) { output[offset++] = '}'; input += 4; continue; }
//illegal entities
return "";
}
output[offset] = 0;
return output;
}
static inline string encode(const char *input) {
string output;
unsigned offset = 0;
while(*input) {
//illegal characters
if(*input == '\r') return "";
if(*input == '\n') {
output[offset++] = '{';
output[offset++] = 'l';
output[offset++] = 'f';
output[offset++] = '}';
input++;
continue;
}
if(*input == '{') {
output[offset++] = '{';
output[offset++] = 'l';
output[offset++] = 'b';
output[offset++] = '}';
input++;
continue;
}
if(*input == '}') {
output[offset++] = '{';
output[offset++] = 'r';
output[offset++] = 'b';
output[offset++] = '}';
input++;
continue;
}
output[offset++] = *input++;
}
output[offset] = 0;
return output;
}
};
}
#endif

View File

@@ -1,185 +0,0 @@
#ifdef NALL_STRING_INTERNAL_HPP
namespace nall {
//convert any (supported) type to a const char* without constructing a new nall::string
//this is used inside istring(...) to build nall::string values
template<typename T> struct stringify;
// base types
template<> struct stringify<bool> {
bool value;
operator const char*() const { return value ? "true" : "false"; }
stringify(bool value) : value(value) {}
};
template<> struct stringify<char> {
char data[256];
operator const char*() const { return data; }
stringify(char value) { integer(data, value); }
};
// signed integers
template<> struct stringify<signed char> {
char data[256];
operator const char*() const { return data; }
stringify(signed char value) { integer(data, value); }
};
template<> struct stringify<signed short> {
char data[256];
operator const char*() const { return data; }
stringify(signed short value) { integer(data, value); }
};
template<> struct stringify<signed int> {
char data[256];
operator const char*() const { return data; }
stringify(signed int value) { integer(data, value); }
};
template<> struct stringify<signed long> {
char data[256];
operator const char*() const { return data; }
stringify(signed long value) { integer(data, value); }
};
template<> struct stringify<signed long long> {
char data[256];
operator const char*() const { return data; }
stringify(signed long long value) { integer(data, value); }
};
template<unsigned bits> struct stringify<int_t<bits>> {
char data[256];
operator const char*() const { return data; }
stringify(int_t<bits> value) { integer(data, value); }
};
// unsigned integers
template<> struct stringify<unsigned char> {
char data[256];
operator const char*() const { return data; }
stringify(unsigned char value) { decimal(data, value); }
};
template<> struct stringify<unsigned short> {
char data[256];
operator const char*() const { return data; }
stringify(unsigned short value) { decimal(data, value); }
};
template<> struct stringify<unsigned int> {
char data[256];
operator const char*() const { return data; }
stringify(unsigned int value) { decimal(data, value); }
};
template<> struct stringify<unsigned long> {
char data[256];
operator const char*() const { return data; }
stringify(unsigned long value) { decimal(data, value); }
};
template<> struct stringify<unsigned long long> {
char data[256];
operator const char*() const { return data; }
stringify(unsigned long long value) { decimal(data, value); }
};
template<unsigned bits> struct stringify<uint_t<bits>> {
char data[256];
operator const char*() const { return data; }
stringify(uint_t<bits> value) { decimal(data, value); }
};
// floating-point
template<> struct stringify<float> {
char data[256];
operator const char*() const { return data; }
stringify(float value) { fp(data, value); }
};
template<> struct stringify<double> {
char data[256];
operator const char*() const { return data; }
stringify(double value) { fp(data, value); }
};
template<> struct stringify<long double> {
char data[256];
operator const char*() const { return data; }
stringify(long double value) { fp(data, value); }
};
// strings
template<> struct stringify<char*> {
const char *value;
operator const char*() const { return value; }
stringify(char *value) : value(value) {}
};
template<> struct stringify<const char*> {
const char *value;
operator const char*() const { return value; }
stringify(const char *value) : value(value) {}
};
template<> struct stringify<string> {
const string &value;
operator const char*() const { return value; }
stringify(const string &value) : value(value) {}
};
template<> struct stringify<const string&> {
const string &value;
operator const char*() const { return value; }
stringify(const string &value) : value(value) {}
};
template<> struct stringify<cstring> {
const char *value;
operator const char*() const { return value; }
stringify(const cstring &value) : value(value) {}
};
template<> struct stringify<const cstring&> {
const char *value;
operator const char*() const { return value; }
stringify(const cstring &value) : value(value) {}
};
#if defined(QSTRING_H)
template<> struct stringify<QString> {
const QString &value;
operator const char*() const { return value.toUtf8().constData(); }
stringify(const QString &value) : value(value) {}
};
template<> struct stringify<const QString&> {
const QString &value;
operator const char*() const { return value.toUtf8().constData(); }
stringify(const QString &value) : value(value) {}
};
string::operator QString() const {
return QString::fromUtf8(*this);
}
#endif
//
template<typename T> stringify<T> make_string(T value) {
return stringify<T>(std::forward<T>(value));
}
}
#endif

View File

@@ -1,69 +0,0 @@
#ifdef NALL_STRING_INTERNAL_HPP
namespace nall {
char chrlower(char c) {
return (c >= 'A' && c <= 'Z') ? c + ('a' - 'A') : c;
}
char chrupper(char c) {
return (c >= 'a' && c <= 'z') ? c - ('a' - 'A') : c;
}
int istrcmp(const char *str1, const char *str2) {
while(*str1) {
if(chrlower(*str1) != chrlower(*str2)) break;
str1++, str2++;
}
return (int)chrlower(*str1) - (int)chrlower(*str2);
}
bool strbegin(const char *str, const char *key) {
int i, ssl = strlen(str), ksl = strlen(key);
if(ksl > ssl) return false;
return (!memcmp(str, key, ksl));
}
bool istrbegin(const char *str, const char *key) {
int ssl = strlen(str), ksl = strlen(key);
if(ksl > ssl) return false;
for(int i = 0; i < ksl; i++) {
if(str[i] >= 'A' && str[i] <= 'Z') {
if(str[i] != key[i] && str[i]+0x20 != key[i])return false;
} else if(str[i] >= 'a' && str[i] <= 'z') {
if(str[i] != key[i] && str[i]-0x20 != key[i])return false;
} else {
if(str[i] != key[i])return false;
}
}
return true;
}
bool strend(const char *str, const char *key) {
int ssl = strlen(str), ksl = strlen(key);
if(ksl > ssl) return false;
return (!memcmp(str + ssl - ksl, key, ksl));
}
bool istrend(const char *str, const char *key) {
int ssl = strlen(str), ksl = strlen(key);
if(ksl > ssl) return false;
for(int i = ssl - ksl, z = 0; i < ssl; i++, z++) {
if(str[i] >= 'A' && str[i] <= 'Z') {
if(str[i] != key[z] && str[i]+0x20 != key[z])return false;
} else if(str[i] >= 'a' && str[i] <= 'z') {
if(str[i] != key[z] && str[i]-0x20 != key[z])return false;
} else {
if(str[i] != key[z])return false;
}
}
return true;
}
}
#endif

View File

@@ -1,64 +0,0 @@
#ifdef NALL_STRING_INTERNAL_HPP
namespace nall {
char* strlower(char *str) {
if(!str) return 0;
int i = 0;
while(str[i]) {
str[i] = chrlower(str[i]);
i++;
}
return str;
}
char* strupper(char *str) {
if(!str) return 0;
int i = 0;
while(str[i]) {
str[i] = chrupper(str[i]);
i++;
}
return str;
}
char* qstrlower(char *s) {
if(!s) return 0;
bool quoted = false;
while(*s) {
if(*s == '\"' || *s == '\'') quoted ^= 1;
if(quoted == false && *s >= 'A' && *s <= 'Z') *s += 0x20;
s++;
}
}
char* qstrupper(char *s) {
if(!s) return 0;
bool quoted = false;
while(*s) {
if(*s == '\"' || *s == '\'') quoted ^= 1;
if(quoted == false && *s >= 'a' && *s <= 'z') *s -= 0x20;
s++;
}
}
char* strtr(char *dest, const char *before, const char *after) {
if(!dest || !before || !after) return dest;
int sl = strlen(dest), bsl = strlen(before), asl = strlen(after);
if(bsl != asl || bsl == 0) return dest; //patterns must be the same length for 1:1 replace
for(unsigned i = 0; i < sl; i++) {
for(unsigned l = 0; l < bsl; l++) {
if(dest[i] == before[l]) {
dest[i] = after[l];
break;
}
}
}
return dest;
}
}
#endif

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