Created
September 3, 2017 08:27
-
-
Save h3lls/da436421d80dffc28f282412275d31d0 to your computer and use it in GitHub Desktop.
Amiibo reader in EmulationStation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//EmulationStation, a graphical front-end for ROM browsing. Created by Alec "Aloshi" Lofquist. | |
//http://www.aloshi.com | |
#include <SDL.h> | |
#include <iostream> | |
#include <iomanip> | |
#include "Renderer.h" | |
#include "views/ViewController.h" | |
#include "SystemData.h" | |
#include <boost/filesystem.hpp> | |
#include "guis/GuiDetectDevice.h" | |
#include "guis/GuiMsgBox.h" | |
#include "AudioManager.h" | |
#include "platform.h" | |
#include "Log.h" | |
#include "Window.h" | |
#include "EmulationStation.h" | |
#include "Settings.h" | |
#include <sstream> | |
#include <boost/locale.hpp> | |
#include "GamelistDB.h" | |
#include "SystemManager.h" | |
#include <nfc/nfc.h> | |
#include <nfc/nfc-types.h> | |
#include "utils/nfc-utils.h" | |
#ifdef WIN32 | |
#include <Windows.h> | |
#endif | |
#define MAX_DEVICE_COUNT 16 | |
namespace fs = boost::filesystem; | |
static nfc_device *pnd = NULL; | |
static nfc_context *context; | |
static bool nfc_initialized = false; | |
void | |
print_nfc_target(const nfc_target *pnt, bool verbose) | |
{ | |
char *s; | |
str_nfc_target(&s, pnt, verbose); | |
printf("%s", s); | |
nfc_free(s); | |
} | |
int | |
snprint_hex(char *dst, size_t size, const uint8_t *pbtData, const size_t szBytes) | |
{ | |
size_t szPos; | |
size_t res = 0; | |
for (szPos = 0; szPos < szBytes; szPos++) { | |
res += snprintf(dst + res, size - res, "%02x", pbtData[szPos]); | |
} | |
//res += snprintf(dst + res, size - res, "\n"); | |
return res; | |
} | |
bool read_nfc_card(nfc_target *nt, bool verbose) | |
{ | |
const uint8_t uiPollNr = 1; | |
const uint8_t uiPeriod = 1; | |
const nfc_modulation nmModulations[1] = { | |
{ .nmt = NMT_ISO14443A, .nbr = NBR_106 }, | |
// { .nmt = NMT_ISO14443B, .nbr = NBR_106 }, | |
// { .nmt = NMT_FELICA, .nbr = NBR_212 }, | |
// { .nmt = NMT_FELICA, .nbr = NBR_424 }, | |
// { .nmt = NMT_JEWEL, .nbr = NBR_106 }, | |
}; | |
const size_t szModulations = 1; | |
const char *acLibnfcVersion = nfc_version(); | |
if (verbose) | |
std::cout << "\nLib NFC: " << acLibnfcVersion << "\n"; | |
int res = 0; | |
if (nfc_initialized == false) | |
{ | |
nfc_init(&context); | |
if (context == NULL) { | |
std::cout << "Unable to init libnfc (malloc)"; | |
return false; | |
} | |
pnd = nfc_open(context, NULL); | |
if (pnd == NULL) { | |
std::cout << "Unable to open NFC device."; | |
nfc_exit(context); | |
return false; | |
} | |
if (nfc_initiator_init(pnd) < 0) { | |
nfc_perror(pnd, "nfc_initiator_init"); | |
nfc_close(pnd); | |
nfc_exit(context); | |
return false; | |
} | |
nfc_initialized = true; | |
} | |
if (verbose) { | |
printf("NFC reader: %s opened\n", nfc_device_get_name(pnd)); | |
printf("NFC device will poll during %ld ms (%u pollings of %lu ms for %" PRIdPTR " modulations)\n", (unsigned long) uiPollNr * szModulations * uiPeriod * 150, uiPollNr, (unsigned long) uiPeriod * 150, szModulations); | |
} | |
if ((res = nfc_initiator_poll_target(pnd, nmModulations, szModulations, uiPollNr, uiPeriod, nt)) < 0) { | |
//nfc_perror(pnd, "nfc_initiator_poll_target"); | |
// nfc_close(pnd); | |
// nfc_exit(context); | |
//printf("Error Polling\n"); | |
return false; | |
//exit(EXIT_FAILURE); | |
} | |
if (res > 0) { | |
if (verbose) | |
print_nfc_target(nt, verbose); | |
} else { | |
printf("No target found.\n"); | |
} | |
// printf("Waiting for card removing..."); | |
// while (0 == nfc_initiator_target_is_present(pnd, NULL)) {} | |
// nfc_perror(pnd, "nfc_initiator_target_is_present"); | |
// printf("done.\n"); | |
// nfc_close(pnd); | |
// nfc_exit(context); | |
if (verbose) | |
std::cout << "\nComplete, exiting\n"; | |
} | |
bool parseArgs(int argc, char* argv[], unsigned int* width, unsigned int* height) | |
{ | |
for(int i = 1; i < argc; i++) | |
{ | |
if(strcmp(argv[i], "--resolution") == 0) | |
{ | |
if(i >= argc - 2) | |
{ | |
std::cerr << "Invalid resolution supplied."; | |
return false; | |
} | |
*width = atoi(argv[i + 1]); | |
*height = atoi(argv[i + 2]); | |
i += 2; // skip the argument value | |
}else if(strcmp(argv[i], "--gamelist-only") == 0) | |
{ | |
Settings::getInstance()->setBool("ParseGamelistOnly", true); | |
}else if(strcmp(argv[i], "--test-nfc") == 0) | |
{ | |
nfc_target nt; | |
return read_nfc_card(&nt, true); | |
//return false; | |
}else if(strcmp(argv[i], "--ignore-gamelist") == 0) | |
{ | |
Settings::getInstance()->setBool("IgnoreGamelist", true); | |
}else if(strcmp(argv[i], "--draw-framerate") == 0) | |
{ | |
Settings::getInstance()->setBool("DrawFramerate", true); | |
}else if(strcmp(argv[i], "--no-exit") == 0) | |
{ | |
Settings::getInstance()->setBool("ShowExit", false); | |
}else if(strcmp(argv[i], "--debug") == 0) | |
{ | |
Settings::getInstance()->setBool("Debug", true); | |
Settings::getInstance()->setBool("HideConsole", false); | |
Log::setReportingLevel(LogDebug); | |
}else if(strcmp(argv[i], "--windowed") == 0) | |
{ | |
Settings::getInstance()->setBool("Windowed", true); | |
}else if(strcmp(argv[i], "--vsync") == 0) | |
{ | |
bool vsync = (strcmp(argv[i + 1], "on") == 0 || strcmp(argv[i + 1], "1") == 0) ? true : false; | |
Settings::getInstance()->setBool("VSync", vsync); | |
i++; // skip vsync value | |
}else if(strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) | |
{ | |
#ifdef WIN32 | |
// This is a bit of a hack, but otherwise output will go to nowhere | |
// when the application is compiled with the "WINDOWS" subsystem (which we usually are). | |
// If you're an experienced Windows programmer and know how to do this | |
// the right way, please submit a pull request! | |
AttachConsole(ATTACH_PARENT_PROCESS); | |
freopen("CONOUT$", "wb", stdout); | |
#endif | |
std::cout << | |
"EmulationStation, a graphical front-end for ROM browsing.\n" | |
"Written by Alec \"Aloshi\" Lofquist.\n" | |
"Version " << PROGRAM_VERSION_STRING << ", built " << PROGRAM_BUILT_STRING << "\n\n" | |
"Command line arguments:\n" | |
"--resolution [width] [height] try and force a particular resolution\n" | |
"--gamelist-only skip automatic game search, only read from gamelist.xml\n" | |
"--ignore-gamelist ignore the gamelist (useful for troubleshooting)\n" | |
"--draw-framerate display the framerate\n" | |
"--no-exit don't show the exit option in the menu\n" | |
"--debug more logging, show console on Windows\n" | |
"--scrape scrape using command line interface\n" | |
"--windowed not fullscreen, should be used with --resolution\n" | |
"--vsync [1/on or 0/off] turn vsync on or off (default is on)\n" | |
"--help, -h summon a sentient, angry tuba\n\n" | |
"More information available in README.md.\n"; | |
return false; //exit after printing help | |
} | |
} | |
return true; | |
} | |
bool verifyHomeFolderExists() | |
{ | |
// make sure the config directory exists | |
std::string home = getHomePath(); | |
std::string configDir = home + "/.emulationstation"; | |
if(!fs::exists(configDir)) | |
{ | |
std::cout << "Creating config directory \"" << configDir << "\"\n"; | |
fs::create_directory(configDir); | |
if(!fs::exists(configDir)) | |
{ | |
std::cerr << "Config directory could not be created!\n"; | |
return false; | |
} | |
} | |
return true; | |
} | |
// Returns true if everything is OK, | |
bool loadSystemConfigFile(const char** errorString) | |
{ | |
*errorString = NULL; | |
try { | |
SystemManager::getInstance()->loadConfig(); | |
} | |
catch(ESException& e) { | |
LOG(LogError) << "Error while parsing systems configuration file!"; | |
LOG(LogError) << e.what(); | |
*errorString = "IT LOOKS LIKE YOUR SYSTEMS CONFIGURATION FILE HAS NOT BEEN SET UP OR IS INVALID. YOU'LL NEED TO DO THIS BY HAND, UNFORTUNATELY.\n\n" | |
"VISIT EMULATIONSTATION.ORG FOR MORE INFORMATION."; | |
return false; | |
} | |
if(SystemManager::getInstance()->getSystems().size() == 0) | |
{ | |
LOG(LogError) << "No systems found! Does at least one system have a game present? (check that extensions match!)\n(Also, make sure you've updated your es_systems.cfg for XML!)"; | |
*errorString = "WE CAN'T FIND ANY SYSTEMS!\n" | |
"CHECK THAT YOUR PATHS ARE CORRECT IN THE SYSTEMS CONFIGURATION FILE, " | |
"AND YOUR GAME DIRECTORY HAS AT LEAST ONE GAME WITH THE CORRECT EXTENSION.\n\n" | |
"VISIT EMULATIONSTATION.ORG FOR MORE INFORMATION."; | |
return false; | |
} | |
return true; | |
} | |
// called on exit, assuming we get far enough to have the log initialized | |
void close_log_on_exit() | |
{ | |
Log::close(); | |
} | |
void delete_singletons_on_exit() | |
{ | |
delete SystemManager::getInstance(); | |
} | |
int main(int argc, char* argv[]) | |
{ | |
unsigned int width = 0; | |
unsigned int height = 0; | |
std::locale::global(boost::locale::generator().generate("")); | |
boost::filesystem::path::imbue(std::locale()); | |
if(!parseArgs(argc, argv, &width, &height)) | |
return 0; | |
// only show the console on Windows if HideConsole is false | |
#ifdef WIN32 | |
// MSVC has a "SubSystem" option, with two primary options: "WINDOWS" and "CONSOLE". | |
// In "WINDOWS" mode, no console is automatically created for us. This is good, | |
// because we can choose to only create the console window if the user explicitly | |
// asks for it, preventing it from flashing open and then closing. | |
// In "CONSOLE" mode, a console is always automatically created for us before we | |
// enter main. In this case, we can only hide the console after the fact, which | |
// will leave a brief flash. | |
// TL;DR: You should compile ES under the "WINDOWS" subsystem. | |
// I have no idea how this works with non-MSVC compilers. | |
if(!Settings::getInstance()->getBool("HideConsole")) | |
{ | |
// we want to show the console | |
// if we're compiled in "CONSOLE" mode, this is already done. | |
// if we're compiled in "WINDOWS" mode, no console is created for us automatically; | |
// the user asked for one, so make one and then hook stdin/stdout/sterr up to it | |
if(AllocConsole()) // should only pass in "WINDOWS" mode | |
{ | |
freopen("CONIN$", "r", stdin); | |
freopen("CONOUT$", "wb", stdout); | |
freopen("CONOUT$", "wb", stderr); | |
} | |
}else{ | |
// we want to hide the console | |
// if we're compiled with the "WINDOWS" subsystem, this is already done. | |
// if we're compiled with the "CONSOLE" subsystem, a console is already created; | |
// it'll flash open, but we hide it nearly immediately | |
if(GetConsoleWindow()) // should only pass in "CONSOLE" mode | |
ShowWindow(GetConsoleWindow(), SW_HIDE); | |
} | |
#endif | |
// if ~/.emulationstation doesn't exist and cannot be created, bail | |
if(!verifyHomeFolderExists()) | |
return 1; | |
// start the logger | |
Log::open(); | |
LOG(LogInfo) << "EmulationStation - v" << PROGRAM_VERSION_STRING << ", built " << PROGRAM_BUILT_STRING; | |
// always close the log on exit | |
atexit(&close_log_on_exit); | |
// delete SystemDatas at exit too | |
atexit(&delete_singletons_on_exit); | |
Window window; | |
ViewController::init(&window); | |
window.pushGui(ViewController::get()); | |
if(!window.init(width, height)) | |
{ | |
LOG(LogError) << "Window failed to initialize!"; | |
return 1; | |
} | |
std::string glExts = (const char*)glGetString(GL_EXTENSIONS); | |
LOG(LogInfo) << "Checking available OpenGL extensions..."; | |
LOG(LogInfo) << " ARB_texture_non_power_of_two: " << (glExts.find("ARB_texture_non_power_of_two") != std::string::npos ? "ok" : "MISSING"); | |
window.renderLoadingScreen(); | |
const char* errorMsg = NULL; | |
if(!loadSystemConfigFile(&errorMsg)) | |
{ | |
// something went terribly wrong | |
if(errorMsg == NULL) | |
{ | |
LOG(LogError) << "Unknown error occured while parsing system config file."; | |
Renderer::deinit(); | |
return 1; | |
} | |
// we can't handle es_systems.cfg file problems inside ES itself, so display the error message then quit | |
window.pushGui(new GuiMsgBox(&window, | |
errorMsg, | |
"QUIT", [] { | |
SDL_Event* quit = new SDL_Event(); | |
quit->type = SDL_QUIT; | |
SDL_PushEvent(quit); | |
})); | |
} | |
//dont generate joystick events while we're loading (hopefully fixes "automatically started emulator" bug) | |
SDL_JoystickEventState(SDL_DISABLE); | |
// preload what we can right away instead of waiting for the user to select it | |
// this makes for no delays when accessing content, but a longer startup time | |
ViewController::get()->preload(); | |
// choose which GUI to open depending on if an input configuration already exists | |
if(errorMsg == NULL) | |
{ | |
if(fs::exists(InputManager::getConfigPath()) && InputManager::getInstance()->getNumConfiguredDevices() > 0) | |
{ | |
ViewController::get()->goToStart(); | |
if(SystemManager::getInstance()->hasNewGamelistXML()) | |
{ | |
window.pushGui(new GuiMsgBox(&window, | |
"NEW GAMELIST.XML DATA FOUND. IMPORT NOW?", | |
"YES", [] { SystemManager::getInstance()->importGamelistXML(true); }, | |
"NO", [] { | |
// don't ask again | |
std::time_t now; | |
time(&now); | |
Settings::getInstance()->setTime("LastXMLImportTime", now); | |
})); | |
} | |
}else{ | |
window.pushGui(new GuiDetectDevice(&window, true, [] { ViewController::get()->goToStart(); })); | |
} | |
} | |
//generate joystick events since we're done loading | |
SDL_JoystickEventState(SDL_ENABLE); | |
int amiiboLastTime = SDL_GetTicks(); | |
int lastTime = SDL_GetTicks(); | |
bool running = true; | |
while(running) | |
{ | |
int amiiboPollTime = SDL_GetTicks() - amiiboLastTime; | |
// cap amiiboPollTime at 1000 | |
if(amiiboPollTime > 1000) | |
{ | |
nfc_target nt; | |
if (read_nfc_card(&nt, false)) | |
{ | |
char *buffer = new char[4096]; | |
snprint_hex(buffer, 4096, nt.nti.nai.abtUid, nt.nti.nai.szUidLen); | |
std::cout << "Amiibo UID: " << buffer << "\n"; | |
if (strcmp("043afeba993c81", buffer) == 0) | |
{ | |
std::cout << "\n\n MARIO!\n\n"; | |
//ViewController::get()->goToNextGameList(); | |
ViewController::get()->goToStart(); | |
SystemData* sd = SystemManager::getInstance()->getSystemByName("nes"); | |
std::cout << "System Game Count: " << sd->getGameCount() << "\n"; | |
std::vector<SystemData*> mSystems = SystemManager::getInstance()->getSystems(); | |
for(auto it = mSystems.begin(); it != mSystems.end(); it++) | |
std::cout << "System: " << (*it)->getName() << "\n"; | |
std::vector<FileData> games = sd->getGames(); | |
for(auto it = games.begin(); it != games.end(); it++) | |
{ | |
std::cout << "Game: " << (*it).getName() << "\n"; | |
if ((*it).getName() == "Super Mario Bros.") | |
{ | |
Eigen::Vector3f target(Renderer::getScreenWidth() / 2.0f, Renderer::getScreenHeight() / 2.0f, 0); | |
ViewController::get()->launch((*it), target); | |
} | |
} | |
} | |
else if (strcmp("04e9319ac23e80", buffer) == 0) | |
{ | |
std::cout << "\n\n YOSHI!\n\n"; | |
} | |
else | |
{ | |
std::cout << "Unknown Amiibo detected.\n"; | |
} | |
delete[] buffer; | |
} | |
//ViewController::get()->launch(game); | |
amiiboLastTime = SDL_GetTicks(); | |
} | |
SDL_Event event; | |
while(SDL_PollEvent(&event)) | |
{ | |
switch(event.type) | |
{ | |
case SDL_JOYHATMOTION: | |
case SDL_JOYBUTTONDOWN: | |
case SDL_JOYBUTTONUP: | |
case SDL_KEYDOWN: | |
case SDL_KEYUP: | |
case SDL_JOYAXISMOTION: | |
case SDL_TEXTINPUT: | |
case SDL_TEXTEDITING: | |
case SDL_JOYDEVICEADDED: | |
case SDL_JOYDEVICEREMOVED: | |
InputManager::getInstance()->parseEvent(event, &window); | |
break; | |
case SDL_QUIT: | |
running = false; | |
break; | |
} | |
} | |
if(window.isSleeping()) | |
{ | |
lastTime = SDL_GetTicks(); | |
SDL_Delay(1); // this doesn't need to be accurate, we're just giving up our CPU time until something wakes us up | |
continue; | |
} | |
int curTime = SDL_GetTicks(); | |
int deltaTime = curTime - lastTime; | |
lastTime = curTime; | |
// cap deltaTime at 1000 | |
if(deltaTime > 1000 || deltaTime < 0) | |
{ | |
deltaTime = 1000; | |
} | |
window.update(deltaTime); | |
window.render(); | |
Renderer::swapBuffers(); | |
Log::flush(); | |
} | |
Settings::getInstance()->saveFile(); | |
while(window.peekGui() != ViewController::get()) | |
delete window.peekGui(); | |
window.deinit(); | |
LOG(LogInfo) << "EmulationStation cleanly shutting down."; | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment