2012-11-01 16:19:01 +01:00
|
|
|
// Headless version of PPSSPP, for testing using http://code.google.com/p/pspautotests/ .
|
|
|
|
// See headless.txt.
|
|
|
|
// To build on non-windows systems, just run CMake in the SDL directory, it will build both a normal ppsspp and the headless version.
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
2012-11-05 13:36:12 +01:00
|
|
|
#include "../Core/Config.h"
|
2012-11-01 16:19:01 +01:00
|
|
|
#include "../Core/Core.h"
|
|
|
|
#include "../Core/CoreTiming.h"
|
|
|
|
#include "../Core/System.h"
|
|
|
|
#include "../Core/MIPS/MIPS.h"
|
|
|
|
#include "../Core/Host.h"
|
|
|
|
#include "Log.h"
|
|
|
|
#include "LogManager.h"
|
|
|
|
|
|
|
|
// TODO: Get rid of this junk
|
|
|
|
class HeadlessHost : public Host
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
// virtual void StartThread()
|
|
|
|
virtual void UpdateUI() {}
|
|
|
|
|
|
|
|
virtual void UpdateMemView() {}
|
|
|
|
virtual void UpdateDisassembly() {}
|
|
|
|
|
|
|
|
virtual void SetDebugMode(bool mode) { }
|
|
|
|
|
|
|
|
virtual void InitGL() {}
|
|
|
|
virtual void BeginFrame() {}
|
|
|
|
virtual void EndFrame() {}
|
|
|
|
virtual void ShutdownGL() {}
|
|
|
|
|
|
|
|
virtual void InitSound(PMixer *mixer) {}
|
|
|
|
virtual void UpdateSound() {}
|
|
|
|
virtual void ShutdownSound() {}
|
|
|
|
|
|
|
|
// this is sent from EMU thread! Make sure that Host handles it properly!
|
|
|
|
virtual void BootDone() {}
|
|
|
|
virtual void PrepareShutdown() {}
|
|
|
|
|
|
|
|
virtual bool IsDebuggingEnabled() {return false;}
|
|
|
|
virtual bool AttemptLoadSymbolMap() {return false;}
|
|
|
|
};
|
|
|
|
|
2012-11-09 13:40:09 +01:00
|
|
|
class PrintfLogger : public LogListener
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
void Log(LogTypes::LOG_LEVELS level, const char *msg)
|
|
|
|
{
|
|
|
|
switch (level)
|
|
|
|
{
|
|
|
|
case LogTypes::LDEBUG:
|
|
|
|
printf("D %s", msg);
|
|
|
|
break;
|
|
|
|
case LogTypes::LINFO:
|
|
|
|
printf("I %s", msg);
|
|
|
|
break;
|
|
|
|
case LogTypes::LERROR:
|
|
|
|
printf("E %s", msg);
|
|
|
|
break;
|
|
|
|
case LogTypes::LWARNING:
|
|
|
|
printf("W %s", msg);
|
|
|
|
break;
|
|
|
|
case LogTypes::LNOTICE:
|
|
|
|
default:
|
|
|
|
printf("N %s", msg);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2012-11-01 16:19:01 +01:00
|
|
|
void printUsage()
|
|
|
|
{
|
|
|
|
fprintf(stderr, "PPSSPP Headless\n");
|
|
|
|
fprintf(stderr, "Usage: ppsspp-headless file.elf [-c] [-m] [-j] [-c]\n");
|
|
|
|
fprintf(stderr, "See headless.txt for details.\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, const char* argv[])
|
|
|
|
{
|
|
|
|
bool fullLog = false;
|
|
|
|
bool useJit = false;
|
|
|
|
bool autoCompare = false;
|
|
|
|
|
|
|
|
const char *bootFilename = argc > 1 ? argv[1] : 0;
|
|
|
|
const char *mountIso = 0;
|
|
|
|
bool readMount = false;
|
|
|
|
|
|
|
|
for (int i = 2; i < argc; i++)
|
|
|
|
{
|
|
|
|
if (readMount)
|
|
|
|
{
|
|
|
|
mountIso = argv[i];
|
|
|
|
readMount = false;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!strcmp(argv[i], "-m"))
|
|
|
|
readMount = true;
|
|
|
|
else if (!strcmp(argv[i], "-l"))
|
|
|
|
fullLog = true;
|
|
|
|
else if (!strcmp(argv[i], "-j"))
|
|
|
|
useJit = true;
|
|
|
|
else if (!strcmp(argv[i], "-c"))
|
|
|
|
autoCompare = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!bootFilename)
|
|
|
|
{
|
|
|
|
printUsage();
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
host = new HeadlessHost();
|
|
|
|
|
|
|
|
LogManager::Init();
|
|
|
|
LogManager *logman = LogManager::GetInstance();
|
|
|
|
|
2012-11-09 13:40:09 +01:00
|
|
|
PrintfLogger *printfLogger = new PrintfLogger();
|
|
|
|
|
2012-11-01 16:19:01 +01:00
|
|
|
for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; i++)
|
|
|
|
{
|
|
|
|
LogTypes::LOG_TYPE type = (LogTypes::LOG_TYPE)i;
|
|
|
|
logman->SetEnable(type, fullLog);
|
|
|
|
logman->SetLogLevel(type, LogTypes::LDEBUG);
|
2012-11-09 13:40:09 +01:00
|
|
|
logman->AddListener(type, printfLogger);
|
2012-11-01 16:19:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
CoreParameter coreParameter;
|
|
|
|
coreParameter.fileToStart = bootFilename;
|
|
|
|
coreParameter.mountIso = mountIso ? mountIso : "";
|
|
|
|
coreParameter.startPaused = false;
|
|
|
|
coreParameter.cpuCore = useJit ? CPU_JIT : CPU_INTERPRETER;
|
|
|
|
coreParameter.gpuCore = GPU_NULL;
|
|
|
|
coreParameter.enableSound = false;
|
|
|
|
coreParameter.headLess = true;
|
2012-11-09 11:03:01 +01:00
|
|
|
coreParameter.printfEmuLog = true;
|
2012-11-01 16:19:01 +01:00
|
|
|
|
2012-11-17 18:08:10 +01:00
|
|
|
g_Config.bEnableSound = false;
|
2012-11-05 13:36:12 +01:00
|
|
|
g_Config.bFirstRun = false;
|
|
|
|
g_Config.bIgnoreBadMemAccess = true;
|
|
|
|
|
2012-11-01 16:19:01 +01:00
|
|
|
std::string error_string;
|
|
|
|
|
|
|
|
if (!PSP_Init(coreParameter, &error_string)) {
|
2012-11-09 11:03:01 +01:00
|
|
|
fprintf(stderr, "Failed to start %s. Error: %s\n", coreParameter.fileToStart.c_str(), error_string.c_str());
|
|
|
|
printf("TESTERROR\n");
|
2012-11-01 16:19:01 +01:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
coreState = CORE_RUNNING;
|
|
|
|
|
|
|
|
while (coreState == CORE_RUNNING)
|
|
|
|
{
|
|
|
|
// Run for a frame at a time, just because.
|
|
|
|
u64 nowTicks = CoreTiming::GetTicks();
|
|
|
|
u64 frameTicks = usToCycles(1000000/60);
|
|
|
|
mipsr4k.RunLoopUntil(nowTicks + frameTicks);
|
|
|
|
}
|
|
|
|
|
|
|
|
// NOTE: we won't get here until I've gotten rid of the exit(0) in sceExitProcess or whatever it's called
|
|
|
|
|
|
|
|
PSP_Shutdown();
|
|
|
|
|
|
|
|
if (autoCompare)
|
|
|
|
{
|
|
|
|
std::string expect_filename = std::string(bootFilename).substr(strlen(bootFilename - 4)) + ".expected";
|
|
|
|
if (File::Exists(expect_filename))
|
|
|
|
{
|
|
|
|
// TODO: Do the compare here
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Expectation file %s not found", expect_filename.c_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|