mirror of
https://github.com/ares-emulator/ares.git
synced 2026-11-10 01:11:44 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
032e924495 | ||
|
|
b389d17c9a | ||
|
|
d59ae34e12 | ||
|
|
85f2e9a6d4 | ||
|
|
019fc1a2c6 | ||
|
|
84e98833ca | ||
|
|
d4751c5244 | ||
|
|
ab345ff20c | ||
|
|
c495c132a7 | ||
|
|
ef746bbda4 | ||
|
|
94b2538af5 | ||
|
|
7f404e6edb | ||
|
|
47dffcae85 | ||
|
|
be625cc0fb | ||
|
|
4cb8b51606 | ||
|
|
87cb164f7c | ||
|
|
c1318961d8 | ||
|
|
791e64951b | ||
|
|
27af50099f | ||
|
|
fbd52c7e5f | ||
|
|
36795e8061 | ||
|
|
ec8350794a | ||
|
|
4545e7c62d | ||
|
|
3302398907 | ||
|
|
189e707594 | ||
|
|
9a8a54c75e | ||
|
|
d418eda97c | ||
|
|
5dbd5f4d0f | ||
|
|
d6001a2df4 | ||
|
|
bd61432322 | ||
|
|
0611fefefa | ||
|
|
6ac7b733bd | ||
|
|
a1e4c67a05 | ||
|
|
73ebe093b8 | ||
|
|
c3f9d421da | ||
|
|
689fc49047 | ||
|
|
cb97d98ad2 | ||
|
|
3cb04b101b | ||
|
|
5d273c5265 | ||
|
|
8703d57030 | ||
|
|
9ad8b7eaac | ||
|
|
76553756a2 | ||
|
|
4fd20f0ae0 | ||
|
|
bb4db22a7d | ||
|
|
67c13f749f | ||
|
|
616372e96b | ||
|
|
bba597fc6f | ||
|
|
abe639ea91 |
No files matched your search
@@ -1,3 +1,5 @@
|
||||
purify/*.o
|
||||
purify/purify
|
||||
purify/analyze-gba
|
||||
ananke/ananke.o
|
||||
ananke/libananke.so
|
||||
@@ -0,0 +1,51 @@
|
||||
include nall/Makefile
|
||||
include phoenix/Makefile
|
||||
|
||||
path := /usr/local/lib
|
||||
flags := -I. -O3 -fomit-frame-pointer
|
||||
ifeq ($(arch),win32)
|
||||
flags := -m32 $(flags)
|
||||
endif
|
||||
|
||||
all:
|
||||
$(cpp) $(flags) -fPIC -o ananke.o -c ananke.cpp
|
||||
ifeq ($(platform),x)
|
||||
$(cpp) $(flags) -shared -Wl,-soname,libananke.so.1 -o libananke.so ananke.o
|
||||
else ifeq ($(platform),win)
|
||||
$(cpp) $(flags) -fPIC -o phoenix.o -c phoenix/phoenix.cpp $(phoenixflags)
|
||||
$(cpp) $(flags) -shared -o phoenix.dll phoenix.o $(phoenixlink)
|
||||
$(cpp) $(flags) -shared -o ananke.dll ananke.o -L. -lphoenix
|
||||
endif
|
||||
|
||||
resource: force
|
||||
sourcery resource/resource.bml resource/resource.cpp resource/resource.hpp
|
||||
|
||||
clean:
|
||||
-@$(call delete,*.o)
|
||||
-@$(call delete,*.so)
|
||||
|
||||
install: uninstall
|
||||
ifeq ($(platform),x)
|
||||
if [ ! -d ~/.config/ananke ]; then mkdir ~/.config/ananke; fi
|
||||
sudo cp libananke.so $(path)/libananke.so.1
|
||||
sudo ln -s $(path)/libananke.so.1 $(path)/libananke.so
|
||||
endif
|
||||
|
||||
uninstall:
|
||||
ifeq ($(platform),x)
|
||||
if [ -f $(path)/libananke.so ]; then sudo rm $(path)/libananke.so; fi
|
||||
if [ -f $(path)/libananke.so.1 ]; then sudo rm $(path)/libananke.so.1; fi
|
||||
endif
|
||||
|
||||
sync:
|
||||
ifeq ($(shell id -un),byuu)
|
||||
if [ -d ./nall ]; then rm -r ./nall; fi
|
||||
if [ -d ./phoenix ]; then rm -r ./phoenix; fi
|
||||
cp -r ../nall ./nall
|
||||
cp -r ../phoenix ./phoenix
|
||||
rm -r nall/test
|
||||
rm -r phoenix/nall
|
||||
rm -r phoenix/test
|
||||
endif
|
||||
|
||||
force:
|
||||
@@ -0,0 +1,143 @@
|
||||
#include <nall/nall.hpp>
|
||||
#include <nall/beat/patch.hpp>
|
||||
#include "heuristics/famicom.hpp"
|
||||
#include "heuristics/super-famicom.hpp"
|
||||
#include "heuristics/game-boy.hpp"
|
||||
#include "heuristics/game-boy-advance.hpp"
|
||||
using namespace nall;
|
||||
|
||||
#include <phoenix/phoenix.hpp>
|
||||
using namespace phoenix;
|
||||
|
||||
namespace Database {
|
||||
#include "database/super-famicom.hpp"
|
||||
#include "database/sufami-turbo.hpp"
|
||||
#include "database/bsx-satellaview.hpp"
|
||||
};
|
||||
|
||||
struct Ananke {
|
||||
#include "configuration.cpp"
|
||||
|
||||
struct Information {
|
||||
string path; //path to selected file
|
||||
string name; //name of selected file (inside of archive if .zip)
|
||||
string archive; //pathname of archive
|
||||
string manifest; //manifest from successfully applied patch
|
||||
} information;
|
||||
|
||||
//archive.cpp
|
||||
vector<uint8_t> extractROM();
|
||||
vector<uint8_t> extractFile(const string &filename);
|
||||
|
||||
//patch.cpp
|
||||
void applyBeatPatch(vector<uint8_t> &buffer);
|
||||
|
||||
//famicom.cpp
|
||||
void copyFamicomSaves(const string &pathname);
|
||||
string createFamicomHeuristic(vector<uint8_t> &buffer);
|
||||
string openFamicom(vector<uint8_t> &buffer);
|
||||
|
||||
//super-famicom.cpp
|
||||
void copySuperFamicomSaves(const string &pathname);
|
||||
string createSuperFamicomDatabase(vector<uint8_t> &buffer, Markup::Node &document, const string &manifest);
|
||||
string createSuperFamicomHeuristic(vector<uint8_t> &buffer);
|
||||
void createSuperFamicomHeuristicFirmware(vector<uint8_t> &buffer, const string &pathname, bool firmware_appended);
|
||||
string openSuperFamicom(vector<uint8_t> &buffer);
|
||||
|
||||
//sufami-turbo.cpp
|
||||
void copySufamiTurboSaves(const string &pathname);
|
||||
string createSufamiTurboDatabase(vector<uint8_t> &buffer, Markup::Node &document, const string &manifest);
|
||||
string createSufamiTurboHeuristic(vector<uint8_t> &buffer);
|
||||
string openSufamiTurbo(vector<uint8_t> &buffer);
|
||||
|
||||
//bsx-satellaview.cpp
|
||||
string createBsxSatellaviewDatabase(vector<uint8_t> &buffer, Markup::Node &document, const string &manifest);
|
||||
string createBsxSatellaviewHeuristic(vector<uint8_t> &buffer);
|
||||
string openBsxSatellaview(vector<uint8_t> &buffer);
|
||||
|
||||
//game-boy.cpp
|
||||
void copyGameBoySaves(const string &pathname);
|
||||
string createGameBoyHeuristic(vector<uint8_t> &buffer);
|
||||
string openGameBoy(vector<uint8_t> &buffer);
|
||||
|
||||
//game-boy-advance.cpp
|
||||
void copyGameBoyAdvanceSaves(const string &pathname);
|
||||
string createGameBoyAdvanceHeuristic(vector<uint8_t> &buffer);
|
||||
string openGameBoyAdvance(vector<uint8_t> &buffer);
|
||||
|
||||
static bool supported(const string &filename);
|
||||
string open(string filename = "");
|
||||
};
|
||||
|
||||
#include "resource/resource.cpp"
|
||||
#include "file-dialog.cpp"
|
||||
#include "archive.cpp"
|
||||
#include "patch.cpp"
|
||||
#include "famicom.cpp"
|
||||
#include "super-famicom.cpp"
|
||||
#include "sufami-turbo.cpp"
|
||||
#include "bsx-satellaview.cpp"
|
||||
#include "game-boy.cpp"
|
||||
#include "game-boy-advance.cpp"
|
||||
|
||||
FileDialog *fileDialog = nullptr;
|
||||
|
||||
bool Ananke::supported(const string &filename) {
|
||||
string extension = nall::extension(filename);
|
||||
|
||||
if(extension == "fc" ) return true;
|
||||
if(extension == "nes") return true;
|
||||
if(extension == "sfc") return true;
|
||||
if(extension == "smc") return true;
|
||||
if(extension == "st" ) return true;
|
||||
if(extension == "bs" ) return true;
|
||||
if(extension == "gb" ) return true;
|
||||
if(extension == "gbc") return true;
|
||||
if(extension == "gba") return true;
|
||||
if(extension == "zip") return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
string Ananke::open(string filename) {
|
||||
if(filename.empty()) {
|
||||
if(!fileDialog) fileDialog = new FileDialog;
|
||||
fileDialog->setPath(config.path);
|
||||
filename = fileDialog->open();
|
||||
}
|
||||
|
||||
if(filename.empty()) return "";
|
||||
|
||||
information.path = dir(filename);
|
||||
information.name = notdir(filename);
|
||||
config.path = information.path; //remember last used directory
|
||||
|
||||
vector<uint8_t> buffer;
|
||||
if(filename.endswith(".zip")) {
|
||||
information.archive = filename;
|
||||
buffer = extractROM();
|
||||
} else {
|
||||
buffer = file::read(filename);
|
||||
}
|
||||
if(buffer.size() == 0) return ""; //failed to read file
|
||||
|
||||
applyBeatPatch(buffer);
|
||||
|
||||
if(information.name.endswith(".fc") || information.name.endswith(".nes")) return openFamicom(buffer);
|
||||
if(information.name.endswith(".sfc") || information.name.endswith(".smc")) return openSuperFamicom(buffer);
|
||||
if(information.name.endswith(".st")) return openSufamiTurbo(buffer);
|
||||
if(information.name.endswith(".bs")) return openBsxSatellaview(buffer);
|
||||
if(information.name.endswith(".gb") || information.name.endswith(".gbc")) return openGameBoy(buffer);
|
||||
if(information.name.endswith(".gba")) return openGameBoyAdvance(buffer);
|
||||
return "";
|
||||
}
|
||||
|
||||
extern "C" string ananke_browse(const string &filename) {
|
||||
Ananke ananke;
|
||||
return ananke.open();
|
||||
}
|
||||
|
||||
extern "C" string ananke_open(const string &filename) {
|
||||
Ananke ananke;
|
||||
return ananke.open(filename);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
vector<uint8_t> Ananke::extractROM() {
|
||||
unzip archive;
|
||||
if(archive.open(information.archive)) {
|
||||
for(auto &file : archive.file) {
|
||||
if(
|
||||
file.name.endswith(".fc") || file.name.endswith(".nes")
|
||||
|| file.name.endswith(".sfc") || file.name.endswith(".smc")
|
||||
|| file.name.endswith(".gb") || file.name.endswith(".gbc")
|
||||
|| file.name.endswith(".gba")
|
||||
) {
|
||||
information.name = notdir(file.name);
|
||||
return archive.extract(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
return vector<uint8_t>();
|
||||
}
|
||||
|
||||
vector<uint8_t> Ananke::extractFile(const string &filename) {
|
||||
unzip archive;
|
||||
if(archive.open(information.archive)) {
|
||||
for(auto &file : archive.file) {
|
||||
if(notdir(file.name) == filename) {
|
||||
return archive.extract(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
return vector<uint8_t>();
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
string Ananke::createBsxSatellaviewDatabase(vector<uint8_t> &buffer, Markup::Node &document, const string &manifest) {
|
||||
string pathname = {
|
||||
userpath(), "Emulation/BS-X Satellaview/",
|
||||
document["release/information/name"].text(),
|
||||
" (", document["release/information/region"].text(), ")",
|
||||
" (", document["release/information/revision"].text(), ")",
|
||||
".bs/"
|
||||
};
|
||||
directory::create(pathname);
|
||||
|
||||
//strip "release" root node from database entry (since a single game manifest isn't part of a database)
|
||||
string markup = manifest;
|
||||
markup.replace("\n ", "\n");
|
||||
markup.replace("information", "\ninformation");
|
||||
markup.ltrim<1>("release\n");
|
||||
|
||||
file::write({pathname, "manifest.bml"}, markup);
|
||||
file::write({pathname, "program.rom"}, buffer);
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
string Ananke::createBsxSatellaviewHeuristic(vector<uint8_t> &buffer) {
|
||||
string pathname = {
|
||||
userpath(), "Emulation/BS-X Satellaview/",
|
||||
nall::basename(information.name),
|
||||
" (!).bs/"
|
||||
};
|
||||
directory::create(pathname);
|
||||
|
||||
file::write({pathname, "manifest.bml"}, {
|
||||
"cartridge\n"
|
||||
" rom name=program.rom size=0x", hex(buffer.size()), " type=FlashROM\n",
|
||||
"\n",
|
||||
"information\n",
|
||||
" title: ", nall::basename(information.name), "\n"
|
||||
});
|
||||
file::write({pathname, "program.rom"}, buffer);
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
string Ananke::openBsxSatellaview(vector<uint8_t> &buffer) {
|
||||
string sha256 = nall::sha256(buffer.data(), buffer.size());
|
||||
|
||||
string databaseText = string::read({configpath(), "ananke/database/BS-X Satellaview.bml"}).strip();
|
||||
if(databaseText.empty()) databaseText = string{Database::BsxSatellaview}.strip();
|
||||
lstring databaseItem = databaseText.split("\n\n");
|
||||
|
||||
for(auto &item : databaseItem) {
|
||||
item.append("\n");
|
||||
auto document = Markup::Document(item);
|
||||
|
||||
if(document["release/information/sha256"].text() == sha256) {
|
||||
return createBsxSatellaviewDatabase(buffer, document, item);
|
||||
}
|
||||
}
|
||||
|
||||
return createBsxSatellaviewHeuristic(buffer);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
struct Configuration : configuration {
|
||||
string path;
|
||||
|
||||
Configuration() {
|
||||
append(path = userpath(), "Path");
|
||||
directory::create({configpath(), "ananke/"});
|
||||
load({configpath(), "ananke/settings.cfg"});
|
||||
}
|
||||
|
||||
~Configuration() {
|
||||
save({configpath(), "ananke/settings.cfg"});
|
||||
}
|
||||
} config;
|
||||
@@ -0,0 +1,19 @@
|
||||
string BsxSatellaview = R"(
|
||||
|
||||
database revision=2013-01-12
|
||||
|
||||
release
|
||||
cartridge
|
||||
rom name=program.rom size=0x80000 type=MaskROM
|
||||
information
|
||||
title: 鮫亀 キャラカセット
|
||||
name: Same Game - Character Cassette
|
||||
region: JP
|
||||
revision: 1.0
|
||||
board: BSMC-CR-01
|
||||
serial: BSMC-ZS5J-JPN
|
||||
sha256: 80c34b50817d58820bc8c88d2d9fa462550b4a76372e19c6467cbfbc8cf5d9ef
|
||||
configuration
|
||||
rom name=program.rom size=0x80000 type=MaskROM
|
||||
|
||||
)";
|
||||
@@ -0,0 +1,162 @@
|
||||
string SufamiTurbo = R"(
|
||||
|
||||
database revision=2013-01-12
|
||||
|
||||
release
|
||||
cartridge linkable
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x800
|
||||
information
|
||||
title: SDウルトラバトル ウルトラマン伝説
|
||||
name: SD Ultra Battle - Ultraman Densetsu
|
||||
region: JP
|
||||
revision: 1.0
|
||||
serial: SFT-0101-JPN
|
||||
sha256: 2bb55214fb668ca603d7b944b14f105dfb10b987a8902d420fe4ae1cb69c1d4a
|
||||
configuration
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x800
|
||||
linkable
|
||||
|
||||
release
|
||||
cartridge linkable
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x800
|
||||
information
|
||||
title: SDウルトラバトル セブン伝説
|
||||
name: SD Ultra Battle - Seven Densetsu
|
||||
region: JP
|
||||
revision: 1.0
|
||||
serial: SFT-0102-JPN
|
||||
sha256: 2fec5f2bc7dee010af10569a3d2bc18715a79a126940800c3eade5abbd625e3f
|
||||
configuration
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x800
|
||||
linkable
|
||||
|
||||
release
|
||||
cartridge linkable
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x800
|
||||
information
|
||||
title: ポイポイ忍者ワールド
|
||||
name: Poi Poi Ninja World
|
||||
region: JP
|
||||
revision: 1.0
|
||||
serial: SFT-0103-JPN
|
||||
sha256: 602b20b788640f5743487108a10f3f77bca5ce2d24208b25b1ca498a96eb0d69
|
||||
configuration
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x800
|
||||
linkable
|
||||
|
||||
release
|
||||
cartridge linkable
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x2000
|
||||
information
|
||||
title: SDガンダムジェネレーション 一年戦争記
|
||||
name: SD Gundam Generation - Ichinen Sensouki
|
||||
region: JP
|
||||
revision: 1.0
|
||||
serial: SFT-0104-JPN
|
||||
sha256: 3e82215bed08274874b30d461fc4a965c6bca932229da5d46d56e36f484d65eb
|
||||
configuration
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x2000
|
||||
linkable
|
||||
|
||||
release
|
||||
cartridge linkable
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x2000
|
||||
information
|
||||
title: SDガンダムジェネレーション グリプス戦記
|
||||
name: SD Gundam Generation - Grips Senki
|
||||
region: JP
|
||||
revision: 1.0
|
||||
serial: SFT-0105-JPN
|
||||
sha256: 8547a08ed11fe408eac282a90ac46654bd2e5f49bda3aec8e5edf166a0a4b9af
|
||||
configuration
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x2000
|
||||
linkable
|
||||
|
||||
release
|
||||
cartridge
|
||||
rom name=program.rom size=0x80000
|
||||
information
|
||||
title: ゲゲゲの鬼太郎 妖怪ドンジャラ
|
||||
name: Gegege no Kitarou - Youkai Donjara
|
||||
region: JP
|
||||
revision: 1.0
|
||||
serial: SFT-0106-JPN
|
||||
sha256: d93b3a570e7cf343f680ab0768a50b77e3577f9c555007e2de3decd6bc4765c8
|
||||
configuration
|
||||
rom name=program.rom size=0x80000
|
||||
|
||||
release
|
||||
cartridge linkable
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x2000
|
||||
information
|
||||
title: SDガンダムジェネレーション アクシズ戦記
|
||||
name: SD Gundam Generation - Axis Senki
|
||||
region: JP
|
||||
revision: 1.0
|
||||
serial: SFT-0107-JPN
|
||||
sha256: 2a9d7c9a61318861028a73ca03e32a48cff162d76cba36fbaab8690b212efe9b
|
||||
configuration
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x2000
|
||||
linkable
|
||||
|
||||
release
|
||||
cartridge linkable
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x2000
|
||||
information
|
||||
title: SDガンダムジェネレーション バビロニア建国戦記
|
||||
name: SD Gundam Generation - Babylonia Kenkoku Senki
|
||||
region: JP
|
||||
revision: 1.0
|
||||
serial: SFT-0108-JPN
|
||||
sha256: 60ac017c18f534e8cf24ca7f38e22ce92db95ea6c30b2d59d76f13c4f1c8a6e4
|
||||
configuration
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x2000
|
||||
linkable
|
||||
|
||||
release
|
||||
cartridge linkable
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x2000
|
||||
information
|
||||
title: SDガンダムジェネレーション ザンスカール戦記
|
||||
name: SD Gundam Generation - Zanscar Senki
|
||||
region: JP
|
||||
revision: 1.0
|
||||
serial: SFT-0110-JPN
|
||||
sha256: 5951a58a91d8e397d0a237ccc2b1248e17c7312cb9cc11cbc350200a97b4e021
|
||||
configuration
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x2000
|
||||
linkable
|
||||
|
||||
release
|
||||
cartridge linkable
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x800
|
||||
information
|
||||
title: SDガンダムジェネレーション コロニー格闘記
|
||||
name: SD Gundam Generation - Colony Kakutouki
|
||||
region: JP
|
||||
revision: 1.0
|
||||
serial: SFT-0111-JPN
|
||||
sha256: e639b5d5d722432b6809ccc6801dc584e1a3016379f34b335ed2dfa73b1ebf69
|
||||
configuration
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x800
|
||||
linkable
|
||||
|
||||
)";
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,32 @@
|
||||
void Ananke::copyFamicomSaves(const string &pathname) {
|
||||
if(!file::exists({pathname, "save.ram"})) {
|
||||
if(file::exists({information.path, nall::basename(information.name), ".sav"})) {
|
||||
file::copy({information.path, nall::basename(information.name), ".srm"}, {pathname, "save.ram"});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string Ananke::createFamicomHeuristic(vector<uint8_t> &buffer) {
|
||||
string pathname = {
|
||||
userpath(), "Emulation/Famicom/",
|
||||
nall::basename(information.name),
|
||||
" (!).fc/"
|
||||
};
|
||||
directory::create(pathname);
|
||||
|
||||
FamicomCartridge info(buffer.data(), buffer.size());
|
||||
string markup = info.markup();
|
||||
markup.append("\ninformation\n title: ", nall::basename(information.name), "\n");
|
||||
if(!information.manifest.empty()) markup = information.manifest; //override with embedded beat manifest, if one exists
|
||||
|
||||
file::write({pathname, "manifest.bml"}, markup);
|
||||
file::write({pathname, "program.rom"}, buffer.data() + 16, info.prgrom);
|
||||
if(info.chrrom > 0) file::write({pathname, "character.rom"}, buffer.data() + 16 + info.prgrom, info.chrrom);
|
||||
|
||||
copyFamicomSaves(pathname);
|
||||
return pathname;
|
||||
}
|
||||
|
||||
string Ananke::openFamicom(vector<uint8_t> &buffer) {
|
||||
return createFamicomHeuristic(buffer);
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
struct FileDialog : Window {
|
||||
VerticalLayout layout;
|
||||
HorizontalLayout pathLayout;
|
||||
LineEdit pathEdit;
|
||||
Button homeButton;
|
||||
Button upButton;
|
||||
ListView fileList;
|
||||
HorizontalLayout controlLayout;
|
||||
Label filterLabel;
|
||||
Button openButton;
|
||||
|
||||
string open() {
|
||||
setModal();
|
||||
setVisible();
|
||||
fileList.setFocused();
|
||||
filename = "";
|
||||
bool backspace = false;
|
||||
|
||||
dialogActive = true;
|
||||
while(dialogActive) {
|
||||
OS::processEvents();
|
||||
if(Keyboard::pressed(Keyboard::Scancode::Escape)) onClose();
|
||||
if(Keyboard::pressed(Keyboard::Scancode::Backspace)) {
|
||||
if(backspace == false) {
|
||||
backspace = true;
|
||||
if(fileList.focused()) upButton.onActivate();
|
||||
}
|
||||
} else {
|
||||
backspace = false;
|
||||
}
|
||||
usleep(20 * 1000);
|
||||
}
|
||||
|
||||
return filename;
|
||||
}
|
||||
|
||||
void setPath(const string &path) {
|
||||
pathname = string{path}.transform("\\", "/");
|
||||
if(pathname.empty()) pathname = userpath();
|
||||
if(pathname.endswith("/") == false) pathname.append("/");
|
||||
pathEdit.setText(pathname);
|
||||
|
||||
fileList.reset();
|
||||
filenameList.reset();
|
||||
|
||||
lstring folders = directory::ifolders(pathname);
|
||||
for(auto &folder : folders) {
|
||||
fileList.append(string{folder}.rtrim<1>("/"));
|
||||
fileList.setImage(filenameList.size(), 0, {resource::folder, sizeof resource::folder});
|
||||
filenameList.append({pathname, folder});
|
||||
}
|
||||
|
||||
lstring files = directory::ifiles(pathname);
|
||||
for(auto &file : files) {
|
||||
if(Ananke::supported(file) == false) continue; //ignore unsupported extensions
|
||||
fileList.append(file);
|
||||
if(extension(file) == "zip") {
|
||||
fileList.setImage(filenameList.size(), 0, {resource::archive, sizeof resource::archive});
|
||||
} else {
|
||||
fileList.setImage(filenameList.size(), 0, {resource::file, sizeof resource::file});
|
||||
}
|
||||
filenameList.append({pathname, file});
|
||||
}
|
||||
|
||||
fileList.setSelection(0);
|
||||
fileList.setSelected();
|
||||
fileList.setFocused();
|
||||
}
|
||||
|
||||
FileDialog() {
|
||||
setFrameGeometry({64, 64, 480, 600});
|
||||
setTitle("Load Image");
|
||||
|
||||
layout.setMargin(5);
|
||||
homeButton.setImage({resource::home, sizeof resource::home});
|
||||
upButton.setImage({resource::up, sizeof resource::up});
|
||||
filterLabel.setText("Filter: *.fc, *.sfc, *.st, *.bs, *.gb, *.gbc, *.gba, *.nes, *.smc, *.zip");
|
||||
openButton.setText("Open");
|
||||
|
||||
append(layout);
|
||||
layout.append(pathLayout, {~0, 0}, 5);
|
||||
pathLayout.append(pathEdit, {~0, 0}, 5);
|
||||
pathLayout.append(homeButton, {28, 28}, 5);
|
||||
pathLayout.append(upButton, {28, 28});
|
||||
layout.append(fileList, {~0, ~0}, 5);
|
||||
layout.append(controlLayout, {~0, 0});
|
||||
controlLayout.append(filterLabel, {~0, 0}, 5);
|
||||
controlLayout.append(openButton, {80, 0});
|
||||
|
||||
pathEdit.onActivate = [&] {
|
||||
string path = pathEdit.text();
|
||||
setPath(path);
|
||||
};
|
||||
|
||||
homeButton.onActivate = [&] {
|
||||
setPath(userpath());
|
||||
};
|
||||
|
||||
upButton.onActivate = [&] {
|
||||
setPath(parentdir(pathname));
|
||||
};
|
||||
|
||||
fileList.onActivate = openButton.onActivate = [&] {
|
||||
if(fileList.selected() == false) return;
|
||||
string name = filenameList(fileList.selection());
|
||||
if(name.empty()) return;
|
||||
|
||||
if(name.endswith("/")) return setPath(name);
|
||||
filename = name;
|
||||
onClose();
|
||||
};
|
||||
|
||||
onClose = [&] {
|
||||
dialogActive = false;
|
||||
setModal(false);
|
||||
setVisible(false);
|
||||
};
|
||||
}
|
||||
|
||||
private:
|
||||
bool dialogActive;
|
||||
string pathname;
|
||||
string filename;
|
||||
lstring filenameList;
|
||||
};
|
||||
@@ -0,0 +1,37 @@
|
||||
void Ananke::copyGameBoyAdvanceSaves(const string &pathname) {
|
||||
if(!file::exists({pathname, "save.ram"})) {
|
||||
if(file::exists({information.path, nall::basename(information.name), ".sav"})) {
|
||||
file::copy({information.path, nall::basename(information.name), ".sav"}, {pathname, "save.ram"});
|
||||
}
|
||||
}
|
||||
|
||||
if(!file::exists({pathname, "rtc.ram"})) {
|
||||
if(file::exists({information.path, nall::basename(information.name), ".rtc"})) {
|
||||
file::copy({information.path, nall::basename(information.name), ".rtc"}, {pathname, "rtc.ram"});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string Ananke::createGameBoyAdvanceHeuristic(vector<uint8_t> &buffer) {
|
||||
string pathname = {
|
||||
userpath(), "Emulation/Game Boy Advance/",
|
||||
nall::basename(information.name),
|
||||
" (!).gba/"
|
||||
};
|
||||
directory::create(pathname);
|
||||
|
||||
GameBoyAdvanceCartridge info(buffer.data(), buffer.size());
|
||||
string markup = info.markup;
|
||||
markup.append("\ninformation\n title: ", nall::basename(information.name), "\n");
|
||||
if(!information.manifest.empty()) markup = information.manifest; //override with embedded beat manifest, if one exists
|
||||
|
||||
file::write({pathname, "manifest.bml"}, markup);
|
||||
file::write({pathname, "program.rom"}, buffer);
|
||||
|
||||
copyGameBoyAdvanceSaves(pathname);
|
||||
return pathname;
|
||||
}
|
||||
|
||||
string Ananke::openGameBoyAdvance(vector<uint8_t> &buffer) {
|
||||
return createGameBoyAdvanceHeuristic(buffer);
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
void Ananke::copyGameBoySaves(const string &pathname) {
|
||||
if(!file::exists({pathname, "save.ram"})) {
|
||||
if(file::exists({information.path, nall::basename(information.name), ".sav"})) {
|
||||
file::copy({information.path, nall::basename(information.name), ".sav"}, {pathname, "save.ram"});
|
||||
}
|
||||
}
|
||||
|
||||
if(!file::exists({pathname, "rtc.ram"})) {
|
||||
if(file::exists({information.path, nall::basename(information.name), ".rtc"})) {
|
||||
file::copy({information.path, nall::basename(information.name), ".rtc"}, {pathname, "rtc.ram"});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string Ananke::createGameBoyHeuristic(vector<uint8_t> &buffer) {
|
||||
GameBoyCartridge info(buffer.data(), buffer.size());
|
||||
|
||||
string pathname = {
|
||||
userpath(),
|
||||
"Emulation/Game Boy", (info.info.cgb ? " Color" : ""), "/",
|
||||
nall::basename(information.name),
|
||||
" (!).", (info.info.cgb ? "gbc" : "gb"), "/"
|
||||
};
|
||||
directory::create(pathname);
|
||||
|
||||
string markup = info.markup;
|
||||
markup.append("\ninformation\n title: ", nall::basename(information.name), "\n");
|
||||
if(!information.manifest.empty()) markup = information.manifest; //override with embedded beat manifest, if one exists
|
||||
|
||||
file::write({pathname, "manifest.bml"}, markup);
|
||||
file::write({pathname, "program.rom"}, buffer);
|
||||
|
||||
copyGameBoySaves(pathname);
|
||||
return pathname;
|
||||
}
|
||||
|
||||
string Ananke::openGameBoy(vector<uint8_t> &buffer) {
|
||||
return createGameBoyHeuristic(buffer);
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
#ifndef NALL_EMULATION_FAMICOM_HPP
|
||||
#define NALL_EMULATION_FAMICOM_HPP
|
||||
|
||||
#include <nall/sha256.hpp>
|
||||
#include <nall/string.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct FamicomCartridge {
|
||||
string markup;
|
||||
inline FamicomCartridge(const uint8_t *data, unsigned size);
|
||||
|
||||
//private:
|
||||
unsigned mapper;
|
||||
unsigned mirror;
|
||||
unsigned prgrom;
|
||||
unsigned prgram;
|
||||
unsigned chrrom;
|
||||
unsigned chrram;
|
||||
};
|
||||
|
||||
FamicomCartridge::FamicomCartridge(const uint8_t *data, unsigned size) {
|
||||
markup = "";
|
||||
if(size < 16) return;
|
||||
if(data[0] != 'N') return;
|
||||
if(data[1] != 'E') return;
|
||||
if(data[2] != 'S') return;
|
||||
if(data[3] != 26) return;
|
||||
|
||||
mapper = ((data[7] >> 4) << 4) | (data[6] >> 4);
|
||||
mirror = ((data[6] & 0x08) >> 2) | (data[6] & 0x01);
|
||||
prgrom = data[4] * 0x4000;
|
||||
chrrom = data[5] * 0x2000;
|
||||
prgram = 0u;
|
||||
chrram = chrrom == 0u ? 8192u : 0u;
|
||||
|
||||
markup.append("cartridge\n");
|
||||
|
||||
switch(mapper) {
|
||||
default:
|
||||
markup.append(" board type=NES-NROM-256\n");
|
||||
markup.append(" mirror mode=", mirror == 0 ? "horizontal" : "vertical", "\n");
|
||||
break;
|
||||
|
||||
case 1:
|
||||
markup.append(" board type=NES-SXROM\n");
|
||||
markup.append(" chip type=MMC1B2\n");
|
||||
prgram = 8192;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
markup.append(" board type=NES-UOROM\n");
|
||||
markup.append(" mirror mode=", mirror == 0 ? "horizontal" : "vertical", "\n");
|
||||
break;
|
||||
|
||||
case 3:
|
||||
markup.append(" board type=NES-CNROM\n");
|
||||
markup.append(" mirror mode=", mirror == 0 ? "horizontal" : "vertical", "\n");
|
||||
break;
|
||||
|
||||
case 4:
|
||||
//MMC3
|
||||
markup.append(" board type=NES-TLROM\n");
|
||||
markup.append(" chip type=MMC3B\n");
|
||||
prgram = 8192;
|
||||
//MMC6
|
||||
//markup.append(" board type=NES-HKROM\n");
|
||||
//markup.append(" chip type=MMC6n");
|
||||
//prgram = 1024;
|
||||
break;
|
||||
|
||||
case 5:
|
||||
markup.append(" board type=NES-ELROM\n");
|
||||
markup.append(" chip type=MMC5\n");
|
||||
prgram = 65536;
|
||||
break;
|
||||
|
||||
case 7:
|
||||
markup.append(" board type=NES-AOROM\n");
|
||||
break;
|
||||
|
||||
case 9:
|
||||
markup.append(" board type=NES-PNROM\n");
|
||||
markup.append(" chip type=MMC2\n");
|
||||
prgram = 8192;
|
||||
break;
|
||||
|
||||
case 10:
|
||||
markup.append(" board type=NES-FKROM\n");
|
||||
markup.append(" chip type=MMC4\n");
|
||||
prgram = 8192;
|
||||
break;
|
||||
|
||||
case 16:
|
||||
markup.append(" board type=BANDAI-FCG\n");
|
||||
markup.append(" chip type=LZ93D50\n");
|
||||
break;
|
||||
|
||||
case 21:
|
||||
case 23:
|
||||
case 25:
|
||||
//VRC4
|
||||
markup.append(" board type=KONAMI-VRC-4\n");
|
||||
markup.append(" chip type=VRC4\n");
|
||||
markup.append(" pinout a0=1 a1=0\n");
|
||||
prgram = 8192;
|
||||
break;
|
||||
|
||||
case 22:
|
||||
//VRC2
|
||||
markup.append(" board type=KONAMI-VRC-2\n");
|
||||
markup.append(" chip type=VRC2\n");
|
||||
markup.append(" pinout a0=0 a1=1\n");
|
||||
break;
|
||||
|
||||
case 24:
|
||||
markup.append(" board type=KONAMI-VRC-6\n");
|
||||
markup.append(" chip type=VRC6\n");
|
||||
break;
|
||||
|
||||
case 26:
|
||||
markup.append(" board type=KONAMI-VRC-6\n");
|
||||
markup.append(" chip type=VRC6\n");
|
||||
prgram = 8192;
|
||||
break;
|
||||
|
||||
case 34:
|
||||
markup.append(" board type=NES-BNROM\n");
|
||||
markup.append(" mirror mode=", mirror == 0 ? "horizontal" : "vertical", "\n");
|
||||
break;
|
||||
|
||||
case 66:
|
||||
markup.append(" board type=NES-GNROM\n");
|
||||
markup.append(" mirror mode=", mirror == 0 ? "horizontal" : "vertical", "\n");
|
||||
break;
|
||||
|
||||
case 69:
|
||||
markup.append(" board type=SUNSOFT-5B\n");
|
||||
markup.append(" chip type=5B\n");
|
||||
prgram = 8192;
|
||||
break;
|
||||
|
||||
case 73:
|
||||
markup.append(" board type=KONAMI-VRC-3\n");
|
||||
markup.append(" chip type=VRC3\n");
|
||||
markup.append(" mirror mode=", mirror == 0 ? "horizontal" : "vertical", "\n");
|
||||
prgram = 8192;
|
||||
break;
|
||||
|
||||
case 75:
|
||||
markup.append(" board type=KONAMI-VRC-1\n");
|
||||
markup.append(" chip type=VRC1\n");
|
||||
break;
|
||||
|
||||
case 85:
|
||||
markup.append(" board type=KONAMI-VRC-7\n");
|
||||
markup.append(" chip type=VRC7\n");
|
||||
prgram = 8192;
|
||||
break;
|
||||
}
|
||||
|
||||
markup.append(" prg\n");
|
||||
if(prgrom) markup.append(" rom name=program.rom size=0x", hex(prgrom), "\n");
|
||||
if(prgram) markup.append(" ram name=save.ram size=0x", hex(prgram), "\n");
|
||||
|
||||
markup.append(" chr\n");
|
||||
if(chrrom) markup.append(" rom name=character.rom size=0x", hex(chrrom), "\n");
|
||||
if(chrram) markup.append(" ram size=0x", hex(chrram), "\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,63 @@
|
||||
#ifndef NALL_EMULATION_GAME_BOY_ADVANCE_HPP
|
||||
#define NALL_EMULATION_GAME_BOY_ADVANCE_HPP
|
||||
|
||||
#include <nall/sha256.hpp>
|
||||
#include <nall/string.hpp>
|
||||
#include <nall/vector.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct GameBoyAdvanceCartridge {
|
||||
string markup;
|
||||
string identifiers;
|
||||
inline GameBoyAdvanceCartridge(const uint8_t *data, unsigned size);
|
||||
};
|
||||
|
||||
GameBoyAdvanceCartridge::GameBoyAdvanceCartridge(const uint8_t *data, unsigned size) {
|
||||
struct Identifier {
|
||||
string name;
|
||||
unsigned size;
|
||||
};
|
||||
vector<Identifier> idlist;
|
||||
idlist.append({"SRAM_V", 6});
|
||||
idlist.append({"SRAM_F_V", 8});
|
||||
idlist.append({"EEPROM_V", 8});
|
||||
idlist.append({"FLASH_V", 7});
|
||||
idlist.append({"FLASH512_V", 10});
|
||||
idlist.append({"FLASH1M_V", 9});
|
||||
|
||||
lstring list;
|
||||
for(auto &id : idlist) {
|
||||
for(signed n = 0; n < size - 16; n++) {
|
||||
if(!memcmp(data + n, (const char*)id.name, id.size)) {
|
||||
const char *p = (const char*)data + n + id.size;
|
||||
if(p[0] >= '0' && p[0] <= '9'
|
||||
&& p[1] >= '0' && p[1] <= '9'
|
||||
&& p[2] >= '0' && p[2] <= '9'
|
||||
) {
|
||||
char text[16];
|
||||
memcpy(text, data + n, id.size + 3);
|
||||
text[id.size + 3] = 0;
|
||||
list.appendonce(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
identifiers = list.concatenate(",");
|
||||
|
||||
markup = "";
|
||||
markup.append("cartridge\n");
|
||||
markup.append(" rom name=program.rom size=0x", hex(size), "\n");
|
||||
if(0);
|
||||
else if(identifiers.beginswith("SRAM_V" )) markup.append(" ram name=save.ram type=SRAM size=0x8000\n");
|
||||
else if(identifiers.beginswith("SRAM_F_V" )) markup.append(" ram name=save.ram type=FRAM size=0x8000\n");
|
||||
else if(identifiers.beginswith("EEPROM_V" )) markup.append(" ram name=save.ram type=EEPROM size=0x0\n");
|
||||
else if(identifiers.beginswith("FLASH_V" )) markup.append(" ram name=save.ram type=FlashROM size=0x10000\n");
|
||||
else if(identifiers.beginswith("FLASH512_V")) markup.append(" ram name=save.ram type=FlashROM size=0x10000\n");
|
||||
else if(identifiers.beginswith("FLASH1M_V" )) markup.append(" ram name=save.ram type=FlashROM size=0x20000\n");
|
||||
//if(identifiers.empty() == false) markup.append(" #detected: ", identifiers, "\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
Executable → Regular
+17
-13
@@ -1,10 +1,12 @@
|
||||
#ifndef NALL_GAMEBOY_CARTRIDGE_HPP
|
||||
#define NALL_GAMEBOY_CARTRIDGE_HPP
|
||||
#ifndef NALL_EMULATION_GAME_BOY_HPP
|
||||
#define NALL_EMULATION_GAME_BOY_HPP
|
||||
|
||||
#include <nall/sha256.hpp>
|
||||
#include <nall/string.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
class GameBoyCartridge {
|
||||
public:
|
||||
struct GameBoyCartridge {
|
||||
string markup;
|
||||
inline GameBoyCartridge(uint8_t *data, unsigned size);
|
||||
|
||||
@@ -18,6 +20,9 @@ public:
|
||||
|
||||
unsigned romsize;
|
||||
unsigned ramsize;
|
||||
|
||||
bool cgb;
|
||||
bool cgbonly;
|
||||
} info;
|
||||
};
|
||||
|
||||
@@ -48,6 +53,9 @@ GameBoyCartridge::GameBoyCartridge(uint8_t *romdata, unsigned romsize) {
|
||||
memcpy(romdata, header, 0x8000);
|
||||
}
|
||||
|
||||
info.cgb = (romdata[0x0143] & 0x80) == 0x80;
|
||||
info.cgbonly = (romdata[0x0143] & 0xc0) == 0xc0;
|
||||
|
||||
switch(romdata[0x0147]) {
|
||||
case 0x00: info.mapper = "none"; break;
|
||||
case 0x01: info.mapper = "MBC1"; break;
|
||||
@@ -100,15 +108,11 @@ GameBoyCartridge::GameBoyCartridge(uint8_t *romdata, unsigned romsize) {
|
||||
|
||||
if(info.mapper == "MBC2") info.ramsize = 512; //512 x 4-bit
|
||||
|
||||
markup.append("cartridge mapper=", info.mapper);
|
||||
if(info.rtc) markup.append(" rtc");
|
||||
if(info.rumble) markup.append(" rumble");
|
||||
markup.append("\n");
|
||||
|
||||
markup.append("\t" "rom size=", hex(romsize), "\n"); //TODO: trust/check info.romsize?
|
||||
|
||||
if(info.ramsize > 0)
|
||||
markup.append("\t" "ram size=", hex(info.ramsize), info.battery ? " non-volatile\n" : "\n");
|
||||
markup = "";
|
||||
markup.append("cartridge\n");
|
||||
markup.append(" board type=", info.mapper, "\n");
|
||||
markup.append(" rom name=program.rom size=0x", hex(romsize), "\n");
|
||||
if(info.ramsize > 0) markup.append(" ram name=save.ram size=0x", hex(info.ramsize), "\n");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#ifndef NALL_EMULATION_SATELLAVIEW_HPP
|
||||
#define NALL_EMULATION_SATELLAVIEW_HPP
|
||||
|
||||
#include <nall/sha256.hpp>
|
||||
#include <nall/string.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct SatellaviewCartridge {
|
||||
string markup;
|
||||
inline SatellaviewCartridge(const uint8_t *data, unsigned size);
|
||||
};
|
||||
|
||||
SatellaviewCartridge::SatellaviewCartridge(const uint8_t *data, unsigned size) {
|
||||
markup = "";
|
||||
|
||||
markup.append("<?xml version='1.0' encoding='UTF-8'?>\n");
|
||||
markup.append("<cartridge sha256='", sha256(data, size) ,"'>\n");
|
||||
markup.append(" <rom name='program.rom' size='0x", hex(size), "'/>\n");
|
||||
markup.append("</cartridge>\n");
|
||||
markup.transform("'", "\"");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,33 @@
|
||||
#ifndef NALL_EMULATION_SUFAMI_TURBO_HPP
|
||||
#define NALL_EMULATION_SUFAMI_TURBO_HPP
|
||||
|
||||
#include <nall/sha256.hpp>
|
||||
#include <nall/string.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct SufamiTurboCartridge {
|
||||
string markup;
|
||||
inline SufamiTurboCartridge(const uint8_t *data, unsigned size);
|
||||
};
|
||||
|
||||
SufamiTurboCartridge::SufamiTurboCartridge(const uint8_t *data, unsigned size) {
|
||||
markup = "";
|
||||
|
||||
if(size < 0x20000) return; //too small to be a valid game?
|
||||
if(memcmp(data, "BANDAI SFC-ADX", 14)) return; //missing required header?
|
||||
unsigned romsize = data[0x36] * 0x20000; //128KB
|
||||
unsigned ramsize = data[0x37] * 0x800; //2KB
|
||||
bool linkable = data[0x35] != 0x00; //TODO: unconfirmed
|
||||
|
||||
markup.append("<?xml version='1.0' encoding='UTF-8'?>\n");
|
||||
markup.append("<cartridge linkable='", linkable, "' sha256='", sha256(data, size) ,"'>\n");
|
||||
markup.append(" <rom name='program.rom' size='0x", hex(romsize), "'/>\n");
|
||||
markup.append(" <ram name='save.ram' size='0x", hex(ramsize), "'/>\n");
|
||||
markup.append("</cartridge>\n");
|
||||
markup.transform("'", "\"");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,814 @@
|
||||
#ifndef NALL_EMULATION_SUPER_FAMICOM_HPP
|
||||
#define NALL_EMULATION_SUPER_FAMICOM_HPP
|
||||
|
||||
#include <nall/sha256.hpp>
|
||||
#include <nall/string.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct SuperFamicomCartridge {
|
||||
string markup;
|
||||
inline SuperFamicomCartridge(const uint8_t *data, unsigned size);
|
||||
|
||||
//private:
|
||||
inline void read_header(const uint8_t *data, unsigned size);
|
||||
inline unsigned find_header(const uint8_t *data, unsigned size);
|
||||
inline unsigned score_header(const uint8_t *data, unsigned size, unsigned addr);
|
||||
|
||||
enum HeaderField {
|
||||
CartName = 0x00,
|
||||
Mapper = 0x15,
|
||||
RomType = 0x16,
|
||||
RomSize = 0x17,
|
||||
RamSize = 0x18,
|
||||
CartRegion = 0x19,
|
||||
Company = 0x1a,
|
||||
Version = 0x1b,
|
||||
Complement = 0x1c, //inverse checksum
|
||||
Checksum = 0x1e,
|
||||
ResetVector = 0x3c,
|
||||
};
|
||||
|
||||
enum Mode {
|
||||
ModeNormal,
|
||||
ModeBsxSlotted,
|
||||
ModeBsx,
|
||||
ModeSufamiTurbo,
|
||||
ModeSuperGameBoy,
|
||||
};
|
||||
|
||||
enum Type {
|
||||
TypeNormal,
|
||||
TypeBsxSlotted,
|
||||
TypeBsxBios,
|
||||
TypeBsx,
|
||||
TypeSufamiTurboBios,
|
||||
TypeSufamiTurbo,
|
||||
TypeSuperGameBoy1Bios,
|
||||
TypeSuperGameBoy2Bios,
|
||||
TypeGameBoy,
|
||||
TypeUnknown,
|
||||
};
|
||||
|
||||
enum Region {
|
||||
NTSC,
|
||||
PAL,
|
||||
};
|
||||
|
||||
enum MemoryMapper {
|
||||
LoROM,
|
||||
HiROM,
|
||||
ExLoROM,
|
||||
ExHiROM,
|
||||
SuperFXROM,
|
||||
SA1ROM,
|
||||
SPC7110ROM,
|
||||
BSCLoROM,
|
||||
BSCHiROM,
|
||||
BSXROM,
|
||||
STROM,
|
||||
};
|
||||
|
||||
enum DSP1MemoryMapper {
|
||||
DSP1Unmapped,
|
||||
DSP1LoROM1MB,
|
||||
DSP1LoROM2MB,
|
||||
DSP1HiROM,
|
||||
};
|
||||
|
||||
bool loaded; //is a base cartridge inserted?
|
||||
unsigned crc32; //crc32 of all cartridges (base+slot(s))
|
||||
unsigned rom_size;
|
||||
unsigned ram_size;
|
||||
bool firmware_appended; //true if firmware is appended to end of ROM data
|
||||
|
||||
Mode mode;
|
||||
Type type;
|
||||
Region region;
|
||||
MemoryMapper mapper;
|
||||
DSP1MemoryMapper dsp1_mapper;
|
||||
|
||||
bool has_bsx_slot;
|
||||
bool has_superfx;
|
||||
bool has_sa1;
|
||||
bool has_srtc;
|
||||
bool has_sdd1;
|
||||
bool has_spc7110;
|
||||
bool has_spc7110rtc;
|
||||
bool has_cx4;
|
||||
bool has_dsp1;
|
||||
bool has_dsp2;
|
||||
bool has_dsp3;
|
||||
bool has_dsp4;
|
||||
bool has_obc1;
|
||||
bool has_st010;
|
||||
bool has_st011;
|
||||
bool has_st018;
|
||||
};
|
||||
|
||||
SuperFamicomCartridge::SuperFamicomCartridge(const uint8_t *data, unsigned size) {
|
||||
firmware_appended = false;
|
||||
|
||||
//skip copier header
|
||||
if((size & 0x7fff) == 512) data += 512, size -= 512;
|
||||
|
||||
markup = "";
|
||||
if(size < 0x8000) return;
|
||||
|
||||
read_header(data, size);
|
||||
|
||||
markup = "";
|
||||
if(type == TypeGameBoy) return;
|
||||
if(type == TypeBsx) return;
|
||||
if(type == TypeSufamiTurbo) return;
|
||||
|
||||
const char *range = (rom_size > 0x200000) || (ram_size > 32 * 1024) ? "0000-7fff" : "0000-ffff";
|
||||
markup.append("cartridge region=", region == NTSC ? "NTSC" : "PAL", "\n");
|
||||
|
||||
if(type == TypeSuperGameBoy1Bios || type == TypeSuperGameBoy2Bios) {
|
||||
markup.append(
|
||||
" rom name=program.rom size=0x", hex(rom_size), "\n"
|
||||
" map id=rom address=00-7f,80-ff:8000-ffff\n"
|
||||
" icd2 revision=1\n"
|
||||
" rom name=boot.rom size=0x100\n"
|
||||
" map id=io address=00-3f,80-bf:6000-7fff\n"
|
||||
);
|
||||
if((rom_size & 0x7fff) == 0x100) {
|
||||
firmware_appended = true;
|
||||
rom_size -= 0x100;
|
||||
}
|
||||
}
|
||||
|
||||
else if(has_cx4) {
|
||||
markup.append(
|
||||
" hitachidsp model=HG51B169 frequency=20000000\n"
|
||||
" rom id=program name=program.rom size=0x", hex(rom_size), "\n"
|
||||
" rom id=data name=cx4.data.rom size=0xc00\n"
|
||||
" ram id=data size=0xc00\n"
|
||||
" map id=io address=00-3f,80-bf:6000-7fff\n"
|
||||
" map id=rom address=00-7f,80-ff:8000-ffff mask=0x8000\n"
|
||||
" map id=ram address=70-77:0000-7fff\n"
|
||||
);
|
||||
if((rom_size & 0x7fff) == 0xc00) {
|
||||
firmware_appended = true;
|
||||
rom_size -= 0xc00;
|
||||
}
|
||||
}
|
||||
|
||||
else if(has_spc7110) {
|
||||
markup.append(
|
||||
" spc7110\n"
|
||||
" rom id=program name=program.rom size=0x100000\n"
|
||||
" rom id=data name=data.rom size=0x", hex(rom_size - 0x100000), "\n"
|
||||
" ram name=save.ram size=0x", hex(ram_size), "\n"
|
||||
" map id=io address=00-3f,80-bf:4800-483f\n"
|
||||
" map id=io address=50:0000-ffff\n"
|
||||
" map id=rom address=00-3f,80-bf:8000-ffff\n"
|
||||
" map id=rom address=c0-ff:0000-ffff\n"
|
||||
" map id=ram address=00-3f,80-bf:6000-7fff mask=0xe000\n"
|
||||
);
|
||||
}
|
||||
|
||||
else if(has_sdd1) {
|
||||
markup.append(
|
||||
" sdd1\n"
|
||||
" rom name=program.rom size=0x", hex(rom_size), "\n"
|
||||
);
|
||||
if(ram_size > 0) markup.append(
|
||||
" ram name=save.ram size=0x", hex(ram_size), "\n"
|
||||
);
|
||||
markup.append(
|
||||
" map id=io address=00-3f,80-bf:4800-4807\n"
|
||||
" map id=rom address=00-3f,80-bf:8000-ffff mask=0x8000\n"
|
||||
" map id=rom address=c0-ff:0000-ffff\n"
|
||||
);
|
||||
if(ram_size > 0) markup.append(
|
||||
" map id=ram address=20-3f,a0-bf:6000-7fff mask=0xe000\n"
|
||||
" map id=ram address=70-7f:0000-7fff\n"
|
||||
);
|
||||
}
|
||||
|
||||
else if(mapper == LoROM) {
|
||||
markup.append(
|
||||
" rom name=program.rom size=0x", hex(rom_size), "\n"
|
||||
);
|
||||
if(ram_size > 0) markup.append(
|
||||
" ram name=save.ram size=0x", hex(ram_size), "\n"
|
||||
);
|
||||
markup.append(
|
||||
" map id=rom address=00-7f,80-ff:8000-ffff mask=0x8000\n"
|
||||
);
|
||||
if(ram_size > 0) markup.append(
|
||||
" map id=ram address=70-7f,f0-ff:", range, "\n"
|
||||
);
|
||||
}
|
||||
|
||||
else if(mapper == HiROM) {
|
||||
markup.append(
|
||||
" rom name=program.rom size=0x", hex(rom_size), "\n"
|
||||
);
|
||||
if(ram_size > 0) markup.append(
|
||||
" ram name=save.ram size=0x", hex(ram_size), "\n"
|
||||
);
|
||||
markup.append(
|
||||
" map id=rom address=00-3f,80-bf:8000-ffff\n"
|
||||
" map id=rom address=40-7f,c0-ff:0000-ffff\n"
|
||||
);
|
||||
if(ram_size > 0) markup.append(
|
||||
" map id=ram address=10-3f,90-bf:6000-7fff mask=0xe000\n"
|
||||
);
|
||||
}
|
||||
|
||||
else if(mapper == ExLoROM) {
|
||||
markup.append(
|
||||
" rom name=program.rom size=0x", hex(rom_size), "\n"
|
||||
);
|
||||
if(ram_size > 0) markup.append(
|
||||
" ram name=save.ram size=0x", hex(ram_size), "\n"
|
||||
);
|
||||
markup.append(
|
||||
" map id=rom address=00-3f,80-bf:8000-ffff mask=0x8000\n"
|
||||
" map id=rom address=40-7f:0000-ffff\n"
|
||||
);
|
||||
if(ram_size > 0) markup.append(
|
||||
" map id=ram address=20-3f,a0-bf:6000-7fff\n"
|
||||
" map id=ram address=70-7f:0000-7fff\n"
|
||||
);
|
||||
}
|
||||
|
||||
else if(mapper == ExHiROM) {
|
||||
markup.append(
|
||||
" rom name=program.rom size=0x", hex(rom_size), "\n"
|
||||
);
|
||||
if(ram_size > 0) markup.append(
|
||||
" ram name=save.ram size=0x", hex(ram_size), "\n"
|
||||
);
|
||||
markup.append(
|
||||
" map id=rom address=00-3f:8000-ffff offset=0x400000\n"
|
||||
" map id=rom address=40-7f:0000-ffff offset=0x400000\n"
|
||||
" map id=rom address=80-bf:8000-ffff offset=0x000000\n"
|
||||
" map id=rom address=c0-ff:0000-ffff offset=0x000000\n"
|
||||
);
|
||||
if(ram_size > 0) markup.append(
|
||||
" map id=ram address=20-3f,a0-bf:6000-7fff mask=0xe000\n"
|
||||
" map id=ram address=70-7f:", range, "\n"
|
||||
);
|
||||
}
|
||||
|
||||
else if(mapper == SuperFXROM) {
|
||||
markup.append(
|
||||
" superfx revision=3\n"
|
||||
" rom name=program.rom size=0x", hex(rom_size), "\n"
|
||||
);
|
||||
if(ram_size > 0) markup.append(
|
||||
" ram name=save.ram size=0x", hex(ram_size), "\n"
|
||||
);
|
||||
markup.append(
|
||||
" map id=io address=00-3f,80-bf:3000-32ff\n"
|
||||
" map id=rom address=00-3f,80-bf:8000-ffff mask=0x8000\n"
|
||||
" map id=rom address=40-5f,c0-df:0000-ffff\n"
|
||||
);
|
||||
if(ram_size > 0) markup.append(
|
||||
" map id=ram address=00-3f,80-bf:6000-7fff size=0x2000\n"
|
||||
" map id=ram address=70-71,f0-f1:0000-ffff\n"
|
||||
);
|
||||
}
|
||||
|
||||
else if(mapper == SA1ROM) {
|
||||
markup.append(
|
||||
" sa1\n"
|
||||
" rom name=program.rom size=0x", hex(rom_size), "\n"
|
||||
);
|
||||
if(ram_size > 0) markup.append(
|
||||
" ram id=bitmap name=save.ram size=0x", hex(ram_size), "\n"
|
||||
);
|
||||
markup.append(
|
||||
" ram id=internal size=0x800\n"
|
||||
" map id=io address=00-3f,80-bf:2200-23ff\n"
|
||||
" map id=rom address=00-3f,80-bf:8000-ffff\n"
|
||||
" map id=rom address=c0-ff:0000-ffff\n"
|
||||
);
|
||||
if(ram_size > 0) markup.append(
|
||||
" map id=bwram address=00-3f,80-bf:6000-7fff\n"
|
||||
" map id=bwram address=40-4f:0000-ffff\n"
|
||||
);
|
||||
markup.append(
|
||||
" map id=iram address=00-3f,80-bf:3000-37ff\n"
|
||||
);
|
||||
}
|
||||
|
||||
else if(mapper == BSCLoROM) {
|
||||
markup.append(
|
||||
" rom name=program.rom size=0x", hex(rom_size), "\n"
|
||||
" ram name=save.ram size=0x", hex(ram_size), "\n"
|
||||
" map id=rom address=00-1f:8000-ffff offset=0x000000 mask=0x8000\n"
|
||||
" map id=rom address=20-3f:8000-ffff offset=0x100000 mask=0x8000\n"
|
||||
" map id=rom address=80-9f:8000-ffff offset=0x200000 mask=0x8000\n"
|
||||
" map id=rom address=a0-bf:8000-ffff offset=0x100000 mask=0x8000\n"
|
||||
" map id=ram address=70-7f,f0-ff:0000-7fff\n"
|
||||
" bsxslot\n"
|
||||
" map id=rom address=c0-ef:0000-ffff\n"
|
||||
);
|
||||
}
|
||||
|
||||
else if(mapper == BSCHiROM) {
|
||||
markup.append(
|
||||
" rom name=program.rom size=0x", hex(rom_size), "\n"
|
||||
" ram name=save.ram size=0x", hex(ram_size), "\n"
|
||||
" map id=rom address=00-1f,80-9f:8000-ffff\n"
|
||||
" map id=rom address=40-5f,c0-df:0000-ffff\n"
|
||||
" map id=ram address=20-3f,a0-bf:6000-7fff\n"
|
||||
" bsxslot\n"
|
||||
" map id=rom address=20-3f,a0-bf:8000-ffff\n"
|
||||
" map id=rom address=60-7f,e0-ff:0000-ffff\n"
|
||||
);
|
||||
}
|
||||
|
||||
else if(mapper == BSXROM) {
|
||||
markup.append(
|
||||
" bsx\n"
|
||||
" rom name=program.rom size=0x", hex(rom_size), "\n"
|
||||
" ram id=save name=save.ram size=0x", hex(ram_size), "\n"
|
||||
" ram id=download name=bsx.ram size=0x40000\n"
|
||||
" map id=io address=00-3f,80-bf:5000-5fff\n"
|
||||
" map id=rom address=00-3f,80-bf:8000-ffff\n"
|
||||
" map id=rom address=40-7f,c0-ff:0000-ffff\n"
|
||||
" map id=ram address=20-3f:6000-7fff\n"
|
||||
);
|
||||
}
|
||||
|
||||
else if(mapper == STROM) {
|
||||
markup.append(
|
||||
" rom name=program.rom size=0x", hex(rom_size), "\n"
|
||||
" map id=rom address='00-1f,80-9f:8000-ffff mask=0x8000\n"
|
||||
" sufamiturbo\n"
|
||||
" slot id=A\n"
|
||||
" map id=rom address=20-3f,a0-bf:8000-ffff mask=0x8000\n"
|
||||
" map id=ram address=60-63,e0-e3:8000-ffff\n"
|
||||
" slot id=B\n"
|
||||
" map id=rom address=40-5f,c0-df:8000-ffff mask=0x8000\n"
|
||||
" map id=ram address=70-73,f0-f3:8000-ffff\n"
|
||||
);
|
||||
}
|
||||
|
||||
if(has_spc7110rtc) {
|
||||
markup.append(
|
||||
" epsonrtc\n"
|
||||
" ram name=rtc.ram size=0x10\n"
|
||||
" map id=io address=00-3f,80-bf:4840-4842\n"
|
||||
);
|
||||
}
|
||||
|
||||
if(has_srtc) {
|
||||
markup.append(
|
||||
" sharprtc\n"
|
||||
" ram name=rtc.ram size=0x10\n"
|
||||
" map id=io address=00-3f,80-bf:2800-2801\n"
|
||||
);
|
||||
}
|
||||
|
||||
if(has_obc1) {
|
||||
markup.append(
|
||||
" obc1\n"
|
||||
" ram name=save.ram size=0x2000\n"
|
||||
" map id=io address=00-3f,80-bf:6000-7fff\n"
|
||||
);
|
||||
}
|
||||
|
||||
if(has_dsp1) {
|
||||
markup.append(
|
||||
" necdsp model=uPD7725 frequency=8000000\n"
|
||||
" rom id=program name=dsp1b.program.rom size=0x1800\n"
|
||||
" rom id=data name=dsp1b.data.rom size=0x800\n"
|
||||
" ram id=data size=0x200\n"
|
||||
);
|
||||
if(dsp1_mapper == DSP1LoROM1MB) markup.append(
|
||||
" map id=io address=20-3f,a0-bf:8000-ffff select=0x4000\n"
|
||||
);
|
||||
if(dsp1_mapper == DSP1LoROM2MB) markup.append(
|
||||
" map id=io address=60-6f,e0-ef:0000-7fff select=0x4000\n"
|
||||
);
|
||||
if(dsp1_mapper == DSP1HiROM) markup.append(
|
||||
" map id=io address=00-1f,80-9f:6000-7fff select=0x1000\n"
|
||||
);
|
||||
if((size & 0x7fff) == 0x2000) {
|
||||
firmware_appended = true;
|
||||
rom_size -= 0x2000;
|
||||
}
|
||||
}
|
||||
|
||||
if(has_dsp2) {
|
||||
markup.append(
|
||||
" necdsp model=uPD7725 frequency=8000000\n"
|
||||
" rom id=program name=dsp2.program.rom size=0x1800\n"
|
||||
" rom id=data name=dsp2.data.rom size=0x800\n"
|
||||
" ram id=data size=0x200\n"
|
||||
" map id=io address=20-3f,a0-bf:8000-ffff select=0x4000\n"
|
||||
);
|
||||
if((size & 0x7fff) == 0x2000) {
|
||||
firmware_appended = true;
|
||||
rom_size -= 0x2000;
|
||||
}
|
||||
}
|
||||
|
||||
if(has_dsp3) {
|
||||
markup.append(
|
||||
" necdsp model=uPD7725 frequency=8000000\n"
|
||||
" rom id=program name=dsp3.program.rom size=0x1800\n"
|
||||
" rom id=data name=dsp3.data.rom size=0x800\n"
|
||||
" ram id=data size=0x200\n"
|
||||
" map id=io address=20-3f,a0-bf:8000-ffff select=0x4000\n"
|
||||
);
|
||||
if((size & 0x7fff) == 0x2000) {
|
||||
firmware_appended = true;
|
||||
rom_size -= 0x2000;
|
||||
}
|
||||
}
|
||||
|
||||
if(has_dsp4) {
|
||||
markup.append(
|
||||
" necdsp model=uPD7725 frequency=8000000\n"
|
||||
" rom id=program name=dsp4.program.rom size=0x1800\n"
|
||||
" rom id=data name=dsp4.data.rom size=0x800\n"
|
||||
" ram id=data size=0x200\n"
|
||||
" map address=30-3f,b0-bf:8000-ffff select=0x4000\n"
|
||||
);
|
||||
if((size & 0x7fff) == 0x2000) {
|
||||
firmware_appended = true;
|
||||
rom_size -= 0x2000;
|
||||
}
|
||||
}
|
||||
|
||||
if(has_st010) {
|
||||
markup.append(
|
||||
" necdsp model=uPD96050 frequency=11000000\n"
|
||||
" rom id=program name=st010.program.rom size=0xc000\n"
|
||||
" rom id=data name=st010.data.rom size=0x1000\n"
|
||||
" ram id=data name=save.ram size=0x1000\n"
|
||||
" map id=io address=60-67,e0-e7:0000-3fff select=0x0001\n"
|
||||
" map id=ram address=68-6f,e8-ef:0000-7fff\n"
|
||||
);
|
||||
if((size & 0xffff) == 0xd000) {
|
||||
firmware_appended = true;
|
||||
rom_size -= 0xd000;
|
||||
}
|
||||
}
|
||||
|
||||
if(has_st011) {
|
||||
markup.append(
|
||||
" necdsp model=uPD96050 frequency=15000000\n"
|
||||
" rom id=program name=st011.program.rom size=0xc000\n"
|
||||
" rom id=data name=st011.data.rom size=0x1000\n"
|
||||
" ram id=data name=save.ram size=0x1000\n"
|
||||
" map id=io address=60-67,e0-e7:0000-3fff select=0x0001\n"
|
||||
" map id=ram address=68-6f,e8-ef:0000-7fff\n"
|
||||
);
|
||||
if((size & 0xffff) == 0xd000) {
|
||||
firmware_appended = true;
|
||||
rom_size -= 0xd000;
|
||||
}
|
||||
}
|
||||
|
||||
if(has_st018) {
|
||||
markup.append(
|
||||
" armdsp frequency=21477272\n"
|
||||
" rom id=program name=st018.program.rom size=0x20000\n"
|
||||
" rom id=data name=st018.data.rom size=0x8000\n"
|
||||
" ram name=save.ram size=0x4000\n"
|
||||
" map id=io address=00-3f,80-bf:3800-38ff\n"
|
||||
);
|
||||
if((size & 0x3ffff) == 0x28000) {
|
||||
firmware_appended = true;
|
||||
rom_size -= 0x28000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SuperFamicomCartridge::read_header(const uint8_t *data, unsigned size) {
|
||||
type = TypeUnknown;
|
||||
mapper = LoROM;
|
||||
dsp1_mapper = DSP1Unmapped;
|
||||
region = NTSC;
|
||||
rom_size = size;
|
||||
ram_size = 0;
|
||||
|
||||
has_bsx_slot = false;
|
||||
has_superfx = false;
|
||||
has_sa1 = false;
|
||||
has_srtc = false;
|
||||
has_sdd1 = false;
|
||||
has_spc7110 = false;
|
||||
has_spc7110rtc = false;
|
||||
has_cx4 = false;
|
||||
has_dsp1 = false;
|
||||
has_dsp2 = false;
|
||||
has_dsp3 = false;
|
||||
has_dsp4 = false;
|
||||
has_obc1 = false;
|
||||
has_st010 = false;
|
||||
has_st011 = false;
|
||||
has_st018 = false;
|
||||
|
||||
//=====================
|
||||
//detect Game Boy carts
|
||||
//=====================
|
||||
|
||||
if(size >= 0x0140) {
|
||||
if(data[0x0104] == 0xce && data[0x0105] == 0xed && data[0x0106] == 0x66 && data[0x0107] == 0x66
|
||||
&& data[0x0108] == 0xcc && data[0x0109] == 0x0d && data[0x010a] == 0x00 && data[0x010b] == 0x0b) {
|
||||
type = TypeGameBoy;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(size < 32768) {
|
||||
type = TypeUnknown;
|
||||
return;
|
||||
}
|
||||
|
||||
const unsigned index = find_header(data, size);
|
||||
const uint8_t mapperid = data[index + Mapper];
|
||||
const uint8_t rom_type = data[index + RomType];
|
||||
const uint8_t rom_size = data[index + RomSize];
|
||||
const uint8_t company = data[index + Company];
|
||||
const uint8_t regionid = data[index + CartRegion] & 0x7f;
|
||||
|
||||
ram_size = 1024 << (data[index + RamSize] & 7);
|
||||
if(ram_size == 1024) ram_size = 0; //no RAM present
|
||||
if(rom_size == 0 && ram_size) ram_size = 0; //fix for Bazooka Blitzkrieg's malformed header (swapped ROM and RAM sizes)
|
||||
|
||||
//0, 1, 13 = NTSC; 2 - 12 = PAL
|
||||
region = (regionid <= 1 || regionid >= 13) ? NTSC : PAL;
|
||||
|
||||
//=======================
|
||||
//detect BS-X flash carts
|
||||
//=======================
|
||||
|
||||
if(data[index + 0x13] == 0x00 || data[index + 0x13] == 0xff) {
|
||||
if(data[index + 0x14] == 0x00) {
|
||||
const uint8_t n15 = data[index + 0x15];
|
||||
if(n15 == 0x00 || n15 == 0x80 || n15 == 0x84 || n15 == 0x9c || n15 == 0xbc || n15 == 0xfc) {
|
||||
if(data[index + 0x1a] == 0x33 || data[index + 0x1a] == 0xff) {
|
||||
type = TypeBsx;
|
||||
mapper = BSXROM;
|
||||
region = NTSC; //BS-X only released in Japan
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//=========================
|
||||
//detect Sufami Turbo carts
|
||||
//=========================
|
||||
|
||||
if(!memcmp(data, "BANDAI SFC-ADX", 14)) {
|
||||
if(!memcmp(data + 16, "SFC-ADX BACKUP", 14)) {
|
||||
type = TypeSufamiTurboBios;
|
||||
} else {
|
||||
type = TypeSufamiTurbo;
|
||||
}
|
||||
mapper = STROM;
|
||||
region = NTSC; //Sufami Turbo only released in Japan
|
||||
return; //RAM size handled outside this routine
|
||||
}
|
||||
|
||||
//==========================
|
||||
//detect Super Game Boy BIOS
|
||||
//==========================
|
||||
|
||||
if(!memcmp(data + index, "Super GAMEBOY2", 14)) {
|
||||
type = TypeSuperGameBoy2Bios;
|
||||
return;
|
||||
}
|
||||
|
||||
if(!memcmp(data + index, "Super GAMEBOY", 13)) {
|
||||
type = TypeSuperGameBoy1Bios;
|
||||
return;
|
||||
}
|
||||
|
||||
//=====================
|
||||
//detect standard carts
|
||||
//=====================
|
||||
|
||||
//detect presence of BS-X flash cartridge connector (reads extended header information)
|
||||
if(data[index - 14] == 'Z') {
|
||||
if(data[index - 11] == 'J') {
|
||||
uint8_t n13 = data[index - 13];
|
||||
if((n13 >= 'A' && n13 <= 'Z') || (n13 >= '0' && n13 <= '9')) {
|
||||
if(company == 0x33 || (data[index - 10] == 0x00 && data[index - 4] == 0x00)) {
|
||||
has_bsx_slot = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(has_bsx_slot) {
|
||||
if(!memcmp(data + index, "Satellaview BS-X ", 21)) {
|
||||
//BS-X base cart
|
||||
type = TypeBsxBios;
|
||||
mapper = BSXROM;
|
||||
region = NTSC; //BS-X only released in Japan
|
||||
return; //RAM size handled internally by load_cart_bsx() -> BSXCart class
|
||||
} else {
|
||||
type = TypeBsxSlotted;
|
||||
mapper = (index == 0x7fc0 ? BSCLoROM : BSCHiROM);
|
||||
region = NTSC; //BS-X slotted cartridges only released in Japan
|
||||
}
|
||||
} else {
|
||||
//standard cart
|
||||
type = TypeNormal;
|
||||
|
||||
if(index == 0x7fc0 && size >= 0x401000) {
|
||||
mapper = ExLoROM;
|
||||
} else if(index == 0x7fc0 && mapperid == 0x32) {
|
||||
mapper = ExLoROM;
|
||||
} else if(index == 0x7fc0) {
|
||||
mapper = LoROM;
|
||||
} else if(index == 0xffc0) {
|
||||
mapper = HiROM;
|
||||
} else { //index == 0x40ffc0
|
||||
mapper = ExHiROM;
|
||||
}
|
||||
}
|
||||
|
||||
if(mapperid == 0x20 && (rom_type == 0x13 || rom_type == 0x14 || rom_type == 0x15 || rom_type == 0x1a)) {
|
||||
has_superfx = true;
|
||||
mapper = SuperFXROM;
|
||||
ram_size = 1024 << (data[index - 3] & 7);
|
||||
if(ram_size == 1024) ram_size = 0;
|
||||
}
|
||||
|
||||
if(mapperid == 0x23 && (rom_type == 0x32 || rom_type == 0x34 || rom_type == 0x35)) {
|
||||
has_sa1 = true;
|
||||
mapper = SA1ROM;
|
||||
}
|
||||
|
||||
if(mapperid == 0x35 && rom_type == 0x55) {
|
||||
has_srtc = true;
|
||||
}
|
||||
|
||||
if(mapperid == 0x32 && (rom_type == 0x43 || rom_type == 0x45)) {
|
||||
has_sdd1 = true;
|
||||
}
|
||||
|
||||
if(mapperid == 0x3a && (rom_type == 0xf5 || rom_type == 0xf9)) {
|
||||
has_spc7110 = true;
|
||||
has_spc7110rtc = (rom_type == 0xf9);
|
||||
mapper = SPC7110ROM;
|
||||
}
|
||||
|
||||
if(mapperid == 0x20 && rom_type == 0xf3) {
|
||||
has_cx4 = true;
|
||||
}
|
||||
|
||||
if((mapperid == 0x20 || mapperid == 0x21) && rom_type == 0x03) {
|
||||
has_dsp1 = true;
|
||||
}
|
||||
|
||||
if(mapperid == 0x30 && rom_type == 0x05 && company != 0xb2) {
|
||||
has_dsp1 = true;
|
||||
}
|
||||
|
||||
if(mapperid == 0x31 && (rom_type == 0x03 || rom_type == 0x05)) {
|
||||
has_dsp1 = true;
|
||||
}
|
||||
|
||||
if(has_dsp1 == true) {
|
||||
if((mapperid & 0x2f) == 0x20 && size <= 0x100000) {
|
||||
dsp1_mapper = DSP1LoROM1MB;
|
||||
} else if((mapperid & 0x2f) == 0x20) {
|
||||
dsp1_mapper = DSP1LoROM2MB;
|
||||
} else if((mapperid & 0x2f) == 0x21) {
|
||||
dsp1_mapper = DSP1HiROM;
|
||||
}
|
||||
}
|
||||
|
||||
if(mapperid == 0x20 && rom_type == 0x05) {
|
||||
has_dsp2 = true;
|
||||
}
|
||||
|
||||
if(mapperid == 0x30 && rom_type == 0x05 && company == 0xb2) {
|
||||
has_dsp3 = true;
|
||||
}
|
||||
|
||||
if(mapperid == 0x30 && rom_type == 0x03) {
|
||||
has_dsp4 = true;
|
||||
}
|
||||
|
||||
if(mapperid == 0x30 && rom_type == 0x25) {
|
||||
has_obc1 = true;
|
||||
}
|
||||
|
||||
if(mapperid == 0x30 && rom_type == 0xf6 && rom_size >= 10) {
|
||||
has_st010 = true;
|
||||
}
|
||||
|
||||
if(mapperid == 0x30 && rom_type == 0xf6 && rom_size < 10) {
|
||||
has_st011 = true;
|
||||
}
|
||||
|
||||
if(mapperid == 0x30 && rom_type == 0xf5) {
|
||||
has_st018 = true;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned SuperFamicomCartridge::find_header(const uint8_t *data, unsigned size) {
|
||||
unsigned score_lo = score_header(data, size, 0x007fc0);
|
||||
unsigned score_hi = score_header(data, size, 0x00ffc0);
|
||||
unsigned score_ex = score_header(data, size, 0x40ffc0);
|
||||
if(score_ex) score_ex += 4; //favor ExHiROM on images > 32mbits
|
||||
|
||||
if(score_lo >= score_hi && score_lo >= score_ex) {
|
||||
return 0x007fc0;
|
||||
} else if(score_hi >= score_ex) {
|
||||
return 0x00ffc0;
|
||||
} else {
|
||||
return 0x40ffc0;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned SuperFamicomCartridge::score_header(const uint8_t *data, unsigned size, unsigned addr) {
|
||||
if(size < addr + 64) return 0; //image too small to contain header at this location?
|
||||
int score = 0;
|
||||
|
||||
uint16_t resetvector = data[addr + ResetVector] | (data[addr + ResetVector + 1] << 8);
|
||||
uint16_t checksum = data[addr + Checksum ] | (data[addr + Checksum + 1] << 8);
|
||||
uint16_t complement = data[addr + Complement ] | (data[addr + Complement + 1] << 8);
|
||||
|
||||
uint8_t resetop = data[(addr & ~0x7fff) | (resetvector & 0x7fff)]; //first opcode executed upon reset
|
||||
uint8_t mapper = data[addr + Mapper] & ~0x10; //mask off irrelevent FastROM-capable bit
|
||||
|
||||
//$00:[000-7fff] contains uninitialized RAM and MMIO.
|
||||
//reset vector must point to ROM at $00:[8000-ffff] to be considered valid.
|
||||
if(resetvector < 0x8000) return 0;
|
||||
|
||||
//some images duplicate the header in multiple locations, and others have completely
|
||||
//invalid header information that cannot be relied upon.
|
||||
//below code will analyze the first opcode executed at the specified reset vector to
|
||||
//determine the probability that this is the correct header.
|
||||
|
||||
//most likely opcodes
|
||||
if(resetop == 0x78 //sei
|
||||
|| resetop == 0x18 //clc (clc; xce)
|
||||
|| resetop == 0x38 //sec (sec; xce)
|
||||
|| resetop == 0x9c //stz $nnnn (stz $4200)
|
||||
|| resetop == 0x4c //jmp $nnnn
|
||||
|| resetop == 0x5c //jml $nnnnnn
|
||||
) score += 8;
|
||||
|
||||
//plausible opcodes
|
||||
if(resetop == 0xc2 //rep #$nn
|
||||
|| resetop == 0xe2 //sep #$nn
|
||||
|| resetop == 0xad //lda $nnnn
|
||||
|| resetop == 0xae //ldx $nnnn
|
||||
|| resetop == 0xac //ldy $nnnn
|
||||
|| resetop == 0xaf //lda $nnnnnn
|
||||
|| resetop == 0xa9 //lda #$nn
|
||||
|| resetop == 0xa2 //ldx #$nn
|
||||
|| resetop == 0xa0 //ldy #$nn
|
||||
|| resetop == 0x20 //jsr $nnnn
|
||||
|| resetop == 0x22 //jsl $nnnnnn
|
||||
) score += 4;
|
||||
|
||||
//implausible opcodes
|
||||
if(resetop == 0x40 //rti
|
||||
|| resetop == 0x60 //rts
|
||||
|| resetop == 0x6b //rtl
|
||||
|| resetop == 0xcd //cmp $nnnn
|
||||
|| resetop == 0xec //cpx $nnnn
|
||||
|| resetop == 0xcc //cpy $nnnn
|
||||
) score -= 4;
|
||||
|
||||
//least likely opcodes
|
||||
if(resetop == 0x00 //brk #$nn
|
||||
|| resetop == 0x02 //cop #$nn
|
||||
|| resetop == 0xdb //stp
|
||||
|| resetop == 0x42 //wdm
|
||||
|| resetop == 0xff //sbc $nnnnnn,x
|
||||
) score -= 8;
|
||||
|
||||
//at times, both the header and reset vector's first opcode will match ...
|
||||
//fallback and rely on info validity in these cases to determine more likely header.
|
||||
|
||||
//a valid checksum is the biggest indicator of a valid header.
|
||||
if((checksum + complement) == 0xffff && (checksum != 0) && (complement != 0)) score += 4;
|
||||
|
||||
if(addr == 0x007fc0 && mapper == 0x20) score += 2; //0x20 is usually LoROM
|
||||
if(addr == 0x00ffc0 && mapper == 0x21) score += 2; //0x21 is usually HiROM
|
||||
if(addr == 0x007fc0 && mapper == 0x22) score += 2; //0x22 is usually ExLoROM
|
||||
if(addr == 0x40ffc0 && mapper == 0x25) score += 2; //0x25 is usually ExHiROM
|
||||
|
||||
if(data[addr + Company] == 0x33) score += 2; //0x33 indicates extended header
|
||||
if(data[addr + RomType] < 0x08) score++;
|
||||
if(data[addr + RomSize] < 0x10) score++;
|
||||
if(data[addr + RamSize] < 0x08) score++;
|
||||
if(data[addr + CartRegion] < 14) score++;
|
||||
|
||||
if(score < 0) score = 0;
|
||||
return score;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
Executable → Regular
+5
-2
@@ -19,6 +19,9 @@ ifeq ($(platform),)
|
||||
ifeq ($(uname),)
|
||||
platform := win
|
||||
delete = del $(subst /,\,$1)
|
||||
else ifneq ($(findstring Windows,$(uname)),)
|
||||
platform := win
|
||||
delete = del $(subst /,\,$1)
|
||||
else ifneq ($(findstring CYGWIN,$(uname)),)
|
||||
platform := win
|
||||
delete = del $(subst /,\,$1)
|
||||
@@ -35,9 +38,9 @@ ifeq ($(compiler),)
|
||||
ifeq ($(platform),win)
|
||||
compiler := gcc
|
||||
else ifeq ($(platform),osx)
|
||||
compiler := gcc-mp-4.6
|
||||
compiler := gcc-mp-4.7
|
||||
else
|
||||
compiler := gcc-4.6
|
||||
compiler := gcc-4.7
|
||||
endif
|
||||
endif
|
||||
|
||||
Executable → Regular
File renamed without changes.
Executable → Regular
+1
-1
@@ -2,7 +2,7 @@
|
||||
#define NALL_ANY_HPP
|
||||
|
||||
#include <typeinfo>
|
||||
#include <nall/type_traits.hpp>
|
||||
#include <nall/traits.hpp>
|
||||
|
||||
namespace nall {
|
||||
struct any {
|
||||
Executable → Regular
+2
@@ -1,6 +1,8 @@
|
||||
#ifndef NALL_ATOI_HPP
|
||||
#define NALL_ATOI_HPP
|
||||
|
||||
#include <nall/stdint.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
//note: this header is intended to form the base for user-defined literals;
|
||||
@@ -0,0 +1,118 @@
|
||||
#ifndef NALL_BASE64_HPP
|
||||
#define NALL_BASE64_HPP
|
||||
|
||||
#include <nall/stdint.hpp>
|
||||
#include <nall/string.hpp>
|
||||
|
||||
namespace nall {
|
||||
struct base64 {
|
||||
static bool encode(char *&output, const uint8_t* input, unsigned inlength) {
|
||||
output = new char[inlength * 8 / 6 + 8]();
|
||||
|
||||
unsigned i = 0, o = 0;
|
||||
while(i < inlength) {
|
||||
switch(i % 3) {
|
||||
|
||||
case 0: {
|
||||
output[o++] = enc(input[i] >> 2);
|
||||
output[o] = enc((input[i] & 3) << 4);
|
||||
break;
|
||||
}
|
||||
|
||||
case 1: {
|
||||
uint8_t prev = dec(output[o]);
|
||||
output[o++] = enc(prev + (input[i] >> 4));
|
||||
output[o] = enc((input[i] & 15) << 2);
|
||||
break;
|
||||
}
|
||||
|
||||
case 2: {
|
||||
uint8_t prev = dec(output[o]);
|
||||
output[o++] = enc(prev + (input[i] >> 6));
|
||||
output[o++] = enc(input[i] & 63);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static string encode(const string &data) {
|
||||
char *buffer = nullptr;
|
||||
encode(buffer, (const uint8_t*)(const char*)data, data.length());
|
||||
string result = buffer;
|
||||
delete[] buffer;
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool decode(uint8_t *&output, unsigned &outlength, const char *input) {
|
||||
unsigned inlength = strlen(input), infix = 0;
|
||||
output = new uint8_t[inlength + 1]();
|
||||
|
||||
unsigned i = 0, o = 0;
|
||||
while(i < inlength) {
|
||||
uint8_t x = dec(input[i]);
|
||||
|
||||
switch(i++ & 3) {
|
||||
|
||||
case 0: {
|
||||
output[o] = x << 2;
|
||||
break;
|
||||
}
|
||||
|
||||
case 1: {
|
||||
output[o++] |= x >> 4;
|
||||
output[o] = (x & 15) << 4;
|
||||
break;
|
||||
}
|
||||
|
||||
case 2: {
|
||||
output[o++] |= x >> 2;
|
||||
output[o] = (x & 3) << 6;
|
||||
break;
|
||||
}
|
||||
|
||||
case 3: {
|
||||
output[o++] |= x;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
outlength = o;
|
||||
return true;
|
||||
}
|
||||
|
||||
static string decode(const string &data) {
|
||||
uint8_t *buffer = nullptr;
|
||||
unsigned size = 0;
|
||||
decode(buffer, size, (const char*)data);
|
||||
string result = (const char*)buffer;
|
||||
delete[] buffer;
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
static char enc(uint8_t n) {
|
||||
//base64 for URL encodings (URL = -_, MIME = +/)
|
||||
static char lookup_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
||||
return lookup_table[n & 63];
|
||||
}
|
||||
|
||||
static uint8_t dec(char n) {
|
||||
if(n >= 'A' && n <= 'Z') return n - 'A';
|
||||
if(n >= 'a' && n <= 'z') return n - 'a' + 26;
|
||||
if(n >= '0' && n <= '9') return n - '0' + 52;
|
||||
if(n == '-') return 62;
|
||||
if(n == '_') return 63;
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,84 @@
|
||||
#ifndef NALL_BEAT_ARCHIVE_HPP
|
||||
#define NALL_BEAT_ARCHIVE_HPP
|
||||
|
||||
#include <nall/beat/base.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct beatArchive : beatBase {
|
||||
bool create(const string &beatname, string pathname, const string &metadata = "") {
|
||||
if(fp.open(beatname, file::mode::write) == false) return false;
|
||||
if(pathname.endswith("/") == false) pathname.append("/");
|
||||
|
||||
checksum = ~0;
|
||||
writeString("BPA1");
|
||||
writeNumber(metadata.length());
|
||||
writeString(metadata);
|
||||
|
||||
lstring list;
|
||||
ls(list, pathname, pathname);
|
||||
for(auto &name : list) {
|
||||
if(name.endswith("/")) {
|
||||
name.rtrim<1>("/");
|
||||
writeNumber(0 | ((name.length() - 1) << 1));
|
||||
writeString(name);
|
||||
} else {
|
||||
file stream;
|
||||
if(stream.open({pathname, name}, file::mode::read) == false) return false;
|
||||
writeNumber(1 | ((name.length() - 1) << 1));
|
||||
writeString(name);
|
||||
unsigned size = stream.size();
|
||||
writeNumber(size);
|
||||
uint32_t checksum = ~0;
|
||||
while(size--) {
|
||||
uint8_t data = stream.read();
|
||||
write(data);
|
||||
checksum = crc32_adjust(checksum, data);
|
||||
}
|
||||
writeChecksum(~checksum);
|
||||
}
|
||||
}
|
||||
|
||||
writeChecksum(~checksum);
|
||||
fp.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool unpack(const string &beatname, string pathname) {
|
||||
if(fp.open(beatname, file::mode::read) == false) return false;
|
||||
if(pathname.endswith("/") == false) pathname.append("/");
|
||||
|
||||
checksum = ~0;
|
||||
if(readString(4) != "BPA1") return false;
|
||||
unsigned length = readNumber();
|
||||
while(length--) read();
|
||||
|
||||
directory::create(pathname);
|
||||
while(fp.offset() < fp.size() - 4) {
|
||||
unsigned data = readNumber();
|
||||
string name = readString((data >> 1) + 1);
|
||||
if(name.position("\\") || name.position("../")) return false; //block path exploits
|
||||
|
||||
if((data & 1) == 0) {
|
||||
directory::create({pathname, name});
|
||||
} else {
|
||||
file stream;
|
||||
if(stream.open({pathname, name}, file::mode::write) == false) return false;
|
||||
unsigned size = readNumber();
|
||||
uint32_t checksum = ~0;
|
||||
while(size--) {
|
||||
uint8_t data = read();
|
||||
stream.write(data);
|
||||
checksum = crc32_adjust(checksum, data);
|
||||
}
|
||||
if(readChecksum(~checksum) == false) return false;
|
||||
}
|
||||
}
|
||||
|
||||
return readChecksum(~checksum);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,92 @@
|
||||
#ifndef NALL_BEAT_BASE_HPP
|
||||
#define NALL_BEAT_BASE_HPP
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct beatBase {
|
||||
protected:
|
||||
file fp;
|
||||
uint32_t checksum;
|
||||
|
||||
void ls(lstring &list, const string &path, const string &basepath) {
|
||||
lstring paths = directory::folders(path);
|
||||
for(auto &pathname : paths) {
|
||||
list.append(string{path, pathname}.ltrim<1>(basepath));
|
||||
ls(list, {path, pathname}, basepath);
|
||||
}
|
||||
|
||||
lstring files = directory::files(path);
|
||||
for(auto &filename : files) {
|
||||
list.append(string{path, filename}.ltrim<1>(basepath));
|
||||
}
|
||||
}
|
||||
|
||||
void write(uint8_t data) {
|
||||
fp.write(data);
|
||||
checksum = crc32_adjust(checksum, data);
|
||||
}
|
||||
|
||||
void writeNumber(uint64_t data) {
|
||||
while(true) {
|
||||
uint64_t x = data & 0x7f;
|
||||
data >>= 7;
|
||||
if(data == 0) return write(0x80 | x);
|
||||
write(x);
|
||||
data--;
|
||||
}
|
||||
}
|
||||
|
||||
void writeString(const string &text) {
|
||||
unsigned length = text.length();
|
||||
for(unsigned n = 0; n < length; n++) write(text[n]);
|
||||
}
|
||||
|
||||
void writeChecksum(uint32_t checksum) {
|
||||
write(checksum >> 0);
|
||||
write(checksum >> 8);
|
||||
write(checksum >> 16);
|
||||
write(checksum >> 24);
|
||||
}
|
||||
|
||||
uint8_t read() {
|
||||
uint8_t data = fp.read();
|
||||
checksum = crc32_adjust(checksum, data);
|
||||
return data;
|
||||
}
|
||||
|
||||
uint64_t readNumber() {
|
||||
uint64_t data = 0, shift = 1;
|
||||
while(true) {
|
||||
uint8_t x = read();
|
||||
data += (x & 0x7f) * shift;
|
||||
if(x & 0x80) break;
|
||||
shift <<= 7;
|
||||
data += shift;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
string readString(unsigned length) {
|
||||
string text;
|
||||
text.reserve(length + 1);
|
||||
for(unsigned n = 0; n < length; n++) {
|
||||
text[n] = fp.read();
|
||||
checksum = crc32_adjust(checksum, text[n]);
|
||||
}
|
||||
text[length] = 0;
|
||||
return text;
|
||||
}
|
||||
|
||||
bool readChecksum(uint32_t source) {
|
||||
uint32_t checksum = 0;
|
||||
checksum |= read() << 0;
|
||||
checksum |= read() << 8;
|
||||
checksum |= read() << 16;
|
||||
checksum |= read() << 24;
|
||||
return checksum == source;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
Executable → Regular
+2
-2
@@ -1,5 +1,5 @@
|
||||
#ifndef NALL_BPS_DELTA_HPP
|
||||
#define NALL_BPS_DELTA_HPP
|
||||
#ifndef NALL_BEAT_DELTA_HPP
|
||||
#define NALL_BEAT_DELTA_HPP
|
||||
|
||||
#include <nall/crc32.hpp>
|
||||
#include <nall/file.hpp>
|
||||
Executable → Regular
+2
-2
@@ -1,5 +1,5 @@
|
||||
#ifndef NALL_BPS_LINEAR_HPP
|
||||
#define NALL_BPS_LINEAR_HPP
|
||||
#ifndef NALL_BEAT_LINEAR_HPP
|
||||
#define NALL_BEAT_LINEAR_HPP
|
||||
|
||||
#include <nall/crc32.hpp>
|
||||
#include <nall/file.hpp>
|
||||
Executable → Regular
+2
-2
@@ -1,5 +1,5 @@
|
||||
#ifndef NALL_BPS_METADATA_HPP
|
||||
#define NALL_BPS_METADATA_HPP
|
||||
#ifndef NALL_BEAT_METADATA_HPP
|
||||
#define NALL_BEAT_METADATA_HPP
|
||||
|
||||
#include <nall/crc32.hpp>
|
||||
#include <nall/file.hpp>
|
||||
@@ -0,0 +1,242 @@
|
||||
#ifndef NALL_BEAT_MULTI_HPP
|
||||
#define NALL_BEAT_MULTI_HPP
|
||||
|
||||
#include <nall/beat/patch.hpp>
|
||||
#include <nall/beat/linear.hpp>
|
||||
#include <nall/beat/delta.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct bpsmulti {
|
||||
enum : unsigned {
|
||||
CreatePath = 0,
|
||||
CreateFile = 1,
|
||||
ModifyFile = 2,
|
||||
MirrorFile = 3,
|
||||
};
|
||||
|
||||
enum : unsigned {
|
||||
OriginSource = 0,
|
||||
OriginTarget = 1,
|
||||
};
|
||||
|
||||
bool create(const string &patchName, const string &sourcePath, const string &targetPath, bool delta = false, const string &metadata = "") {
|
||||
if(fp.open()) fp.close();
|
||||
fp.open(patchName, file::mode::write);
|
||||
checksum = ~0;
|
||||
|
||||
writeString("BPM1"); //signature
|
||||
writeNumber(metadata.length());
|
||||
writeString(metadata);
|
||||
|
||||
lstring sourceList, targetList;
|
||||
ls(sourceList, sourcePath, sourcePath);
|
||||
ls(targetList, targetPath, targetPath);
|
||||
|
||||
for(auto &targetName : targetList) {
|
||||
if(targetName.endswith("/")) {
|
||||
targetName.rtrim<1>("/");
|
||||
writeNumber(CreatePath | ((targetName.length() - 1) << 2));
|
||||
writeString(targetName);
|
||||
} else if(auto position = sourceList.find(targetName)) { //if sourceName == targetName
|
||||
file sp, dp;
|
||||
sp.open({sourcePath, targetName}, file::mode::read);
|
||||
dp.open({targetPath, targetName}, file::mode::read);
|
||||
|
||||
bool identical = sp.size() == dp.size();
|
||||
uint32_t cksum = ~0;
|
||||
|
||||
for(unsigned n = 0; n < sp.size(); n++) {
|
||||
uint8_t byte = sp.read();
|
||||
if(identical && byte != dp.read()) identical = false;
|
||||
cksum = crc32_adjust(cksum, byte);
|
||||
}
|
||||
|
||||
if(identical) {
|
||||
writeNumber(MirrorFile | ((targetName.length() - 1) << 2));
|
||||
writeString(targetName);
|
||||
writeNumber(OriginSource);
|
||||
writeChecksum(~cksum);
|
||||
} else {
|
||||
writeNumber(ModifyFile | ((targetName.length() - 1) << 2));
|
||||
writeString(targetName);
|
||||
writeNumber(OriginSource);
|
||||
|
||||
if(delta == false) {
|
||||
bpslinear patch;
|
||||
patch.source({sourcePath, targetName});
|
||||
patch.target({targetPath, targetName});
|
||||
patch.create({temppath(), "temp.bps"});
|
||||
} else {
|
||||
bpsdelta patch;
|
||||
patch.source({sourcePath, targetName});
|
||||
patch.target({targetPath, targetName});
|
||||
patch.create({temppath(), "temp.bps"});
|
||||
}
|
||||
|
||||
auto buffer = file::read({temppath(), "temp.bps"});
|
||||
writeNumber(buffer.size());
|
||||
for(auto &byte : buffer) write(byte);
|
||||
}
|
||||
} else {
|
||||
writeNumber(CreateFile | ((targetName.length() - 1) << 2));
|
||||
writeString(targetName);
|
||||
auto buffer = file::read({targetPath, targetName});
|
||||
writeNumber(buffer.size());
|
||||
for(auto &byte : buffer) write(byte);
|
||||
writeChecksum(crc32_calculate(buffer.data(), buffer.size()));
|
||||
}
|
||||
}
|
||||
|
||||
//checksum
|
||||
writeChecksum(~checksum);
|
||||
fp.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool apply(const string &patchName, const string &sourcePath, const string &targetPath) {
|
||||
directory::remove(targetPath); //start with a clean directory
|
||||
directory::create(targetPath);
|
||||
|
||||
if(fp.open()) fp.close();
|
||||
fp.open(patchName, file::mode::read);
|
||||
checksum = ~0;
|
||||
|
||||
if(readString(4) != "BPM1") return false;
|
||||
auto metadataLength = readNumber();
|
||||
while(metadataLength--) read();
|
||||
|
||||
while(fp.offset() < fp.size() - 4) {
|
||||
auto encoding = readNumber();
|
||||
unsigned action = encoding & 3;
|
||||
unsigned targetLength = (encoding >> 2) + 1;
|
||||
string targetName = readString(targetLength);
|
||||
|
||||
if(action == CreatePath) {
|
||||
directory::create({targetPath, targetName, "/"});
|
||||
} else if(action == CreateFile) {
|
||||
file fp;
|
||||
fp.open({targetPath, targetName}, file::mode::write);
|
||||
auto fileSize = readNumber();
|
||||
while(fileSize--) fp.write(read());
|
||||
uint32_t cksum = readChecksum();
|
||||
} else if(action == ModifyFile) {
|
||||
auto encoding = readNumber();
|
||||
string originPath = encoding & 1 ? targetPath : sourcePath;
|
||||
string sourceName = (encoding >> 1) == 0 ? targetName : readString(encoding >> 1);
|
||||
auto patchSize = readNumber();
|
||||
vector<uint8_t> buffer;
|
||||
buffer.resize(patchSize);
|
||||
for(unsigned n = 0; n < patchSize; n++) buffer[n] = read();
|
||||
bpspatch patch;
|
||||
patch.modify(buffer.data(), buffer.size());
|
||||
patch.source({originPath, sourceName});
|
||||
patch.target({targetPath, targetName});
|
||||
if(patch.apply() != bpspatch::result::success) return false;
|
||||
} else if(action == MirrorFile) {
|
||||
auto encoding = readNumber();
|
||||
string originPath = encoding & 1 ? targetPath : sourcePath;
|
||||
string sourceName = (encoding >> 1) == 0 ? targetName : readString(encoding >> 1);
|
||||
file::copy({originPath, sourceName}, {targetPath, targetName});
|
||||
uint32_t cksum = readChecksum();
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t cksum = ~checksum;
|
||||
if(read() != (uint8_t)(cksum >> 0)) return false;
|
||||
if(read() != (uint8_t)(cksum >> 8)) return false;
|
||||
if(read() != (uint8_t)(cksum >> 16)) return false;
|
||||
if(read() != (uint8_t)(cksum >> 24)) return false;
|
||||
|
||||
fp.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
file fp;
|
||||
uint32_t checksum;
|
||||
|
||||
//create() functions
|
||||
void ls(lstring &list, const string &path, const string &basepath) {
|
||||
lstring paths = directory::folders(path);
|
||||
for(auto &pathname : paths) {
|
||||
list.append(string{path, pathname}.ltrim<1>(basepath));
|
||||
ls(list, {path, pathname}, basepath);
|
||||
}
|
||||
|
||||
lstring files = directory::files(path);
|
||||
for(auto &filename : files) {
|
||||
list.append(string{path, filename}.ltrim<1>(basepath));
|
||||
}
|
||||
}
|
||||
|
||||
void write(uint8_t data) {
|
||||
fp.write(data);
|
||||
checksum = crc32_adjust(checksum, data);
|
||||
}
|
||||
|
||||
void writeNumber(uint64_t data) {
|
||||
while(true) {
|
||||
uint64_t x = data & 0x7f;
|
||||
data >>= 7;
|
||||
if(data == 0) {
|
||||
write(0x80 | x);
|
||||
break;
|
||||
}
|
||||
write(x);
|
||||
data--;
|
||||
}
|
||||
}
|
||||
|
||||
void writeString(const string &text) {
|
||||
unsigned length = text.length();
|
||||
for(unsigned n = 0; n < length; n++) write(text[n]);
|
||||
}
|
||||
|
||||
void writeChecksum(uint32_t cksum) {
|
||||
write(cksum >> 0);
|
||||
write(cksum >> 8);
|
||||
write(cksum >> 16);
|
||||
write(cksum >> 24);
|
||||
}
|
||||
|
||||
//apply() functions
|
||||
uint8_t read() {
|
||||
uint8_t data = fp.read();
|
||||
checksum = crc32_adjust(checksum, data);
|
||||
return data;
|
||||
}
|
||||
|
||||
uint64_t readNumber() {
|
||||
uint64_t data = 0, shift = 1;
|
||||
while(true) {
|
||||
uint8_t x = read();
|
||||
data += (x & 0x7f) * shift;
|
||||
if(x & 0x80) break;
|
||||
shift <<= 7;
|
||||
data += shift;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
string readString(unsigned length) {
|
||||
string text;
|
||||
text.reserve(length + 1);
|
||||
for(unsigned n = 0; n < length; n++) text[n] = read();
|
||||
text[length] = 0;
|
||||
return text;
|
||||
}
|
||||
|
||||
uint32_t readChecksum() {
|
||||
uint32_t checksum = 0;
|
||||
checksum |= read() << 0;
|
||||
checksum |= read() << 8;
|
||||
checksum |= read() << 16;
|
||||
checksum |= read() << 24;
|
||||
return checksum;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
Executable → Regular
+2
-2
@@ -1,5 +1,5 @@
|
||||
#ifndef NALL_BPS_PATCH_HPP
|
||||
#define NALL_BPS_PATCH_HPP
|
||||
#ifndef NALL_BEAT_PATCH_HPP
|
||||
#define NALL_BEAT_PATCH_HPP
|
||||
|
||||
#include <nall/crc32.hpp>
|
||||
#include <nall/file.hpp>
|
||||
Executable → Regular
+2
@@ -1,6 +1,8 @@
|
||||
#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) {
|
||||
Executable → Regular
File renamed without changes.
Executable → Regular
File renamed without changes.
Executable → Regular
File renamed without changes.
@@ -0,0 +1,25 @@
|
||||
#ifndef NALL_CRC16_HPP
|
||||
#define NALL_CRC16_HPP
|
||||
|
||||
#include <nall/stdint.hpp>
|
||||
|
||||
namespace nall {
|
||||
inline uint16_t crc16_adjust(uint16_t crc16, uint8_t data) {
|
||||
for(unsigned n = 0; n < 8; n++) {
|
||||
if((crc16 & 1) ^ (data & 1)) crc16 = (crc16 >> 1) ^ 0x8408;
|
||||
else crc16 >>= 1;
|
||||
data >>= 1;
|
||||
}
|
||||
return crc16;
|
||||
}
|
||||
|
||||
inline uint16_t crc16_calculate(const uint8_t *data, unsigned length) {
|
||||
uint16_t crc16 = ~0;
|
||||
for(unsigned n = 0; n < length; n++) {
|
||||
crc16 = crc16_adjust(crc16, data[n]);
|
||||
}
|
||||
return ~crc16;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
Executable → Regular
File renamed without changes.
Executable → Regular
+97
-26
@@ -1,6 +1,7 @@
|
||||
#ifndef NALL_DIRECTORY_HPP
|
||||
#define NALL_DIRECTORY_HPP
|
||||
|
||||
#include <nall/file.hpp>
|
||||
#include <nall/intrinsics.hpp>
|
||||
#include <nall/sort.hpp>
|
||||
#include <nall/string.hpp>
|
||||
@@ -17,20 +18,88 @@
|
||||
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 = "*");
|
||||
static lstring files(const string &pathname, const string &pattern = "*");
|
||||
static lstring contents(const string &pathname, const string &pattern = "*");
|
||||
|
||||
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) {
|
||||
DWORD result = GetFileAttributes(utf16_t(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::folders(const string &pathname, const string &pattern) {
|
||||
inline lstring directory::ufolders(const string &pathname, const string &pattern) {
|
||||
lstring list;
|
||||
string path = pathname;
|
||||
path.transform("/", "\\");
|
||||
@@ -56,12 +125,11 @@ struct directory {
|
||||
}
|
||||
FindClose(handle);
|
||||
}
|
||||
if(list.size() > 0) list.sort();
|
||||
for(auto &name : list) name.append("/"); //must append after sorting
|
||||
return list;
|
||||
}
|
||||
|
||||
inline lstring directory::files(const string &pathname, const string &pattern) {
|
||||
inline lstring directory::ufiles(const string &pathname, const string &pattern) {
|
||||
lstring list;
|
||||
string path = pathname;
|
||||
path.transform("/", "\\");
|
||||
@@ -83,17 +151,29 @@ struct directory {
|
||||
}
|
||||
FindClose(handle);
|
||||
}
|
||||
if(list.size() > 0) list.sort();
|
||||
return list;
|
||||
}
|
||||
|
||||
inline lstring directory::contents(const string &pathname, const string &pattern) {
|
||||
lstring folders = directory::folders(pathname); //pattern search of contents() should only filter files
|
||||
lstring files = directory::files(pathname, pattern);
|
||||
for(auto &file : files) folders.append(file);
|
||||
return folders;
|
||||
}
|
||||
#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;
|
||||
@@ -101,7 +181,7 @@ struct directory {
|
||||
return true;
|
||||
}
|
||||
|
||||
inline lstring directory::folders(const string &pathname, const string &pattern) {
|
||||
inline lstring directory::ufolders(const string &pathname, const string &pattern) {
|
||||
lstring list;
|
||||
DIR *dp;
|
||||
struct dirent *ep;
|
||||
@@ -116,12 +196,11 @@ struct directory {
|
||||
}
|
||||
closedir(dp);
|
||||
}
|
||||
if(list.size() > 0) list.sort();
|
||||
for(auto &name : list) name.append("/"); //must append after sorting
|
||||
return list;
|
||||
}
|
||||
|
||||
inline lstring directory::files(const string &pathname, const string &pattern) {
|
||||
inline lstring directory::ufiles(const string &pathname, const string &pattern) {
|
||||
lstring list;
|
||||
DIR *dp;
|
||||
struct dirent *ep;
|
||||
@@ -136,16 +215,8 @@ struct directory {
|
||||
}
|
||||
closedir(dp);
|
||||
}
|
||||
if(list.size() > 0) list.sort();
|
||||
return list;
|
||||
}
|
||||
|
||||
inline lstring directory::contents(const string &pathname, const string &pattern) {
|
||||
lstring folders = directory::folders(pathname); //pattern search of contents() should only filter files
|
||||
lstring files = directory::files(pathname, pattern);
|
||||
for(auto &file : files) folders.append(file);
|
||||
return folders;
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
Executable → Regular
File renamed without changes.
Executable → Regular
File renamed without changes.
Executable → Regular
File renamed without changes.
Executable → Regular
File renamed without changes.
Executable → Regular
File renamed without changes.
Executable → Regular
File renamed without changes.
Executable → Regular
File renamed without changes.
Executable → Regular
File renamed without changes.
Executable → Regular
File renamed without changes.
Executable → Regular
File renamed without changes.
Executable → Regular
File renamed without changes.
Executable → Regular
File renamed without changes.
Executable → Regular
File renamed without changes.
Executable → Regular
+8
-10
@@ -1,5 +1,5 @@
|
||||
#ifndef NALL_SNES_USART_HPP
|
||||
#define NALL_SNES_USART_HPP
|
||||
#ifndef NALL_EMULATION_SUPER_FAMICOM_USART_HPP
|
||||
#define NALL_EMULATION_SUPER_FAMICOM_USART_HPP
|
||||
|
||||
#include <nall/platform.hpp>
|
||||
#include <nall/function.hpp>
|
||||
@@ -35,7 +35,7 @@ extern "C" usartproc void usart_init(
|
||||
usart_write = write;
|
||||
}
|
||||
|
||||
extern "C" usartproc void usart_main();
|
||||
extern "C" usartproc void usart_main(int, char**);
|
||||
|
||||
//
|
||||
|
||||
@@ -84,21 +84,19 @@ static void sigint(int) {
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
//requires superuser privileges; otherwise priority = +0
|
||||
setpriority(PRIO_PROCESS, 0, -20);
|
||||
setpriority(PRIO_PROCESS, 0, -20); //requires superuser privileges; otherwise priority = +0
|
||||
signal(SIGINT, sigint);
|
||||
|
||||
bool result = false;
|
||||
if(argc == 1) result = usart.open("/dev/ttyACM0", 57600, true);
|
||||
if(argc == 2) result = usart.open(argv[1], 57600, true);
|
||||
if(result == false) {
|
||||
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();
|
||||
usart_main(argc, argv);
|
||||
usart.close();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Executable → Regular
File renamed without changes.
Executable → Regular
+72
-14
@@ -6,6 +6,7 @@
|
||||
#include <nall/string.hpp>
|
||||
#include <nall/utility.hpp>
|
||||
#include <nall/windows/utf8.hpp>
|
||||
#include <nall/stream/memory.hpp>
|
||||
|
||||
namespace nall {
|
||||
inline FILE* fopen_utf8(const string &utf8_filename, const char *mode) {
|
||||
@@ -16,25 +17,77 @@ namespace nall {
|
||||
#endif
|
||||
}
|
||||
|
||||
class file {
|
||||
public:
|
||||
enum class mode : unsigned { read, write, readwrite, writeread };
|
||||
struct file {
|
||||
enum class mode : unsigned { read, write, modify, append, readwrite = modify, writeread = append };
|
||||
enum class index : unsigned { absolute, relative };
|
||||
enum class time : unsigned { create, modify, access };
|
||||
|
||||
static bool read(const string &filename, uint8_t *&data, unsigned &size) {
|
||||
data = 0;
|
||||
static bool copy(const string &sourcename, const string &targetname) {
|
||||
file rd, wr;
|
||||
if(rd.open(sourcename, mode::read) == false) return false;
|
||||
if(wr.open(targetname, mode::write) == false) return false;
|
||||
for(unsigned n = 0; n < rd.size(); n++) wr.write(rd.read());
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool move(const string &sourcename, const string &targetname) {
|
||||
#if !defined(_WIN32)
|
||||
return rename(sourcename, targetname) == 0;
|
||||
#else
|
||||
return _wrename(utf16_t(sourcename), utf16_t(targetname)) == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool remove(const string &filename) {
|
||||
return unlink(filename) == 0;
|
||||
}
|
||||
|
||||
static bool truncate(const string &filename, unsigned size) {
|
||||
#if !defined(_WIN32)
|
||||
return truncate(filename, size) == 0;
|
||||
#else
|
||||
bool result = false;
|
||||
FILE *fp = fopen(filename, "rb+");
|
||||
if(fp) {
|
||||
result = _chsize(fileno(fp), size) == 0;
|
||||
fclose(fp);
|
||||
}
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
static vector<uint8_t> read(const string &filename) {
|
||||
vector<uint8_t> memory;
|
||||
file fp;
|
||||
if(fp.open(filename, mode::read)) {
|
||||
memory.resize(fp.size());
|
||||
fp.read(memory.data(), memory.size());
|
||||
}
|
||||
return memory;
|
||||
}
|
||||
|
||||
static bool read(const string &filename, uint8_t *data, unsigned size) {
|
||||
file fp;
|
||||
if(fp.open(filename, mode::read) == false) return false;
|
||||
size = fp.size();
|
||||
data = new uint8_t[size];
|
||||
fp.read(data, size);
|
||||
fp.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool read(const string &filename, const uint8_t *&data, unsigned &size) {
|
||||
return file::read(filename, (uint8_t*&)data, size);
|
||||
static bool write(const string &filename, const string &text) {
|
||||
file fp;
|
||||
if(fp.open(filename, mode::write) == false) return false;
|
||||
fp.print(text);
|
||||
fp.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool write(const string &filename, const vector<uint8_t> &buffer) {
|
||||
file fp;
|
||||
if(fp.open(filename, mode::write) == false) return false;
|
||||
fp.write(buffer.data(), buffer.size());
|
||||
fp.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool write(const string &filename, const uint8_t *data, unsigned size) {
|
||||
@@ -45,6 +98,11 @@ namespace nall {
|
||||
return true;
|
||||
}
|
||||
|
||||
static string sha256(const string &filename) {
|
||||
auto buffer = read(filename);
|
||||
return nall::sha256(buffer.data(), buffer.size());
|
||||
}
|
||||
|
||||
uint8_t read() {
|
||||
if(!fp) return 0xff; //file not open
|
||||
if(file_mode == mode::write) return 0xff; //reads not permitted
|
||||
@@ -134,13 +192,13 @@ namespace nall {
|
||||
file_offset = req_offset;
|
||||
}
|
||||
|
||||
int offset() const {
|
||||
if(!fp) return -1; //file not open
|
||||
unsigned offset() const {
|
||||
if(!fp) return 0; //file not open
|
||||
return file_offset;
|
||||
}
|
||||
|
||||
int size() const {
|
||||
if(!fp) return -1; //file not open
|
||||
unsigned size() const {
|
||||
if(!fp) return 0; //file not open
|
||||
return file_size;
|
||||
}
|
||||
|
||||
@@ -232,7 +290,7 @@ namespace nall {
|
||||
|
||||
file() {
|
||||
memset(buffer, 0, sizeof buffer);
|
||||
buffer_offset = -1;
|
||||
buffer_offset = -1; //invalidate buffer
|
||||
buffer_dirty = false;
|
||||
fp = 0;
|
||||
file_offset = 0;
|
||||
Executable → Regular
File renamed without changes.
Executable → Regular
File renamed without changes.
Executable → Regular
+4
-6
@@ -19,12 +19,10 @@ struct gzip {
|
||||
};
|
||||
|
||||
bool gzip::decompress(const string &filename) {
|
||||
uint8_t *data;
|
||||
unsigned size;
|
||||
if(file::read(filename, data, size) == false) return false;
|
||||
bool result = decompress(data, size);
|
||||
delete[] data;
|
||||
return result;
|
||||
if(auto memory = file::read(filename)) {
|
||||
return decompress(memory.data(), memory.size());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool gzip::decompress(const uint8_t *data, unsigned size) {
|
||||
Executable → Regular
+1
-1
@@ -7,9 +7,9 @@
|
||||
#include <netinet/in.h>
|
||||
#include <netdb.h>
|
||||
#else
|
||||
#include <windows.h>
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include <nall/platform.hpp>
|
||||
Executable → Regular
+74
@@ -24,6 +24,14 @@ struct image {
|
||||
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);
|
||||
@@ -31,11 +39,16 @@ struct image {
|
||||
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();
|
||||
|
||||
@@ -43,6 +56,7 @@ struct image {
|
||||
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);
|
||||
@@ -86,6 +100,26 @@ uint64_t image::normalize(uint64_t color, unsigned sourceDepth, unsigned targetD
|
||||
|
||||
//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();
|
||||
|
||||
@@ -107,6 +141,8 @@ image& image::operator=(const image &source) {
|
||||
}
|
||||
|
||||
image& image::operator=(image &&source) {
|
||||
free();
|
||||
|
||||
width = source.width;
|
||||
height = source.height;
|
||||
pitch = source.pitch;
|
||||
@@ -146,6 +182,38 @@ image::image(bool endian, unsigned depth, uint64_t alphaMask, uint64_t redMask,
|
||||
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;
|
||||
|
||||
@@ -187,6 +255,12 @@ void image::free() {
|
||||
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();
|
||||
Executable → Regular
File renamed without changes.
Executable → Regular
File renamed without changes.
Executable → Regular
File renamed without changes.
Executable → Regular
File renamed without changes.
@@ -0,0 +1,52 @@
|
||||
#ifndef NALL_INVOKE_HPP
|
||||
#define NALL_INVOKE_HPP
|
||||
|
||||
//void invoke(const string &name, const string& args...);
|
||||
//if a program is specified, it is executed with the arguments provided
|
||||
//if a file is specified, the file is opened using the program associated with said file type
|
||||
//if a folder is specified, the folder is opened using the associated file explorer
|
||||
//if a URL is specified, the default web browser is opened and pointed at the URL requested
|
||||
//path environment variable is always consulted
|
||||
//execution is asynchronous (non-blocking); use system() for synchronous execution
|
||||
|
||||
#include <nall/string.hpp>
|
||||
#ifdef _WIN32
|
||||
#include <nall/windows/utf8.hpp>
|
||||
#endif
|
||||
|
||||
namespace nall {
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
template<typename... Args>
|
||||
inline void invoke(const string &name, Args&&... args) {
|
||||
lstring argl(std::forward<Args>(args)...);
|
||||
for(auto &arg : argl) if(arg.position(" ")) arg = {"\"", arg, "\""};
|
||||
string arguments = argl.concatenate(" ");
|
||||
ShellExecuteW(NULL, NULL, utf16_t(name), utf16_t(arguments), NULL, SW_SHOWNORMAL);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
template<typename... Args>
|
||||
inline void invoke(const string &name, Args&&... args) {
|
||||
pid_t pid = fork();
|
||||
if(pid == 0) {
|
||||
const char *argv[1 + sizeof...(args) + 1], **argp = argv;
|
||||
lstring argl(std::forward<Args>(args)...);
|
||||
*argp++ = (const char*)name;
|
||||
for(auto &arg : argl) *argp++ = (const char*)arg;
|
||||
*argp++ = nullptr;
|
||||
|
||||
if(execvp(name, (char* const*)argv) < 0) {
|
||||
execlp("xdg-open", "xdg-open", (const char*)name, nullptr);
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
Executable → Regular
-10
@@ -11,8 +11,6 @@ struct ips {
|
||||
inline bool apply();
|
||||
inline void source(const uint8_t *data, unsigned size);
|
||||
inline void modify(const uint8_t *data, unsigned size);
|
||||
inline bool source(const string &filename);
|
||||
inline bool modify(const string &filename);
|
||||
inline ips();
|
||||
inline ~ips();
|
||||
|
||||
@@ -88,14 +86,6 @@ void ips::modify(const uint8_t *data, unsigned size) {
|
||||
modifyData = data, modifySize = size;
|
||||
}
|
||||
|
||||
bool ips::source(const string &filename) {
|
||||
return file::read(filename, sourceData, sourceSize);
|
||||
}
|
||||
|
||||
bool ips::modify(const string &filename) {
|
||||
return file::read(filename, modifyData, modifySize);
|
||||
}
|
||||
|
||||
ips::ips() : data(nullptr), sourceData(nullptr), modifyData(nullptr) {
|
||||
}
|
||||
|
||||
Executable → Regular
File renamed without changes.
Executable → Regular
+1
@@ -68,6 +68,7 @@ struct map {
|
||||
}
|
||||
|
||||
inline RHS& operator()(const LHS &name) {
|
||||
if(auto position = find(name)) return list[position()].data;
|
||||
return insert(name, RHS());
|
||||
}
|
||||
|
||||
Executable → Regular
File renamed without changes.
Executable → Regular
File renamed without changes.
Executable → Regular
+5
-5
@@ -17,24 +17,24 @@ struct context {
|
||||
unsigned blockHeight;
|
||||
unsigned blockStride;
|
||||
unsigned blockOffset;
|
||||
array<unsigned> block;
|
||||
vector<unsigned> block;
|
||||
|
||||
unsigned tileWidth;
|
||||
unsigned tileHeight;
|
||||
unsigned tileStride;
|
||||
unsigned tileOffset;
|
||||
array<unsigned> tile;
|
||||
vector<unsigned> tile;
|
||||
|
||||
unsigned mosaicWidth;
|
||||
unsigned mosaicHeight;
|
||||
unsigned mosaicStride;
|
||||
unsigned mosaicOffset;
|
||||
array<unsigned> mosaic;
|
||||
vector<unsigned> mosaic;
|
||||
|
||||
unsigned paddingWidth;
|
||||
unsigned paddingHeight;
|
||||
unsigned paddingColor;
|
||||
array<unsigned> palette;
|
||||
vector<unsigned> palette;
|
||||
|
||||
inline unsigned objectWidth() const { return blockWidth * tileWidth * mosaicWidth + paddingWidth; }
|
||||
inline unsigned objectHeight() const { return blockHeight * tileHeight * mosaicHeight + paddingHeight; }
|
||||
@@ -52,7 +52,7 @@ struct context {
|
||||
return result;
|
||||
}
|
||||
|
||||
inline void eval(array<unsigned> &buffer, const string &expression_) {
|
||||
inline void eval(vector<unsigned> &buffer, const string &expression_) {
|
||||
string expression = expression_;
|
||||
bool function = false;
|
||||
for(auto &c : expression) {
|
||||
Executable → Regular
File renamed without changes.
@@ -0,0 +1,58 @@
|
||||
#ifndef NALL_HPP
|
||||
#define NALL_HPP
|
||||
|
||||
//include the most common nall headers with one statement
|
||||
//does not include the most obscure components with high cost and low usage
|
||||
|
||||
#include <nall/platform.hpp>
|
||||
|
||||
#include <nall/algorithm.hpp>
|
||||
#include <nall/any.hpp>
|
||||
#include <nall/atoi.hpp>
|
||||
#include <nall/base64.hpp>
|
||||
#include <nall/bit.hpp>
|
||||
#include <nall/bmp.hpp>
|
||||
#include <nall/config.hpp>
|
||||
#include <nall/crc16.hpp>
|
||||
#include <nall/crc32.hpp>
|
||||
#include <nall/directory.hpp>
|
||||
#include <nall/dl.hpp>
|
||||
#include <nall/endian.hpp>
|
||||
#include <nall/file.hpp>
|
||||
#include <nall/filemap.hpp>
|
||||
#include <nall/function.hpp>
|
||||
#include <nall/gzip.hpp>
|
||||
#include <nall/http.hpp>
|
||||
#include <nall/image.hpp>
|
||||
#include <nall/inflate.hpp>
|
||||
#include <nall/interpolation.hpp>
|
||||
#include <nall/intrinsics.hpp>
|
||||
#include <nall/invoke.hpp>
|
||||
#include <nall/map.hpp>
|
||||
#include <nall/png.hpp>
|
||||
#include <nall/property.hpp>
|
||||
#include <nall/random.hpp>
|
||||
#include <nall/serializer.hpp>
|
||||
#include <nall/set.hpp>
|
||||
#include <nall/sha256.hpp>
|
||||
#include <nall/sort.hpp>
|
||||
#include <nall/stdint.hpp>
|
||||
#include <nall/stream.hpp>
|
||||
#include <nall/string.hpp>
|
||||
#include <nall/traits.hpp>
|
||||
#include <nall/unzip.hpp>
|
||||
#include <nall/utility.hpp>
|
||||
#include <nall/varint.hpp>
|
||||
#include <nall/vector.hpp>
|
||||
#include <nall/zip.hpp>
|
||||
|
||||
#if defined(PLATFORM_WINDOWS)
|
||||
#include <nall/windows/registry.hpp>
|
||||
#include <nall/windows/utf8.hpp>
|
||||
#endif
|
||||
|
||||
#if defined(PLATFORM_X)
|
||||
#include <nall/serial.hpp>
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,86 @@
|
||||
#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
|
||||
Executable → Regular
+4
-6
@@ -58,12 +58,10 @@ protected:
|
||||
};
|
||||
|
||||
bool png::decode(const string &filename) {
|
||||
uint8_t *data;
|
||||
unsigned size;
|
||||
if(file::read(filename, data, size) == false) return false;
|
||||
bool result = decode(data, size);
|
||||
delete[] data;
|
||||
return result;
|
||||
if(auto memory = file::read(filename)) {
|
||||
return decode(memory.data(), memory.size());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool png::decode(const uint8_t *sourceData, unsigned sourceSize) {
|
||||
Executable → Regular
+2
-2
@@ -1,5 +1,5 @@
|
||||
#ifndef NALL_PRIORITYQUEUE_HPP
|
||||
#define NALL_PRIORITYQUEUE_HPP
|
||||
#ifndef NALL_PRIORITY_QUEUE_HPP
|
||||
#define NALL_PRIORITY_QUEUE_HPP
|
||||
|
||||
#include <limits>
|
||||
#include <nall/function.hpp>
|
||||
Executable → Regular
File renamed without changes.
Executable → Regular
File renamed without changes.
Executable → Regular
File renamed without changes.
Executable → Regular
File renamed without changes.
Executable → Regular
File renamed without changes.
@@ -0,0 +1,158 @@
|
||||
#ifndef NALL_SET_HPP
|
||||
#define NALL_SET_HPP
|
||||
|
||||
//set
|
||||
//* unordered
|
||||
//* intended for unique items
|
||||
//* dynamic growth
|
||||
//* reference-based variant
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <algorithm>
|
||||
#include <initializer_list>
|
||||
#include <utility>
|
||||
#include <nall/algorithm.hpp>
|
||||
#include <nall/bit.hpp>
|
||||
#include <nall/sort.hpp>
|
||||
#include <nall/traits.hpp>
|
||||
#include <nall/utility.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
template<typename T, typename Enable = void> struct set;
|
||||
|
||||
template<typename T> struct set<T, typename std::enable_if<!std::is_reference<T>::value>::type> {
|
||||
struct exception_out_of_bounds{};
|
||||
|
||||
protected:
|
||||
T *pool;
|
||||
unsigned poolsize, objectsize;
|
||||
|
||||
public:
|
||||
unsigned size() const { return objectsize; }
|
||||
unsigned capacity() const { return poolsize; }
|
||||
};
|
||||
|
||||
//reference set
|
||||
template<typename TR> struct set<TR, typename std::enable_if<std::is_reference<TR>::value>::type> {
|
||||
struct exception_out_of_bounds{};
|
||||
|
||||
protected:
|
||||
typedef typename std::remove_reference<TR>::type T;
|
||||
T **pool;
|
||||
unsigned poolsize, objectsize;
|
||||
|
||||
public:
|
||||
unsigned size() const { return objectsize; }
|
||||
unsigned capacity() const { return poolsize; }
|
||||
|
||||
void reset() {
|
||||
if(pool) free(pool);
|
||||
pool = nullptr;
|
||||
poolsize = 0;
|
||||
objectsize = 0;
|
||||
}
|
||||
|
||||
void reserve(unsigned size) {
|
||||
if(size == poolsize) return;
|
||||
pool = (T**)realloc(pool, sizeof(T*) * size);
|
||||
poolsize = size;
|
||||
objectsize = min(objectsize, size);
|
||||
}
|
||||
|
||||
void resize(unsigned size) {
|
||||
if(size > poolsize) reserve(bit::round(size)); //amortize growth
|
||||
objectsize = size;
|
||||
}
|
||||
|
||||
bool append(T& data) {
|
||||
if(find(data)) return false;
|
||||
unsigned offset = objectsize++;
|
||||
if(offset >= poolsize) resize(offset + 1);
|
||||
pool[offset] = &data;
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
bool append(T& data, Args&&... args) {
|
||||
bool result = append(data);
|
||||
append(std::forward<Args>(args)...);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool remove(T& data) {
|
||||
if(auto position = find(data)) {
|
||||
for(signed i = position(); i < objectsize - 1; i++) pool[i] = pool[i + 1];
|
||||
resize(objectsize - 1);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
optional<unsigned> find(const T& data) {
|
||||
for(unsigned n = 0; n < objectsize; n++) if(pool[n] == &data) return {true, n};
|
||||
return {false, 0u};
|
||||
}
|
||||
|
||||
template<typename... Args> set(Args&&... args) : pool(nullptr), poolsize(0), objectsize(0) {
|
||||
construct(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
~set() {
|
||||
reset();
|
||||
}
|
||||
|
||||
set& operator=(const set &source) {
|
||||
if(&source == this) return *this;
|
||||
if(pool) free(pool);
|
||||
objectsize = source.objectsize;
|
||||
poolsize = source.poolsize;
|
||||
pool = (T**)malloc(sizeof(T*) * poolsize);
|
||||
memcpy(pool, source.pool, sizeof(T*) * objectsize);
|
||||
return *this;
|
||||
}
|
||||
|
||||
set& operator=(const set &&source) {
|
||||
if(&source == this) return *this;
|
||||
if(pool) free(pool);
|
||||
pool = source.pool;
|
||||
poolsize = source.poolsize;
|
||||
objectsize = source.objectsize;
|
||||
source.pool = nullptr;
|
||||
source.reset();
|
||||
return *this;
|
||||
}
|
||||
|
||||
T& operator[](unsigned position) const {
|
||||
if(position >= objectsize) throw exception_out_of_bounds();
|
||||
return *pool[position];
|
||||
}
|
||||
|
||||
struct iterator {
|
||||
bool operator!=(const iterator &source) const { return position != source.position; }
|
||||
T& operator*() { return source.operator[](position); }
|
||||
iterator& operator++() { position++; return *this; }
|
||||
iterator(const set &source, unsigned position) : source(source), position(position) {}
|
||||
private:
|
||||
const set &source;
|
||||
unsigned position;
|
||||
};
|
||||
|
||||
iterator begin() { return iterator(*this, 0); }
|
||||
iterator end() { return iterator(*this, objectsize); }
|
||||
const iterator begin() const { return iterator(*this, 0); }
|
||||
const iterator end() const { return iterator(*this, objectsize); }
|
||||
|
||||
private:
|
||||
void construct() {}
|
||||
void construct(const set &source) { operator=(source); }
|
||||
void construct(const set &&source) { operator=(std::move(source)); }
|
||||
template<typename... Args> void construct(T& data, Args&&... args) {
|
||||
append(data);
|
||||
construct(std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
Executable → Regular
File renamed without changes.
Executable → Regular
+2
-2
@@ -29,7 +29,7 @@ namespace nall {
|
||||
for(signed i = 1, j; i < size; i++) {
|
||||
T copy = std::move(list[i]);
|
||||
for(j = i - 1; j >= 0; j--) {
|
||||
if(lessthan(list[j], copy)) break;
|
||||
if(!lessthan(copy, list[j])) break;
|
||||
list[j + 1] = std::move(list[j]);
|
||||
}
|
||||
list[j + 1] = std::move(copy);
|
||||
@@ -55,7 +55,7 @@ namespace nall {
|
||||
T *buffer = new T[size];
|
||||
unsigned offset = 0, left = 0, right = middle;
|
||||
while(left < middle && right < size) {
|
||||
if(lessthan(list[left], list[right])) {
|
||||
if(!lessthan(list[right], list[left])) {
|
||||
buffer[offset++] = std::move(list[left++]);
|
||||
} else {
|
||||
buffer[offset++] = std::move(list[right++]);
|
||||
Executable → Regular
File renamed without changes.
Executable → Regular
File renamed without changes.
Executable → Regular
+2
-1
@@ -1,4 +1,5 @@
|
||||
#ifdef NALL_STREAM_INTERNAL_HPP
|
||||
#ifndef NALL_STREAM_AUTO_HPP
|
||||
#define NALL_STREAM_AUTO_HPP
|
||||
|
||||
namespace nall {
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
#ifndef NALL_STREAM_FILE_HPP
|
||||
#define NALL_STREAM_FILE_HPP
|
||||
|
||||
#include <nall/file.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct filestream : stream {
|
||||
using stream::read;
|
||||
using stream::write;
|
||||
|
||||
bool seekable() const { return true; }
|
||||
bool readable() const { return true; }
|
||||
bool writable() const { return pwritable; }
|
||||
bool randomaccess() const { return false; }
|
||||
|
||||
unsigned size() const { return pfile.size(); }
|
||||
unsigned offset() const { return pfile.offset(); }
|
||||
void seek(unsigned offset) const { pfile.seek(offset); }
|
||||
|
||||
uint8_t read() const { return pfile.read(); }
|
||||
void write(uint8_t data) const { pfile.write(data); }
|
||||
|
||||
filestream(const string &filename) {
|
||||
pfile.open(filename, file::mode::readwrite);
|
||||
pwritable = pfile.open();
|
||||
if(!pwritable) pfile.open(filename, file::mode::read);
|
||||
}
|
||||
|
||||
filestream(const string &filename, file::mode mode) {
|
||||
pfile.open(filename, mode);
|
||||
pwritable = mode == file::mode::write || mode == file::mode::readwrite;
|
||||
}
|
||||
|
||||
private:
|
||||
mutable file pfile;
|
||||
bool pwritable;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
Executable → Regular
+9
-3
@@ -1,9 +1,15 @@
|
||||
#ifdef NALL_STREAM_INTERNAL_HPP
|
||||
#ifndef NALL_STREAM_GZIP_HPP
|
||||
#define NALL_STREAM_GZIP_HPP
|
||||
|
||||
#include <nall/gzip.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct gzipstream : memorystream {
|
||||
inline gzipstream(const stream &stream) {
|
||||
using stream::read;
|
||||
using stream::write;
|
||||
|
||||
gzipstream(const stream &stream) {
|
||||
unsigned size = stream.size();
|
||||
uint8_t *data = new uint8_t[size];
|
||||
stream.read(data, size);
|
||||
@@ -18,7 +24,7 @@ struct gzipstream : memorystream {
|
||||
memcpy(pdata, archive.data, psize);
|
||||
}
|
||||
|
||||
inline ~gzipstream() {
|
||||
~gzipstream() {
|
||||
if(pdata) delete[] pdata;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,49 @@
|
||||
#ifndef NALL_STREAM_HTTP_HPP
|
||||
#define NALL_STREAM_HTTP_HPP
|
||||
|
||||
#include <nall/http.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct httpstream : stream {
|
||||
using stream::read;
|
||||
using stream::write;
|
||||
|
||||
bool seekable() const { return true; }
|
||||
bool readable() const { return true; }
|
||||
bool writable() const { return true; }
|
||||
bool randomaccess() const { return true; }
|
||||
|
||||
unsigned size() const { return psize; }
|
||||
unsigned offset() const { return poffset; }
|
||||
void seek(unsigned offset) const { poffset = offset; }
|
||||
|
||||
uint8_t read() const { return pdata[poffset++]; }
|
||||
void write(uint8_t data) const { pdata[poffset++] = data; }
|
||||
|
||||
uint8_t read(unsigned offset) const { return pdata[offset]; }
|
||||
void write(unsigned offset, uint8_t data) const { pdata[offset] = data; }
|
||||
|
||||
httpstream(const string &url, unsigned port) : pdata(nullptr), psize(0), poffset(0) {
|
||||
string uri = url;
|
||||
uri.ltrim<1>("http://");
|
||||
lstring part = uri.split<1>("/");
|
||||
part[1] = { "/", part[1] };
|
||||
|
||||
http connection;
|
||||
if(connection.connect(part[0], port) == false) return;
|
||||
connection.download(part[1], pdata, psize);
|
||||
}
|
||||
|
||||
~httpstream() {
|
||||
if(pdata) delete[] pdata;
|
||||
}
|
||||
|
||||
private:
|
||||
mutable uint8_t *pdata;
|
||||
mutable unsigned psize, poffset;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,47 @@
|
||||
#ifndef NALL_STREAM_MEMORY_HPP
|
||||
#define NALL_STREAM_MEMORY_HPP
|
||||
|
||||
#include <nall/stream/stream.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct memorystream : stream {
|
||||
using stream::read;
|
||||
using stream::write;
|
||||
|
||||
bool seekable() const { return true; }
|
||||
bool readable() const { return true; }
|
||||
bool writable() const { return pwritable; }
|
||||
bool randomaccess() const { return true; }
|
||||
|
||||
uint8_t *data() const { return pdata; }
|
||||
unsigned size() const { return psize; }
|
||||
unsigned offset() const { return poffset; }
|
||||
void seek(unsigned offset) const { poffset = offset; }
|
||||
|
||||
uint8_t read() const { return pdata[poffset++]; }
|
||||
void write(uint8_t data) const { pdata[poffset++] = data; }
|
||||
|
||||
uint8_t read(unsigned offset) const { return pdata[offset]; }
|
||||
void write(unsigned offset, uint8_t data) const { pdata[offset] = data; }
|
||||
|
||||
memorystream() : pdata(nullptr), psize(0), poffset(0), pwritable(true) {}
|
||||
|
||||
memorystream(uint8_t *data, unsigned size) {
|
||||
pdata = data, psize = size, poffset = 0;
|
||||
pwritable = true;
|
||||
}
|
||||
|
||||
memorystream(const uint8_t *data, unsigned size) {
|
||||
pdata = (uint8_t*)data, psize = size, poffset = 0;
|
||||
pwritable = false;
|
||||
}
|
||||
|
||||
protected:
|
||||
mutable uint8_t *pdata;
|
||||
mutable unsigned psize, poffset, pwritable;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,42 @@
|
||||
#ifndef NALL_STREAM_MMAP_HPP
|
||||
#define NALL_STREAM_MMAP_HPP
|
||||
|
||||
#include <nall/filemap.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct mmapstream : stream {
|
||||
using stream::read;
|
||||
using stream::write;
|
||||
|
||||
bool seekable() const { return true; }
|
||||
bool readable() const { return true; }
|
||||
bool writable() const { return pwritable; }
|
||||
bool randomaccess() const { return true; }
|
||||
|
||||
unsigned size() const { return pmmap.size(); }
|
||||
unsigned offset() const { return poffset; }
|
||||
void seek(unsigned offset) const { poffset = offset; }
|
||||
|
||||
uint8_t read() const { return pdata[poffset++]; }
|
||||
void write(uint8_t data) const { pdata[poffset++] = data; }
|
||||
|
||||
uint8_t read(unsigned offset) const { return pdata[offset]; }
|
||||
void write(unsigned offset, uint8_t data) const { pdata[offset] = data; }
|
||||
|
||||
mmapstream(const string &filename) {
|
||||
pmmap.open(filename, filemap::mode::readwrite);
|
||||
pwritable = pmmap.open();
|
||||
if(!pwritable) pmmap.open(filename, filemap::mode::read);
|
||||
pdata = pmmap.data(), poffset = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
mutable filemap pmmap;
|
||||
mutable uint8_t *pdata;
|
||||
mutable unsigned pwritable, poffset;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
Executable → Regular
+24
-22
@@ -9,6 +9,7 @@ struct stream {
|
||||
virtual bool writable() const = 0;
|
||||
virtual bool randomaccess() const = 0;
|
||||
|
||||
virtual uint8_t* data() const { return nullptr; }
|
||||
virtual unsigned size() const = 0;
|
||||
virtual unsigned offset() const = 0;
|
||||
virtual void seek(unsigned offset) const = 0;
|
||||
@@ -16,44 +17,45 @@ struct stream {
|
||||
virtual uint8_t read() const = 0;
|
||||
virtual void write(uint8_t data) const = 0;
|
||||
|
||||
inline virtual uint8_t read(unsigned) const { return 0; }
|
||||
inline virtual void write(unsigned, uint8_t) const {}
|
||||
virtual uint8_t read(unsigned) const { return 0; }
|
||||
virtual void write(unsigned, uint8_t) const {}
|
||||
|
||||
inline bool end() const {
|
||||
operator bool() const {
|
||||
return size();
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
bool end() const {
|
||||
return offset() >= size();
|
||||
}
|
||||
|
||||
inline void copy(uint8_t *&data, unsigned &length) const {
|
||||
seek(0);
|
||||
length = size();
|
||||
data = new uint8_t[length];
|
||||
for(unsigned n = 0; n < length; n++) data[n] = read();
|
||||
}
|
||||
|
||||
inline uintmax_t readl(unsigned length = 1) const {
|
||||
uintmax_t readl(unsigned length = 1) const {
|
||||
uintmax_t data = 0, shift = 0;
|
||||
while(length--) { data |= read() << shift; shift += 8; }
|
||||
return data;
|
||||
}
|
||||
|
||||
inline uintmax_t readm(unsigned length = 1) const {
|
||||
uintmax_t readm(unsigned length = 1) const {
|
||||
uintmax_t data = 0;
|
||||
while(length--) data = (data << 8) | read();
|
||||
return data;
|
||||
}
|
||||
|
||||
inline void read(uint8_t *data, unsigned length) const {
|
||||
void read(uint8_t *data, unsigned length) const {
|
||||
while(length--) *data++ = read();
|
||||
}
|
||||
|
||||
inline void writel(uintmax_t data, unsigned length = 1) const {
|
||||
void writel(uintmax_t data, unsigned length = 1) const {
|
||||
while(length--) {
|
||||
write(data);
|
||||
data >>= 8;
|
||||
}
|
||||
}
|
||||
|
||||
inline void writem(uintmax_t data, unsigned length = 1) const {
|
||||
void writem(uintmax_t data, unsigned length = 1) const {
|
||||
uintmax_t shift = 8 * length;
|
||||
while(length--) {
|
||||
shift -= 8;
|
||||
@@ -61,26 +63,26 @@ struct stream {
|
||||
}
|
||||
}
|
||||
|
||||
inline void write(const uint8_t *data, unsigned length) const {
|
||||
void write(const uint8_t *data, unsigned length) const {
|
||||
while(length--) write(*data++);
|
||||
}
|
||||
|
||||
struct byte {
|
||||
inline operator uint8_t() const { return s.read(offset); }
|
||||
inline byte& operator=(uint8_t data) { s.write(offset, data); }
|
||||
inline byte(const stream &s, unsigned offset) : s(s), offset(offset) {}
|
||||
operator uint8_t() const { return s.read(offset); }
|
||||
byte& operator=(uint8_t data) { s.write(offset, data); return *this; }
|
||||
byte(const stream &s, unsigned offset) : s(s), offset(offset) {}
|
||||
|
||||
private:
|
||||
const stream &s;
|
||||
const unsigned offset;
|
||||
};
|
||||
|
||||
inline byte operator[](unsigned offset) const {
|
||||
byte operator[](unsigned offset) const {
|
||||
return byte(*this, offset);
|
||||
}
|
||||
|
||||
inline stream() {}
|
||||
inline virtual ~stream() {}
|
||||
stream() {}
|
||||
virtual ~stream() {}
|
||||
stream(const stream&) = delete;
|
||||
stream& operator=(const stream&) = delete;
|
||||
};
|
||||
@@ -0,0 +1,39 @@
|
||||
#ifndef NALL_STREAM_VECTOR_HPP
|
||||
#define NALL_STREAM_VECTOR_HPP
|
||||
|
||||
#include <nall/stream/stream.hpp>
|
||||
#include <nall/vector.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct vectorstream : stream {
|
||||
using stream::read;
|
||||
using stream::write;
|
||||
|
||||
bool seekable() const { return true; }
|
||||
bool readable() const { return true; }
|
||||
bool writable() const { return pwritable; }
|
||||
bool randomaccess() const { return true; }
|
||||
|
||||
uint8_t* data() const { return memory.data(); }
|
||||
unsigned size() const { return memory.size(); }
|
||||
unsigned offset() const { return poffset; }
|
||||
void seek(unsigned offset) const { poffset = offset; }
|
||||
|
||||
uint8_t read() const { return memory[poffset++]; }
|
||||
void write(uint8_t data) const { memory[poffset++] = data; }
|
||||
|
||||
uint8_t read(unsigned offset) const { return memory[offset]; }
|
||||
void write(unsigned offset, uint8_t data) const { memory[offset] = data; }
|
||||
|
||||
vectorstream(vector<uint8_t> &memory) : memory(memory), poffset(0), pwritable(true) {}
|
||||
vectorstream(const vector<uint8_t> &memory) : memory((vector<uint8_t>&)memory), poffset(0), pwritable(false) {}
|
||||
|
||||
protected:
|
||||
vector<uint8_t> &memory;
|
||||
mutable unsigned poffset, pwritable;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
Executable → Regular
+13
-5
@@ -1,26 +1,34 @@
|
||||
#ifdef NALL_STREAM_INTERNAL_HPP
|
||||
#ifndef NALL_STREAM_ZIP_HPP
|
||||
#define NALL_STREAM_ZIP_HPP
|
||||
|
||||
#include <nall/unzip.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct zipstream : memorystream {
|
||||
inline zipstream(const stream &stream, const string &filter = "*") {
|
||||
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);
|
||||
|
||||
zip archive;
|
||||
unzip archive;
|
||||
if(archive.open(data, size) == false) return;
|
||||
delete[] data;
|
||||
|
||||
for(auto &file : archive.file) {
|
||||
if(file.name.wildcard(filter)) {
|
||||
archive.extract(file, pdata, psize);
|
||||
auto buffer = archive.extract(file);
|
||||
psize = buffer.size();
|
||||
pdata = buffer.move();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline ~zipstream() {
|
||||
~zipstream() {
|
||||
if(pdata) delete[] pdata;
|
||||
}
|
||||
};
|
||||
Executable → Regular
+6
-3
@@ -9,7 +9,6 @@
|
||||
#include <algorithm>
|
||||
#include <initializer_list>
|
||||
|
||||
#include <nall/array.hpp>
|
||||
#include <nall/atoi.hpp>
|
||||
#include <nall/function.hpp>
|
||||
#include <nall/platform.hpp>
|
||||
@@ -23,13 +22,13 @@
|
||||
|
||||
#define NALL_STRING_INTERNAL_HPP
|
||||
#include <nall/string/base.hpp>
|
||||
#include <nall/string/bml.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>
|
||||
@@ -39,12 +38,16 @@
|
||||
#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/xml.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
|
||||
Executable → Regular
+24
-4
@@ -22,6 +22,11 @@ namespace nall {
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
@@ -63,6 +68,7 @@ namespace nall {
|
||||
template<unsigned limit = 0> inline string& ltrim(const char *key = " ");
|
||||
template<unsigned limit = 0> inline string& rtrim(const char *key = " ");
|
||||
template<unsigned limit = 0> inline string& trim(const char *key = " ", const char *rkey = 0);
|
||||
inline string& strip();
|
||||
|
||||
inline optional<unsigned> position(const char *key) const;
|
||||
inline optional<unsigned> iposition(const char *key) const;
|
||||
@@ -111,6 +117,11 @@ namespace nall {
|
||||
|
||||
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*);
|
||||
@@ -119,8 +130,14 @@ namespace nall {
|
||||
inline bool operator==(const lstring&) const;
|
||||
inline bool operator!=(const lstring&) const;
|
||||
|
||||
inline lstring();
|
||||
inline lstring(std::initializer_list<string>);
|
||||
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*);
|
||||
@@ -147,9 +164,11 @@ namespace nall {
|
||||
inline bool strmath(const char *str, int &result);
|
||||
|
||||
//platform.hpp
|
||||
inline string realpath(const char *name);
|
||||
inline string activepath();
|
||||
inline string realpath(const string &name);
|
||||
inline string userpath();
|
||||
inline string currentpath();
|
||||
inline string configpath();
|
||||
inline string temppath();
|
||||
|
||||
//strm.hpp
|
||||
inline unsigned strmcpy(char *target, const char *source, unsigned length);
|
||||
@@ -169,6 +188,7 @@ namespace nall {
|
||||
template<unsigned limit = 0> inline char* ltrim(char *str, const char *key = " ");
|
||||
template<unsigned limit = 0> inline char* rtrim(char *str, const char *key = " ");
|
||||
template<unsigned limit = 0> inline char* trim(char *str, const char *key = " ", const char *rkey = 0);
|
||||
inline char* strip(char *s);
|
||||
|
||||
//utility.hpp
|
||||
template<bool Insensitive> alwaysinline bool chrequal(char x, char y);
|
||||
Executable → Regular
File renamed without changes.
Executable → Regular
File renamed without changes.
Executable → Regular
File renamed without changes.
Executable → Regular
File renamed without changes.
Loaded 100 of 2033 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user