Files
Mu/qtBuildSystem/Mu/emuwrapper.cpp
meepingsnesroms 4d88e893e0 Launcher work
Gonna try and make the launcher as simple as possible, no zip files, the frontend can handle that if it wants.

Going to make the Qt port launch directorys instead of files too since the file dialog only allows selecting files or directorys and some apps have resource files.
2019-05-18 20:01:28 -07:00

525 lines
14 KiB
C++

#include <QString>
#include <QVector>
#include <QPixmap>
#include <QImage>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QByteArray>
#include <QDateTime>
#include <QDate>
#include <QTime>
#include <new>
#include <chrono>
#include <thread>
#include <string>
#include <stdint.h>
#include "emuwrapper.h"
#include "../../src/emulator.h"
#include "../../src/fileLauncher/launcher.h"
extern "C"{
#include "../../src/flx68000.h"
#if defined(EMU_SUPPORT_PALM_OS5)
#include "../../src/pxa255/pxa255.h"
#endif
#include "../../src/debug/sandbox.h"
}
static bool alreadyExists = false;//there can only be one of this class since it wrappers C code
static QVector<QString> debugStrings;
static QVector<uint64_t> duplicateCallCount;
uint32_t frontendDebugStringSize;
char* frontendDebugString;
void frontendHandleDebugPrint(){
QString newDebugString = frontendDebugString;
//this debug handler doesnt need the \n
if(newDebugString.back() == '\n')
newDebugString.remove(newDebugString.length() - 1, 1);
else
newDebugString.append("MISSING \"\\n\"");
if(!debugStrings.empty() && newDebugString == debugStrings.back()){
duplicateCallCount.back()++;
}
else{
debugStrings.push_back(newDebugString);
duplicateCallCount.push_back(1);
}
}
EmuWrapper::EmuWrapper(){
if(alreadyExists == true)
throw std::bad_alloc();
alreadyExists = true;
emuInited = false;
emuThreadJoin = false;
emuRunning = false;
emuPaused = false;
emuNewFrameReady = false;
frontendDebugString = new char[200];
frontendDebugStringSize = 200;
}
EmuWrapper::~EmuWrapper(){
if(emuInited)
exit();
delete[] frontendDebugString;
frontendDebugStringSize = 0;
debugStrings.clear();
duplicateCallCount.clear();
}
void EmuWrapper::emuThreadRun(){
while(!emuThreadJoin){
if(emuRunning){
emuPaused = false;
if(!emuNewFrameReady){
palmInput = emuInput;
emulatorRunFrame();
emuNewFrameReady = true;
}
}
else{
emuPaused = true;
}
std::this_thread::sleep_for(std::chrono::milliseconds(3));
}
}
uint32_t EmuWrapper::init(const QString& romPath, const QString& bootloaderPath, const QString& ramPath, const QString& sdCardPath, uint32_t features){
if(!emuRunning && !emuInited){
//start emu
uint32_t error;
QFile romFile(romPath);
QFile bootloaderFile(bootloaderPath);
QByteArray romData;
QByteArray bootloaderData;
if(romFile.open(QFile::ReadOnly | QFile::ExistingOnly)){
romData = romFile.readAll();
romFile.close();
}
else{
return EMU_ERROR_INVALID_PARAMETER;
}
if(bootloaderPath != ""){
if(bootloaderFile.open(QFile::ReadOnly | QFile::ExistingOnly)){
bootloaderData = bootloaderFile.readAll();
bootloaderFile.close();
}
else{
return EMU_ERROR_INVALID_PARAMETER;
}
}
error = emulatorInit((uint8_t*)romData.data(), romData.size(), (uint8_t*)bootloaderData.data(), bootloaderData.size(), features);
if(error == EMU_ERROR_NONE){
QTime now = QTime::currentTime();
emulatorSetRtc(QDate::currentDate().day(), now.hour(), now.minute(), now.second());
if(ramPath != ""){
QFile ramFile(ramPath);
if(ramFile.exists()){
if(ramFile.open(QFile::ReadOnly | QFile::ExistingOnly)){
QByteArray ramData;
ramData = ramFile.readAll();
ramFile.close();
//only copy in data if its the correct size, the file will be overwritten on exit with the devices RAM either way
//(changing RAM size requires a factory reset for now and always will when going from 128mb back to 16mb)
if(ramData.size() == emulatorGetRamSize())
emulatorLoadRam((uint8_t*)ramData.data(), ramData.size());
}
}
}
if(sdCardPath != ""){
QFile sdCardFile(sdCardPath);
if(sdCardFile.exists()){
if(sdCardFile.open(QFile::ReadOnly | QFile::ExistingOnly)){
QByteArray sdCardData;
sdCardData = sdCardFile.readAll();
sdCardFile.close();
emulatorInsertSdCard((uint8_t*)sdCardData.data(), sdCardData.size(), NULL);
}
}
}
emuInput = palmInput;
emuRamFilePath = ramPath;
emuSdCardFilePath = sdCardPath;
emuThreadJoin = false;
emuInited = true;
emuRunning = true;
emuPaused = false;
emuNewFrameReady = false;
emuThread = std::thread(&EmuWrapper::emuThreadRun, this);
}
else{
return error;
}
}
return EMU_ERROR_NONE;
}
void EmuWrapper::exit(){
emuThreadJoin = true;
emuRunning = false;
if(emuThread.joinable())
emuThread.join();
if(emuInited){
if(emuRamFilePath != ""){
QFile ramFile(emuRamFilePath);
uint32_t emuRamSize = emulatorGetRamSize();
uint8_t* emuRamData = new uint8_t[emuRamSize];
emulatorSaveRam(emuRamData, emuRamSize);
//save out RAM before exit
if(ramFile.open(QFile::WriteOnly | QFile::Truncate)){
ramFile.write((const char*)emuRamData, emuRamSize);
ramFile.close();
}
delete[] emuRamData;
}
if(emuSdCardFilePath != ""){
uint32_t emuSdCardSize = emulatorGetSdCardSize();
uint8_t* emuSdCardData = new uint8_t[emuSdCardSize];
if(emulatorGetSdCardData(emuSdCardData, emuSdCardSize) == EMU_ERROR_NONE){
QFile sdCardFile(emuSdCardFilePath);
//save out SD card before exit
if(sdCardFile.open(QFile::WriteOnly | QFile::Truncate)){
sdCardFile.write((const char*)emuSdCardData, emuSdCardSize);
sdCardFile.close();
}
}
}
emulatorExit();
}
}
void EmuWrapper::pause(){
if(emuInited){
emuRunning = false;
while(!emuPaused)
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
}
void EmuWrapper::resume(){
if(emuInited){
emuRunning = true;
while(emuPaused)
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
}
void EmuWrapper::reset(bool hard){
if(emuInited){
bool wasPaused = isPaused();
if(!wasPaused)
pause();
if(hard)
emulatorHardReset();
else
emulatorSoftReset();
if(!wasPaused)
resume();
}
}
uint32_t EmuWrapper::bootFromFileOrDirectory(const QString& mainPath){
bool wasPaused = isPaused();
uint32_t error = EMU_ERROR_NONE;
QFileInfo pathInfo(mainPath);
QStringList paths;
QVector<QByteArray> fileDataBuffers;
QVector<QByteArray> fileInfoBuffers;
launcher_file_t* files;
int newestBootableFile = -1;
if(!wasPaused)
pause();
if(pathInfo.isDir()){
//get Palm file list
QDir dirInfo(mainPath);
QStringList filters;
filters += "*.prc";
filters += "*.pdb";
filters += "*.pqa";
filters += "*.img";
paths = dirInfo.entryList(filters);
//make paths full direct paths
for(int index = 0; index < paths.length(); index++)
paths[index].prepend(mainPath + "/");
}
else if(pathInfo.exists()){
//use single file
paths = QStringList(mainPath);
}
else{
//error
error = EMU_ERROR_INVALID_PARAMETER;
goto errorOccurred;
}
fileDataBuffers.resize(paths.length());
fileInfoBuffers.resize(paths.length());
files = new launcher_file_t[paths.length()];
memset(files, 0x00, sizeof(launcher_file_t) * paths.length());
for(int index = 0; index < paths.length(); index++){
QFile appFile(paths[index]);
if(appFile.open(QFile::ReadOnly)){
QString suffix = QFileInfo(paths[index]).suffix().toLower();
fileDataBuffers[index] = appFile.readAll();
appFile.close();
files[index].fileData = (uint8_t*)fileDataBuffers[index].data();
files[index].fileSize = fileDataBuffers[index].size();
if(suffix == "prc"){
files[index].type = LAUNCHER_FILE_TYPE_PRC;
newestBootableFile = index;
}
else if(suffix == "pdb"){
files[index].type = LAUNCHER_FILE_TYPE_PDB;
}
else if(suffix == "pqa"){
files[index].type = LAUNCHER_FILE_TYPE_PQA;
newestBootableFile = index;
}
else if(suffix == "img"){
QFile infoFile(paths[index]);
if(infoFile.exists()){
if(infoFile.open(QFile::ReadOnly)){
fileInfoBuffers[index] = infoFile.readAll();
files[index].infoData = (uint8_t*)fileInfoBuffers[index].data();
files[index].infoSize = fileInfoBuffers[index].size();
}
}
files[index].type = LAUNCHER_FILE_TYPE_IMG;
newestBootableFile = index;
}
else{
error = EMU_ERROR_INVALID_PARAMETER;
goto errorOccurred;
}
}
else{
error = EMU_ERROR_RESOURCE_LOCKED;
goto errorOccurred;
}
}
if(newestBootableFile != -1){
files[newestBootableFile].boot = true;
}
else{
error = EMU_ERROR_INVALID_PARAMETER;
goto errorOccurred;
}
//actually pass everything to the emu
//TODO actually hook up the save files
error = launcherLaunch(files, paths.length(), NULL, 0, NULL, 0);
//need this goto because the emulator must be released before returning
errorOccurred:
if(!wasPaused)
resume();
return error;
}
uint32_t EmuWrapper::saveState(const QString& path){
bool wasPaused = isPaused();
uint32_t error = EMU_ERROR_INVALID_PARAMETER;
QFile stateFile(path);
if(!wasPaused)
pause();
//save here
if(stateFile.open(QFile::WriteOnly)){
uint32_t stateSize = emulatorGetStateSize();
uint8_t* stateData = new uint8_t[stateSize];
emulatorSaveState(stateData, stateSize);//no need to check for errors since the buffer is always the right size
stateFile.write((const char*)stateData, stateSize);
stateFile.close();
error = EMU_ERROR_NONE;
}
if(!wasPaused)
resume();
return error;
}
uint32_t EmuWrapper::loadState(const QString& path){
bool wasPaused = isPaused();
uint32_t error = EMU_ERROR_INVALID_PARAMETER;
QFile stateFile(path);
if(!wasPaused)
pause();
if(stateFile.open(QFile::ReadOnly)){
QByteArray stateDataBuffer;
stateDataBuffer = stateFile.readAll();
stateFile.close();
if(emulatorLoadState((uint8_t*)stateDataBuffer.data(), stateDataBuffer.size()))
error = EMU_ERROR_NONE;
}
if(!wasPaused)
resume();
return error;
}
void EmuWrapper::setPenValue(float x, float y, bool touched){
emuInput.touchscreenX = x;
emuInput.touchscreenY = y;
emuInput.touchscreenTouched = touched;
}
void EmuWrapper::setKeyValue(uint8_t key, bool pressed){
switch(key){
case BUTTON_UP:
emuInput.buttonUp = pressed;
break;
case BUTTON_DOWN:
emuInput.buttonDown = pressed;
break;
case BUTTON_CALENDAR:
emuInput.buttonCalendar = pressed;
break;
case BUTTON_ADDRESS:
emuInput.buttonAddress = pressed;
break;
case BUTTON_TODO:
emuInput.buttonTodo = pressed;
break;
case BUTTON_NOTES:
emuInput.buttonNotes = pressed;
break;
case BUTTON_POWER:
emuInput.buttonPower = pressed;
break;
default:
break;
}
}
uint32_t EmuWrapper::debugInstallApplication(const QString& path){
bool wasPaused = isPaused();
uint32_t error = EMU_ERROR_INVALID_PARAMETER;
QFile appFile(path);
if(!wasPaused)
pause();
if(appFile.open(QFile::ReadOnly)){
QByteArray appDataBuffer = appFile.readAll();
uintptr_t values[2];
values[0] = (uintptr_t)appDataBuffer.data();
values[1] = appDataBuffer.size();
appFile.close();
error = sandboxCommand(SANDBOX_CMD_DEBUG_INSTALL_APP, values);
}
if(!wasPaused)
resume();
return error;
}
QVector<QString>& EmuWrapper::getDebugStrings(){
return debugStrings;
}
QVector<uint64_t>& EmuWrapper::getDuplicateCallCount(){
return duplicateCallCount;
}
QString EmuWrapper::getCpuRegisterString(){
QString regString = "";
#if defined(EMU_SUPPORT_PALM_OS5)
if(palmEmulatingTungstenC){
for(uint8_t regs = 0; regs < 16; regs++)
regString += QString::asprintf("R%d:0x%08X\n", regs, pxa255GetRegister(regs));
regString.resize(regString.size() - 1);//remove extra '\n'
}
else{
#endif
for(uint8_t dRegs = 0; dRegs < 8; dRegs++)
regString += QString::asprintf("D%d:0x%08X\n", dRegs, flx68000GetRegister(dRegs));
for(uint8_t aRegs = 0; aRegs < 8; aRegs++)
regString += QString::asprintf("A%d:0x%08X\n", aRegs, flx68000GetRegister(8 + aRegs));
regString += QString::asprintf("SP:0x%08X\n", flx68000GetRegister(15));
regString += QString::asprintf("PC:0x%08X\n", flx68000GetRegister(16));
regString += QString::asprintf("SR:0x%04X", flx68000GetRegister(17));
#if defined(EMU_SUPPORT_PALM_OS5)
}
#endif
return regString;
}
uint64_t EmuWrapper::getEmulatorMemory(uint32_t address, uint8_t size){
return flx68000ReadArbitraryMemory(address, size);
}