diff --git a/.gitignore b/.gitignore index 1f0cd120e6..5e6b96b51c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,37 +1,46 @@ +# For MSVC +*.lastcodeanalysissucceeded *.pdb *.ilk *.obj *.pch -Logs *.log *.dll *.rar *.exe -*.ini *.map *.lib *.user *.sdf *.ncb -Debug -DebugFast -Release *.opensdf *.suo *.aps +*.exp +Debug +DebugFast +Release +Windows/x64 +Windows/ipch + +# For ppsspp.ini, etc. +*.ini + +Logs +Memstick + bin gen libs obj -*.exp +build*/ + .pspsh.hist -GameLogNotes.txt -Windows/x64 -Windows/ipch -Memstick -android/ui_atlas.zim __testoutput.txt __testerror.txt +__testfinish.txt +GameLogNotes.txt + +android/ui_atlas.zim ppge_atlas.zim.png local.properties -build*/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 858bc710c4..5fd1b01c88 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -109,6 +109,7 @@ endif() if(NOT MSVC) # Disable some warnings + add_definitions(-O2) add_definitions(-Wno-multichar) add_definitions(-fno-strict-aliasing) if(NOT APPLE) @@ -764,6 +765,8 @@ add_library(${CoreLibName} ${CoreLinkType} Core/PSPLoaders.h Core/PSPMixer.cpp Core/PSPMixer.h + Core/SaveState.cpp + Core/SaveState.h Core/System.cpp Core/System.h Core/Util/BlockAllocator.cpp @@ -786,6 +789,8 @@ add_library(GPU OBJECT GPU/GLES/FragmentShaderGenerator.h GPU/GLES/Framebuffer.cpp GPU/GLES/Framebuffer.h + GPU/GLES/IndexGenerator.cpp + GPU/GLES/IndexGenerator.h GPU/GLES/ShaderManager.cpp GPU/GLES/ShaderManager.h GPU/GLES/StateMapping.cpp @@ -799,12 +804,12 @@ add_library(GPU OBJECT GPU/GLES/VertexShaderGenerator.cpp GPU/GLES/VertexShaderGenerator.h GPU/GPUInterface.h + GPU/GeDisasm.cpp + GPU/GeDisasm.h GPU/GPUState.cpp GPU/GPUState.h GPU/Math3D.cpp GPU/Math3D.h -# GPU/Null/NullDisplayListInterpreter.cpp -# GPU/Null/NullDisplayListInterpreter.h GPU/Null/NullGpu.cpp GPU/Null/NullGpu.h GPU/ge_constants.h) diff --git a/Common/Action.h b/Common/Action.h index dbe7fb3759..ae3524b923 100644 --- a/Common/Action.h +++ b/Common/Action.h @@ -1,6 +1,7 @@ #pragma once #include +#include "ChunkFile.h" // Sometimes you want to set something to happen later, without that later place really needing // to know about all the things that might happen. That's when you use an Action, and add it @@ -17,4 +18,6 @@ class Action public: virtual ~Action() {} virtual void run() = 0; + virtual void DoState(PointerWrap &p) = 0; + int actionTypeID; }; diff --git a/Common/ChunkFile.h b/Common/ChunkFile.h index 7228365ccd..1671cecc8a 100644 --- a/Common/ChunkFile.h +++ b/Common/ChunkFile.h @@ -31,6 +31,8 @@ #include #include #include +#include +#include #include "Common.h" #include "FileUtil.h" @@ -112,14 +114,59 @@ public: } } + template + void Do(std::multimap &x) + { + unsigned int number = (unsigned int)x.size(); + Do(number); + switch (mode) { + case MODE_READ: + { + x.clear(); + while (number > 0) + { + K first; + Do(first); + T second; + Do(second); + x.insert(std::make_pair(first, second)); + --number; + } + } + break; + case MODE_WRITE: + case MODE_MEASURE: + case MODE_VERIFY: + { + typename std::multimap::iterator itr = x.begin(); + while (number > 0) + { + Do(itr->first); + Do(itr->second); + --number; + ++itr; + } + } + break; + } + } + // Store vectors. template void Do(std::vector &x) + { + T dv; + Do(x, dv); + } + + template + void Do(std::vector &x, T &default_val) { u32 vec_size = (u32)x.size(); Do(vec_size); - x.resize(vec_size); - DoArray(&x[0], vec_size); + x.resize(vec_size, default_val); + if (vec_size > 0) + DoArray(&x[0], vec_size); } // Store deques. @@ -133,7 +180,62 @@ public: for(i = 0; i < deq_size; i++) DoVoid(&x[i],sizeof(T)); } - + + // Store STL lists. + template + void Do(std::list &x) + { + T dv; + Do(x, dv); + } + + template + void Do(std::list &x, T &default_val) + { + u32 list_size = (u32)x.size(); + Do(list_size); + x.resize(list_size, default_val); + + typename std::list::iterator itr, end; + for (itr = x.begin(), end = x.end(); itr != end; ++itr) + Do(*itr); + } + + // Store STL sets. + template + void Do(std::set &x) + { + unsigned int number = (unsigned int)x.size(); + Do(number); + + switch (mode) + { + case MODE_READ: + { + x.clear(); + while (number-- > 0) + { + T it; + Do(it); + x.insert(it); + } + } + break; + case MODE_WRITE: + case MODE_MEASURE: + case MODE_VERIFY: + { + typename std::set::iterator itr = x.begin(); + while (number-- > 0) + Do(*itr++); + } + break; + + default: + ERROR_LOG(COMMON, "Savestate error: invalid mode %d.", mode); + } + } + // Store strings. void Do(std::string &x) { @@ -371,6 +473,30 @@ public: return true; } + template + static bool Verify(T& _class) + { + u8 *ptr = 0; + + // Step 1: Measure the space required. + PointerWrap p(&ptr, PointerWrap::MODE_MEASURE); + _class.DoState(p); + size_t const sz = (size_t)ptr; + std::vector buffer(sz); + + // Step 2: Dump the state. + ptr = &buffer[0]; + p.SetMode(PointerWrap::MODE_WRITE); + _class.DoState(p); + + // Step 3: Verify the state. + ptr = &buffer[0]; + p.SetMode(PointerWrap::MODE_VERIFY); + _class.DoState(p); + + return true; + } + private: struct SChunkHeader { diff --git a/Common/CommonPaths.h b/Common/CommonPaths.h index 0fdf5f887c..949c8d390b 100644 --- a/Common/CommonPaths.h +++ b/Common/CommonPaths.h @@ -100,7 +100,7 @@ #define LOGGER_CONFIG "Logger.ini" // Files in the directory returned by GetUserPath(D_LOGS_IDX) -#define MAIN_LOG "dolphin.log" +#define MAIN_LOG "ppsspp.log" // Sys files #define TOTALDB "totaldb.dsy" diff --git a/Common/ConsoleListener.cpp b/Common/ConsoleListener.cpp index 04729985d0..e590d46c15 100644 --- a/Common/ConsoleListener.cpp +++ b/Common/ConsoleListener.cpp @@ -318,9 +318,7 @@ void ConsoleListener::Log(LogTypes::LOG_LEVELS Level, const char *Text) Text += 10; } SetConsoleTextAttribute(hConsole, Color); - size_t len = strlen(Text); - if (Text[len-1] == '\n' && Text[len-1] == '\r') - len--; + size_t len = strlen(Text); WriteConsole(hConsole, Text, (DWORD)len, &cCharsWritten, NULL); #else char ColorAttr[16] = ""; diff --git a/Common/FileUtil.cpp b/Common/FileUtil.cpp index e0a3fe8609..8ba82fa901 100644 --- a/Common/FileUtil.cpp +++ b/Common/FileUtil.cpp @@ -335,6 +335,8 @@ bool Copy(const std::string &srcFilename, const std::string &destFilename) ERROR_LOG(COMMON, "Copy: failed reading from source, %s --> %s: %s", srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); + fclose(input); + fclose(output); return false; } } @@ -346,6 +348,8 @@ bool Copy(const std::string &srcFilename, const std::string &destFilename) ERROR_LOG(COMMON, "Copy: failed writing to output, %s --> %s: %s", srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); + fclose(input); + fclose(output); return false; } } @@ -713,6 +717,8 @@ std::string &GetUserPath(const unsigned int DirIDX, const std::string &newPath) #ifdef _WIN32 // TODO: use GetExeDirectory() here instead of ROOT_DIR so that if the cwd is changed we still have the correct paths? paths[D_USER_IDX] = ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP; +#elif defined(__SYMBIAN32__) + paths[D_USER_IDX] = "E:" DIR_SEP "PPSSPP" DIR_SEP; #else if (File::Exists(ROOT_DIR DIR_SEP USERDATA_DIR)) paths[D_USER_IDX] = ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP; diff --git a/Common/FixedSizeQueue.h b/Common/FixedSizeQueue.h index fbfd94c415..b7c9b4d458 100644 --- a/Common/FixedSizeQueue.h +++ b/Common/FixedSizeQueue.h @@ -87,13 +87,32 @@ public: return count_; } + size_t capacity() const { + return N; + } + int room() const { return N - count_; } - bool empty() { - return count_; - } + bool empty() { + return count_ == 0; + } + + void DoState(PointerWrap &p) { + int size = N; + p.Do(size); + if (size != N) + { + ERROR_LOG(HLE, "Savestate failure: Incompatible queue size."); + return; + } + p.DoArray(storage_, N); + p.Do(head_); + p.Do(tail_); + p.Do(count_); + p.DoMarker("FixedSizeQueue"); + } private: T *storage_; diff --git a/Common/LogManager.cpp b/Common/LogManager.cpp index 265197ab1b..f6fd94c98f 100644 --- a/Common/LogManager.cpp +++ b/Common/LogManager.cpp @@ -69,6 +69,8 @@ LogManager::LogManager() m_fileLog = new FileLogListener(File::GetUserPath(F_MAINLOG_IDX).c_str()); m_consoleLog = new ConsoleListener(); m_debuggerLog = new DebuggerLogListener(); +#else + m_fileLog = NULL; #endif for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; ++i) @@ -89,8 +91,9 @@ LogManager::~LogManager() { for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; ++i) { + if (m_fileLog != NULL) + m_logManager->RemoveListener((LogTypes::LOG_TYPE)i, m_fileLog); #if !defined(ANDROID) && !defined(IOS) && !defined(BLACKBERRY) - m_logManager->RemoveListener((LogTypes::LOG_TYPE)i, m_fileLog); m_logManager->RemoveListener((LogTypes::LOG_TYPE)i, m_consoleLog); m_logManager->RemoveListener((LogTypes::LOG_TYPE)i, m_debuggerLog); #endif @@ -98,12 +101,30 @@ LogManager::~LogManager() for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; ++i) delete m_Log[i]; + if (m_fileLog != NULL) + delete m_fileLog; #if !defined(ANDROID) && !defined(IOS) && !defined(BLACKBERRY) - delete m_fileLog; delete m_consoleLog; #endif } +void LogManager::ChangeFileLog(const char *filename) +{ + if (m_fileLog != NULL) + { + for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; ++i) + m_logManager->RemoveListener((LogTypes::LOG_TYPE)i, m_fileLog); + delete m_fileLog; + } + + if (filename != NULL) + { + m_fileLog = new FileLogListener(filename); + for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; ++i) + m_Log[i]->AddListener(m_fileLog); + } +} + void LogManager::SaveConfig(IniFile::Section *section) { for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; i++) @@ -139,7 +160,7 @@ void LogManager::Log(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, const static const char level_to_char[7] = "-NEWID"; char formattedTime[13]; Common::Timer::GetTimeFormatted(formattedTime); - sprintf(msg, "%s %s:%u %c[%s]: %s\n", + sprintf(msg, "%s %s:%d %c[%s]: %s\n", formattedTime, file, line, level_to_char[(int)level], log->GetShortName(), temp); diff --git a/Common/LogManager.h b/Common/LogManager.h index 9b6614e696..faef623276 100644 --- a/Common/LogManager.h +++ b/Common/LogManager.h @@ -179,6 +179,8 @@ public: static void Init(); static void Shutdown(); + void ChangeFileLog(const char *filename); + void SaveConfig(IniFile::Section *section); void LoadConfig(IniFile::Section *section); }; diff --git a/Common/MemArena.cpp b/Common/MemArena.cpp index 22acf02f73..35b5dee794 100644 --- a/Common/MemArena.cpp +++ b/Common/MemArena.cpp @@ -283,7 +283,7 @@ static bool Memory_TryBase(u8 *base, const MemoryView *views, int num_views, u32 int i; for (i = 0; i < num_views; i++) { - const MemoryView &view = views[i]; + const MemoryView &view = views[i]; SKIP(flags, view.flags); if (view.flags & MV_MIRROR_PREVIOUS) { position = last_position; diff --git a/Common/MemoryUtil.cpp b/Common/MemoryUtil.cpp index 2a4d74a469..282ffe2ee4 100644 --- a/Common/MemoryUtil.cpp +++ b/Common/MemoryUtil.cpp @@ -55,6 +55,7 @@ void* AllocateExecutableMemory(size_t size, bool low) void* ptr = VirtualAlloc(0, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE); #elif defined(__SYMBIAN32__) // On Symbian, we will need to create an RChunk and allocate with ->CreateLocalCode(size, size); + static char *map_hint = 0; void* ptr = mmap(map_hint, size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE, -1, 0); #else static char *map_hint = 0; diff --git a/Core/Config.cpp b/Core/Config.cpp index eb09dece92..e0ea8cff4e 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -49,14 +49,18 @@ void CConfig::Load(const char *iniFileName) general->Get("IgnoreBadMemAccess", &bIgnoreBadMemAccess, true); general->Get("CurrentDirectory", ¤tDirectory, ""); general->Get("ShowDebuggerOnLoad", &bShowDebuggerOnLoad, false); + IniFile::Section *cpu = iniFile.GetOrCreateSection("CPU"); cpu->Get("Core", &iCpuCore, 0); + cpu->Get("FastMemory", &bFastMemory, false); IniFile::Section *graphics = iniFile.GetOrCreateSection("Graphics"); graphics->Get("ShowFPSCounter", &bShowFPSCounter, false); graphics->Get("DisplayFramebuffer", &bDisplayFramebuffer, false); graphics->Get("WindowZoom", &iWindowZoom, 1); graphics->Get("BufferedRendering", &bBufferedRendering, true); + graphics->Get("HardwareTransform", &bHardwareTransform, false); + graphics->Get("LinearFiltering", &bLinearFiltering, false); IniFile::Section *sound = iniFile.GetOrCreateSection("Sound"); sound->Get("Enable", &bEnableSound, true); @@ -64,6 +68,10 @@ void CConfig::Load(const char *iniFileName) IniFile::Section *control = iniFile.GetOrCreateSection("Control"); control->Get("ShowStick", &bShowAnalogStick, false); control->Get("ShowTouchControls", &bShowTouchControls, true); + + + // Ephemeral settings + bDrawWireframe = false; } void CConfig::Save() @@ -83,12 +91,15 @@ void CConfig::Save() general->Set("ShowDebuggerOnLoad", bShowDebuggerOnLoad); IniFile::Section *cpu = iniFile.GetOrCreateSection("CPU"); cpu->Set("Core", iCpuCore); + cpu->Set("FastMemory", bFastMemory); IniFile::Section *graphics = iniFile.GetOrCreateSection("Graphics"); graphics->Set("ShowFPSCounter", bShowFPSCounter); graphics->Set("DisplayFramebuffer", bDisplayFramebuffer); graphics->Set("WindowZoom", iWindowZoom); graphics->Set("BufferedRendering", bBufferedRendering); + graphics->Set("HardwareTransform", bHardwareTransform); + graphics->Set("LinearFiltering", bLinearFiltering); IniFile::Section *sound = iniFile.GetOrCreateSection("Sound"); sound->Set("Enable", bEnableSound); diff --git a/Core/Config.h b/Core/Config.h index d873fcb725..030d3ea8c1 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -32,25 +32,38 @@ public: CConfig(); ~CConfig(); - // Many of these are currently broken. - bool bEnableSound; - bool bAutoLoadLast; + // Whether to save the config on close. bool bSaveSettings; + + // These are broken + bool bAutoLoadLast; bool bFirstRun; bool bAutoRun; bool bSpeedLimit; bool bConfirmOnQuit; - bool bIgnoreBadMemAccess; - bool bDisplayFramebuffer; - bool bBufferedRendering; + // Core + bool bIgnoreBadMemAccess; + bool bFastMemory; + int iCpuCore; + + // GFX + bool bDisplayFramebuffer; + bool bHardwareTransform; + bool bBufferedRendering; + bool bDrawWireframe; + bool bLinearFiltering; + int iWindowZoom; // for Windows + + // Sound + bool bEnableSound; + + // UI bool bShowTouchControls; bool bShowDebuggerOnLoad; bool bShowAnalogStick; bool bShowFPSCounter; bool bShowDebugStats; - int iWindowZoom; // for Windows - int iCpuCore; std::string currentDirectory; std::string memCardDirectory; diff --git a/Core/Core.cpp b/Core/Core.cpp index e1c553eff2..33124240f6 100644 --- a/Core/Core.cpp +++ b/Core/Core.cpp @@ -81,7 +81,7 @@ void Core_SingleStep() // Some platforms, like Android, do not call this function but handle things on their own. void Core_Run() { -#if _DEBUG +#if defined(_DEBUG) host->UpdateDisassembly(); #endif @@ -129,14 +129,14 @@ void Core_EnableStepping(bool step) //PowerPC::Pause(); // Sleep(1); sleep_ms(1); -#if _DEBUG +#if defined(_DEBUG) host->SetDebugMode(true); #endif coreState=CORE_STEPPING; } else { -#if _DEBUG +#if defined(_DEBUG) host->SetDebugMode(false); #endif coreState = CORE_RUNNING; diff --git a/Core/Core.h b/Core/Core.h index 66a0a19be0..d9b07c7f6a 100644 --- a/Core/Core.h +++ b/Core/Core.h @@ -33,9 +33,10 @@ void Core_Halt(const char *msg); bool Core_IsStepping(); +// RUNNING must be at 0. enum CoreState { - CORE_RUNNING, + CORE_RUNNING = 0, CORE_STEPPING, CORE_POWERDOWN, CORE_ERROR, diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index 0e795fcf0b..5e73a44574 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -91,6 +91,9 @@ true true ../common;..;../native;../native/ext/glew;../ext/zlib + false + StreamingSIMDExtensions2 + Fast true @@ -251,6 +254,7 @@ + @@ -370,6 +374,7 @@ + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index 34227ceabe..5419b5cc77 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -351,6 +351,9 @@ HLE\Libraries + + Core + @@ -647,6 +650,9 @@ HLE\Libraries + + Core + diff --git a/Core/CoreTiming.cpp b/Core/CoreTiming.cpp index 6cffd3d3eb..01f6627a3b 100644 --- a/Core/CoreTiming.cpp +++ b/Core/CoreTiming.cpp @@ -24,6 +24,7 @@ #include "CoreTiming.h" #include "Core.h" #include "HLE/sceKernelThread.h" +#include "../Common/ChunkFile.h" int CPU_HZ = 222000000; @@ -36,6 +37,11 @@ namespace CoreTiming struct EventType { + EventType() {} + + EventType(TimedCallback cb, const char *n) + : callback(cb), name(n) {} + TimedCallback callback; const char *name; }; @@ -50,12 +56,6 @@ struct BaseEvent // Event *next; }; -template -struct LinkedListItem : public T -{ - LinkedListItem *next; -}; - typedef LinkedListItem Event; Event *first; @@ -74,11 +74,13 @@ s64 idledCycles; static std::recursive_mutex externalEventSection; +// Warning: not included in save state. void (*advanceCallback)(int cyclesExecuted) = NULL; void SetClockFrequencyMHz(int cpuMhz) { CPU_HZ = cpuMhz * 1000000; + // TODO: Rescale times of scheduled events? } int GetClockFrequencyMHz() @@ -124,13 +126,24 @@ void FreeTsEvent(Event* ev) int RegisterEvent(const char *name, TimedCallback callback) { - EventType type; - type.name = name; - type.callback = callback; - event_types.push_back(type); + event_types.push_back(EventType(callback, name)); return (int)event_types.size() - 1; } +void AntiCrashCallback(u64 userdata, int cyclesLate) +{ + ERROR_LOG(CPU, "Savestate broken: an unregistered event was called."); + Core_Halt("invalid timing events"); +} + +void RestoreRegisterEvent(int event_type, const char *name, TimedCallback callback) +{ + if (event_type >= (int) event_types.size()) + event_types.resize(event_type + 1, EventType(AntiCrashCallback, "INVALID EVENT")); + + event_types[event_type] = EventType(callback, name); +} + void UnregisterAllEvents() { if (first) @@ -245,7 +258,7 @@ void ScheduleEvent(int cyclesIntoFuture, int event_type, u64 userdata) Event *ne = GetNewEvent(); ne->userdata = userdata; ne->type = event_type; - ne->time = globalTimer + cyclesIntoFuture; + ne->time = GetTicks() + cyclesIntoFuture; AddEventToQueue(ne); } @@ -294,6 +307,7 @@ u64 UnscheduleEvent(int event_type, u64 userdata) return result; } +// Warning: not included in save state. void RegisterAdvanceCallback(void (*callback)(int cyclesExecuted)) { advanceCallback = callback; @@ -420,7 +434,7 @@ void ProcessFifoWaitEvents() { break; } - } + } } void MoveEvents() @@ -437,7 +451,7 @@ void MoveEvents() // Move free events to threadsafe pool while(allocatedTsEvents > 0 && eventPool) - { + { Event *ev = eventPool; eventPool = ev->next; ev->next = eventTsPool; @@ -454,7 +468,7 @@ void Advance() ProcessFifoWaitEvents(); - if (!first) + if (!first) { // WARN_LOG(CPU, "WARNING - no events in queue. Setting downcount to 10000"); downcount += 10000; @@ -529,4 +543,29 @@ std::string GetScheduledEventsSummary() return text; } +void Event_DoState(PointerWrap &p, BaseEvent *ev) +{ + p.Do(*ev); +} + +void DoState(PointerWrap &p) +{ + std::lock_guard lk(externalEventSection); + + int n = (int) event_types.size(); + p.Do(n); + // These (should) be filled in later by the modules. + event_types.resize(n, EventType(AntiCrashCallback, "INVALID EVENT")); + + p.DoLinkedList(first, (Event **) NULL); + p.DoLinkedList(tsFirst, &tsLast); + + p.Do(CPU_HZ); + p.Do(downcount); + p.Do(slicelength); + p.Do(globalTimer); + p.Do(idledCycles); + p.DoMarker("CoreTiming"); +} + } // namespace diff --git a/Core/CoreTiming.h b/Core/CoreTiming.h index f771d7e484..e5aa8f8772 100644 --- a/Core/CoreTiming.h +++ b/Core/CoreTiming.h @@ -32,6 +32,7 @@ // ScheduleEvent(periodInCycles - cyclesLate, callback, "whatever") #include "../Globals.h" +#include "../Common/ChunkFile.h" #include @@ -58,6 +59,10 @@ inline int usToCycles(int us) { return (int)(CPU_HZ / 1000000 * us); } +inline u64 usToCycles(u64 us) { + return (u64)(CPU_HZ / 1000000ULL * us); +} + inline u64 cyclesToUs(u64 cycles) { return cycles / (CPU_HZ / 1000000); } @@ -74,6 +79,8 @@ namespace CoreTiming // Returns the event_type identifier. int RegisterEvent(const char *name, TimedCallback callback); + // For save states. + void RestoreRegisterEvent(int event_type, const char *name, TimedCallback callback); void UnregisterAllEvents(); // userdata MAY NOT CONTAIN POINTERS. userdata might get written and reloaded from disk, @@ -99,10 +106,13 @@ namespace CoreTiming void LogPendingEvents(); + // Warning: not included in save states. void RegisterAdvanceCallback(void (*callback)(int cyclesExecuted)); std::string GetScheduledEventsSummary(); + void DoState(PointerWrap &p); + void SetClockFrequencyMHz(int cpuMhz); int GetClockFrequencyMHz(); extern int downcount; diff --git a/Core/Debugger/Breakpoints.cpp b/Core/Debugger/Breakpoints.cpp index f682b96727..c426dfb7d7 100644 --- a/Core/Debugger/Breakpoints.cpp +++ b/Core/Debugger/Breakpoints.cpp @@ -40,7 +40,7 @@ void MemCheck::Action(u32 iValue, u32 addr, bool write, int size, u32 pc) if (bLog) { char temp[256]; - printf(temp,"CHK %08x %s%i at %08x (%s), PC=%08x (%s)",iValue,write?"Write":"Read",size*8,addr,symbolMap.GetDescription(addr),pc,symbolMap.GetDescription(pc)); + sprintf(temp,"CHK %08x %s%i at %08x (%s), PC=%08x (%s)",iValue,write?"Write":"Read",size*8,addr,symbolMap.GetDescription(addr),pc,symbolMap.GetDescription(pc)); ERROR_LOG(MEMMAP,"%s",temp); } if (bBreak) diff --git a/Core/Debugger/SymbolMap.cpp b/Core/Debugger/SymbolMap.cpp index b4f454b32f..c2481ec71d 100644 --- a/Core/Debugger/SymbolMap.cpp +++ b/Core/Debugger/SymbolMap.cpp @@ -144,10 +144,9 @@ bool SymbolMap::LoadSymbolMap(const char *filename) { char line[512],temp[256]; fgets(line,511,f); - if (strlen(line)<4) + if (strlen(line) < 4 || sscanf(line, "%s", temp) != 1) continue; - sscanf(line,"%s",temp); if (strcmp(temp,"UNUSED")==0) continue; if (strcmp(temp,".text")==0) {started=true;continue;}; if (strcmp(temp,".init")==0) {started=true;continue;}; @@ -233,7 +232,7 @@ int SymbolMap::GetSymbolNum(unsigned int address, SymbolType symmask) } -char temp[256]; +char descriptionTemp[256]; char *SymbolMap::GetDescription(unsigned int address) { @@ -244,8 +243,8 @@ char *SymbolMap::GetDescription(unsigned int address) return entries[fun].name; else { - sprintf(temp, "(%08x)", address); - return temp; + sprintf(descriptionTemp, "(%08x)", address); + return descriptionTemp; } //} //else @@ -438,11 +437,12 @@ void SymbolMap::UseFuncSignaturesFile(const char *filename, u32 maxAddress) //#1: Read the signature file and put them in a fast data structure FILE *f = fopen(filename, "r"); int count; - fscanf(f,"%08x\n",&count); - u32 inst,size,hash; + if (fscanf(f, "%08x\n", &count) != 1) + count = 0; char name[256]; for (int a=0; a 0) + inputChars.resize(inputChars.size() - 1); + } + else if (IsButtonPressed(CTRL_START)) { status = SCE_UTILITY_STATUS_FINISHED; } @@ -222,17 +216,33 @@ void PSPOskDialog::Update() { status = SCE_UTILITY_STATUS_SHUTDOWN; } - // just fake the return values to be "000000" as this will work for most cases e.g. when restricted to entering just numbers - Memory::Write_U16(0x0030,oskData.outtextPtr); - Memory::Write_U16(0x0030,oskData.outtextPtr+2); - Memory::Write_U16(0x0030,oskData.outtextPtr+4); - Memory::Write_U16(0x0030,oskData.outtextPtr+6); - Memory::Write_U16(0x0030,oskData.outtextPtr+8); - Memory::Write_U16(0x0030,oskData.outtextPtr+10); - Memory::Write_U16(0x0030,oskData.outtextPtr+12); - oskData.outtextlength = 6; + + for (int i = 0; i < limit; ++i) + { + u16 value = 0; + if (i < (int) inputChars.size()) + value = 0x0000 ^ inputChars[i]; + Memory::Write_U16(value, oskData.outtextPtr + (2 * i)); + } + + oskData.outtextlength = inputChars.size(); oskParams.base.result= 0; oskData.result = PSP_UTILITY_OSK_RESULT_CHANGED; Memory::WriteStruct(oskParams.SceUtilityOskDataPtr, &oskData); Memory::WriteStruct(oskParamsAddr, &oskParams); + + return 0; +} + +void PSPOskDialog::DoState(PointerWrap &p) +{ + p.Do(oskParams); + p.Do(oskData); + p.Do(oskDesc); + p.Do(oskIntext); + p.Do(oskOuttext); + p.Do(oskParamsAddr); + p.Do(selectedChar); + p.Do(inputChars); + p.DoMarker("PSPOskDialog"); } diff --git a/Core/Dialog/PSPOskDialog.h b/Core/Dialog/PSPOskDialog.h index 889fac9a7e..1683edf040 100644 --- a/Core/Dialog/PSPOskDialog.h +++ b/Core/Dialog/PSPOskDialog.h @@ -19,14 +19,146 @@ #include "PSPDialog.h" #include "../Core/MemMap.h" + + + +/** +* Enumeration for input language +*/ +enum SceUtilityOskInputLanguage +{ + PSP_UTILITY_OSK_LANGUAGE_DEFAULT = 0x00, + PSP_UTILITY_OSK_LANGUAGE_JAPANESE = 0x01, + PSP_UTILITY_OSK_LANGUAGE_ENGLISH = 0x02, + PSP_UTILITY_OSK_LANGUAGE_FRENCH = 0x03, + PSP_UTILITY_OSK_LANGUAGE_SPANISH = 0x04, + PSP_UTILITY_OSK_LANGUAGE_GERMAN = 0x05, + PSP_UTILITY_OSK_LANGUAGE_ITALIAN = 0x06, + PSP_UTILITY_OSK_LANGUAGE_DUTCH = 0x07, + PSP_UTILITY_OSK_LANGUAGE_PORTUGESE = 0x08, + PSP_UTILITY_OSK_LANGUAGE_RUSSIAN = 0x09, + PSP_UTILITY_OSK_LANGUAGE_KOREAN = 0x0a +}; + +/** +* Enumeration for OSK internal state +*/ +enum SceUtilityOskState +{ + PSP_UTILITY_OSK_DIALOG_NONE = 0, /**< No OSK is currently active */ + PSP_UTILITY_OSK_DIALOG_INITING, /**< The OSK is currently being initialized */ + PSP_UTILITY_OSK_DIALOG_INITED, /**< The OSK is initialised */ + PSP_UTILITY_OSK_DIALOG_VISIBLE, /**< The OSK is visible and ready for use */ + PSP_UTILITY_OSK_DIALOG_QUIT, /**< The OSK has been cancelled and should be shut down */ + PSP_UTILITY_OSK_DIALOG_FINISHED /**< The OSK has successfully shut down */ +}; + +/** +* Enumeration for OSK field results +*/ +enum SceUtilityOskResult +{ + PSP_UTILITY_OSK_RESULT_UNCHANGED = 0, + PSP_UTILITY_OSK_RESULT_CANCELLED, + PSP_UTILITY_OSK_RESULT_CHANGED +}; + +/** +* Enumeration for input types (these are limited by initial choice of language) +*/ +enum SceUtilityOskInputType +{ + PSP_UTILITY_OSK_INPUTTYPE_ALL = 0x00000000, + PSP_UTILITY_OSK_INPUTTYPE_LATIN_DIGIT = 0x00000001, + PSP_UTILITY_OSK_INPUTTYPE_LATIN_SYMBOL = 0x00000002, + PSP_UTILITY_OSK_INPUTTYPE_LATIN_LOWERCASE = 0x00000004, + PSP_UTILITY_OSK_INPUTTYPE_LATIN_UPPERCASE = 0x00000008, + PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_DIGIT = 0x00000100, + PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_SYMBOL = 0x00000200, + PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_LOWERCASE = 0x00000400, + PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_UPPERCASE = 0x00000800, + // http://en.wikipedia.org/wiki/Hiragana + PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_HIRAGANA = 0x00001000, + // http://en.wikipedia.org/wiki/Katakana + // Half-width Katakana + PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_HALF_KATAKANA = 0x00002000, + PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_KATAKANA = 0x00004000, + // http://en.wikipedia.org/wiki/Kanji + PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_KANJI = 0x00008000, + PSP_UTILITY_OSK_INPUTTYPE_RUSSIAN_LOWERCASE = 0x00010000, + PSP_UTILITY_OSK_INPUTTYPE_RUSSIAN_UPPERCASE = 0x00020000, + PSP_UTILITY_OSK_INPUTTYPE_KOREAN = 0x00040000, + PSP_UTILITY_OSK_INPUTTYPE_URL = 0x00080000 +}; + +/** +* OSK Field data +*/ +typedef struct _SceUtilityOskData +{ + /** Unknown. Pass 0. */ + int unk_00; + /** Unknown. Pass 0. */ + int unk_04; + /** One of ::SceUtilityOskInputLanguage */ + int language; + /** Unknown. Pass 0. */ + int unk_12; + /** One or more of ::SceUtilityOskInputType (types that are selectable by pressing SELECT) */ + int inputtype; + /** Number of lines */ + int lines; + /** Unknown. Pass 0. */ + int unk_24; + /** Description text */ + u32 descPtr; + /** Initial text */ + u32 intextPtr; + /** Length of output text */ + int outtextlength; + /** Pointer to the output text */ + u32 outtextPtr; + /** Result. One of ::SceUtilityOskResult */ + int result; + /** The max text that can be input */ + int outtextlimit; + +} SceUtilityOskData; + +/** +* OSK parameters +*/ +typedef struct _SceUtilityOskParams +{ + pspUtilityDialogCommon base; + int datacount; /** Number of input fields */ + u32 SceUtilityOskDataPtr; /** Pointer to the start of the data for the input fields */ + int state; /** The local OSK state, one of ::SceUtilityOskState */ + int unk_60;/** Unknown. Pass 0 */ + +} SceUtilityOskParams; + + class PSPOskDialog: public PSPDialog { public: PSPOskDialog(); virtual ~PSPOskDialog(); virtual int Init(u32 oskPtr); - virtual void Update(); + virtual int Update(); + virtual void DoState(PointerWrap &p); private: void HackyGetStringWide(std::string& _string, const u32 em_address); + void RenderKeyboard(); + + SceUtilityOskParams oskParams; + SceUtilityOskData oskData; + std::string oskDesc; + std::string oskIntext; + std::string oskOuttext; + int oskParamsAddr; + + int selectedChar; + std::string inputChars; }; diff --git a/Core/Dialog/PSPPlaceholderDialog.cpp b/Core/Dialog/PSPPlaceholderDialog.cpp index ccd26ed8b6..72e8670316 100644 --- a/Core/Dialog/PSPPlaceholderDialog.cpp +++ b/Core/Dialog/PSPPlaceholderDialog.cpp @@ -25,12 +25,13 @@ PSPPlaceholderDialog::~PSPPlaceholderDialog() { } -void PSPPlaceholderDialog::Init() +int PSPPlaceholderDialog::Init() { status = SCE_UTILITY_STATUS_INITIALIZE; + return 0; } -void PSPPlaceholderDialog::Update() +int PSPPlaceholderDialog::Update() { //__UtilityUpdate(); if (status == SCE_UTILITY_STATUS_INITIALIZE) @@ -45,4 +46,5 @@ void PSPPlaceholderDialog::Update() { status = SCE_UTILITY_STATUS_SHUTDOWN; } + return 0; } diff --git a/Core/Dialog/PSPPlaceholderDialog.h b/Core/Dialog/PSPPlaceholderDialog.h index 7854b07dc2..7ba4a989de 100644 --- a/Core/Dialog/PSPPlaceholderDialog.h +++ b/Core/Dialog/PSPPlaceholderDialog.h @@ -24,7 +24,7 @@ public: PSPPlaceholderDialog(); virtual ~PSPPlaceholderDialog(); - virtual void Init(); - virtual void Update(); + virtual int Init(); + virtual int Update(); }; diff --git a/Core/Dialog/PSPSaveDialog.cpp b/Core/Dialog/PSPSaveDialog.cpp index 421ebd12b4..4c02582986 100644 --- a/Core/Dialog/PSPSaveDialog.cpp +++ b/Core/Dialog/PSPSaveDialog.cpp @@ -31,12 +31,24 @@ PSPSaveDialog::PSPSaveDialog() PSPSaveDialog::~PSPSaveDialog() { } -void PSPSaveDialog::Init(int paramAddr) +int PSPSaveDialog::Init(int paramAddr) { - param.SetPspParam((SceUtilitySavedataParam*)Memory::GetPointer(paramAddr)); + // Ignore if already running + if (status != SCE_UTILITY_STATUS_NONE && status != SCE_UTILITY_STATUS_SHUTDOWN) + { + return 0; + } - DEBUG_LOG(HLE,"sceUtilitySavedataInitStart(%08x)", paramAddr); - DEBUG_LOG(HLE,"Mode: %i", param.GetPspParam()->mode); + int size = Memory::Read_U32(paramAddr); + memset(&request,0,sizeof(request)); + // Only copy the right size to support different save request format + Memory::Memcpy(&request,paramAddr,size); + requestAddr = paramAddr; + + u32 retval = param.SetPspParam(&request); + + INFO_LOG(HLE,"sceUtilitySavedataInitStart(%08x)", paramAddr); + INFO_LOG(HLE,"Mode: %i", param.GetPspParam()->mode); switch(param.GetPspParam()->mode) { @@ -79,13 +91,14 @@ void PSPSaveDialog::Init(int paramAddr) { ERROR_LOG(HLE, "Load/Save function %d not coded. Title: %s Save: %s File: %s", param.GetPspParam()->mode, param.GetGameName(param.GetPspParam()).c_str(), param.GetGameName(param.GetPspParam()).c_str(), param.GetFileName(param.GetPspParam()).c_str()); param.GetPspParam()->result = 0; + status = SCE_UTILITY_STATUS_INITIALIZE; display = DS_NONE; - return; // Return 0 should allow the game to continue, but missing function must be implemented and returning the right value or the game can block. + return 0; // Return 0 should allow the game to continue, but missing function must be implemented and returning the right value or the game can block. } break; } - status = SCE_UTILITY_STATUS_INITIALIZE; + status = (int)retval < 0 ? SCE_UTILITY_STATUS_SHUTDOWN : SCE_UTILITY_STATUS_INITIALIZE; currentSelectedSave = 0; lastButtons = __CtrlPeekButtons(); @@ -121,7 +134,7 @@ void PSPSaveDialog::Init(int paramAddr) INFO_LOG(HLE,"snd0 data : %08x",*((unsigned int*)¶m.GetPspParam()->snd0FileData.buf)); INFO_LOG(HLE,"snd0 size : %u",param.GetPspParam()->snd0FileData.bufSize);*/ - + return retval; } void PSPSaveDialog::DisplaySaveList(bool canMove) @@ -137,16 +150,16 @@ void PSPSaveDialog::DisplaySaveList(bool canMove) } // Calc save image position on screen - int w = 150; - int h = 80; - int x = 20; + float w = 150; + float h = 80; + float x = 20; if(displayCount != currentSelectedSave) { w = 80; h = 40; x = 50; } - int y = 80; + float y = 80; if(displayCount < currentSelectedSave) y -= 50 * (currentSelectedSave - displayCount); else if(displayCount > currentSelectedSave) @@ -194,10 +207,10 @@ void PSPSaveDialog::DisplaySaveIcon() } // Calc save image position on screen - int w = 150; - int h = 80; - int x = 20; - int y = 80; + float w = 150; + float h = 80; + float x = 20; + float y = 80; int tw = 256; int th = 256; @@ -226,8 +239,10 @@ void PSPSaveDialog::DisplaySaveDataInfo1() } else { - char txt[1024]; - sprintf(txt,"%s\n%02d/%02d/%d %02d:%02d %d KB\n%s\n%s" + char txt[2048]; + _dbg_assert_msg_(HLE, sizeof(txt) > sizeof(SaveFileInfo), "Local buffer is too small."); + + sprintf(txt,"%s\n%02d/%02d/%d %02d:%02d %lld KB\n%s\n%s" , param.GetFileInfo(currentSelectedSave).title , param.GetFileInfo(currentSelectedSave).modif_time.tm_mday , param.GetFileInfo(currentSelectedSave).modif_time.tm_mon + 1 @@ -251,7 +266,7 @@ void PSPSaveDialog::DisplaySaveDataInfo2() else { char txt[1024]; - sprintf(txt,"%s\n%02d/%02d/%d %02d:%02d\n%d KB" + sprintf(txt,"%s\n%02d/%02d/%d %02d:%02d\n%lld KB" , param.GetFileInfo(currentSelectedSave).saveTitle , param.GetFileInfo(currentSelectedSave).modif_time.tm_mday , param.GetFileInfo(currentSelectedSave).modif_time.tm_mon + 1 @@ -303,22 +318,24 @@ void PSPSaveDialog::DisplayBack() PPGeDrawText("Back", 270, 220, PPGE_ALIGN_LEFT, 0.5f, 0xFFFFFFFF); } -void PSPSaveDialog::Update() +int PSPSaveDialog::Update() { switch (status) { case SCE_UTILITY_STATUS_FINISHED: status = SCE_UTILITY_STATUS_SHUTDOWN; break; + default: + break; } if (status != SCE_UTILITY_STATUS_RUNNING) { - return; + return 0; } if (!param.GetPspParam()) { status = SCE_UTILITY_STATUS_SHUTDOWN; - return; + return 0; } buttons = __CtrlPeekButtons(); @@ -629,7 +646,6 @@ void PSPSaveDialog::Update() else param.GetPspParam()->result = SCE_UTILITY_SAVEDATA_ERROR_LOAD_NO_DATA; status = SCE_UTILITY_STATUS_FINISHED; - return; break; case SCE_UTILITY_SAVEDATA_TYPE_SAVE: // Only save and exit case SCE_UTILITY_SAVEDATA_TYPE_AUTOSAVE: @@ -638,39 +654,71 @@ void PSPSaveDialog::Update() else param.GetPspParam()->result = SCE_UTILITY_SAVEDATA_ERROR_SAVE_MS_NOSPACE; status = SCE_UTILITY_STATUS_FINISHED; - return; break; case SCE_UTILITY_SAVEDATA_TYPE_SIZES: - param.GetSizes(param.GetPspParam()); - param.GetPspParam()->result = SCE_UTILITY_SAVEDATA_ERROR_SIZES_NO_DATA; + if(param.GetSizes(param.GetPspParam())) + { + param.GetPspParam()->result = 0; + } + else + { + param.GetPspParam()->result = SCE_UTILITY_SAVEDATA_ERROR_SIZES_NO_DATA; + } status = SCE_UTILITY_STATUS_FINISHED; - return; + break; case SCE_UTILITY_SAVEDATA_TYPE_LIST: param.GetList(param.GetPspParam()); param.GetPspParam()->result = 0; status = SCE_UTILITY_STATUS_FINISHED; - return; + break; + // TODO: Don't know the name? + case 12: + // Pretend we have nothing, always. + param.GetPspParam()->result = SCE_UTILITY_SAVEDATA_ERROR_RW_NO_DATA; + status = SCE_UTILITY_STATUS_FINISHED; + break; default: status = SCE_UTILITY_STATUS_FINISHED; - return; break; } } break; default: status = SCE_UTILITY_STATUS_FINISHED; - return; break; } lastButtons = buttons; - + if(status == SCE_UTILITY_STATUS_FINISHED) + { + Memory::Memcpy(requestAddr,&request,request.size); + } + + return 0; } -void PSPSaveDialog::Shutdown() +int PSPSaveDialog::Shutdown() { PSPDialog::Shutdown(); param.SetPspParam(0); + + return 0; } +void PSPSaveDialog::DoState(PointerWrap &p) +{ + p.Do(display); + param.DoState(p); + p.Do(request); + // Just reset it. + param.SetPspParam(&request); + p.Do(requestAddr); + p.Do(currentSelectedSave); + p.Do(yesnoChoice); + p.Do(okButtonImg); + p.Do(cancelButtonImg); + p.Do(okButtonFlag); + p.Do(cancelButtonFlag); + p.DoMarker("PSPSaveDialog"); +} diff --git a/Core/Dialog/PSPSaveDialog.h b/Core/Dialog/PSPSaveDialog.h index b5b5c55d86..1e9eaea054 100644 --- a/Core/Dialog/PSPSaveDialog.h +++ b/Core/Dialog/PSPSaveDialog.h @@ -30,6 +30,8 @@ #define SCE_UTILITY_SAVEDATA_ERROR_LOAD_PARAM (0x80110308) #define SCE_UTILITY_SAVEDATA_ERROR_LOAD_INTERNAL (0x8011030b) +#define SCE_UTILITY_SAVEDATA_ERROR_RW_NO_DATA (0x80110327) + #define SCE_UTILITY_SAVEDATA_ERROR_SAVE_NO_MS (0x80110381) #define SCE_UTILITY_SAVEDATA_ERROR_SAVE_EJECT_MS (0x80110382) #define SCE_UTILITY_SAVEDATA_ERROR_SAVE_MS_NOSPACE (0x80110383) @@ -62,9 +64,10 @@ public: PSPSaveDialog(); virtual ~PSPSaveDialog(); - virtual void Init(int paramAddr); - virtual void Update(); - void Shutdown(); + virtual int Init(int paramAddr); + virtual int Update(); + virtual int Shutdown(); + virtual void DoState(PointerWrap &p); private : @@ -102,6 +105,8 @@ private : DisplayState display; SavedataParam param; + SceUtilitySavedataParam request; + int requestAddr; int currentSelectedSave; int yesnoChoice; diff --git a/Core/Dialog/SavedataParam.cpp b/Core/Dialog/SavedataParam.cpp index 189b04f11b..4abafe0c73 100644 --- a/Core/Dialog/SavedataParam.cpp +++ b/Core/Dialog/SavedataParam.cpp @@ -16,10 +16,11 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include "SavedataParam.h" -#include "../System.h" #include "image/png_load.h" #include "../HLE/sceKernelMemory.h" #include "../ELF/ParamSFO.h" +#include "Core/HW/MemoryStick.h" +#include "PSPSaveDialog.h" std::string icon0Name = "ICON0.PNG"; std::string icon1Name = "ICON1.PMF"; @@ -29,19 +30,66 @@ std::string sfoName = "PARAM.SFO"; std::string savePath = "ms0:/PSP/SAVEDATA/"; +namespace +{ + int getSizeNormalized(int size) + { + int sizeCluster = (int)MemoryStick_SectorSize(); + return ((int)((size + sizeCluster - 1) / sizeCluster)) * sizeCluster; + } + + void SetStringFromSFO(ParamSFOData &sfoFile, const char *name, char *str, int strLength) + { + std::string value = sfoFile.GetValueString(name); + strncpy(str, value.c_str(), strLength - 1); + str[strLength - 1] = 0; + } + + bool ReadPSPFile(std::string filename, u8 *data, s64 dataSize) + { + u32 handle = pspFileSystem.OpenFile(filename, FILEACCESS_READ); + if (handle == 0) + return false; + + int result = pspFileSystem.ReadFile(handle, data, dataSize); + pspFileSystem.CloseFile(handle); + + return result != 0; + } + + bool WritePSPFile(std::string filename, u8 *data, int dataSize) + { + u32 handle = pspFileSystem.OpenFile(filename, (FileAccess)(FILEACCESS_WRITE | FILEACCESS_CREATE)); + if (handle == 0) + return false; + + int result = pspFileSystem.WriteFile(handle, data, dataSize); + pspFileSystem.CloseFile(handle); + + return result != 0; + } + + struct EncryptFileInfo + { + int fileVersion; + u8 key[16]; + int sdkVersion; + }; +} + SavedataParam::SavedataParam() : pspParam(0) , selectedSave(0) - , saveNameListData(0) , saveDataList(0) , saveNameListDataCount(0) + , saveDataListCount(0) { } void SavedataParam::Init() { - if(!pspFileSystem.GetFileInfo(savePath).exists) + if (!pspFileSystem.GetFileInfo(savePath).exists) { pspFileSystem.MkDir(savePath); } @@ -54,7 +102,7 @@ std::string SavedataParam::GetSaveDir(SceUtilitySavedataParam* param, int saveId } std::string dirPath = GetGameName(param)+GetSaveName(param); - if(saveId >= 0 && saveNameListDataCount > 0) // if user selection, use it + if (saveId >= 0 && saveNameListDataCount > 0) // if user selection, use it dirPath = std::string(GetGameName(param))+GetFilename(saveId); return dirPath; @@ -101,9 +149,9 @@ bool SavedataParam::Delete(SceUtilitySavedataParam* param, int saveId) } std::string dirPath = GetSaveFilePath(param,saveId); - if(saveId >= 0 && saveNameListDataCount > 0) // if user selection, use it + if (saveId >= 0 && saveNameListDataCount > 0) // if user selection, use it { - if(saveDataList[saveId].size == 0) // don't delete no existing file + if (saveDataList[saveId].size == 0) // don't delete no existing file { return false; } @@ -119,31 +167,22 @@ bool SavedataParam::Save(SceUtilitySavedataParam* param, int saveId) return false; } - u8* data_ = (u8*)Memory::GetPointer(*((unsigned int*)¶m->dataBuf)); + u8 *data_ = (u8*)Memory::GetPointer(*((unsigned int*)¶m->dataBuf)); std::string dirPath = GetSaveFilePath(param, saveId); - if(!pspFileSystem.GetFileInfo(dirPath).exists) + if (!pspFileSystem.GetFileInfo(dirPath).exists) pspFileSystem.MkDir(dirPath); std::string filePath = dirPath+"/"+GetFileName(param); INFO_LOG(HLE,"Saving file with size %u in %s",param->dataBufSize,filePath.c_str()); - unsigned int handle = pspFileSystem.OpenFile(filePath,(FileAccess)(FILEACCESS_WRITE | FILEACCESS_CREATE)); - if(handle == 0) + if (!WritePSPFile(filePath, data_, param->dataBufSize)) { - ERROR_LOG(HLE,"Error opening file %s",filePath.c_str()); - return false; - } - if(!pspFileSystem.WriteFile(handle, data_, param->dataBufSize)) - { - pspFileSystem.CloseFile(handle); ERROR_LOG(HLE,"Error writing file %s",filePath.c_str()); return false; } else { - pspFileSystem.CloseFile(handle); - // SAVE PARAM.SFO ParamSFOData sfoFile; sfoFile.SetValue("TITLE",param->sfoParam.title,128); @@ -152,85 +191,87 @@ bool SavedataParam::Save(SceUtilitySavedataParam* param, int saveId) sfoFile.SetValue("PARENTAL_LEVEL",param->sfoParam.parentalLevel,4); sfoFile.SetValue("CATEGORY","MS",4); sfoFile.SetValue("SAVEDATA_DIRECTORY",GetSaveDir(param,saveId),64); - sfoFile.SetValue("SAVEDATA_FILE_LIST","",3168); // This need to be filed with the save filename and a hash - sfoFile.SetValue("SAVEDATA_PARAMS","",128); // This need to be filled with a hash of the save file encrypted. - u8* sfoData; + + // For each file, 32 bytes for filename, 32 bytes for file hash (0 in PPSSPP) + u8* tmpData = new u8[3168]; + memset(tmpData, 0, 3168); + sprintf((char*)tmpData,"%s",GetFileName(param).c_str()); + sfoFile.SetValue("SAVEDATA_FILE_LIST", tmpData, 3168, 3168); + delete[] tmpData; + + // No crypted save, so fill with 0 + tmpData = new u8[128]; + memset(tmpData, 0, 128); + sfoFile.SetValue("SAVEDATA_PARAMS", tmpData, 128, 128); + delete[] tmpData; + + u8 *sfoData; size_t sfoSize; sfoFile.WriteSFO(&sfoData,&sfoSize); std::string sfopath = dirPath+"/"+sfoName; - handle = pspFileSystem.OpenFile(sfopath,(FileAccess)(FILEACCESS_WRITE | FILEACCESS_CREATE)); - if(handle) - { - pspFileSystem.WriteFile(handle, sfoData, sfoSize); - pspFileSystem.CloseFile(handle); - } + WritePSPFile(sfopath, sfoData, sfoSize); delete[] sfoData; // SAVE ICON0 - if(param->icon0FileData.buf) + if (param->icon0FileData.buf) { data_ = (u8*)Memory::GetPointer(*((unsigned int*)¶m->icon0FileData.buf)); std::string icon0path = dirPath+"/"+icon0Name; - handle = pspFileSystem.OpenFile(icon0path,(FileAccess)(FILEACCESS_WRITE | FILEACCESS_CREATE)); - if(handle) - { - pspFileSystem.WriteFile(handle, data_, param->icon0FileData.bufSize); - pspFileSystem.CloseFile(handle); - } + WritePSPFile(icon0path, data_, param->icon0FileData.bufSize); } // SAVE ICON1 - if(param->icon1FileData.buf) + if (param->icon1FileData.buf) { data_ = (u8*)Memory::GetPointer(*((unsigned int*)¶m->icon1FileData.buf)); std::string icon1path = dirPath+"/"+icon1Name; - handle = pspFileSystem.OpenFile(icon1path,(FileAccess)(FILEACCESS_WRITE | FILEACCESS_CREATE)); - if(handle) - { - pspFileSystem.WriteFile(handle, data_, param->icon1FileData.bufSize); - pspFileSystem.CloseFile(handle); - } + WritePSPFile(icon1path, data_, param->icon1FileData.bufSize); } // SAVE PIC1 - if(param->pic1FileData.buf) + if (param->pic1FileData.buf) { data_ = (u8*)Memory::GetPointer(*((unsigned int*)¶m->pic1FileData.buf)); std::string pic1path = dirPath+"/"+pic1Name; - handle = pspFileSystem.OpenFile(pic1path,(FileAccess)(FILEACCESS_WRITE | FILEACCESS_CREATE)); - if(handle) - { - pspFileSystem.WriteFile(handle, data_, param->pic1FileData.bufSize); - pspFileSystem.CloseFile(handle); - } + WritePSPFile(pic1path, data_, param->pic1FileData.bufSize); } // Save SND - if(param->snd0FileData.buf) + if (param->snd0FileData.buf) { data_ = (u8*)Memory::GetPointer(*((unsigned int*)¶m->snd0FileData.buf)); std::string snd0path = dirPath+"/"+snd0Name; - handle = pspFileSystem.OpenFile(snd0path,(FileAccess)(FILEACCESS_WRITE | FILEACCESS_CREATE)); - if(handle) - { - pspFileSystem.WriteFile(handle, data_, param->snd0FileData.bufSize); - pspFileSystem.CloseFile(handle); - } + WritePSPFile(snd0path, data_, param->snd0FileData.bufSize); + } + + // Save Encryption Data + { + EncryptFileInfo encryptInfo; + int dataSize = sizeof(encryptInfo); // version + key + sdkVersion + memset(&encryptInfo,0,dataSize); + + encryptInfo.fileVersion = 1; + encryptInfo.sdkVersion = sceKernelGetCompiledSdkVersion(); + if(param->size > 1500) + memcpy(encryptInfo.key,param->key,16); + + std::string encryptInfoPath = dirPath+"/"+"ENCRYPT_INFO.BIN"; + WritePSPFile(encryptInfoPath, (u8*)&encryptInfo, dataSize); } } return true; } -bool SavedataParam::Load(SceUtilitySavedataParam* param, int saveId) +bool SavedataParam::Load(SceUtilitySavedataParam *param, int saveId) { if (!param) { return false; } - u8* data_ = (u8*)Memory::GetPointer(*((unsigned int*)¶m->dataBuf)); + u8 *data_ = (u8*)Memory::GetPointer(*((unsigned int*)¶m->dataBuf)); std::string dirPath = GetSaveFilePath(param, saveId); - if(saveId >= 0 && saveNameListDataCount > 0) // if user selection, use it + if (saveId >= 0 && saveNameListDataCount > 0) // if user selection, use it { - if(saveDataList[saveId].size == 0) // don't read no existing file + if (saveDataList[saveId].size == 0) // don't read no existing file { return false; } @@ -238,62 +279,120 @@ bool SavedataParam::Load(SceUtilitySavedataParam* param, int saveId) std::string filePath = dirPath+"/"+GetFileName(param); INFO_LOG(HLE,"Loading file with size %u in %s",param->dataBufSize,filePath.c_str()); - u32 handle = pspFileSystem.OpenFile(filePath,FILEACCESS_READ); - if(!handle) + if (!ReadPSPFile(filePath, data_, param->dataBufSize)) { - ERROR_LOG(HLE,"Error opening file %s",filePath.c_str()); - return false; - } - if(!pspFileSystem.ReadFile(handle, data_, param->dataBufSize)) - { - pspFileSystem.CloseFile(handle); ERROR_LOG(HLE,"Error reading file %s",filePath.c_str()); return false; } - pspFileSystem.CloseFile(handle); return true; } -bool SavedataParam::GetSizes(SceUtilitySavedataParam* param) +std::string SavedataParam::GetSpaceText(int size) +{ + char text[50]; + + if(size < 1024) + { + sprintf(text,"%d B",size); + return std::string(text); + } + + size /= 1024; + + if(size < 1024) + { + sprintf(text,"%d KB",size); + return std::string(text); + } + + size /= 1024; + + if(size < 1024) + { + sprintf(text,"%d MB",size); + return std::string(text); + } + + size /= 1024; + sprintf(text,"%d GB",size); + return std::string(text); +} + +// From my test, PSP only answer with data for save of size 1500 (sdk < 2) +// Perhaps changed to use mode 22 id SDK >= 2 +// For now we always return results +bool SavedataParam::GetSizes(SceUtilitySavedataParam *param) { if (!param) { return false; } - if(Memory::IsValidAddress(param->msFree)) + bool ret = true; + + if (Memory::IsValidAddress(param->msFree)) { - Memory::Write_U32(32768,param->msFree); - Memory::Write_U32(32768,param->msFree+4); - Memory::Write_U32(1048576,param->msFree+8); - Memory::Write_U8(0,param->msFree+12); + Memory::Write_U32((u32)MemoryStick_SectorSize(),param->msFree); // cluster Size + Memory::Write_U32((u32)(MemoryStick_FreeSpace() / MemoryStick_SectorSize()),param->msFree+4); // Free cluster + Memory::Write_U32((u32)(MemoryStick_FreeSpace() / 0x400),param->msFree+8); // Free space (in KB) + std::string spaceTxt = SavedataParam::GetSpaceText((int)MemoryStick_FreeSpace()); + Memory::Memset(param->msFree+12,0,spaceTxt.size()+1); + Memory::Memcpy(param->msFree+12,spaceTxt.c_str(),spaceTxt.size()); // Text representing free space } - if(Memory::IsValidAddress(param->msData)) + if (Memory::IsValidAddress(param->msData)) { - Memory::Write_U32(0,param->msData+36); - Memory::Write_U32(0,param->msData+40); - Memory::Write_U8(0,param->msData+44); - Memory::Write_U32(0,param->msData+52); - Memory::Write_U8(0,param->msData+56); + std::string path = GetSaveFilePath(param,0); + PSPFileInfo finfo = pspFileSystem.GetFileInfo(path); + if(finfo.exists) + { + // TODO : fill correctly with the total save size + Memory::Write_U32(1,param->msData+36); //1 + Memory::Write_U32(0x20,param->msData+40); // 0x20 + Memory::Write_U8(0,param->msData+44); // "32 KB" // 8 u8 + Memory::Write_U32(0x20,param->msData+52); // 0x20 + Memory::Write_U8(0,param->msData+56); // "32 KB" // 8 u8 + } + else + { + Memory::Write_U32(0,param->msData+36); + Memory::Write_U32(0,param->msData+40); + Memory::Write_U8(0,param->msData+44); + Memory::Write_U32(0,param->msData+52); + Memory::Write_U8(0,param->msData+56); + ret = false; + // this should return SCE_UTILITY_SAVEDATA_ERROR_SIZES_NO_DATA + } } - if(Memory::IsValidAddress(param->utilityData)) + if (Memory::IsValidAddress(param->utilityData)) { - Memory::Write_U32(13,param->utilityData); - Memory::Write_U32(416,param->utilityData+4); - Memory::Write_U8(0,param->utilityData+8); - Memory::Write_U32(416,param->utilityData+16); - Memory::Write_U8(0,param->utilityData+20); + int total_size = 0; + total_size += getSizeNormalized(1); // SFO; + total_size += getSizeNormalized(param->dataSize); // Save Data + total_size += getSizeNormalized(param->icon0FileData.size); + total_size += getSizeNormalized(param->icon1FileData.size); + total_size += getSizeNormalized(param->pic1FileData.size); + total_size += getSizeNormalized(param->snd0FileData.size); + + Memory::Write_U32(total_size / (u32)MemoryStick_SectorSize(),param->utilityData); // num cluster + Memory::Write_U32(total_size / 0x400,param->utilityData+4); // save size in KB + std::string spaceTxt = SavedataParam::GetSpaceText(total_size); + Memory::Memset(param->utilityData+8,0,spaceTxt.size()+1); + Memory::Memcpy(param->utilityData+8,spaceTxt.c_str(),spaceTxt.size()); // save size in text + Memory::Write_U32(total_size / 0x400,param->utilityData+16); // save size in KB + spaceTxt = SavedataParam::GetSpaceText(total_size); + Memory::Memset(param->utilityData+20,0,spaceTxt.size()+1); + Memory::Memcpy(param->utilityData+20,spaceTxt.c_str(),spaceTxt.size()); // save size in text } - return true; + return ret; } -bool SavedataParam::GetList(SceUtilitySavedataParam* param) +bool SavedataParam::GetList(SceUtilitySavedataParam *param) { if (!param) { return false; } - if(Memory::IsValidAddress(param->idListAddr)) + if (Memory::IsValidAddress(param->idListAddr)) { Memory::Write_U32(0,param->idListAddr+4); } @@ -302,11 +401,11 @@ bool SavedataParam::GetList(SceUtilitySavedataParam* param) void SavedataParam::Clear() { - if(saveDataList) + if (saveDataList) { - for(int i = 0; i < saveNameListDataCount; i++) + for (int i = 0; i < saveNameListDataCount; i++) { - if(saveDataList[i].textureData != 0) + if (saveDataList[i].textureData != 0) kernelMemory.Free(saveDataList[i].textureData); saveDataList[i].textureData = 0; } @@ -316,111 +415,55 @@ void SavedataParam::Clear() } } -void SavedataParam::SetPspParam(SceUtilitySavedataParam* param) +int SavedataParam::SetPspParam(SceUtilitySavedataParam *param) { pspParam = param; - if(!pspParam) + if (!pspParam) { Clear(); - return; + return 0; } bool listEmptyFile = true; - if(param->mode == SCE_UTILITY_SAVEDATA_TYPE_LISTLOAD || + if (param->mode == SCE_UTILITY_SAVEDATA_TYPE_LISTLOAD || param->mode == SCE_UTILITY_SAVEDATA_TYPE_LISTDELETE) { listEmptyFile = false; } - if(param->saveNameList != 0) + char (*saveNameListData)[20]; + if (param->saveNameList != 0) { saveNameListData = (char(*)[20])Memory::GetPointer(param->saveNameList); // Get number of fileName in array - int count = 0; + saveDataListCount = 0; do { - count++; - } while(saveNameListData[count][0] != 0); + saveDataListCount++; + } while(saveNameListData[saveDataListCount][0] != 0); Clear(); - saveDataList = new SaveFileInfo[count]; + saveDataList = new SaveFileInfo[saveDataListCount]; // get and stock file info for each file int realCount = 0; - for(int i = 0; i fileName; PSPFileInfo info = pspFileSystem.GetFileInfo(fileDataPath); - if(info.exists) + if (info.exists) { - saveDataList[realCount].size = info.size; - saveDataList[realCount].saveName = saveNameListData[i]; - saveDataList[realCount].idx = i; - saveDataList[realCount].modif_time = info.mtime; - - // Search save image icon0 - // TODO : If icon0 don't exist, need to use icon1 which is a moving icon. Also play sound - std::string fileDataPath2 = savePath+GetGameName(param)+saveNameListData[i]+"/"+icon0Name; - PSPFileInfo info2 = pspFileSystem.GetFileInfo(fileDataPath2); - if(info2.exists) - { - u8* textureDataPNG = new u8[info2.size]; - int handle = pspFileSystem.OpenFile(fileDataPath2,FILEACCESS_READ); - pspFileSystem.ReadFile(handle,textureDataPNG,info2.size); - pspFileSystem.CloseFile(handle); - unsigned char* textureData; - int w,h; - pngLoadPtr(textureDataPNG, info2.size, &w, &h, &textureData, false); - delete[] textureDataPNG; - u32 texSize = w*h*4; - u32 atlasPtr = kernelMemory.Alloc(texSize, true, "SaveData Icon"); - saveDataList[realCount].textureData = atlasPtr; - Memory::Memcpy(atlasPtr, textureData, texSize); - free(textureData); - saveDataList[realCount].textureWidth = w; - saveDataList[realCount].textureHeight = h; - } - else - { - saveDataList[realCount].textureData = 0; - } - - // Load info in PARAM.SFO - fileDataPath2 = savePath+GetGameName(param)+saveNameListData[i]+"/"+sfoName; - info2 = pspFileSystem.GetFileInfo(fileDataPath2); - if(info2.exists) - { - u8* sfoParam = new u8[info2.size]; - int handle = pspFileSystem.OpenFile(fileDataPath2,FILEACCESS_READ); - pspFileSystem.ReadFile(handle,sfoParam,info2.size); - pspFileSystem.CloseFile(handle); - ParamSFOData sfoFile; - if(sfoFile.ReadSFO(sfoParam,info2.size)) - { - std::string title = sfoFile.GetValueString("TITLE"); - memcpy(saveDataList[realCount].title,title.c_str(),title.size()); - saveDataList[realCount].title[title.size()] = 0; - - std::string savetitle = sfoFile.GetValueString("SAVEDATA_TITLE"); - memcpy(saveDataList[realCount].saveTitle,savetitle.c_str(),savetitle.size()); - saveDataList[realCount].saveTitle[savetitle.size()] = 0; - - std::string savedetail = sfoFile.GetValueString("SAVEDATA_DETAIL"); - memcpy(saveDataList[realCount].saveDetail,savedetail.c_str(),savedetail.size()); - saveDataList[realCount].saveDetail[savedetail.size()] = 0; - } - delete sfoParam; - } + SetFileInfo(realCount, info, saveNameListData[i]); DEBUG_LOG(HLE,"%s Exist",fileDataPath.c_str()); realCount++; } else { - if(listEmptyFile) + if (listEmptyFile) { saveDataList[realCount].size = 0; saveDataList[realCount].saveName = saveNameListData[i]; @@ -435,7 +478,7 @@ void SavedataParam::SetPspParam(SceUtilitySavedataParam* param) } else // Load info on only save { - saveNameListData == 0; + saveNameListData = 0; Clear(); saveDataList = new SaveFileInfo[1]; @@ -445,73 +488,16 @@ void SavedataParam::SetPspParam(SceUtilitySavedataParam* param) std::string fileDataPath = savePath+GetGameName(param)+GetSaveName(param)+"/"+param->fileName; PSPFileInfo info = pspFileSystem.GetFileInfo(fileDataPath); - if(info.exists) + if (info.exists) { - saveDataList[0].size = info.size; - saveDataList[0].saveName = GetSaveName(param); - saveDataList[0].idx = 0; - saveDataList[0].modif_time = info.mtime; - - // Search save image icon0 - // TODO : If icon0 don't exist, need to use icon1 which is a moving icon. Also play sound - std::string fileDataPath2 = savePath+GetGameName(param)+GetSaveName(param)+"/"+icon0Name; - PSPFileInfo info2 = pspFileSystem.GetFileInfo(fileDataPath2); - if(info2.exists) - { - u8* textureDataPNG = new u8[info2.size]; - int handle = pspFileSystem.OpenFile(fileDataPath2,FILEACCESS_READ); - pspFileSystem.ReadFile(handle,textureDataPNG,info2.size); - pspFileSystem.CloseFile(handle); - unsigned char* textureData; - int w,h; - pngLoadPtr(textureDataPNG, info2.size, &w, &h, &textureData, false); - delete[] textureDataPNG; - u32 texSize = w*h*4; - u32 atlasPtr = kernelMemory.Alloc(texSize, true, "SaveData Icon"); - saveDataList[0].textureData = atlasPtr; - Memory::Memcpy(atlasPtr, textureData, texSize); - free(textureData); - saveDataList[0].textureWidth = w; - saveDataList[0].textureHeight = h; - } - else - { - saveDataList[0].textureData = 0; - } - - // Load info in PARAM.SFO - fileDataPath2 = savePath+GetGameName(param)+GetSaveName(param)+"/"+sfoName; - info2 = pspFileSystem.GetFileInfo(fileDataPath2); - if(info2.exists) - { - u8* sfoParam = new u8[info2.size]; - int handle = pspFileSystem.OpenFile(fileDataPath2,FILEACCESS_READ); - pspFileSystem.ReadFile(handle,sfoParam,info2.size); - pspFileSystem.CloseFile(handle); - ParamSFOData sfoFile; - if(sfoFile.ReadSFO(sfoParam,info2.size)) - { - std::string title = sfoFile.GetValueString("TITLE"); - memcpy(saveDataList[0].title,title.c_str(),title.size()); - saveDataList[0].title[title.size()] = 0; - - std::string savetitle = sfoFile.GetValueString("SAVEDATA_TITLE"); - memcpy(saveDataList[0].saveTitle,savetitle.c_str(),savetitle.size()); - saveDataList[0].saveTitle[savetitle.size()] = 0; - - std::string savedetail = sfoFile.GetValueString("SAVEDATA_DETAIL"); - memcpy(saveDataList[0].saveDetail,savedetail.c_str(),savedetail.size()); - saveDataList[0].saveDetail[savedetail.size()] = 0; - } - delete sfoParam; - } + SetFileInfo(0, info, GetSaveName(pspParam)); DEBUG_LOG(HLE,"%s Exist",fileDataPath.c_str()); saveNameListDataCount = 1; } else { - if(listEmptyFile) + if (listEmptyFile) { saveDataList[0].size = 0; saveDataList[0].saveName = GetSaveName(param); @@ -520,8 +506,71 @@ void SavedataParam::SetPspParam(SceUtilitySavedataParam* param) DEBUG_LOG(HLE,"Don't Exist"); } saveNameListDataCount = 0; + return 0; } } + return 0; +} + +void SavedataParam::SetFileInfo(int idx, PSPFileInfo &info, std::string saveName) +{ + saveDataList[idx].size = info.size; + saveDataList[idx].saveName = saveName; + saveDataList[idx].idx = 0; + saveDataList[idx].modif_time = info.mtime; + + // Start with a blank slate. + saveDataList[idx].textureData = 0; + saveDataList[idx].title[0] = 0; + saveDataList[idx].saveTitle[0] = 0; + saveDataList[idx].saveDetail[0] = 0; + + // Search save image icon0 + // TODO : If icon0 don't exist, need to use icon1 which is a moving icon. Also play sound + std::string fileDataPath2 = savePath + GetGameName(pspParam) + saveName + "/" + icon0Name; + PSPFileInfo info2 = pspFileSystem.GetFileInfo(fileDataPath2); + if (info2.exists) + { + u8 *textureDataPNG = new u8[(size_t)info2.size]; + ReadPSPFile(fileDataPath2, textureDataPNG, info2.size); + unsigned char *textureData; + int w,h; + + int success = pngLoadPtr(textureDataPNG, (int)info2.size, &w, &h, &textureData, false); + delete[] textureDataPNG; + + u32 texSize = w*h*4; + u32 atlasPtr; + if (success) + atlasPtr = kernelMemory.Alloc(texSize, true, "SaveData Icon"); + if (success && atlasPtr > 0) + { + saveDataList[idx].textureData = atlasPtr; + Memory::Memcpy(atlasPtr, textureData, texSize); + free(textureData); + saveDataList[idx].textureWidth = w; + saveDataList[idx].textureHeight = h; + } + else + WARN_LOG(HLE, "Unable to load PNG data for savedata."); + } + + // Load info in PARAM.SFO + fileDataPath2 = savePath + GetGameName(pspParam) + saveName + "/" + sfoName; + info2 = pspFileSystem.GetFileInfo(fileDataPath2); + if (info2.exists) + { + u8 *sfoParam = new u8[(size_t)info2.size]; + ReadPSPFile(fileDataPath2, sfoParam, info2.size); + ParamSFOData sfoFile; + if (sfoFile.ReadSFO(sfoParam,(size_t)info2.size)) + { + SetStringFromSFO(sfoFile, "TITLE", saveDataList[idx].title, sizeof(saveDataList[idx].title)); + SetStringFromSFO(sfoFile, "SAVEDATA_TITLE", saveDataList[idx].saveTitle, sizeof(saveDataList[idx].saveTitle)); + SetStringFromSFO(sfoFile, "SAVEDATA_DETAIL", saveDataList[idx].saveDetail, sizeof(saveDataList[idx].saveDetail)); + } + delete [] sfoParam; + } } SceUtilitySavedataParam* SavedataParam::GetPspParam() @@ -547,8 +596,18 @@ int SavedataParam::GetSelectedSave() { return selectedSave; } + void SavedataParam::SetSelectedSave(int idx) { selectedSave = idx; } +void SavedataParam::DoState(PointerWrap &p) +{ + // pspParam is handled in PSPSaveDialog. + p.Do(selectedSave); + p.Do(saveDataListCount); + p.Do(saveNameListDataCount); + p.DoArray(saveDataList, saveDataListCount); + p.DoMarker("SavedataParam"); +} diff --git a/Core/Dialog/SavedataParam.h b/Core/Dialog/SavedataParam.h index 23c77e28ad..4a1384a8ac 100644 --- a/Core/Dialog/SavedataParam.h +++ b/Core/Dialog/SavedataParam.h @@ -18,7 +18,7 @@ #pragma once #include "../HLE/sceKernel.h" - +#include "../System.h" enum SceUtilitySavedataType { @@ -118,9 +118,10 @@ struct SceUtilitySavedataParam }; +// Non native, this one we can reorganize as we like struct SaveFileInfo { - int size; + s64 size; std::string saveName; int idx; @@ -151,9 +152,11 @@ public: std::string GetSaveName(SceUtilitySavedataParam* param); std::string GetFileName(SceUtilitySavedataParam* param); + static std::string GetSpaceText(int size); + SavedataParam(); - void SetPspParam(SceUtilitySavedataParam* param); + int SetPspParam(SceUtilitySavedataParam* param); SceUtilitySavedataParam* GetPspParam(); int GetFilenameCount(); @@ -163,13 +166,16 @@ public: int GetSelectedSave(); void SetSelectedSave(int idx); + void DoState(PointerWrap &p); + private: void Clear(); + void SetFileInfo(int idx, PSPFileInfo &info, std::string saveName); SceUtilitySavedataParam* pspParam; int selectedSave; - char (*saveNameListData)[20]; SaveFileInfo* saveDataList; + int saveDataListCount; int saveNameListDataCount; }; diff --git a/Core/ELF/ElfReader.cpp b/Core/ELF/ElfReader.cpp index 36f5d08899..2969b975e5 100644 --- a/Core/ELF/ElfReader.cpp +++ b/Core/ELF/ElfReader.cpp @@ -50,6 +50,118 @@ void addrToHiLo(u32 addr, u16 &hi, s16 &lo) } } +void ElfReader::LoadRelocations(Elf32_Rel *rels, int numRelocs) +{ + for (int r = 0; r < numRelocs; r++) + { + u32 info = rels[r].r_info; + u32 addr = rels[r].r_offset; + + int type = info & 0xf; + + int readwrite = (info>>8) & 0xff; + int relative = (info>>16) & 0xff; + + //0 = code + //1 = data + + addr += segmentVAddr[readwrite]; + + u32 op = Memory::ReadUnchecked_U32(addr); + + const bool log = false; + //log=true; + if (log) + { + DEBUG_LOG(LOADER,"rel at: %08x type: %08x",addr,info); + } + u32 relocateTo = segmentVAddr[relative]; + +#define R_MIPS32 2 +#define R_MIPS26 4 +#define R_MIPS16_HI 5 +#define R_MIPS16_LO 6 + + switch (type) + { + case R_MIPS32: + if (log) + DEBUG_LOG(LOADER,"Full address reloc %08x", addr); + //full address, no problemo + op += relocateTo; + break; + + case R_MIPS26: //j, jal + //add on to put in correct address space + if (log) + DEBUG_LOG(LOADER,"j/jal reloc %08x", addr); + op = (op & 0xFC000000) | (((op&0x03FFFFFF)+(relocateTo>>2))&0x03FFFFFFF); + break; + + case R_MIPS16_HI: //lui part of lui-addiu pairs + { + if (log) + DEBUG_LOG(LOADER,"HI reloc %08x", addr); + + u32 cur = (op & 0xFFFF) << 16; + u16 hi = 0; + bool found = false; + for (int t = r + 1; te_phnum); // First pass : Get the damn bits into RAM - u32 segmentVAddr[32]; - u32 baseAddress = bRelocate?vaddr:0; + for (int i=0; ie_phnum; i++) { Elf32_Phdr *p = segments + i; @@ -170,7 +281,6 @@ bool ElfReader::LoadInto(u32 loadAddress) if (s->sh_type == SHT_PSPREL) { //We have a relocation table! - int symbolSection = s->sh_link; int sectionToModify = s->sh_info; if (!(sections[sectionToModify].sh_flags & SHF_ALLOC)) @@ -184,116 +294,7 @@ bool ElfReader::LoadInto(u32 loadAddress) Elf32_Rel *rels = (Elf32_Rel *)GetSectionDataPtr(i); DEBUG_LOG(LOADER,"%s: Performing %i relocations on %s",name,numRelocs,GetSectionName(sectionToModify)); - - for (int r = 0; r < numRelocs; r++) - { - u32 info = rels[r].r_info; - u32 addr = rels[r].r_offset; - - int type = info & 0xf; - - int readwrite = (info>>8) & 0xff; - int relative = (info>>16) & 0xff; - - //0 = code - //1 = data - - addr += segmentVAddr[readwrite]; - - u32 op = Memory::ReadUnchecked_U32(addr); - - const bool log = false; - //log=true; - if (log) - { - DEBUG_LOG(LOADER,"rel at: %08x type: %08x",addr,info); - } - u32 relocateTo = segmentVAddr[relative]; - -#define R_MIPS32 2 -#define R_MIPS26 4 -#define R_MIPS16_HI 5 -#define R_MIPS16_LO 6 - - switch (type) - { - case R_MIPS32: - if (log) - DEBUG_LOG(LOADER,"Full address reloc %08x", addr); - //full address, no problemo - op += relocateTo; - break; - - case R_MIPS26: //j, jal - //add on to put in correct address space - if (log) - DEBUG_LOG(LOADER,"j/jal reloc %08x", addr); - op = (op & 0xFC000000) | (((op&0x03FFFFFF)+(relocateTo>>2))&0x03FFFFFFF); - break; - - case R_MIPS16_HI: //lui part of lui-addiu pairs - { - if (log) - DEBUG_LOG(LOADER,"HI reloc %08x", addr); - - u32 cur = (op & 0xFFFF) << 16; - u16 hi = 0; - bool found = false; - for (int t = r + 1; tsh_type == SHT_REL) { @@ -305,7 +306,6 @@ bool ElfReader::LoadInto(u32 loadAddress) else { //We have a relocation table! - int symbolSection = s->sh_link; int sectionToModify = s->sh_info; if (!(sections[sectionToModify].sh_flags & SHF_ALLOC)) { @@ -317,6 +317,24 @@ bool ElfReader::LoadInto(u32 loadAddress) } } + // Segment relocations (a few games use them) + if (GetNumSections() == 0) + { + for (int i=0; ie_phnum; i++) + { + Elf32_Phdr *p = &segments[i]; + if (p->p_type == 0x700000A0) + { + INFO_LOG(LOADER,"Loading segment relocations"); + + int numRelocs = p->p_filesz / sizeof(Elf32_Rel); + + Elf32_Rel *rels = (Elf32_Rel *)GetSegmentPtr(i); + LoadRelocations(rels, numRelocs); + } + } + } + NOTICE_LOG(LOADER,"ELF loading completed successfully."); return true; } diff --git a/Core/ELF/ElfReader.h b/Core/ELF/ElfReader.h index 9290cc22b5..9e98b46b9f 100644 --- a/Core/ELF/ElfReader.h +++ b/Core/ELF/ElfReader.h @@ -42,6 +42,7 @@ class ElfReader bool bRelocate; u32 entryPoint; u32 vaddr; + u32 segmentVAddr[32]; public: ElfReader(void *ptr) { @@ -104,6 +105,10 @@ public: { return segments[segment].p_offset; } + u32 GetSegmentVaddr(int segment) + { + return segmentVAddr[segment]; + } bool DidRelocate() { return bRelocate; @@ -117,4 +122,5 @@ public: // More indepth stuff:) bool LoadInto(u32 vaddr); bool LoadSymbols(); + void LoadRelocations(Elf32_Rel *rels, int numRelocs); }; diff --git a/Core/ELF/ParamSFO.cpp b/Core/ELF/ParamSFO.cpp index e2b423b29e..7965c1455b 100644 --- a/Core/ELF/ParamSFO.cpp +++ b/Core/ELF/ParamSFO.cpp @@ -46,23 +46,18 @@ void ParamSFOData::SetValue(std::string key, unsigned int value, int max_size) } void ParamSFOData::SetValue(std::string key, std::string value, int max_size) { - if(key == "ACCOUNT_ID" || - key == "PADDING" || - key == "PARAMS" || - key == "PARAMS2" || - key == "SAVEDATA_FILE_LIST" || - key == "SAVEDATA_PARAMS") - { - values[key].type = VT_UTF8_SPE; - } - else - { - values[key].type = VT_UTF8; - } + values[key].type = VT_UTF8; values[key].s_value = value; values[key].max_size = max_size; } +void ParamSFOData::SetValue(std::string key, const u8* value, unsigned int size, int max_size) +{ + values[key].type = VT_UTF8_SPE; + values[key].SetData(value,size); + values[key].max_size = max_size; +} + int ParamSFOData::GetValueInt(std::string key) { std::map::iterator it = values.find(key); @@ -73,10 +68,21 @@ int ParamSFOData::GetValueInt(std::string key) std::string ParamSFOData::GetValueString(std::string key) { std::map::iterator it = values.find(key); - if(it == values.end() || (it->second.type != VT_UTF8 && it->second.type != VT_UTF8_SPE)) + if(it == values.end() || (it->second.type != VT_UTF8)) return ""; return it->second.s_value; } +u8* ParamSFOData::GetValueData(std::string key, unsigned int *size) +{ + std::map::iterator it = values.find(key); + if(it == values.end() || (it->second.type != VT_UTF8_SPE)) + return 0; + if(size) + { + *size = it->second.u_size; + } + return it->second.u_value; +} // I'm so sorry Ced but this is highly endian unsafe :( bool ParamSFOData::ReadSFO(const u8 *paramsfo, size_t size) @@ -108,9 +114,9 @@ bool ParamSFOData::ReadSFO(const u8 *paramsfo, size_t size) case 0x0004: // Special format UTF-8 { - const char *utfdata = (const char *)(data_start + indexTables[i].data_table_offset); + const u8 *utfdata = (const u8 *)(data_start + indexTables[i].data_table_offset); DEBUG_LOG(LOADER, "%s %s", key, utfdata); - SetValue(key,std::string(utfdata,indexTables[i].param_len),indexTables[i].param_max_len); + SetValue(key, utfdata, indexTables[i].param_len, indexTables[i].param_max_len); } break; case 0x0204: @@ -187,10 +193,10 @@ bool ParamSFOData::WriteSFO(u8 **paramsfo, size_t *size) else if(it->second.type == VT_UTF8_SPE) { index_ptr->param_fmt = 0x0004; - index_ptr->param_len = it->second.s_value.size()+1; + index_ptr->param_len = it->second.u_size; - memcpy(data_ptr,it->second.s_value.c_str(),index_ptr->param_len); - data_ptr[index_ptr->param_len] = 0; + memset(data_ptr,0,index_ptr->param_max_len); + memcpy(data_ptr,it->second.u_value,index_ptr->param_len); } else if(it->second.type == VT_UTF8) { diff --git a/Core/ELF/ParamSFO.h b/Core/ELF/ParamSFO.h index 3d3441c521..fc4e07efc7 100644 --- a/Core/ELF/ParamSFO.h +++ b/Core/ELF/ParamSFO.h @@ -25,9 +25,11 @@ class ParamSFOData public: void SetValue(std::string key, unsigned int value, int max_size); void SetValue(std::string key, std::string value, int max_size); + void SetValue(std::string key, const u8* value, unsigned int size, int max_size); int GetValueInt(std::string key); std::string GetValueString(std::string key); + u8* GetValueData(std::string key, unsigned int *size); bool ReadSFO(const u8 *paramsfo, size_t size); bool WriteSFO(u8 **paramsfo, size_t *size); @@ -37,14 +39,48 @@ private: { VT_INT, VT_UTF8, - VT_UTF8_SPE + VT_UTF8_SPE // raw data in u8 }; - struct ValueData + class ValueData { + public: ValueType type; int max_size; std::string s_value; int i_value; + + u8* u_value; + unsigned int u_size; + + void SetData(const u8* data, int size) + { + if(u_value) + { + delete[] u_value; + u_value = 0; + } + if(size > 0) + { + u_value = new u8[size]; + memcpy(u_value,data,size); + } + u_size = size; + } + + ValueData() + { + u_value = 0; + u_size = 0; + type = VT_INT; + max_size = 0; + i_value = 0; + } + + ~ValueData() + { + if(u_value) + delete[] u_value; + } }; std::map values; diff --git a/Core/ELF/PrxDecrypter.cpp b/Core/ELF/PrxDecrypter.cpp index 4419dcd665..8541c0358c 100644 --- a/Core/ELF/PrxDecrypter.cpp +++ b/Core/ELF/PrxDecrypter.cpp @@ -10,248 +10,248 @@ extern "C" // Thank you PSARDUMPER & JPCSP keys // PRXDecrypter 16-byte tag keys. -u8 keys260_0[] = {0xC3, 0x24, 0x89, 0xD3, 0x80, 0x87, 0xB2, 0x4E, 0x4C, 0xD7, 0x49, 0xE4, 0x9D, 0x1D, 0x34, 0xD1}; -u8 keys260_1[] = {0xF3, 0xAC, 0x6E, 0x7C, 0x04, 0x0A, 0x23, 0xE7, 0x0D, 0x33, 0xD8, 0x24, 0x73, 0x39, 0x2B, 0x4A}; -u8 keys260_2[] = {0x72, 0xB4, 0x39, 0xFF, 0x34, 0x9B, 0xAE, 0x82, 0x30, 0x34, 0x4A, 0x1D, 0xA2, 0xD8, 0xB4, 0x3C}; -u8 keys280_0[] = {0xCA, 0xFB, 0xBF, 0xC7, 0x50, 0xEA, 0xB4, 0x40, 0x8E, 0x44, 0x5C, 0x63, 0x53, 0xCE, 0x80, 0xB1}; -u8 keys280_1[] = {0x40, 0x9B, 0xC6, 0x9B, 0xA9, 0xFB, 0x84, 0x7F, 0x72, 0x21, 0xD2, 0x36, 0x96, 0x55, 0x09, 0x74}; -u8 keys280_2[] = {0x03, 0xA7, 0xCC, 0x4A, 0x5B, 0x91, 0xC2, 0x07, 0xFF, 0xFC, 0x26, 0x25, 0x1E, 0x42, 0x4B, 0xB5}; -u8 keys300_0[] = {0x9F, 0x67, 0x1A, 0x7A, 0x22, 0xF3, 0x59, 0x0B, 0xAA, 0x6D, 0xA4, 0xC6, 0x8B, 0xD0, 0x03, 0x77}; -u8 keys300_1[] = {0x15, 0x07, 0x63, 0x26, 0xDB, 0xE2, 0x69, 0x34, 0x56, 0x08, 0x2A, 0x93, 0x4E, 0x4B, 0x8A, 0xB2}; -u8 keys300_2[] = {0x56, 0x3B, 0x69, 0xF7, 0x29, 0x88, 0x2F, 0x4C, 0xDB, 0xD5, 0xDE, 0x80, 0xC6, 0x5C, 0xC8, 0x73}; -u8 keys303_0[] = {0x7b, 0xa1, 0xe2, 0x5a, 0x91, 0xb9, 0xd3, 0x13, 0x77, 0x65, 0x4a, 0xb7, 0xc2, 0x8a, 0x10, 0xaf}; -u8 keys310_0[] = {0xa2, 0x41, 0xe8, 0x39, 0x66, 0x5b, 0xfa, 0xbb, 0x1b, 0x2d, 0x6e, 0x0e, 0x33, 0xe5, 0xd7, 0x3f}; -u8 keys310_1[] = {0xA4, 0x60, 0x8F, 0xAB, 0xAB, 0xDE, 0xA5, 0x65, 0x5D, 0x43, 0x3A, 0xD1, 0x5E, 0xC3, 0xFF, 0xEA}; -u8 keys310_2[] = {0xE7, 0x5C, 0x85, 0x7A, 0x59, 0xB4, 0xE3, 0x1D, 0xD0, 0x9E, 0xCE, 0xC2, 0xD6, 0xD4, 0xBD, 0x2B}; -u8 keys310_3[] = {0x2E, 0x00, 0xF6, 0xF7, 0x52, 0xCF, 0x95, 0x5A, 0xA1, 0x26, 0xB4, 0x84, 0x9B, 0x58, 0x76, 0x2F}; -u8 keys330_0[] = {0x3B, 0x9B, 0x1A, 0x56, 0x21, 0x80, 0x14, 0xED, 0x8E, 0x8B, 0x08, 0x42, 0xFA, 0x2C, 0xDC, 0x3A}; -u8 keys330_1[] = {0xE8, 0xBE, 0x2F, 0x06, 0xB1, 0x05, 0x2A, 0xB9, 0x18, 0x18, 0x03, 0xE3, 0xEB, 0x64, 0x7D, 0x26}; -u8 keys330_2[] = {0xAB, 0x82, 0x25, 0xD7, 0x43, 0x6F, 0x6C, 0xC1, 0x95, 0xC5, 0xF7, 0xF0, 0x63, 0x73, 0x3F, 0xE7}; -u8 keys330_3[] = {0xA8, 0xB1, 0x47, 0x77, 0xDC, 0x49, 0x6A, 0x6F, 0x38, 0x4C, 0x4D, 0x96, 0xBD, 0x49, 0xEC, 0x9B}; -u8 keys330_4[] = {0xEC, 0x3B, 0xD2, 0xC0, 0xFA, 0xC1, 0xEE, 0xB9, 0x9A, 0xBC, 0xFF, 0xA3, 0x89, 0xF2, 0x60, 0x1F}; -u8 keys360_0[] = {0x3C, 0x2B, 0x51, 0xD4, 0x2D, 0x85, 0x47, 0xDA, 0x2D, 0xCA, 0x18, 0xDF, 0xFE, 0x54, 0x09, 0xED}; -u8 keys360_1[] = {0x31, 0x1F, 0x98, 0xD5, 0x7B, 0x58, 0x95, 0x45, 0x32, 0xAB, 0x3A, 0xE3, 0x89, 0x32, 0x4B, 0x34}; -u8 keys370_0[] = {0x26, 0x38, 0x0A, 0xAC, 0xA5, 0xD8, 0x74, 0xD1, 0x32, 0xB7, 0x2A, 0xBF, 0x79, 0x9E, 0x6D, 0xDB}; -u8 keys370_1[] = {0x53, 0xE7, 0xAB, 0xB9, 0xC6, 0x4A, 0x4B, 0x77, 0x92, 0x17, 0xB5, 0x74, 0x0A, 0xDA, 0xA9, 0xEA}; -u8 keys370_2[] = {0x71, 0x10, 0xF0, 0xA4, 0x16, 0x14, 0xD5, 0x93, 0x12, 0xFF, 0x74, 0x96, 0xDF, 0x1F, 0xDA, 0x89}; -u8 keys390_0[] = {0x45, 0xEF, 0x5C, 0x5D, 0xED, 0x81, 0x99, 0x84, 0x12, 0x94, 0x8F, 0xAB, 0xE8, 0x05, 0x6D, 0x7D}; -u8 keys390_1[] = {0x70, 0x1B, 0x08, 0x25, 0x22, 0xA1, 0x4D, 0x3B, 0x69, 0x21, 0xF9, 0x71, 0x0A, 0xA8, 0x41, 0xA9}; -u8 keys500_0[] = {0xEB, 0x1B, 0x53, 0x0B, 0x62, 0x49, 0x32, 0x58, 0x1F, 0x83, 0x0A, 0xF4, 0x99, 0x3D, 0x75, 0xD0}; -u8 keys500_1[] = {0xBA, 0xE2, 0xA3, 0x12, 0x07, 0xFF, 0x04, 0x1B, 0x64, 0xA5, 0x11, 0x85, 0xF7, 0x2F, 0x99, 0x5B}; -u8 keys500_2[] = {0x2C, 0x8E, 0xAF, 0x1D, 0xFF, 0x79, 0x73, 0x1A, 0xAD, 0x96, 0xAB, 0x09, 0xEA, 0x35, 0x59, 0x8B}; -u8 keys500_c[] = {0xA3, 0x5D, 0x51, 0xE6, 0x56, 0xC8, 0x01, 0xCA, 0xE3, 0x77, 0xBF, 0xCD, 0xFF, 0x24, 0xDA, 0x4D}; -u8 keys505_a[] = {0x7B, 0x94, 0x72, 0x27, 0x4C, 0xCC, 0x54, 0x3B, 0xAE, 0xDF, 0x46, 0x37, 0xAC, 0x01, 0x4D, 0x87}; -u8 keys505_0[] = {0x2E, 0x8E, 0x97, 0xA2, 0x85, 0x42, 0x70, 0x73, 0x18, 0xDA, 0xA0, 0x8A, 0xF8, 0x62, 0xA2, 0xB0}; -u8 keys505_1[] = {0x58, 0x2A, 0x4C, 0x69, 0x19, 0x7B, 0x83, 0x3D, 0xD2, 0x61, 0x61, 0xFE, 0x14, 0xEE, 0xAA, 0x11}; -u8 keys570_5k[] = {0x6D, 0x72, 0xA4, 0xBA, 0x7F, 0xBF, 0xD1, 0xF1, 0xA9, 0xF3, 0xBB, 0x07, 0x1B, 0xC0, 0xB3, 0x66}; -u8 keys600_1[] = {0xE3, 0x52, 0x39, 0x97, 0x3B, 0x84, 0x41, 0x1C, 0xC3, 0x23, 0xF1, 0xB8, 0xA9, 0x09, 0x4B, 0xF0}; -u8 keys600_2[] = {0xE1, 0x45, 0x93, 0x2C, 0x53, 0xE2, 0xAB, 0x06, 0x6F, 0xB6, 0x8F, 0x0B, 0x66, 0x91, 0xE7, 0x1E}; -u8 keys620_0[] = {0xD6, 0xBD, 0xCE, 0x1E, 0x12, 0xAF, 0x9A, 0xE6, 0x69, 0x30, 0xDE, 0xDA, 0x88, 0xB8, 0xFF, 0xFB}; -u8 keys620_1[] = {0x1D, 0x13, 0xE9, 0x50, 0x04, 0x73, 0x3D, 0xD2, 0xE1, 0xDA, 0xB9, 0xC1, 0xE6, 0x7B, 0x25, 0xA7}; -u8 keys620_a[] = {0xAC, 0x34, 0xBA, 0xB1, 0x97, 0x8D, 0xAE, 0x6F, 0xBA, 0xE8, 0xB1, 0xD6, 0xDF, 0xDF, 0xF1, 0xA2}; -u8 keys620_e[] = {0xB1, 0xB3, 0x7F, 0x76, 0xC3, 0xFB, 0x88, 0xE6, 0xF8, 0x60, 0xD3, 0x35, 0x3C, 0xA3, 0x4E, 0xF3}; -u8 keys620_5[] = {0xF1, 0xBC, 0x17, 0x07, 0xAE, 0xB7, 0xC8, 0x30, 0xD8, 0x34, 0x9D, 0x40, 0x6A, 0x8E, 0xDF, 0x4E}; -u8 keys620_5k[] = {0x41, 0x8A, 0x35, 0x4F, 0x69, 0x3A, 0xDF, 0x04, 0xFD, 0x39, 0x46, 0xA2, 0x5C, 0x2D, 0xF2, 0x21}; -u8 keys620_5v[] = {0xF2, 0x8F, 0x75, 0xA7, 0x31, 0x91, 0xCE, 0x9E, 0x75, 0xBD, 0x27, 0x26, 0xB4, 0xB4, 0x0C, 0x32}; -u8 keys630_k1[] = {0x36, 0xB0, 0xDC, 0xFC, 0x59, 0x2A, 0x95, 0x1D, 0x80, 0x2D, 0x80, 0x3F, 0xCD, 0x30, 0xA0, 0x1B}; -u8 keys630_k2[] = {0xd4, 0x35, 0x18, 0x02, 0x29, 0x68, 0xfb, 0xa0, 0x6a, 0xa9, 0xa5, 0xed, 0x78, 0xfd, 0x2e, 0x9d}; -u8 keys630_k3[] = {0x23, 0x8D, 0x3D, 0xAE, 0x41, 0x50, 0xA0, 0xFA, 0xF3, 0x2F, 0x32, 0xCE, 0xC7, 0x27, 0xCD, 0x50}; -u8 keys630_k4[] = {0xAA, 0xA1, 0xB5, 0x7C, 0x93, 0x5A, 0x95, 0xBD, 0xEF, 0x69, 0x16, 0xFC, 0x2B, 0x92, 0x31, 0xDD}; -u8 keys630_k5[] = {0x87, 0x37, 0x21, 0xCC, 0x65, 0xAE, 0xAA, 0x5F, 0x40, 0xF6, 0x6F, 0x2A, 0x86, 0xC7, 0xA1, 0xC8}; -u8 keys630_k6[] = {0x8D, 0xDB, 0xDC, 0x5C, 0xF2, 0x70, 0x2B, 0x40, 0xB2, 0x3D, 0x00, 0x09, 0x61, 0x7C, 0x10, 0x60}; -u8 keys630_k7[] = {0x77, 0x1C, 0x06, 0x5F, 0x53, 0xEC, 0x3F, 0xFC, 0x22, 0xCE, 0x5A, 0x27, 0xFF, 0x78, 0xA8, 0x48}; -u8 keys630_k8[] = {0x81, 0xD1, 0x12, 0x89, 0x35, 0xC8, 0xEA, 0x8B, 0xE0, 0x02, 0x2D, 0x2D, 0x6A, 0x18, 0x67, 0xB8}; -u8 keys636_k1[] = {0x07, 0xE3, 0x08, 0x64, 0x7F, 0x60, 0xA3, 0x36, 0x6A, 0x76, 0x21, 0x44, 0xC9, 0xD7, 0x06, 0x83}; -u8 keys636_k2[] = {0x91, 0xF2, 0x02, 0x9E, 0x63, 0x32, 0x30, 0xA9, 0x1D, 0xDA, 0x0B, 0xA8, 0xB7, 0x41, 0xA3, 0xCC}; -u8 keys638_k4[] = {0x98, 0x43, 0xFF, 0x85, 0x68, 0xB2, 0xDB, 0x3B, 0xD4, 0x22, 0xD0, 0x4F, 0xAB, 0x5F, 0x0A, 0x31}; -u8 keys639_k3[] = {0x01, 0x7B, 0xF0, 0xE9, 0xBE, 0x9A, 0xDD, 0x54, 0x37, 0xEA, 0x0E, 0xC4, 0xD6, 0x4D, 0x8E, 0x9E}; -u8 keys660_k1[] = {0x76, 0xF2, 0x6C, 0x0A, 0xCA, 0x3A, 0xBA, 0x4E, 0xAC, 0x76, 0xD2, 0x40, 0xF5, 0xC3, 0xBF, 0xF9}; -u8 keys660_k2[] = {0x7A, 0x3E, 0x55, 0x75, 0xB9, 0x6A, 0xFC, 0x4F, 0x3E, 0xE3, 0xDF, 0xB3, 0x6C, 0xE8, 0x2A, 0x82}; -u8 keys660_k3[] = {0xFA, 0x79, 0x09, 0x36, 0xE6, 0x19, 0xE8, 0xA4, 0xA9, 0x41, 0x37, 0x18, 0x81, 0x02, 0xE9, 0xB3}; -u8 keys660_v1[] = {0xBA, 0x76, 0x61, 0x47, 0x8B, 0x55, 0xA8, 0x72, 0x89, 0x15, 0x79, 0x6D, 0xD7, 0x2F, 0x78, 0x0E}; -u8 keys660_v2[] = {0xF9, 0x4A, 0x6B, 0x96, 0x79, 0x3F, 0xEE, 0x0A, 0x04, 0xC8, 0x8D, 0x7E, 0x5F, 0x38, 0x3A, 0xCF}; -u8 keys660_v3[] = {0x88, 0xAF, 0x18, 0xE9, 0xC3, 0xAA, 0x6B, 0x56, 0xF7, 0xC5, 0xA8, 0xBF, 0x1A, 0x84, 0xE9, 0xF3}; -u8 keys660_v4[] = {0xD1, 0xB0, 0xAE, 0xC3, 0x24, 0x36, 0x13, 0x49, 0xD6, 0x49, 0xD7, 0x88, 0xEA, 0xA4, 0x99, 0x86}; -u8 keys660_v5[] = {0xCB, 0x93, 0x12, 0x38, 0x31, 0xC0, 0x2D, 0x2E, 0x7A, 0x18, 0x5C, 0xAC, 0x92, 0x93, 0xAB, 0x32}; -u8 keys660_v6[] = {0x92, 0x8C, 0xA4, 0x12, 0xD6, 0x5C, 0x55, 0x31, 0x5B, 0x94, 0x23, 0x9B, 0x62, 0xB3, 0xDB, 0x47}; -u8 keys660_k4[] = {0xC8, 0xA0, 0x70, 0x98, 0xAE, 0xE6, 0x2B, 0x80, 0xD7, 0x91, 0xE6, 0xCA, 0x4C, 0xA9, 0x78, 0x4E}; -u8 keys660_k5[] = {0xBF, 0xF8, 0x34, 0x02, 0x84, 0x47, 0xBD, 0x87, 0x1C, 0x52, 0x03, 0x23, 0x79, 0xBB, 0x59, 0x81}; -u8 keys660_k6[] = {0xD2, 0x83, 0xCC, 0x63, 0xBB, 0x10, 0x15, 0xE7, 0x7B, 0xC0, 0x6D, 0xEE, 0x34, 0x9E, 0x4A, 0xFA}; -u8 keys660_k7[] = {0xEB, 0xD9, 0x1E, 0x05, 0x3C, 0xAE, 0xAB, 0x62, 0xE3, 0xB7, 0x1F, 0x37, 0xE5, 0xCD, 0x68, 0xC3}; -u8 keys660_v7[] = {0xC5, 0x9C, 0x77, 0x9C, 0x41, 0x01, 0xE4, 0x85, 0x79, 0xC8, 0x71, 0x63, 0xA5, 0x7D, 0x4F, 0xFB}; -u8 keys660_v8[] = {0x86, 0xA0, 0x7D, 0x4D, 0xB3, 0x6B, 0xA2, 0xFD, 0xF4, 0x15, 0x85, 0x70, 0x2D, 0x6A, 0x0D, 0x3A}; -u8 keys660_k8[] = {0x85, 0x93, 0x1F, 0xED, 0x2C, 0x4D, 0xA4, 0x53, 0x59, 0x9C, 0x3F, 0x16, 0xF3, 0x50, 0xDE, 0x46}; -u8 key_21C0[] = {0x6A, 0x19, 0x71, 0xF3, 0x18, 0xDE, 0xD3, 0xA2, 0x6D, 0x3B, 0xDE, 0xC7, 0xBE, 0x98, 0xE2, 0x4C}; -u8 key_2250[] = {0x50, 0xCC, 0x03, 0xAC, 0x3F, 0x53, 0x1A, 0xFA, 0x0A, 0xA4, 0x34, 0x23, 0x86, 0x61, 0x7F, 0x97}; -u8 key_22E0[] = {0x66, 0x0F, 0xCB, 0x3B, 0x30, 0x75, 0xE3, 0x10, 0x0A, 0x95, 0x65, 0xC7, 0x3C, 0x93, 0x87, 0x22}; -u8 key_2D80[] = {0x40, 0x02, 0xC0, 0xBF, 0x20, 0x02, 0xC0, 0xBF, 0x5C, 0x68, 0x2B, 0x95, 0x5F, 0x40, 0x7B, 0xB8}; -u8 key_2D90[] = {0x55, 0x19, 0x35, 0x10, 0x48, 0xD8, 0x2E, 0x46, 0xA8, 0xB1, 0x47, 0x77, 0xDC, 0x49, 0x6A, 0x6F}; -u8 key_2DA8[] = {0x80, 0x02, 0xC0, 0xBF, 0x00, 0x0A, 0xC0, 0xBF, 0x40, 0x03, 0xC0, 0xBF, 0x40, 0x00, 0x00, 0x00}; -u8 key_2DB8[] = {0x4C, 0x2D, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xB8, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; -u8 key_D91605F0[] = {0xB8, 0x8C, 0x45, 0x8B, 0xB6, 0xE7, 0x6E, 0xB8, 0x51, 0x59, 0xA6, 0x53, 0x7C, 0x5E, 0x86, 0x31}; -u8 key_D91606F0[] = {0xED, 0x10, 0xE0, 0x36, 0xC4, 0xFE, 0x83, 0xF3, 0x75, 0x70, 0x5E, 0xF6, 0xA4, 0x40, 0x05, 0xF7}; -u8 key_D91608F0[] = {0x5C, 0x77, 0x0C, 0xBB, 0xB4, 0xC2, 0x4F, 0xA2, 0x7E, 0x3B, 0x4E, 0xB4, 0xB4, 0xC8, 0x70, 0xAF}; -u8 key_D91609F0[] = {0xD0, 0x36, 0x12, 0x75, 0x80, 0x56, 0x20, 0x43, 0xC4, 0x30, 0x94, 0x3E, 0x1C, 0x75, 0xD1, 0xBF}; -u8 key_D9160AF0[] = {0x10, 0xA9, 0xAC, 0x16, 0xAE, 0x19, 0xC0, 0x7E, 0x3B, 0x60, 0x77, 0x86, 0x01, 0x6F, 0xF2, 0x63}; -u8 key_D9160BF0[] = {0x83, 0x83, 0xF1, 0x37, 0x53, 0xD0, 0xBE, 0xFC, 0x8D, 0xA7, 0x32, 0x52, 0x46, 0x0A, 0xC2, 0xC2}; -u8 key_D91611F0[] = {0x61, 0xB0, 0xC0, 0x58, 0x71, 0x57, 0xD9, 0xFA, 0x74, 0x67, 0x0E, 0x5C, 0x7E, 0x6E, 0x95, 0xB9}; -u8 key_D91612F0[] = {0x9E, 0x20, 0xE1, 0xCD, 0xD7, 0x88, 0xDE, 0xC0, 0x31, 0x9B, 0x10, 0xAF, 0xC5, 0xB8, 0x73, 0x23}; -u8 key_D91613F0[] = {0xEB, 0xFF, 0x40, 0xD8, 0xB4, 0x1A, 0xE1, 0x66, 0x91, 0x3B, 0x8F, 0x64, 0xB6, 0xFC, 0xB7, 0x12}; -u8 key_D91614F0[] = {0xFD, 0xF7, 0xB7, 0x3C, 0x9F, 0xD1, 0x33, 0x95, 0x11, 0xB8, 0xB5, 0xBB, 0x54, 0x23, 0x73, 0x85}; -u8 key_D91615F0[] = {0xC8, 0x03, 0xE3, 0x44, 0x50, 0xF1, 0xE7, 0x2A, 0x6A, 0x0D, 0xC3, 0x61, 0xB6, 0x8E, 0x5F, 0x51}; -u8 key_D91616F0[] = {0x53, 0x03, 0xB8, 0x6A, 0x10, 0x19, 0x98, 0x49, 0x1C, 0xAF, 0x30, 0xE4, 0x25, 0x1B, 0x6B, 0x28}; -u8 key_D91617F0[] = {0x02, 0xFA, 0x48, 0x73, 0x75, 0xAF, 0xAE, 0x0A, 0x67, 0x89, 0x2B, 0x95, 0x4B, 0x09, 0x87, 0xA3}; -u8 key_D91618F0[] = {0x96, 0x96, 0x7C, 0xC3, 0xF7, 0x12, 0xDA, 0x62, 0x1B, 0xF6, 0x9A, 0x9A, 0x44, 0x44, 0xBC, 0x48}; -u8 key_D91619F0[] = {0xE0, 0x32, 0xA7, 0x08, 0x6B, 0x2B, 0x29, 0x2C, 0xD1, 0x4D, 0x5B, 0xEE, 0xA8, 0xC8, 0xB4, 0xE9}; -u8 key_D9161AF0[] = {0x27, 0xE5, 0xA7, 0x49, 0x52, 0xE1, 0x94, 0x67, 0x35, 0x66, 0x91, 0x0C, 0xE8, 0x9A, 0x25, 0x24}; -u8 key_D91620F0[] = {0x52, 0x1C, 0xB4, 0x5F, 0x40, 0x3B, 0x9A, 0xDD, 0xAC, 0xFC, 0xEA, 0x92, 0xFD, 0xDD, 0xF5, 0x90}; -u8 key_D91621F0[] = {0xD1, 0x91, 0x2E, 0xA6, 0x21, 0x14, 0x29, 0x62, 0xF6, 0xED, 0xAE, 0xCB, 0xDD, 0xA3, 0xBA, 0xFE}; -u8 key_D91622F0[] = {0x59, 0x5D, 0x78, 0x4D, 0x21, 0xB2, 0x01, 0x17, 0x6C, 0x9A, 0xB5, 0x1B, 0xDA, 0xB7, 0xF9, 0xE6}; -u8 key_D91623F0[] = {0xAA, 0x45, 0xEB, 0x4F, 0x62, 0xFB, 0xD1, 0x0D, 0x71, 0xD5, 0x62, 0xD2, 0xF5, 0xBF, 0xA5, 0x2F}; -u8 key_D91624F0[] = {0x61, 0xB7, 0x26, 0xAF, 0x8B, 0xF1, 0x41, 0x58, 0x83, 0x6A, 0xC4, 0x92, 0x12, 0xCB, 0xB1, 0xE9}; -u8 key_D91628F0[] = {0x49, 0xA4, 0xFC, 0x66, 0xDC, 0xE7, 0x62, 0x21, 0xDB, 0x18, 0xA7, 0x50, 0xD6, 0xA8, 0xC1, 0xB6}; -u8 key_D91680F0[] = {0x2C, 0x22, 0x9B, 0x12, 0x36, 0x74, 0x11, 0x67, 0x49, 0xD1, 0xD1, 0x88, 0x92, 0xF6, 0xA1, 0xD8}; -u8 key_D91681F0[] = {0x52, 0xB6, 0x36, 0x6C, 0x8C, 0x46, 0x7F, 0x7A, 0xCC, 0x11, 0x62, 0x99, 0xC1, 0x99, 0xBE, 0x98}; -u8 key_2E5E10F0[] = {0x9D, 0x5C, 0x5B, 0xAF, 0x8C, 0xD8, 0x69, 0x7E, 0x51, 0x9F, 0x70, 0x96, 0xE6, 0xD5, 0xC4, 0xE8}; -u8 key_2E5E12F0[] = {0x8A, 0x7B, 0xC9, 0xD6, 0x52, 0x58, 0x88, 0xEA, 0x51, 0x83, 0x60, 0xCA, 0x16, 0x79, 0xE2, 0x07}; -u8 key_2E5E13F0[] = {0xFF, 0xA4, 0x68, 0xC3, 0x31, 0xCA, 0xB7, 0x4C, 0xF1, 0x23, 0xFF, 0x01, 0x65, 0x3D, 0x26, 0x36}; -u8 key_2FD30BF0[] = {0xD8, 0x58, 0x79, 0xF9, 0xA4, 0x22, 0xAF, 0x86, 0x90, 0xAC, 0xDA, 0x45, 0xCE, 0x60, 0x40, 0x3F}; -u8 key_2FD311F0[] = {0x3A, 0x6B, 0x48, 0x96, 0x86, 0xA5, 0xC8, 0x80, 0x69, 0x6C, 0xE6, 0x4B, 0xF6, 0x04, 0x17, 0x44}; -u8 key_2FD312F0[] = {0xC5, 0xFB, 0x69, 0x03, 0x20, 0x7A, 0xCF, 0xBA, 0x2C, 0x90, 0xF8, 0xB8, 0x4D, 0xD2, 0xF1, 0xDE}; -u8 keys02G_E[] = {0x9D, 0x09, 0xFD, 0x20, 0xF3, 0x8F, 0x10, 0x69, 0x0D, 0xB2, 0x6F, 0x00, 0xCC, 0xC5, 0x51, 0x2E}; -u8 keys03G_E[] = {0x4F, 0x44, 0x5C, 0x62, 0xB3, 0x53, 0xC4, 0x30, 0xFC, 0x3A, 0xA4, 0x5B, 0xEC, 0xFE, 0x51, 0xEA}; -u8 keys05G_E[] = {0x5D, 0xAA, 0x72, 0xF2, 0x26, 0x60, 0x4D, 0x1C, 0xE7, 0x2D, 0xC8, 0xA3, 0x2F, 0x79, 0xC5, 0x54}; -u8 oneseg_310[] = {0xC7, 0x27, 0x72, 0x85, 0xAB, 0xA7, 0xF7, 0xF0, 0x4C, 0xC1, 0x86, 0xCC, 0xE3, 0x7F, 0x17, 0xCA}; -u8 oneseg_300[] = {0x76, 0x40, 0x9E, 0x08, 0xDB, 0x9B, 0x3B, 0xA1, 0x47, 0x8A, 0x96, 0x8E, 0xF3, 0xF7, 0x62, 0x92}; -u8 oneseg_280[] = {0x23, 0xDC, 0x3B, 0xB5, 0xA9, 0x82, 0xD6, 0xEA, 0x63, 0xA3, 0x6E, 0x2B, 0x2B, 0xE9, 0xE1, 0x54}; -u8 oneseg_260_271[] = {0x22, 0x43, 0x57, 0x68, 0x2F, 0x41, 0xCE, 0x65, 0x4C, 0xA3, 0x7C, 0xC6, 0xC4, 0xAC, 0xF3, 0x60}; -u8 oneseg_slim[] = {0x12, 0x57, 0x0D, 0x8A, 0x16, 0x6D, 0x87, 0x06, 0x03, 0x7D, 0xC8, 0x8B, 0x62, 0xA3, 0x32, 0xA9}; -u8 ms_app_main[] = {0x1E, 0x2E, 0x38, 0x49, 0xDA, 0xD4, 0x16, 0x08, 0x27, 0x2E, 0xF3, 0xBC, 0x37, 0x75, 0x80, 0x93}; -u8 demokeys_280[] = {0x12, 0x99, 0x70, 0x5E, 0x24, 0x07, 0x6C, 0xD0, 0x2D, 0x06, 0xFE, 0x7E, 0xB3, 0x0C, 0x11, 0x26}; -u8 demokeys_3XX_1[] = {0x47, 0x05, 0xD5, 0xE3, 0x56, 0x1E, 0x81, 0x9B, 0x09, 0x2F, 0x06, 0xDB, 0x6B, 0x12, 0x92, 0xE0}; -u8 demokeys_3XX_2[] = {0xF6, 0x62, 0x39, 0x6E, 0x26, 0x22, 0x4D, 0xCA, 0x02, 0x64, 0x16, 0x99, 0x7B, 0x9A, 0xE7, 0xB8}; -u8 ebootbin_271_new[] = {0xF4, 0xAE, 0xF4, 0xE1, 0x86, 0xDD, 0xD2, 0x9C, 0x7C, 0xC5, 0x42, 0xA6, 0x95, 0xA0, 0x83, 0x88}; -u8 ebootbin_280_new[] = {0xB8, 0x8C, 0x45, 0x8B, 0xB6, 0xE7, 0x6E, 0xB8, 0x51, 0x59, 0xA6, 0x53, 0x7C, 0x5E, 0x86, 0x31}; -u8 ebootbin_300_new[] = {0xED, 0x10, 0xE0, 0x36, 0xC4, 0xFE, 0x83, 0xF3, 0x75, 0x70, 0x5E, 0xF6, 0xA4, 0x40, 0x05, 0xF7}; -u8 ebootbin_310_new[] = {0x5C, 0x77, 0x0C, 0xBB, 0xB4, 0xC2, 0x4F, 0xA2, 0x7E, 0x3B, 0x4E, 0xB4, 0xB4, 0xC8, 0x70, 0xAF}; -u8 gameshare_260_271[] = {0xF9, 0x48, 0x38, 0x0C, 0x96, 0x88, 0xA7, 0x74, 0x4F, 0x65, 0xA0, 0x54, 0xC2, 0x76, 0xD9, 0xB8}; -u8 gameshare_280[] = {0x2D, 0x86, 0x77, 0x3A, 0x56, 0xA4, 0x4F, 0xDD, 0x3C, 0x16, 0x71, 0x93, 0xAA, 0x8E, 0x11, 0x43}; -u8 gameshare_300[] = {0x78, 0x1A, 0xD2, 0x87, 0x24, 0xBD, 0xA2, 0x96, 0x18, 0x3F, 0x89, 0x36, 0x72, 0x90, 0x92, 0x85}; -u8 gameshare_310[] = {0xC9, 0x7D, 0x3E, 0x0A, 0x54, 0x81, 0x6E, 0xC7, 0x13, 0x74, 0x99, 0x74, 0x62, 0x18, 0xE7, 0xDD}; -u8 key_380210F0[] = {0x32, 0x2C, 0xFA, 0x75, 0xE4, 0x7E, 0x93, 0xEB, 0x9F, 0x22, 0x80, 0x85, 0x57, 0x08, 0x98, 0x48}; -u8 key_380280F0[] = {0x97, 0x09, 0x12, 0xD3, 0xDB, 0x02, 0xBD, 0xD8, 0xE7, 0x74, 0x51, 0xFE, 0xF0, 0xEA, 0x6C, 0x5C}; -u8 key_380283F0[] = {0x34, 0x20, 0x0C, 0x8E, 0xA1, 0x86, 0x79, 0x84, 0xAF, 0x13, 0xAE, 0x34, 0x77, 0x6F, 0xEA, 0x89}; -u8 key_407810F0[] = {0xAF, 0xAD, 0xCA, 0xF1, 0x95, 0x59, 0x91, 0xEC, 0x1B, 0x27, 0xD0, 0x4E, 0x8A, 0xF3, 0x3D, 0xE7}; -u8 drmkeys_6XX_1[] = {0x36, 0xEF, 0x82, 0x4E, 0x74, 0xFB, 0x17, 0x5B, 0x14, 0x14, 0x05, 0xF3, 0xB3, 0x8A, 0x76, 0x18}; -u8 drmkeys_6XX_2[] = {0x21, 0x52, 0x5D, 0x76, 0xF6, 0x81, 0x0F, 0x15, 0x2F, 0x4A, 0x40, 0x89, 0x63, 0xA0, 0x10, 0x55}; +static const u8 keys260_0[] = {0xC3, 0x24, 0x89, 0xD3, 0x80, 0x87, 0xB2, 0x4E, 0x4C, 0xD7, 0x49, 0xE4, 0x9D, 0x1D, 0x34, 0xD1}; +static const u8 keys260_1[] = {0xF3, 0xAC, 0x6E, 0x7C, 0x04, 0x0A, 0x23, 0xE7, 0x0D, 0x33, 0xD8, 0x24, 0x73, 0x39, 0x2B, 0x4A}; +static const u8 keys260_2[] = {0x72, 0xB4, 0x39, 0xFF, 0x34, 0x9B, 0xAE, 0x82, 0x30, 0x34, 0x4A, 0x1D, 0xA2, 0xD8, 0xB4, 0x3C}; +static const u8 keys280_0[] = {0xCA, 0xFB, 0xBF, 0xC7, 0x50, 0xEA, 0xB4, 0x40, 0x8E, 0x44, 0x5C, 0x63, 0x53, 0xCE, 0x80, 0xB1}; +static const u8 keys280_1[] = {0x40, 0x9B, 0xC6, 0x9B, 0xA9, 0xFB, 0x84, 0x7F, 0x72, 0x21, 0xD2, 0x36, 0x96, 0x55, 0x09, 0x74}; +static const u8 keys280_2[] = {0x03, 0xA7, 0xCC, 0x4A, 0x5B, 0x91, 0xC2, 0x07, 0xFF, 0xFC, 0x26, 0x25, 0x1E, 0x42, 0x4B, 0xB5}; +static const u8 keys300_0[] = {0x9F, 0x67, 0x1A, 0x7A, 0x22, 0xF3, 0x59, 0x0B, 0xAA, 0x6D, 0xA4, 0xC6, 0x8B, 0xD0, 0x03, 0x77}; +static const u8 keys300_1[] = {0x15, 0x07, 0x63, 0x26, 0xDB, 0xE2, 0x69, 0x34, 0x56, 0x08, 0x2A, 0x93, 0x4E, 0x4B, 0x8A, 0xB2}; +static const u8 keys300_2[] = {0x56, 0x3B, 0x69, 0xF7, 0x29, 0x88, 0x2F, 0x4C, 0xDB, 0xD5, 0xDE, 0x80, 0xC6, 0x5C, 0xC8, 0x73}; +static const u8 keys303_0[] = {0x7b, 0xa1, 0xe2, 0x5a, 0x91, 0xb9, 0xd3, 0x13, 0x77, 0x65, 0x4a, 0xb7, 0xc2, 0x8a, 0x10, 0xaf}; +static const u8 keys310_0[] = {0xa2, 0x41, 0xe8, 0x39, 0x66, 0x5b, 0xfa, 0xbb, 0x1b, 0x2d, 0x6e, 0x0e, 0x33, 0xe5, 0xd7, 0x3f}; +static const u8 keys310_1[] = {0xA4, 0x60, 0x8F, 0xAB, 0xAB, 0xDE, 0xA5, 0x65, 0x5D, 0x43, 0x3A, 0xD1, 0x5E, 0xC3, 0xFF, 0xEA}; +static const u8 keys310_2[] = {0xE7, 0x5C, 0x85, 0x7A, 0x59, 0xB4, 0xE3, 0x1D, 0xD0, 0x9E, 0xCE, 0xC2, 0xD6, 0xD4, 0xBD, 0x2B}; +static const u8 keys310_3[] = {0x2E, 0x00, 0xF6, 0xF7, 0x52, 0xCF, 0x95, 0x5A, 0xA1, 0x26, 0xB4, 0x84, 0x9B, 0x58, 0x76, 0x2F}; +static const u8 keys330_0[] = {0x3B, 0x9B, 0x1A, 0x56, 0x21, 0x80, 0x14, 0xED, 0x8E, 0x8B, 0x08, 0x42, 0xFA, 0x2C, 0xDC, 0x3A}; +static const u8 keys330_1[] = {0xE8, 0xBE, 0x2F, 0x06, 0xB1, 0x05, 0x2A, 0xB9, 0x18, 0x18, 0x03, 0xE3, 0xEB, 0x64, 0x7D, 0x26}; +static const u8 keys330_2[] = {0xAB, 0x82, 0x25, 0xD7, 0x43, 0x6F, 0x6C, 0xC1, 0x95, 0xC5, 0xF7, 0xF0, 0x63, 0x73, 0x3F, 0xE7}; +static const u8 keys330_3[] = {0xA8, 0xB1, 0x47, 0x77, 0xDC, 0x49, 0x6A, 0x6F, 0x38, 0x4C, 0x4D, 0x96, 0xBD, 0x49, 0xEC, 0x9B}; +static const u8 keys330_4[] = {0xEC, 0x3B, 0xD2, 0xC0, 0xFA, 0xC1, 0xEE, 0xB9, 0x9A, 0xBC, 0xFF, 0xA3, 0x89, 0xF2, 0x60, 0x1F}; +static const u8 keys360_0[] = {0x3C, 0x2B, 0x51, 0xD4, 0x2D, 0x85, 0x47, 0xDA, 0x2D, 0xCA, 0x18, 0xDF, 0xFE, 0x54, 0x09, 0xED}; +static const u8 keys360_1[] = {0x31, 0x1F, 0x98, 0xD5, 0x7B, 0x58, 0x95, 0x45, 0x32, 0xAB, 0x3A, 0xE3, 0x89, 0x32, 0x4B, 0x34}; +static const u8 keys370_0[] = {0x26, 0x38, 0x0A, 0xAC, 0xA5, 0xD8, 0x74, 0xD1, 0x32, 0xB7, 0x2A, 0xBF, 0x79, 0x9E, 0x6D, 0xDB}; +static const u8 keys370_1[] = {0x53, 0xE7, 0xAB, 0xB9, 0xC6, 0x4A, 0x4B, 0x77, 0x92, 0x17, 0xB5, 0x74, 0x0A, 0xDA, 0xA9, 0xEA}; +static const u8 keys370_2[] = {0x71, 0x10, 0xF0, 0xA4, 0x16, 0x14, 0xD5, 0x93, 0x12, 0xFF, 0x74, 0x96, 0xDF, 0x1F, 0xDA, 0x89}; +static const u8 keys390_0[] = {0x45, 0xEF, 0x5C, 0x5D, 0xED, 0x81, 0x99, 0x84, 0x12, 0x94, 0x8F, 0xAB, 0xE8, 0x05, 0x6D, 0x7D}; +static const u8 keys390_1[] = {0x70, 0x1B, 0x08, 0x25, 0x22, 0xA1, 0x4D, 0x3B, 0x69, 0x21, 0xF9, 0x71, 0x0A, 0xA8, 0x41, 0xA9}; +static const u8 keys500_0[] = {0xEB, 0x1B, 0x53, 0x0B, 0x62, 0x49, 0x32, 0x58, 0x1F, 0x83, 0x0A, 0xF4, 0x99, 0x3D, 0x75, 0xD0}; +static const u8 keys500_1[] = {0xBA, 0xE2, 0xA3, 0x12, 0x07, 0xFF, 0x04, 0x1B, 0x64, 0xA5, 0x11, 0x85, 0xF7, 0x2F, 0x99, 0x5B}; +static const u8 keys500_2[] = {0x2C, 0x8E, 0xAF, 0x1D, 0xFF, 0x79, 0x73, 0x1A, 0xAD, 0x96, 0xAB, 0x09, 0xEA, 0x35, 0x59, 0x8B}; +static const u8 keys500_c[] = {0xA3, 0x5D, 0x51, 0xE6, 0x56, 0xC8, 0x01, 0xCA, 0xE3, 0x77, 0xBF, 0xCD, 0xFF, 0x24, 0xDA, 0x4D}; +static const u8 keys505_a[] = {0x7B, 0x94, 0x72, 0x27, 0x4C, 0xCC, 0x54, 0x3B, 0xAE, 0xDF, 0x46, 0x37, 0xAC, 0x01, 0x4D, 0x87}; +static const u8 keys505_0[] = {0x2E, 0x8E, 0x97, 0xA2, 0x85, 0x42, 0x70, 0x73, 0x18, 0xDA, 0xA0, 0x8A, 0xF8, 0x62, 0xA2, 0xB0}; +static const u8 keys505_1[] = {0x58, 0x2A, 0x4C, 0x69, 0x19, 0x7B, 0x83, 0x3D, 0xD2, 0x61, 0x61, 0xFE, 0x14, 0xEE, 0xAA, 0x11}; +static const u8 keys570_5k[] = {0x6D, 0x72, 0xA4, 0xBA, 0x7F, 0xBF, 0xD1, 0xF1, 0xA9, 0xF3, 0xBB, 0x07, 0x1B, 0xC0, 0xB3, 0x66}; +static const u8 keys600_1[] = {0xE3, 0x52, 0x39, 0x97, 0x3B, 0x84, 0x41, 0x1C, 0xC3, 0x23, 0xF1, 0xB8, 0xA9, 0x09, 0x4B, 0xF0}; +static const u8 keys600_2[] = {0xE1, 0x45, 0x93, 0x2C, 0x53, 0xE2, 0xAB, 0x06, 0x6F, 0xB6, 0x8F, 0x0B, 0x66, 0x91, 0xE7, 0x1E}; +static const u8 keys620_0[] = {0xD6, 0xBD, 0xCE, 0x1E, 0x12, 0xAF, 0x9A, 0xE6, 0x69, 0x30, 0xDE, 0xDA, 0x88, 0xB8, 0xFF, 0xFB}; +static const u8 keys620_1[] = {0x1D, 0x13, 0xE9, 0x50, 0x04, 0x73, 0x3D, 0xD2, 0xE1, 0xDA, 0xB9, 0xC1, 0xE6, 0x7B, 0x25, 0xA7}; +static const u8 keys620_a[] = {0xAC, 0x34, 0xBA, 0xB1, 0x97, 0x8D, 0xAE, 0x6F, 0xBA, 0xE8, 0xB1, 0xD6, 0xDF, 0xDF, 0xF1, 0xA2}; +static const u8 keys620_e[] = {0xB1, 0xB3, 0x7F, 0x76, 0xC3, 0xFB, 0x88, 0xE6, 0xF8, 0x60, 0xD3, 0x35, 0x3C, 0xA3, 0x4E, 0xF3}; +static const u8 keys620_5[] = {0xF1, 0xBC, 0x17, 0x07, 0xAE, 0xB7, 0xC8, 0x30, 0xD8, 0x34, 0x9D, 0x40, 0x6A, 0x8E, 0xDF, 0x4E}; +static const u8 keys620_5k[] = {0x41, 0x8A, 0x35, 0x4F, 0x69, 0x3A, 0xDF, 0x04, 0xFD, 0x39, 0x46, 0xA2, 0x5C, 0x2D, 0xF2, 0x21}; +static const u8 keys620_5v[] = {0xF2, 0x8F, 0x75, 0xA7, 0x31, 0x91, 0xCE, 0x9E, 0x75, 0xBD, 0x27, 0x26, 0xB4, 0xB4, 0x0C, 0x32}; +static const u8 keys630_k1[] = {0x36, 0xB0, 0xDC, 0xFC, 0x59, 0x2A, 0x95, 0x1D, 0x80, 0x2D, 0x80, 0x3F, 0xCD, 0x30, 0xA0, 0x1B}; +static const u8 keys630_k2[] = {0xd4, 0x35, 0x18, 0x02, 0x29, 0x68, 0xfb, 0xa0, 0x6a, 0xa9, 0xa5, 0xed, 0x78, 0xfd, 0x2e, 0x9d}; +static const u8 keys630_k3[] = {0x23, 0x8D, 0x3D, 0xAE, 0x41, 0x50, 0xA0, 0xFA, 0xF3, 0x2F, 0x32, 0xCE, 0xC7, 0x27, 0xCD, 0x50}; +static const u8 keys630_k4[] = {0xAA, 0xA1, 0xB5, 0x7C, 0x93, 0x5A, 0x95, 0xBD, 0xEF, 0x69, 0x16, 0xFC, 0x2B, 0x92, 0x31, 0xDD}; +static const u8 keys630_k5[] = {0x87, 0x37, 0x21, 0xCC, 0x65, 0xAE, 0xAA, 0x5F, 0x40, 0xF6, 0x6F, 0x2A, 0x86, 0xC7, 0xA1, 0xC8}; +static const u8 keys630_k6[] = {0x8D, 0xDB, 0xDC, 0x5C, 0xF2, 0x70, 0x2B, 0x40, 0xB2, 0x3D, 0x00, 0x09, 0x61, 0x7C, 0x10, 0x60}; +static const u8 keys630_k7[] = {0x77, 0x1C, 0x06, 0x5F, 0x53, 0xEC, 0x3F, 0xFC, 0x22, 0xCE, 0x5A, 0x27, 0xFF, 0x78, 0xA8, 0x48}; +static const u8 keys630_k8[] = {0x81, 0xD1, 0x12, 0x89, 0x35, 0xC8, 0xEA, 0x8B, 0xE0, 0x02, 0x2D, 0x2D, 0x6A, 0x18, 0x67, 0xB8}; +static const u8 keys636_k1[] = {0x07, 0xE3, 0x08, 0x64, 0x7F, 0x60, 0xA3, 0x36, 0x6A, 0x76, 0x21, 0x44, 0xC9, 0xD7, 0x06, 0x83}; +static const u8 keys636_k2[] = {0x91, 0xF2, 0x02, 0x9E, 0x63, 0x32, 0x30, 0xA9, 0x1D, 0xDA, 0x0B, 0xA8, 0xB7, 0x41, 0xA3, 0xCC}; +static const u8 keys638_k4[] = {0x98, 0x43, 0xFF, 0x85, 0x68, 0xB2, 0xDB, 0x3B, 0xD4, 0x22, 0xD0, 0x4F, 0xAB, 0x5F, 0x0A, 0x31}; +static const u8 keys639_k3[] = {0x01, 0x7B, 0xF0, 0xE9, 0xBE, 0x9A, 0xDD, 0x54, 0x37, 0xEA, 0x0E, 0xC4, 0xD6, 0x4D, 0x8E, 0x9E}; +static const u8 keys660_k1[] = {0x76, 0xF2, 0x6C, 0x0A, 0xCA, 0x3A, 0xBA, 0x4E, 0xAC, 0x76, 0xD2, 0x40, 0xF5, 0xC3, 0xBF, 0xF9}; +static const u8 keys660_k2[] = {0x7A, 0x3E, 0x55, 0x75, 0xB9, 0x6A, 0xFC, 0x4F, 0x3E, 0xE3, 0xDF, 0xB3, 0x6C, 0xE8, 0x2A, 0x82}; +static const u8 keys660_k3[] = {0xFA, 0x79, 0x09, 0x36, 0xE6, 0x19, 0xE8, 0xA4, 0xA9, 0x41, 0x37, 0x18, 0x81, 0x02, 0xE9, 0xB3}; +static const u8 keys660_v1[] = {0xBA, 0x76, 0x61, 0x47, 0x8B, 0x55, 0xA8, 0x72, 0x89, 0x15, 0x79, 0x6D, 0xD7, 0x2F, 0x78, 0x0E}; +static const u8 keys660_v2[] = {0xF9, 0x4A, 0x6B, 0x96, 0x79, 0x3F, 0xEE, 0x0A, 0x04, 0xC8, 0x8D, 0x7E, 0x5F, 0x38, 0x3A, 0xCF}; +static const u8 keys660_v3[] = {0x88, 0xAF, 0x18, 0xE9, 0xC3, 0xAA, 0x6B, 0x56, 0xF7, 0xC5, 0xA8, 0xBF, 0x1A, 0x84, 0xE9, 0xF3}; +static const u8 keys660_v4[] = {0xD1, 0xB0, 0xAE, 0xC3, 0x24, 0x36, 0x13, 0x49, 0xD6, 0x49, 0xD7, 0x88, 0xEA, 0xA4, 0x99, 0x86}; +static const u8 keys660_v5[] = {0xCB, 0x93, 0x12, 0x38, 0x31, 0xC0, 0x2D, 0x2E, 0x7A, 0x18, 0x5C, 0xAC, 0x92, 0x93, 0xAB, 0x32}; +static const u8 keys660_v6[] = {0x92, 0x8C, 0xA4, 0x12, 0xD6, 0x5C, 0x55, 0x31, 0x5B, 0x94, 0x23, 0x9B, 0x62, 0xB3, 0xDB, 0x47}; +static const u8 keys660_k4[] = {0xC8, 0xA0, 0x70, 0x98, 0xAE, 0xE6, 0x2B, 0x80, 0xD7, 0x91, 0xE6, 0xCA, 0x4C, 0xA9, 0x78, 0x4E}; +static const u8 keys660_k5[] = {0xBF, 0xF8, 0x34, 0x02, 0x84, 0x47, 0xBD, 0x87, 0x1C, 0x52, 0x03, 0x23, 0x79, 0xBB, 0x59, 0x81}; +static const u8 keys660_k6[] = {0xD2, 0x83, 0xCC, 0x63, 0xBB, 0x10, 0x15, 0xE7, 0x7B, 0xC0, 0x6D, 0xEE, 0x34, 0x9E, 0x4A, 0xFA}; +static const u8 keys660_k7[] = {0xEB, 0xD9, 0x1E, 0x05, 0x3C, 0xAE, 0xAB, 0x62, 0xE3, 0xB7, 0x1F, 0x37, 0xE5, 0xCD, 0x68, 0xC3}; +static const u8 keys660_v7[] = {0xC5, 0x9C, 0x77, 0x9C, 0x41, 0x01, 0xE4, 0x85, 0x79, 0xC8, 0x71, 0x63, 0xA5, 0x7D, 0x4F, 0xFB}; +static const u8 keys660_v8[] = {0x86, 0xA0, 0x7D, 0x4D, 0xB3, 0x6B, 0xA2, 0xFD, 0xF4, 0x15, 0x85, 0x70, 0x2D, 0x6A, 0x0D, 0x3A}; +static const u8 keys660_k8[] = {0x85, 0x93, 0x1F, 0xED, 0x2C, 0x4D, 0xA4, 0x53, 0x59, 0x9C, 0x3F, 0x16, 0xF3, 0x50, 0xDE, 0x46}; +static const u8 key_21C0[] = {0x6A, 0x19, 0x71, 0xF3, 0x18, 0xDE, 0xD3, 0xA2, 0x6D, 0x3B, 0xDE, 0xC7, 0xBE, 0x98, 0xE2, 0x4C}; +static const u8 key_2250[] = {0x50, 0xCC, 0x03, 0xAC, 0x3F, 0x53, 0x1A, 0xFA, 0x0A, 0xA4, 0x34, 0x23, 0x86, 0x61, 0x7F, 0x97}; +static const u8 key_22E0[] = {0x66, 0x0F, 0xCB, 0x3B, 0x30, 0x75, 0xE3, 0x10, 0x0A, 0x95, 0x65, 0xC7, 0x3C, 0x93, 0x87, 0x22}; +static const u8 key_2D80[] = {0x40, 0x02, 0xC0, 0xBF, 0x20, 0x02, 0xC0, 0xBF, 0x5C, 0x68, 0x2B, 0x95, 0x5F, 0x40, 0x7B, 0xB8}; +static const u8 key_2D90[] = {0x55, 0x19, 0x35, 0x10, 0x48, 0xD8, 0x2E, 0x46, 0xA8, 0xB1, 0x47, 0x77, 0xDC, 0x49, 0x6A, 0x6F}; +static const u8 key_2DA8[] = {0x80, 0x02, 0xC0, 0xBF, 0x00, 0x0A, 0xC0, 0xBF, 0x40, 0x03, 0xC0, 0xBF, 0x40, 0x00, 0x00, 0x00}; +static const u8 key_2DB8[] = {0x4C, 0x2D, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xB8, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +static const u8 key_D91605F0[] = {0xB8, 0x8C, 0x45, 0x8B, 0xB6, 0xE7, 0x6E, 0xB8, 0x51, 0x59, 0xA6, 0x53, 0x7C, 0x5E, 0x86, 0x31}; +static const u8 key_D91606F0[] = {0xED, 0x10, 0xE0, 0x36, 0xC4, 0xFE, 0x83, 0xF3, 0x75, 0x70, 0x5E, 0xF6, 0xA4, 0x40, 0x05, 0xF7}; +static const u8 key_D91608F0[] = {0x5C, 0x77, 0x0C, 0xBB, 0xB4, 0xC2, 0x4F, 0xA2, 0x7E, 0x3B, 0x4E, 0xB4, 0xB4, 0xC8, 0x70, 0xAF}; +static const u8 key_D91609F0[] = {0xD0, 0x36, 0x12, 0x75, 0x80, 0x56, 0x20, 0x43, 0xC4, 0x30, 0x94, 0x3E, 0x1C, 0x75, 0xD1, 0xBF}; +static const u8 key_D9160AF0[] = {0x10, 0xA9, 0xAC, 0x16, 0xAE, 0x19, 0xC0, 0x7E, 0x3B, 0x60, 0x77, 0x86, 0x01, 0x6F, 0xF2, 0x63}; +static const u8 key_D9160BF0[] = {0x83, 0x83, 0xF1, 0x37, 0x53, 0xD0, 0xBE, 0xFC, 0x8D, 0xA7, 0x32, 0x52, 0x46, 0x0A, 0xC2, 0xC2}; +static const u8 key_D91611F0[] = {0x61, 0xB0, 0xC0, 0x58, 0x71, 0x57, 0xD9, 0xFA, 0x74, 0x67, 0x0E, 0x5C, 0x7E, 0x6E, 0x95, 0xB9}; +static const u8 key_D91612F0[] = {0x9E, 0x20, 0xE1, 0xCD, 0xD7, 0x88, 0xDE, 0xC0, 0x31, 0x9B, 0x10, 0xAF, 0xC5, 0xB8, 0x73, 0x23}; +static const u8 key_D91613F0[] = {0xEB, 0xFF, 0x40, 0xD8, 0xB4, 0x1A, 0xE1, 0x66, 0x91, 0x3B, 0x8F, 0x64, 0xB6, 0xFC, 0xB7, 0x12}; +static const u8 key_D91614F0[] = {0xFD, 0xF7, 0xB7, 0x3C, 0x9F, 0xD1, 0x33, 0x95, 0x11, 0xB8, 0xB5, 0xBB, 0x54, 0x23, 0x73, 0x85}; +static const u8 key_D91615F0[] = {0xC8, 0x03, 0xE3, 0x44, 0x50, 0xF1, 0xE7, 0x2A, 0x6A, 0x0D, 0xC3, 0x61, 0xB6, 0x8E, 0x5F, 0x51}; +static const u8 key_D91616F0[] = {0x53, 0x03, 0xB8, 0x6A, 0x10, 0x19, 0x98, 0x49, 0x1C, 0xAF, 0x30, 0xE4, 0x25, 0x1B, 0x6B, 0x28}; +static const u8 key_D91617F0[] = {0x02, 0xFA, 0x48, 0x73, 0x75, 0xAF, 0xAE, 0x0A, 0x67, 0x89, 0x2B, 0x95, 0x4B, 0x09, 0x87, 0xA3}; +static const u8 key_D91618F0[] = {0x96, 0x96, 0x7C, 0xC3, 0xF7, 0x12, 0xDA, 0x62, 0x1B, 0xF6, 0x9A, 0x9A, 0x44, 0x44, 0xBC, 0x48}; +static const u8 key_D91619F0[] = {0xE0, 0x32, 0xA7, 0x08, 0x6B, 0x2B, 0x29, 0x2C, 0xD1, 0x4D, 0x5B, 0xEE, 0xA8, 0xC8, 0xB4, 0xE9}; +static const u8 key_D9161AF0[] = {0x27, 0xE5, 0xA7, 0x49, 0x52, 0xE1, 0x94, 0x67, 0x35, 0x66, 0x91, 0x0C, 0xE8, 0x9A, 0x25, 0x24}; +static const u8 key_D91620F0[] = {0x52, 0x1C, 0xB4, 0x5F, 0x40, 0x3B, 0x9A, 0xDD, 0xAC, 0xFC, 0xEA, 0x92, 0xFD, 0xDD, 0xF5, 0x90}; +static const u8 key_D91621F0[] = {0xD1, 0x91, 0x2E, 0xA6, 0x21, 0x14, 0x29, 0x62, 0xF6, 0xED, 0xAE, 0xCB, 0xDD, 0xA3, 0xBA, 0xFE}; +static const u8 key_D91622F0[] = {0x59, 0x5D, 0x78, 0x4D, 0x21, 0xB2, 0x01, 0x17, 0x6C, 0x9A, 0xB5, 0x1B, 0xDA, 0xB7, 0xF9, 0xE6}; +static const u8 key_D91623F0[] = {0xAA, 0x45, 0xEB, 0x4F, 0x62, 0xFB, 0xD1, 0x0D, 0x71, 0xD5, 0x62, 0xD2, 0xF5, 0xBF, 0xA5, 0x2F}; +static const u8 key_D91624F0[] = {0x61, 0xB7, 0x26, 0xAF, 0x8B, 0xF1, 0x41, 0x58, 0x83, 0x6A, 0xC4, 0x92, 0x12, 0xCB, 0xB1, 0xE9}; +static const u8 key_D91628F0[] = {0x49, 0xA4, 0xFC, 0x66, 0xDC, 0xE7, 0x62, 0x21, 0xDB, 0x18, 0xA7, 0x50, 0xD6, 0xA8, 0xC1, 0xB6}; +static const u8 key_D91680F0[] = {0x2C, 0x22, 0x9B, 0x12, 0x36, 0x74, 0x11, 0x67, 0x49, 0xD1, 0xD1, 0x88, 0x92, 0xF6, 0xA1, 0xD8}; +static const u8 key_D91681F0[] = {0x52, 0xB6, 0x36, 0x6C, 0x8C, 0x46, 0x7F, 0x7A, 0xCC, 0x11, 0x62, 0x99, 0xC1, 0x99, 0xBE, 0x98}; +static const u8 key_2E5E10F0[] = {0x9D, 0x5C, 0x5B, 0xAF, 0x8C, 0xD8, 0x69, 0x7E, 0x51, 0x9F, 0x70, 0x96, 0xE6, 0xD5, 0xC4, 0xE8}; +static const u8 key_2E5E12F0[] = {0x8A, 0x7B, 0xC9, 0xD6, 0x52, 0x58, 0x88, 0xEA, 0x51, 0x83, 0x60, 0xCA, 0x16, 0x79, 0xE2, 0x07}; +static const u8 key_2E5E13F0[] = {0xFF, 0xA4, 0x68, 0xC3, 0x31, 0xCA, 0xB7, 0x4C, 0xF1, 0x23, 0xFF, 0x01, 0x65, 0x3D, 0x26, 0x36}; +static const u8 key_2FD30BF0[] = {0xD8, 0x58, 0x79, 0xF9, 0xA4, 0x22, 0xAF, 0x86, 0x90, 0xAC, 0xDA, 0x45, 0xCE, 0x60, 0x40, 0x3F}; +static const u8 key_2FD311F0[] = {0x3A, 0x6B, 0x48, 0x96, 0x86, 0xA5, 0xC8, 0x80, 0x69, 0x6C, 0xE6, 0x4B, 0xF6, 0x04, 0x17, 0x44}; +static const u8 key_2FD312F0[] = {0xC5, 0xFB, 0x69, 0x03, 0x20, 0x7A, 0xCF, 0xBA, 0x2C, 0x90, 0xF8, 0xB8, 0x4D, 0xD2, 0xF1, 0xDE}; +static const u8 keys02G_E[] = {0x9D, 0x09, 0xFD, 0x20, 0xF3, 0x8F, 0x10, 0x69, 0x0D, 0xB2, 0x6F, 0x00, 0xCC, 0xC5, 0x51, 0x2E}; +static const u8 keys03G_E[] = {0x4F, 0x44, 0x5C, 0x62, 0xB3, 0x53, 0xC4, 0x30, 0xFC, 0x3A, 0xA4, 0x5B, 0xEC, 0xFE, 0x51, 0xEA}; +static const u8 keys05G_E[] = {0x5D, 0xAA, 0x72, 0xF2, 0x26, 0x60, 0x4D, 0x1C, 0xE7, 0x2D, 0xC8, 0xA3, 0x2F, 0x79, 0xC5, 0x54}; +static const u8 oneseg_310[] = {0xC7, 0x27, 0x72, 0x85, 0xAB, 0xA7, 0xF7, 0xF0, 0x4C, 0xC1, 0x86, 0xCC, 0xE3, 0x7F, 0x17, 0xCA}; +static const u8 oneseg_300[] = {0x76, 0x40, 0x9E, 0x08, 0xDB, 0x9B, 0x3B, 0xA1, 0x47, 0x8A, 0x96, 0x8E, 0xF3, 0xF7, 0x62, 0x92}; +static const u8 oneseg_280[] = {0x23, 0xDC, 0x3B, 0xB5, 0xA9, 0x82, 0xD6, 0xEA, 0x63, 0xA3, 0x6E, 0x2B, 0x2B, 0xE9, 0xE1, 0x54}; +static const u8 oneseg_260_271[] = {0x22, 0x43, 0x57, 0x68, 0x2F, 0x41, 0xCE, 0x65, 0x4C, 0xA3, 0x7C, 0xC6, 0xC4, 0xAC, 0xF3, 0x60}; +static const u8 oneseg_slim[] = {0x12, 0x57, 0x0D, 0x8A, 0x16, 0x6D, 0x87, 0x06, 0x03, 0x7D, 0xC8, 0x8B, 0x62, 0xA3, 0x32, 0xA9}; +static const u8 ms_app_main[] = {0x1E, 0x2E, 0x38, 0x49, 0xDA, 0xD4, 0x16, 0x08, 0x27, 0x2E, 0xF3, 0xBC, 0x37, 0x75, 0x80, 0x93}; +static const u8 demokeys_280[] = {0x12, 0x99, 0x70, 0x5E, 0x24, 0x07, 0x6C, 0xD0, 0x2D, 0x06, 0xFE, 0x7E, 0xB3, 0x0C, 0x11, 0x26}; +static const u8 demokeys_3XX_1[] = {0x47, 0x05, 0xD5, 0xE3, 0x56, 0x1E, 0x81, 0x9B, 0x09, 0x2F, 0x06, 0xDB, 0x6B, 0x12, 0x92, 0xE0}; +static const u8 demokeys_3XX_2[] = {0xF6, 0x62, 0x39, 0x6E, 0x26, 0x22, 0x4D, 0xCA, 0x02, 0x64, 0x16, 0x99, 0x7B, 0x9A, 0xE7, 0xB8}; +static const u8 ebootbin_271_new[] = {0xF4, 0xAE, 0xF4, 0xE1, 0x86, 0xDD, 0xD2, 0x9C, 0x7C, 0xC5, 0x42, 0xA6, 0x95, 0xA0, 0x83, 0x88}; +static const u8 ebootbin_280_new[] = {0xB8, 0x8C, 0x45, 0x8B, 0xB6, 0xE7, 0x6E, 0xB8, 0x51, 0x59, 0xA6, 0x53, 0x7C, 0x5E, 0x86, 0x31}; +static const u8 ebootbin_300_new[] = {0xED, 0x10, 0xE0, 0x36, 0xC4, 0xFE, 0x83, 0xF3, 0x75, 0x70, 0x5E, 0xF6, 0xA4, 0x40, 0x05, 0xF7}; +static const u8 ebootbin_310_new[] = {0x5C, 0x77, 0x0C, 0xBB, 0xB4, 0xC2, 0x4F, 0xA2, 0x7E, 0x3B, 0x4E, 0xB4, 0xB4, 0xC8, 0x70, 0xAF}; +static const u8 gameshare_260_271[] = {0xF9, 0x48, 0x38, 0x0C, 0x96, 0x88, 0xA7, 0x74, 0x4F, 0x65, 0xA0, 0x54, 0xC2, 0x76, 0xD9, 0xB8}; +static const u8 gameshare_280[] = {0x2D, 0x86, 0x77, 0x3A, 0x56, 0xA4, 0x4F, 0xDD, 0x3C, 0x16, 0x71, 0x93, 0xAA, 0x8E, 0x11, 0x43}; +static const u8 gameshare_300[] = {0x78, 0x1A, 0xD2, 0x87, 0x24, 0xBD, 0xA2, 0x96, 0x18, 0x3F, 0x89, 0x36, 0x72, 0x90, 0x92, 0x85}; +static const u8 gameshare_310[] = {0xC9, 0x7D, 0x3E, 0x0A, 0x54, 0x81, 0x6E, 0xC7, 0x13, 0x74, 0x99, 0x74, 0x62, 0x18, 0xE7, 0xDD}; +static const u8 key_380210F0[] = {0x32, 0x2C, 0xFA, 0x75, 0xE4, 0x7E, 0x93, 0xEB, 0x9F, 0x22, 0x80, 0x85, 0x57, 0x08, 0x98, 0x48}; +static const u8 key_380280F0[] = {0x97, 0x09, 0x12, 0xD3, 0xDB, 0x02, 0xBD, 0xD8, 0xE7, 0x74, 0x51, 0xFE, 0xF0, 0xEA, 0x6C, 0x5C}; +static const u8 key_380283F0[] = {0x34, 0x20, 0x0C, 0x8E, 0xA1, 0x86, 0x79, 0x84, 0xAF, 0x13, 0xAE, 0x34, 0x77, 0x6F, 0xEA, 0x89}; +static const u8 key_407810F0[] = {0xAF, 0xAD, 0xCA, 0xF1, 0x95, 0x59, 0x91, 0xEC, 0x1B, 0x27, 0xD0, 0x4E, 0x8A, 0xF3, 0x3D, 0xE7}; +static const u8 drmkeys_6XX_1[] = {0x36, 0xEF, 0x82, 0x4E, 0x74, 0xFB, 0x17, 0x5B, 0x14, 0x14, 0x05, 0xF3, 0xB3, 0x8A, 0x76, 0x18}; +static const u8 drmkeys_6XX_2[] = {0x21, 0x52, 0x5D, 0x76, 0xF6, 0x81, 0x0F, 0x15, 0x2F, 0x4A, 0x40, 0x89, 0x63, 0xA0, 0x10, 0x55}; // PRXDecrypter 144-byte tag keys. -u32 g_key0[] = { +static const u32 g_key0[] = { 0x7b21f3be, 0x299c5e1d, 0x1c9c5e71, 0x96cb4645, 0x3c9b1be0, 0xeb85de3d, 0x4a7f2022, 0xc2206eaa, 0xd50b3265, 0x55770567, 0x3c080840, 0x981d55f2, 0x5fd8f6f3, 0xee8eb0c5, 0x944d8152, 0xf8278651, 0x2705bafa, 0x8420e533, 0x27154ae9, 0x4819aa32, 0x59a3aa40, 0x2cb3cf65, 0xf274466d, 0x3a655605, 0x21b0f88f, 0xc5b18d26, 0x64c19051, 0xd669c94e, 0xe87035f2, 0x9d3a5909, 0x6f4e7102, 0xdca946ce, 0x8416881b, 0xbab097a5, 0x249125c6, 0xb34c0872}; -u32 g_key2[] = { +static const u32 g_key2[] = { 0xccfda932, 0x51c06f76, 0x046dcccf, 0x49e1821e, 0x7d3b024c, 0x9dda5865, 0xcc8c9825, 0xd1e97db5, 0x6874d8cb, 0x3471c987, 0x72edb3fc, 0x81c8365d, 0xe161e33a, 0xfc92db59, 0x2009b1ec, 0xb1a94ce4, 0x2f03696b, 0x87e236d8, 0x3b2b8ce9, 0x0305e784, 0xf9710883, 0xb039db39, 0x893bea37, 0xe74d6805, 0x2a5c38bd, 0xb08dc813, 0x15b32375, 0x46be4525, 0x0103fd90, 0xa90e87a2, 0x52aba66a, 0x85bf7b80, 0x45e8ce63, 0x4dd716d3, 0xf5e30d2d, 0xaf3ae456}; -u32 g_key3[] = { +static const u32 g_key3[] = { 0xa6c8f5ca, 0x6d67c080, 0x924f4d3a, 0x047ca06a, 0x08640297, 0x4fd4a758, 0xbd685a87, 0x9b2701c2, 0x83b62a35, 0x726b533c, 0xe522fa0c, 0xc24b06b4, 0x459d1cac, 0xa8c5417b, 0x4fea62a2, 0x0615d742, 0x30628d09, 0xc44fab14, 0x69ff715e, 0xd2d8837d, 0xbeed0b8b, 0x1e6e57ae, 0x61e8c402, 0xbe367a06, 0x543f2b5e, 0xdb3ec058, 0xbe852075, 0x1e7e4dcc, 0x1564ea55, 0xec7825b4, 0xc0538cad, 0x70f72c7f, 0x49e8c3d0, 0xeda97ec5, 0xf492b0a4, 0xe05eb02a}; -u32 g_key44[] = { +static const u32 g_key44[] = { 0xef80e005, 0x3a54689f, 0x43c99ccd, 0x1b7727be, 0x5cb80038, 0xdd2efe62, 0xf369f92c, 0x160f94c5, 0x29560019, 0xbf3c10c5, 0xf2ce5566, 0xcea2c626, 0xb601816f, 0x64e7481e, 0x0c34debd, 0x98f29cb0, 0x3fc504d7, 0xc8fb39f0, 0x0221b3d8, 0x63f936a2, 0x9a3a4800, 0x6ecc32e3, 0x8e120cfd, 0xb0361623, 0xaee1e689, 0x745502eb, 0xe4a6c61c, 0x74f23eb4, 0xd7fa5813, 0xb01916eb, 0x12328457, 0xd2bc97d2, 0x646425d8, 0x328380a5, 0x43da8ab1, 0x4b122ac9}; -u32 g_key20[] = { +static const u32 g_key20[] = { 0x33b50800, 0xf32f5fcd, 0x3c14881f, 0x6e8a2a95, 0x29feefd5, 0x1394eae3, 0xbd6bd443, 0x0821c083, 0xfab379d3, 0xe613e165, 0xf5a754d3, 0x108b2952, 0x0a4b1e15, 0x61eadeba, 0x557565df, 0x3b465301, 0xae54ecc3, 0x61423309, 0x70c9ff19, 0x5b0ae5ec, 0x989df126, 0x9d987a5f, 0x55bc750e, 0xc66eba27, 0x2de988e8, 0xf76600da, 0x0382dccb, 0x5569f5f2, 0x8e431262, 0x288fe3d3, 0x656f2187, 0x37d12e9c, 0x2f539eb4, 0xa492998e, 0xed3958f7, 0x39e96523}; -u32 g_key3A[] = { +static const u32 g_key3A[] = { 0x67877069, 0x3abd5617, 0xc23ab1dc, 0xab57507d, 0x066a7f40, 0x24def9b9, 0x06f759e4, 0xdcf524b1, 0x13793e5e, 0x0359022d, 0xaae7e1a2, 0x76b9b2fa, 0x9a160340, 0x87822fba, 0x19e28fbb, 0x9e338a02, 0xd8007e9a, 0xea317af1, 0x630671de, 0x0b67ca7c, 0x865192af, 0xea3c3526, 0x2b448c8e, 0x8b599254, 0x4602e9cb, 0x4de16cda, 0xe164d5bb, 0x07ecd88e, 0x99ffe5f8, 0x768800c1, 0x53b091ed, 0x84047434, 0xb426dbbc, 0x36f948bb, 0x46142158, 0x749bb492}; -u32 g_keyEBOOT1xx[] = { +static const u32 g_keyEBOOT1xx[] = { 0x18CB69EF, 0x158E8912, 0xDEF90EBB, 0x4CB0FB23, 0x3687EE18, 0x868D4A6E, 0x19B5C756, 0xEE16551D, 0xE7CB2D6C, 0x9747C660, 0xCE95143F, 0x2956F477, 0x03824ADE, 0x210C9DF1, 0x5029EB24, 0x81DFE69F, 0x39C89B00, 0xB00C8B91, 0xEF2DF9C2, 0xE13A93FC, 0x8B94A4A8, 0x491DD09D, 0x686A400D, 0xCED4C7E4, 0x96C8B7C9, 0x1EAADC28, 0xA4170B84, 0x505D5DDC, 0x5DA6C3CF, 0x0E5DFA2D, 0x6E7919B5, 0xCE5E29C7, 0xAAACDB94, 0x45F70CDD, 0x62A73725, 0xCCE6563D}; -u32 g_keyEBOOT2xx[] = { +static const u32 g_keyEBOOT2xx[] = { 0xDA8E36FA, 0x5DD97447, 0x76C19874, 0x97E57EAF, 0x1CAB09BD, 0x9835BAC6, 0x03D39281, 0x03B205CF, 0x2882E734, 0xE714F663, 0xB96E2775, 0xBD8AAFC7, 0x1DD3EC29, 0xECA4A16C, 0x5F69EC87, 0x85981E92, 0x7CFCAE21, 0xBAE9DD16, 0xE6A97804, 0x2EEE02FC, 0x61DF8A3D, 0xDD310564, 0x9697E149, 0xC2453F3B, 0xF91D8456, 0x39DA6BC8, 0xB3E5FEF5, 0x89C593A3, 0xFB5C8ABC, 0x6C0B7212, 0xE10DD3CB, 0x98D0B2A8, 0x5FD61847, 0xF0DC2357, 0x7701166A, 0x0F5C3B68}; -u32 g_keyUPDATER[] = { +static const u32 g_keyUPDATER[] = { 0xA5603CBF, 0xD7482441, 0xF65764CC, 0x1F90060B, 0x4EA73E45, 0xE551D192, 0xE7B75D8A, 0x465A506E, 0x40FB1022, 0x2C273350, 0x8096DA44, 0x9947198E, 0x278DEE77, 0x745D062E, 0xC148FA45, 0x832582AF, 0x5FDB86DA, 0xCB15C4CE, 0x2524C62F, 0x6C2EC3B1, 0x369BE39E, 0xF7EB1FC4, 0x1E51CE1A, 0xD70536F4, 0xC34D39D8, 0x7418FB13, 0xE3C84DE1, 0xB118F03C, 0xA2018D4E, 0xE6D8770D, 0x5720F390, 0x17F96341, 0x60A4A68F, 0x1327DD28, 0x05944C64, 0x0C2C4C12}; -u32 g_keyMEIMG250[] = { +static const u32 g_keyMEIMG250[] = { 0xA381FEBC, 0x99B9D5C9, 0x6C560A8D, 0x30309F95, 0x792646CC, 0x82B64E5E, 0x1A3951AD, 0x0A182EC4, 0xC46131B4, 0x77C50C8A, 0x325F16C6, 0x02D1942E, 0x0AA38AC4, 0x2A940AC6, 0x67034726, 0xE52DB133, 0xD2EF2107, 0x85C81E90, 0xC8D164BA, 0xC38DCE1D, 0x948BA275, 0x0DB84603, 0xE2473637, 0xCD74FCDA, 0x588E3D66, 0x6D28E822, 0x891E548B, 0xF53CF56D, 0x0BBDDB66, 0xC4B286AA, 0x2BEBBC4B, 0xFC261FF4, 0x92B8E705, 0xDCEE6952, 0x5E0442E5, 0x8BEB7F21}; -u32 g_keyMEIMG260[] = { +static const u32 g_keyMEIMG260[] = { 0x11BFD698, 0xD7F9B324, 0xDD524927, 0x16215B86, 0x504AC36D, 0x5843B217, 0xE5A0DA47, 0xBB73A1E7, 0x2915DB35, 0x375CFD3A, 0xBB70A905, 0x272BEFCA, 0x2E960791, 0xEA0799BB, 0xB85AE6C8, 0xC9CAF773, 0x250EE641, 0x06E74A9E, 0x5244895D, 0x466755A5, 0x9A84AF53, 0xE1024174, 0xEEBA031E, 0xED80B9CE, 0xBC315F72, 0x5821067F, 0xE8313058, 0xD2D0E706, 0xE6D8933E, 0xD7D17FB4, 0x505096C4, 0xFDA50B3B, 0x4635AE3D, 0xEB489C8A, 0x422D762D, 0x5A8B3231}; -u32 g_keyDEMOS27X[] = { +static const u32 g_keyDEMOS27X[] = { 0x1ABF102F, 0xD596D071, 0x6FC552B2, 0xD4F2531F, 0xF025CDD9, 0xAF9AAF03, 0xE0CF57CF, 0x255494C4, 0x7003675E, 0x907BC884, 0x002D4EE4, 0x0B687A0D, 0x9E3AA44F, 0xF58FDA81, 0xEC26AC8C, 0x3AC9B49D, 0x3471C037, 0xB0F3834D, 0x10DC4411, 0xA232EA31, 0xE2E5FA6B, 0x45594B03, 0xE43A1C87, 0x31DAD9D1, 0x08CD7003, 0xFA9C2FDF, 0x5A891D25, 0x9B5C1934, 0x22F366E5, 0x5F084A32, 0x695516D5, 0x2245BE9F, 0x4F6DD705, 0xC4B8B8A1, 0xBC13A600, 0x77B7FC3B}; -u32 g_keyUNK1[] = { +static const u32 g_keyUNK1[] = { 0x33B50800, 0xF32F5FCD, 0x3C14881F, 0x6E8A2A95, 0x29FEEFD5, 0x1394EAE3, 0xBD6BD443, 0x0821C083, 0xFAB379D3, 0xE613E165, 0xF5A754D3, 0x108B2952, 0x0A4B1E15, 0x61EADEBA, 0x557565DF, 0x3B465301, 0xAE54ECC3, 0x61423309, 0x70C9FF19, 0x5B0AE5EC, 0x989DF126, 0x9D987A5F, 0x55BC750E, 0xC66EBA27, 0x2DE988E8, 0xF76600DA, 0x0382DCCB, 0x5569F5F2, 0x8E431262, 0x288FE3D3, 0x656F2187, 0x37D12E9C, 0x2F539EB4, 0xA492998E, 0xED3958F7, 0x39E96523}; -u32 g_key_GAMESHARE1xx[] = { +static const u32 g_key_GAMESHARE1xx[] = { 0x721B53E8, 0xFC3E31C6, 0xF85BA2A2, 0x3CF0AC72, 0x54EEA7AB, 0x5959BFCB, 0x54B8836B, 0xBC431313, 0x989EF2CF, 0xF0CE36B2, 0x98BA4CF8, 0xE971C931, 0xA0375DC8, 0x08E52FA0, 0xAC0DD426, 0x57E4D601, 0xC56E61C7, 0xEF1AB98A, 0xD1D9F8F4, 0x5FE9A708, 0x3EF09D07, 0xFA0C1A8C, 0xA91EEA5C, 0x58F482C5, 0x2C800302, 0x7EE6F6C3, 0xFF6ABBBB, 0x2110D0D0, 0xD3297A88, 0x980012D3, 0xDC59C87B, 0x7FDC5792, 0xDB3F5DA6, 0xFC23B787, 0x22698ED3, 0xB680E812}; -u32 g_key_GAMESHARE2xx[] = { +static const u32 g_key_GAMESHARE2xx[] = { 0x94A757C7, 0x9FD39833, 0xF8508371, 0x328B0B29, 0x2CBCB9DA, 0x2918B9C6, 0x944C50BA, 0xF1DCE7D0, 0x640C3966, 0xC90B3D08, 0xF4AD17BA, 0x6CA0F84B, 0xF7767C67, 0xA4D3A55A, 0x4A085C6A, 0x6BB27071, 0xFA8B38FB, 0x3FDB31B8, 0x8B7196F2, 0xDB9BED4A, 0x51625B84, 0x4C1481B4, 0xF684F508, 0x30B44770, 0x93AA8E74, 0x90C579BC, 0x246EC88D, 0x2E051202, 0xC774842E, 0xA185D997, 0x7A2B3ADD, 0xFE835B6D, 0x508F184D, 0xEB4C4F13, 0x0E1993D3, 0xBA96DFD2}; -u32 g_key_INDEXDAT1xx[] = { +static const u32 g_key_INDEXDAT1xx[] = { 0x76CB00AF, 0x111CE62F, 0xB7B27E36, 0x6D8DE8F9, 0xD54BF16A, 0xD9E90373, 0x7599D982, 0x51F82B0E, 0x636103AD, 0x8E40BC35, 0x2F332C94, 0xF513AAE9, 0xD22AFEE9, 0x04343987, 0xFC5BB80C, 0x12349D89, 0x14A481BB, 0x25ED3AE8, @@ -259,13 +259,13 @@ u32 g_key_INDEXDAT1xx[] = { 0xA34D8C80, 0x962B235D, 0x3E420548, 0x09CF9FFE, 0xD4883F5C, 0xD90E9CB5, 0x00AEF4E9, 0xF0886DE9, 0x62A58A5B, 0x52A55546, 0x971941B5, 0xF5B79FAC}; -typedef struct +struct TAG_INFO { u32 tag; // 4 byte value at offset 0xD0 in the PRX file - u32* key; // "step1_result" use for XOR step + const u32 *key; // "step1_result" use for XOR step u8 code; u8 codeExtra; -} TAG_INFO; +}; static const TAG_INFO g_tagInfo[] = { @@ -287,7 +287,7 @@ static const TAG_INFO g_tagInfo[] = { 0xBB67C59F, g_key_GAMESHARE2xx, 0x5E, 0x5E } }; -static TAG_INFO const* GetTagInfo(u32 tagFind) +static const TAG_INFO *GetTagInfo(u32 tagFind) { for (u32 iTag = 0; iTag < sizeof(g_tagInfo)/sizeof(TAG_INFO); iTag++) if (g_tagInfo[iTag].tag == tagFind) @@ -336,7 +336,7 @@ static int DecryptPRX1(const u8* pbIn, u8* pbOut, int cbTotal, u32 tag) int i, retsize; u8 bD0[0x80], b80[0x50], b00[0x80], bB0[0x20]; - TAG_INFO const* pti = GetTagInfo(tag); + const TAG_INFO *pti = GetTagInfo(tag); if (pti == NULL) { return -1; @@ -350,9 +350,15 @@ static int DecryptPRX1(const u8* pbIn, u8* pbOut, int cbTotal, u32 tag) break; } + // Scramble the key (!) + // + // NOTE: I can't make much sense out of this code. Scramble seems really odd, appears + // to write to stuff that should be before the actual key. + u8 key[0x90]; + memcpy(key, pti->key, 0x90); if (i == 0x14) { - Scramble(pti->key, 0x90, pti->code); + Scramble((u32 *)key, 0x90, pti->code); } // build conversion into pbOut @@ -387,7 +393,7 @@ static int DecryptPRX1(const u8* pbIn, u8* pbOut, int cbTotal, u32 tag) int ret; int iXOR; for (iXOR = 0; iXOR < 0x70; iXOR++) - pbOut[0x40+iXOR] = pbOut[0x40+iXOR] ^ ((u8*)pti->key)[0x14+iXOR]; + pbOut[0x40+iXOR] = pbOut[0x40+iXOR] ^ key[0x14+iXOR]; ret = sceUtilsBufferCopyWithRange(pbOut+0x2C, 20+0x70, pbOut+0x2C, 20+0x70, 7); if (ret != 0) @@ -396,7 +402,7 @@ static int DecryptPRX1(const u8* pbIn, u8* pbOut, int cbTotal, u32 tag) } for (iXOR = 0x6F; iXOR >= 0; iXOR--) - pbOut[0x40+iXOR] = pbOut[0x2C+iXOR] ^ ((u8*)pti->key)[0x20+iXOR]; + pbOut[0x40+iXOR] = pbOut[0x2C+iXOR] ^ key[0x20+iXOR]; memset(pbOut+0x80, 0, 0x30); // $40 bytes kept, clean up pbOut[0xA0] = 1; @@ -418,13 +424,13 @@ static int DecryptPRX1(const u8* pbIn, u8* pbOut, int cbTotal, u32 tag) ////////// Decryption 2 ////////// -typedef struct +struct TAG_INFO2 { u32 tag; // 4 byte value at offset 0xD0 in the PRX file - u8 *key; // 16 bytes keys + const u8 *key; // 16 bytes keys u8 code; // code for scramble u8 type; -} TAG_INFO2; +}; static TAG_INFO2 g_tagInfo2[] = { @@ -575,11 +581,6 @@ static TAG_INFO2 *GetTagInfo2(u32 tagFind) return NULL; // not found } -// Moving these out here is a really ugly hack to avoid a stack corruption warning, warranted or not -static u8 padding1[0x100]; -static u8 tmp1[0x150], tmp2[0x90+0x14], tmp3[0x60+0x14], tmp4[0x20]; -static u8 padding2[0x100]; - static int DecryptPRX2(const u8 *inbuf, u8 *outbuf, u32 size, u32 tag) { TAG_INFO2 * pti = GetTagInfo2(tag); @@ -590,10 +591,11 @@ static int DecryptPRX2(const u8 *inbuf, u8 *outbuf, u32 size, u32 tag) } int retsize = *(int *)&inbuf[0xB0]; + u8 tmp1[0x150], tmp2[0x90+0x14], tmp3[0x90+0x14], tmp4[0x20]; memset(tmp1, 0, 0x150); memset(tmp2, 0, 0x90+0x14); - memset(tmp3, 0, 0x60+0x14); + memset(tmp3, 0, 0x90+0x14); memset(tmp4, 0, 0x20); if (inbuf != outbuf) @@ -619,10 +621,8 @@ static int DecryptPRX2(const u8 *inbuf, u8 *outbuf, u32 size, u32 tag) for (j = 0; j < 0x10; j++) { p[(i << 4) + j] = pti->key[j]; - } - - p[(i << 4)] = i; + p[(i << 4)] = i; // really? } if (Scramble((u32 *)tmp2, 0x90, pti->code) < 0) @@ -673,9 +673,7 @@ static int DecryptPRX2(const u8 *inbuf, u8 *outbuf, u32 size, u32 tag) return -8; } - int iXOR; - - for (iXOR = 0; iXOR < 0x40; iXOR++) + for (int iXOR = 0; iXOR < 0x40; iXOR++) { tmp3[iXOR+0x14] = outbuf[iXOR+0x80] ^ tmp2[iXOR+0x10]; } @@ -685,7 +683,7 @@ static int DecryptPRX2(const u8 *inbuf, u8 *outbuf, u32 size, u32 tag) return -9; } - for (iXOR = 0x3F; iXOR >= 0; iXOR--) + for (int iXOR = 0x3F; iXOR >= 0; iXOR--) { outbuf[iXOR+0x40] = tmp3[iXOR] ^ tmp2[iXOR+0x50]; // uns 8 } @@ -708,7 +706,7 @@ static int DecryptPRX2(const u8 *inbuf, u8 *outbuf, u32 size, u32 tag) memcpy(outbuf+0xD0, outbuf+0xD0, 0x80); // The real decryption - if (sceUtilsBufferCopyWithRange(outbuf, size, outbuf+0x40, size-0x40, 0x1) != 0) + if (sceUtilsBufferCopyWithRange(outbuf, size, outbuf + 0x40, size - 0x40, 0x1) != 0) { return -1; } diff --git a/Core/FileSystems/BlockDevices.cpp b/Core/FileSystems/BlockDevices.cpp index cd743741f5..025e3427ea 100644 --- a/Core/FileSystems/BlockDevices.cpp +++ b/Core/FileSystems/BlockDevices.cpp @@ -122,11 +122,11 @@ bool CISOFileBlockDevice::ReadBlock(int blockNumber, u8 *outPtr) { u32 idx = index[blockNumber]; u32 idx2 = index[blockNumber+1]; - u8 inbuffer[4096]; //too big - z_stream z; - + u8 inbuffer[4096]; //too big + z_stream z; + int plain = idx & 0x80000000; - + idx = (idx & 0x7FFFFFFF) << indexShift; idx2 = (idx2 & 0x7FFFFFFF) << indexShift; diff --git a/Core/FileSystems/DirectoryFileSystem.cpp b/Core/FileSystems/DirectoryFileSystem.cpp index 6513fb7b9a..e57be86eb2 100644 --- a/Core/FileSystems/DirectoryFileSystem.cpp +++ b/Core/FileSystems/DirectoryFileSystem.cpp @@ -18,30 +18,142 @@ #ifdef _WIN32 #include #else +#include #include #include +#include #endif #include "FileUtil.h" #include "DirectoryFileSystem.h" -DirectoryFileSystem::DirectoryFileSystem(IHandleAllocator *_hAlloc, std::string _basePath) : basePath(_basePath) + +#if HOST_IS_CASE_SENSITIVE + +static bool FixFilenameCase(const std::string &path, std::string &filename) { + // Are we lucky? + if (File::Exists(path + filename)) + return true; + + size_t filenameSize = filename.size(); + for (size_t i = 0; i < filenameSize; i++) + { + filename[i] = tolower(filename[i]); + } + + //TODO: lookup filename in cache for "path" + + struct dirent_large { struct dirent entry; char padding[FILENAME_MAX+1]; } diren; + struct dirent_large; + struct dirent *result = NULL; + + DIR *dirp = opendir(path.c_str()); + if (!dirp) + return false; + + bool retValue = false; + + while (!readdir_r(dirp, (dirent*) &diren, &result) && result) + { + // Hm, is this check UTF-8 compatible? (size vs strlen) + if (strlen(result->d_name) != filenameSize) + continue; + + size_t i; + for (i = 0; i < filenameSize; i++) + { + if (filename[i] != tolower(result->d_name[i])) + break; + } + + if (i < filenameSize) + continue; + + filename = result->d_name; + retValue = true; + } + + closedir(dirp); + + return retValue; +} + +bool DirectoryFileSystem::FixPathCase(std::string &path, FixPathCaseBehavior behavior) +{ + size_t len = path.size(); + + if (len == 0) + return true; + + if (path[len - 1] == '/') + { + len--; + + if (len == 0) + return true; + } + + std::string fullPath; + fullPath.reserve(basePath.size() + len + 1); + fullPath.append(basePath); + + size_t start = 0; + while (start < len) + { + size_t i = path.find('/', start); + if (i == std::string::npos) + i = len; + + if (i > start) + { + std::string component = path.substr(start, i - start); + + // Fix case and stop on nonexistant path component + if (FixFilenameCase(fullPath, component) == false) { + // Still counts as success if partial matches allowed or if this + // is the last component and only the ones before it are required + return (behavior == FPC_PARTIAL_ALLOWED || (behavior == FPC_PATH_MUST_EXIST && i >= len)); + } + + path.replace(start, i - start, component); + + fullPath.append(component); + fullPath.append(1, '/'); + } + + start = i + 1; + } + + return true; +} + +#endif + +DirectoryFileSystem::DirectoryFileSystem(IHandleAllocator *_hAlloc, std::string _basePath) : basePath(_basePath) { File::CreateFullPath(basePath); hAlloc = _hAlloc; } -std::string DirectoryFileSystem::GetLocalPath(std::string localpath) -{ +DirectoryFileSystem::~DirectoryFileSystem() { + for (auto iter = entries.begin(); iter != entries.end(); ++iter) { +#ifdef _WIN32 + CloseHandle((*iter).second.hFile); +#else + fclose((*iter).second.hFile); +#endif + } +} + +std::string DirectoryFileSystem::GetLocalPath(std::string localpath) { if (localpath.empty()) return basePath; if (localpath[0] == '/') localpath.erase(0,1); - //Convert slashes + //Convert slashes #ifdef _WIN32 - for (size_t i = 0; i < localpath.size(); i++) - { + for (size_t i = 0; i < localpath.size(); i++) { if (localpath[i] == '/') localpath[i] = '\\'; } @@ -49,18 +161,38 @@ std::string DirectoryFileSystem::GetLocalPath(std::string localpath) return basePath + localpath; } +bool DirectoryFileSystem::MkDir(const std::string &dirname) { -bool DirectoryFileSystem::MkDir(const std::string &dirname) -{ - std::string fullName = GetLocalPath(dirname); +#if HOST_IS_CASE_SENSITIVE + // Must fix case BEFORE attempting, because MkDir would create + // duplicate (different case) directories - return File::CreateFullPath(fullName); + std::string fixedCase = dirname; + if ( ! FixPathCase(fixedCase, FPC_PARTIAL_ALLOWED) ) + return false; + return File::CreateFullPath(GetLocalPath(fixedCase)); +#else + return File::CreateFullPath(GetLocalPath(dirname)); +#endif } -bool DirectoryFileSystem::RmDir(const std::string &dirname) -{ +bool DirectoryFileSystem::RmDir(const std::string &dirname) { std::string fullName = GetLocalPath(dirname); + +#if HOST_IS_CASE_SENSITIVE + // Maybe we're lucky? + if (File::DeleteDirRecursively(fullName)) + return true; + + // Nope, fix case and try again + fullName = dirname; + if ( ! FixPathCase(fullName, FPC_FILE_MUST_EXIST) ) + return false; // or go on and attempt (for a better error code than just false?) + + fullName = GetLocalPath(fullName); +#endif + /*#ifdef _WIN32 return RemoveDirectory(fullName.c_str()) == TRUE; #else @@ -69,82 +201,185 @@ bool DirectoryFileSystem::RmDir(const std::string &dirname) return File::DeleteDirRecursively(fullName); } -bool DirectoryFileSystem::RenameFile(const std::string &from, const std::string &to) -{ - std::string fullFrom = GetLocalPath(from); +bool DirectoryFileSystem::RenameFile(const std::string &from, const std::string &to) { std::string fullTo = to; // TO filename may not include path. Intention is that it uses FROM's path if (to.find("/") != std::string::npos) { - int offset = from.find_last_of("/"); - if (offset >= 0) { + size_t offset = from.find_last_of("/"); + if (offset != std::string::npos) { fullTo = from.substr(0, offset + 1) + to; } } + std::string fullFrom = GetLocalPath(from); + +#if HOST_IS_CASE_SENSITIVE + // In case TO should overwrite a file with different case + if ( ! FixPathCase(fullTo, FPC_PATH_MUST_EXIST) ) + return false; // or go on and attempt (for a better error code than just false?) +#endif + fullTo = GetLocalPath(fullTo); + const char * fullToC = fullTo.c_str(); + #ifdef _WIN32 - return MoveFile(fullFrom.c_str(), fullTo.c_str()) == TRUE; + bool retValue = (MoveFile(fullFrom.c_str(), fullToC) == TRUE); #else - return 0 == rename(fullFrom.c_str(), fullTo.c_str()); + bool retValue = (0 == rename(fullFrom.c_str(), fullToC)); #endif + +#if HOST_IS_CASE_SENSITIVE + if (! retValue) + { + // May have failed due to case sensitivity on FROM, so try again + fullFrom = from; + if ( ! FixPathCase(fullFrom, FPC_FILE_MUST_EXIST) ) + return false; // or go on and attempt (for a better error code than just false?) + fullFrom = GetLocalPath(fullFrom); + +#ifdef _WIN32 + retValue = (MoveFile(fullFrom.c_str(), fullToC) == TRUE); +#else + retValue = (0 == rename(fullFrom.c_str(), fullToC)); +#endif + } +#endif + + return retValue; } -bool DirectoryFileSystem::DeleteFile(const std::string &filename) -{ +bool DirectoryFileSystem::DeleteFile(const std::string &filename) { std::string fullName = GetLocalPath(filename); #ifdef _WIN32 - return DeleteFile(fullName.c_str()) == TRUE; + bool retValue = (::DeleteFile(fullName.c_str()) == TRUE); #else - return 0 == unlink(fullName.c_str()); + bool retValue = (0 == unlink(fullName.c_str())); #endif + +#if HOST_IS_CASE_SENSITIVE + if (! retValue) + { + // May have failed due to case sensitivity, so try again + fullName = filename; + if ( ! FixPathCase(fullName, FPC_FILE_MUST_EXIST) ) + return false; // or go on and attempt (for a better error code than just false?) + fullName = GetLocalPath(fullName); + +#ifdef _WIN32 + retValue = (::DeleteFile(fullName.c_str()) == TRUE); +#else + retValue = (0 == unlink(fullName.c_str())); +#endif + } +#endif + + return retValue; } -u32 DirectoryFileSystem::OpenFile(std::string filename, FileAccess access) -{ +u32 DirectoryFileSystem::OpenFile(std::string filename, FileAccess access) { +#if HOST_IS_CASE_SENSITIVE + if (access & (FILEACCESS_APPEND|FILEACCESS_CREATE|FILEACCESS_WRITE)) + { + DEBUG_LOG(HLE, "Checking case for path %s", filename.c_str()); + if ( ! FixPathCase(filename, FPC_PATH_MUST_EXIST) ) + return 0; // or go on and attempt (for a better error code than just 0?) + } + // else we try fopen first (in case we're lucky) before simulating case insensitivity +#endif + std::string fullName = GetLocalPath(filename); - INFO_LOG(HLE,"Actually opening %s (%s)", fullName.c_str(), filename.c_str()); + const char *fullNameC = fullName.c_str(); + INFO_LOG(HLE,"Actually opening %s (%s)", fullNameC, filename.c_str()); OpenFileEntry entry; + //TODO: tests, should append seek to end of file? seeking in a file opened for append? #ifdef _WIN32 // Convert parameters to Windows permissions and access DWORD desired = 0; DWORD sharemode = 0; DWORD openmode = 0; - if (access & FILEACCESS_READ) - { + if (access & FILEACCESS_READ) { desired |= GENERIC_READ; sharemode |= FILE_SHARE_READ; } - if (access & FILEACCESS_WRITE) - { + if (access & FILEACCESS_WRITE) { desired |= GENERIC_WRITE; sharemode |= FILE_SHARE_WRITE; } - if (access & FILEACCESS_CREATE) - { + if (access & FILEACCESS_CREATE) { openmode = OPEN_ALWAYS; - } - else + } else { openmode = OPEN_EXISTING; - + } //Let's do it! - entry.hFile = CreateFile(fullName.c_str(), desired, sharemode, 0, openmode, 0, 0); + entry.hFile = CreateFile(fullNameC, desired, sharemode, 0, openmode, 0, 0); bool success = entry.hFile != INVALID_HANDLE_VALUE; #else - entry.hFile = fopen(fullName.c_str(), access & FILEACCESS_WRITE ? "wb" : "rb"); - bool success = entry.hFile != 0; + // Convert flags in access parameter to fopen access mode + const char *mode = NULL; + if (access & FILEACCESS_APPEND) { + if (access & FILEACCESS_READ) + mode = "ab+"; // append+read, create if needed + else + mode = "ab"; // append only, create if needed + } else if (access & FILEACCESS_WRITE) { + if (access & FILEACCESS_READ) { + // FILEACCESS_CREATE is ignored for read only, write only, and append + // because C++ standard fopen's nonexistant file creation can only be + // customized for files opened read+write + if (access & FILEACCESS_CREATE) + mode = "wb+"; // read+write, create if needed + else + mode = "rb+"; // read+write, but don't create + } else { + mode = "wb"; // write only, create if needed + } + } else { // neither write nor append, so default to read only + mode = "rb"; // read only, don't create + } + + entry.hFile = fopen(fullNameC, mode); + bool success = entry.hFile != 0; #endif - if (!success) +#if HOST_IS_CASE_SENSITIVE + if (!success && + !(access & FILEACCESS_APPEND) && + !(access & FILEACCESS_CREATE) && + !(access & FILEACCESS_WRITE)) { + if ( ! FixPathCase(filename, FPC_PATH_MUST_EXIST) ) + return 0; // or go on and attempt (for a better error code than just 0?) + fullName = GetLocalPath(filename); + fullNameC = fullName.c_str(); + + DEBUG_LOG(HLE, "Case may have been incorrect, second try opening %s (%s)", fullNameC, filename.c_str()); + + // And try again with the correct case this time #ifdef _WIN32 - ERROR_LOG(HLE, "DirectoryFileSystem::OpenFile: FAILED, %i - access = %i", GetLastError(), (int)access); + entry.hFile = CreateFile(fullNameC, desired, sharemode, 0, openmode, 0, 0); + success = entry.hFile != INVALID_HANDLE_VALUE; +#else + entry.hFile = fopen(fullNameC, mode); + success = entry.hFile != 0; +#endif + } +#endif + + if (!success) { +#ifdef _WIN32 + ERROR_LOG(HLE, "DirectoryFileSystem::OpenFile: FAILED, %i - access = %i", GetLastError(), (int)access); +#else + ERROR_LOG(HLE, "DirectoryFileSystem::OpenFile: FAILED, access = %i", (int)access); #endif //wwwwaaaaahh!! return 0; - } - else - { + } else { +#ifdef _WIN32 + if (access & FILEACCESS_APPEND) + SetFilePointer(entry.hFile, 0, NULL, FILE_END); +#endif + u32 newHandle = hAlloc->GetNewHandle(); entries[newHandle] = entry; @@ -152,55 +387,46 @@ u32 DirectoryFileSystem::OpenFile(std::string filename, FileAccess access) } } -void DirectoryFileSystem::CloseFile(u32 handle) -{ +void DirectoryFileSystem::CloseFile(u32 handle) { EntryMap::iterator iter = entries.find(handle); - if (iter != entries.end()) - { + if (iter != entries.end()) { hAlloc->FreeHandle(handle); #ifdef _WIN32 CloseHandle((*iter).second.hFile); #else - fclose((*iter).second.hFile); + fclose((*iter).second.hFile); #endif entries.erase(iter); - } - else - { + } else { //This shouldn't happen... ERROR_LOG(HLE,"Cannot close file that hasn't been opened: %08x", handle); } } -bool DirectoryFileSystem::OwnsHandle(u32 handle) -{ +bool DirectoryFileSystem::OwnsHandle(u32 handle) { EntryMap::iterator iter = entries.find(handle); return (iter != entries.end()); } -size_t DirectoryFileSystem::ReadFile(u32 handle, u8 *pointer, s64 size) -{ +size_t DirectoryFileSystem::ReadFile(u32 handle, u8 *pointer, s64 size) { EntryMap::iterator iter = entries.find(handle); if (iter != entries.end()) { - size_t bytesRead; + size_t bytesRead; #ifdef _WIN32 ::ReadFile(iter->second.hFile, (LPVOID)pointer, (DWORD)size, (LPDWORD)&bytesRead, 0); #else - bytesRead = fread(pointer, 1, size, iter->second.hFile); + bytesRead = fread(pointer, 1, size, iter->second.hFile); #endif return bytesRead; - } - else - { + } else { //This shouldn't happen... ERROR_LOG(HLE,"Cannot read file that hasn't been opened: %08x", handle); return 0; } } -size_t DirectoryFileSystem::WriteFile(u32 handle, const u8 *pointer, s64 size) -{ +size_t DirectoryFileSystem::WriteFile(u32 handle, const u8 *pointer, s64 size) { EntryMap::iterator iter = entries.find(handle); if (iter != entries.end()) { @@ -208,67 +434,66 @@ size_t DirectoryFileSystem::WriteFile(u32 handle, const u8 *pointer, s64 size) #ifdef _WIN32 ::WriteFile(iter->second.hFile, (LPVOID)pointer, (DWORD)size, (LPDWORD)&bytesWritten, 0); #else - bytesWritten = fwrite(pointer, 1, size, iter->second.hFile); + bytesWritten = fwrite(pointer, 1, size, iter->second.hFile); #endif return bytesWritten; - } - else - { + } else { //This shouldn't happen... ERROR_LOG(HLE,"Cannot write to file that hasn't been opened: %08x", handle); return 0; } } -size_t DirectoryFileSystem::SeekFile(u32 handle, s32 position, FileMove type) -{ +size_t DirectoryFileSystem::SeekFile(u32 handle, s32 position, FileMove type) { EntryMap::iterator iter = entries.find(handle); - if (iter != entries.end()) - { + if (iter != entries.end()) { #ifdef _WIN32 DWORD moveMethod = 0; - switch (type) - { - case FILEMOVE_BEGIN: moveMethod = FILE_BEGIN; break; - case FILEMOVE_CURRENT: moveMethod = FILE_CURRENT; break; - case FILEMOVE_END: moveMethod = FILE_END; break; + switch (type) { + case FILEMOVE_BEGIN: moveMethod = FILE_BEGIN; break; + case FILEMOVE_CURRENT: moveMethod = FILE_CURRENT; break; + case FILEMOVE_END: moveMethod = FILE_END; break; } DWORD newPos = SetFilePointer((*iter).second.hFile, (LONG)position, 0, moveMethod); - return newPos; + return newPos; #else - int moveMethod = 0; - switch (type) { - case FILEMOVE_BEGIN: moveMethod = SEEK_SET; break; - case FILEMOVE_CURRENT: moveMethod = SEEK_CUR; break; - case FILEMOVE_END: moveMethod = SEEK_END; break; - } - fseek(iter->second.hFile, position, moveMethod); + int moveMethod = 0; + switch (type) { + case FILEMOVE_BEGIN: moveMethod = SEEK_SET; break; + case FILEMOVE_CURRENT: moveMethod = SEEK_CUR; break; + case FILEMOVE_END: moveMethod = SEEK_END; break; + } + fseek(iter->second.hFile, position, moveMethod); return ftell(iter->second.hFile); #endif - } - else - { + } else { //This shouldn't happen... ERROR_LOG(HLE,"Cannot seek in file that hasn't been opened: %08x", handle); return 0; } } -PSPFileInfo DirectoryFileSystem::GetFileInfo(std::string filename) -{ - PSPFileInfo x; +PSPFileInfo DirectoryFileSystem::GetFileInfo(std::string filename) { + PSPFileInfo x; x.name = filename; - std::string fullName = GetLocalPath(filename); - if (!File::Exists(fullName)) { + if (! File::Exists(fullName)) { +#if HOST_IS_CASE_SENSITIVE + if (! FixPathCase(filename, FPC_FILE_MUST_EXIST)) + return x; + fullName = GetLocalPath(filename); + + if (! File::Exists(fullName)) + return x; +#else return x; +#endif } x.type = File::IsDirectory(fullName) ? FILETYPE_NORMAL : FILETYPE_DIRECTORY; x.exists = true; #ifdef _WIN32 - WIN32_FILE_ATTRIBUTE_DATA data; GetFileAttributesEx(fullName.c_str(), GetFileExInfoStandard, &data); @@ -282,8 +507,7 @@ PSPFileInfo DirectoryFileSystem::GetFileInfo(std::string filename) return x; } -std::vector DirectoryFileSystem::GetDirListing(std::string path) -{ +std::vector DirectoryFileSystem::GetDirListing(std::string path) { std::vector myVector; #ifdef _WIN32 WIN32_FIND_DATA findData; @@ -293,33 +517,37 @@ std::vector DirectoryFileSystem::GetDirListing(std::string path) hFind = FindFirstFile(w32path.c_str(), &findData); - if (hFind == INVALID_HANDLE_VALUE) - { + if (hFind == INVALID_HANDLE_VALUE) { return myVector; //the empty list } - while (true) - { + while (true) { PSPFileInfo entry; - if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) entry.type = FILETYPE_DIRECTORY; else entry.type = FILETYPE_NORMAL; - - if (!strcmp(findData.cFileName, "..") )// TODO: is this just for .. or all sub directories? Need to add a directory to the test to find out. Also why so different than the old test results? + // TODO: is this just for .. or all subdirectories? Need to add a directory to the test + // to find out. Also why so different than the old test results? + if (!strcmp(findData.cFileName, "..") ) entry.size = 4096; else entry.size = findData.nFileSizeLow | ((u64)findData.nFileSizeHigh<<32); entry.name = findData.cFileName; - myVector.push_back(entry); int retval = FindNextFile(hFind, &findData); if (!retval) break; } +#else + ERROR_LOG(HLE, "GetDirListing not implemented on non-Windows"); #endif return myVector; } +void DirectoryFileSystem::DoState(PointerWrap &p) { + if (!entries.empty()) { + ERROR_LOG(FILESYS, "FIXME: Open files during savestate, could go badly."); + } +} diff --git a/Core/FileSystems/DirectoryFileSystem.h b/Core/FileSystems/DirectoryFileSystem.h index df81ccab34..6cb334fc53 100644 --- a/Core/FileSystems/DirectoryFileSystem.h +++ b/Core/FileSystems/DirectoryFileSystem.h @@ -28,29 +28,32 @@ typedef void * HANDLE; #endif +#if defined(__APPLE__) -class DirectoryFileSystem : public IFileSystem -{ - struct OpenFileEntry - { -#ifdef _WIN32 - HANDLE hFile; +#if TARGET_OS_IPHONE +#define HOST_IS_CASE_SENSITIVE 1 +#elif TARGET_IPHONE_SIMULATOR +#define HOST_IS_CASE_SENSITIVE 0 #else - FILE *hFile; +// Mac OSX case sensitivity defaults off, but is user configurable (when +// creating a filesytem), so assume the worst: +#define HOST_IS_CASE_SENSITIVE 1 #endif - }; - typedef std::map EntryMap; - EntryMap entries; - std::string basePath; - IHandleAllocator *hAlloc; +#elif defined(_WIN32) || defined(__SYMBIAN32__) +#define HOST_IS_CASE_SENSITIVE 0 +#else // Android, Linux, BSD (and the rest?) +#define HOST_IS_CASE_SENSITIVE 1 - // In case of Windows: Translate slashes, etc. - std::string GetLocalPath(std::string localpath); +#endif +class DirectoryFileSystem : public IFileSystem { public: DirectoryFileSystem(IHandleAllocator *_hAlloc, std::string _basePath); + ~DirectoryFileSystem(); + + void DoState(PointerWrap &p); std::vector GetDirListing(std::string path); u32 OpenFile(std::string filename, FileAccess access); void CloseFile(u32 handle); @@ -59,9 +62,35 @@ public: size_t SeekFile(u32 handle, s32 position, FileMove type); PSPFileInfo GetFileInfo(std::string filename); bool OwnsHandle(u32 handle); + bool MkDir(const std::string &dirname); bool RmDir(const std::string &dirname); bool RenameFile(const std::string &from, const std::string &to); bool DeleteFile(const std::string &filename); + +private: + struct OpenFileEntry { +#ifdef _WIN32 + HANDLE hFile; +#else + FILE *hFile; +#endif + }; + + typedef std::map EntryMap; + EntryMap entries; + std::string basePath; + IHandleAllocator *hAlloc; + + // In case of Windows: Translate slashes, etc. + std::string GetLocalPath(std::string localpath); + +#if HOST_IS_CASE_SENSITIVE + typedef enum { + FPC_FILE_MUST_EXIST, // all path components must exist (rmdir, move from) + FPC_PATH_MUST_EXIST, // all except the last one must exist - still tries to fix last one (fopen, move to) + FPC_PARTIAL_ALLOWED, // don't care how many exist (mkdir recursive) + } FixPathCaseBehavior; + bool FixPathCase(std::string &path, FixPathCaseBehavior behavior); +#endif }; - diff --git a/Core/FileSystems/FileSystem.h b/Core/FileSystems/FileSystem.h index ab1c1e48d2..4d8670effd 100644 --- a/Core/FileSystems/FileSystem.h +++ b/Core/FileSystems/FileSystem.h @@ -18,6 +18,7 @@ #pragma once #include "../../Globals.h" +#include "../../Common/ChunkFile.h" #include enum FileAccess @@ -56,6 +57,20 @@ struct PSPFileInfo PSPFileInfo() : size(0), access(0), exists(false), type(FILETYPE_NORMAL), isOnSectorSystem(false), startSector(0), numSectors(0) {} + void DoState(PointerWrap &p) + { + p.Do(name); + p.Do(size); + p.Do(access); + p.Do(exists); + p.Do(type); + p.Do(mtime); + p.Do(isOnSectorSystem); + p.Do(startSector); + p.Do(numSectors); + p.DoMarker("PSPFileInfo"); + } + std::string name; s64 size; u32 access; //unix 777 @@ -75,6 +90,7 @@ class IFileSystem public: virtual ~IFileSystem() {} + virtual void DoState(PointerWrap &p) = 0; virtual std::vector GetDirListing(std::string path) = 0; virtual u32 OpenFile(std::string filename, FileAccess access) = 0; virtual void CloseFile(u32 handle) = 0; @@ -93,6 +109,7 @@ public: class EmptyFileSystem : public IFileSystem { public: + virtual void DoState(PointerWrap &p) {} std::vector GetDirListing(std::string path) {std::vector vec; return vec;} u32 OpenFile(std::string filename, FileAccess access) {return 0;} void CloseFile(u32 handle) {} diff --git a/Core/FileSystems/ISOFileSystem.cpp b/Core/FileSystems/ISOFileSystem.cpp index 11c8e88954..b054b6bf72 100644 --- a/Core/FileSystems/ISOFileSystem.cpp +++ b/Core/FileSystems/ISOFileSystem.cpp @@ -16,10 +16,11 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include "Globals.h" -#include "Log.h" +#include "Common.h" #include "ISOFileSystem.h" #include #include +#include const int sectorSize = 2048; @@ -28,11 +29,13 @@ static bool parseLBN(std::string filename, u32 *sectorStart, u32 *readSize) { if (filename.substr(0, 8) != "/sce_lbn") return false; - std::string yo = filename; + std::string prev = filename; filename.erase(0, 10); - sscanf(filename.c_str(), "%08x", sectorStart); + if (sscanf(filename.c_str(), "%08x", sectorStart) != 1) + WARN_LOG(FILESYS, "Invalid LBN reference: %s", prev.c_str()); filename.erase(0, filename.find("_size") + 7); - sscanf(filename.c_str(), "%08x", readSize); + if (sscanf(filename.c_str(), "%08x", readSize) != 1) + WARN_LOG(FILESYS, "Incomplete LBN reference: %s", prev.c_str()); return true; } @@ -128,6 +131,7 @@ ISOFileSystem::ISOFileSystem(IHandleAllocator *_hAlloc, BlockDevice *_blockDevic entireISO.size = _blockDevice->GetNumBlocks() * _blockDevice->GetBlockSize(); entireISO.isBlockSectorMode = true; entireISO.flags = 0; + entireISO.parent = NULL; if (!memcmp(desc.cd001, "CD001", 5)) { @@ -139,6 +143,12 @@ ISOFileSystem::ISOFileSystem(IHandleAllocator *_hAlloc, BlockDevice *_blockDevic } treeroot = new TreeEntry; + treeroot->isDirectory = true; + treeroot->startingPosition = 0; + treeroot->size = 0; + treeroot->isBlockSectorMode = false; + treeroot->flags = 0; + treeroot->parent = NULL; u32 rootSector = desc.root.firstDataSectorLE; u32 rootSize = desc.root.dataLengthLE; @@ -149,6 +159,7 @@ ISOFileSystem::ISOFileSystem(IHandleAllocator *_hAlloc, BlockDevice *_blockDevic ISOFileSystem::~ISOFileSystem() { delete blockDevice; + delete treeroot; } void ISOFileSystem::ReadDirectory(u32 startsector, u32 dirsize, TreeEntry *root) @@ -208,6 +219,7 @@ nextblock: e->isDirectory = !isFile; e->flags = dir.flags; e->isBlockSectorMode = false; + e->parent = root; // Let's not excessively spam the log - I commented this line out. //DEBUG_LOG(FILESYS, "%s: %s %08x %08x %i", e->isDirectory?"D":"F", name, dir.firstDataSectorLE, e->startingPosition, e->startingPosition); @@ -392,6 +404,8 @@ size_t ISOFileSystem::ReadFile(u32 handle, u8 *pointer, s64 size) } else { + _dbg_assert_msg_(HLE, e.file != 0, "Expecting non-raw fd to have a tree entry."); + //clamp read length if ((s64)e.seekPos > e.file->size - (s64)size) { @@ -535,3 +549,70 @@ std::vector ISOFileSystem::GetDirListing(std::string path) } return myVector; } + +std::string ISOFileSystem::EntryFullPath(TreeEntry *e) +{ + size_t fullLen = 0; + TreeEntry *cur = e; + while (cur != NULL && cur != treeroot) + { + // For the "/". + fullLen += 1 + cur->name.size(); + cur = cur->parent; + } + + std::string path; + path.resize(fullLen); + + cur = e; + while (cur != NULL && cur != treeroot) + { + path.replace(fullLen - cur->name.size(), cur->name.size(), cur->name); + path.replace(fullLen - cur->name.size() - 1, 1, "/"); + fullLen -= 1 + cur->name.size(); + cur = cur->parent; + } + + return path; +} + +void ISOFileSystem::DoState(PointerWrap &p) +{ + int n = (int) entries.size(); + p.Do(n); + + if (p.mode == p.MODE_READ) + { + entries.clear(); + for (int i = 0; i < n; ++i) + { + u32 fd; + p.Do(fd); + std::string path; + p.Do(path); + OpenFileEntry of; + of.file = path.empty() ? NULL : GetFromPath(path); + p.Do(of.seekPos); + p.Do(of.isRawSector); + p.Do(of.sectorStart); + p.Do(of.openSize); + entries[fd] = of; + } + } + else + { + for (EntryMap::iterator it = entries.begin(), end = entries.end(); it != end; ++it) + { + p.Do(it->first); + std::string path = ""; + if (it->second.file != NULL) + path = EntryFullPath(it->second.file); + p.Do(path); + p.Do(it->second.seekPos); + p.Do(it->second.isRawSector); + p.Do(it->second.sectorStart); + p.Do(it->second.openSize); + } + } + p.DoMarker("ISOFileSystem"); +} diff --git a/Core/FileSystems/ISOFileSystem.h b/Core/FileSystems/ISOFileSystem.h index 61f30016f1..96e88e0710 100644 --- a/Core/FileSystems/ISOFileSystem.h +++ b/Core/FileSystems/ISOFileSystem.h @@ -42,8 +42,9 @@ class ISOFileSystem : public IFileSystem u32 startingPosition; s64 size; bool isDirectory; - bool isBlockSectorMode; // "umd:" mode: all sizes and offsets are in 2048 byte chunks + bool isBlockSectorMode; // "umd:" mode: all sizes and offsets are in 2048 byte chunks + TreeEntry *parent; std::vector children; }; @@ -67,10 +68,12 @@ class ISOFileSystem : public IFileSystem void ReadDirectory(u32 startsector, u32 dirsize, TreeEntry *root); TreeEntry *GetFromPath(std::string path); + std::string EntryFullPath(TreeEntry *e); public: ISOFileSystem(IHandleAllocator *_hAlloc, BlockDevice *_blockDevice); ~ISOFileSystem(); + void DoState(PointerWrap &p); std::vector GetDirListing(std::string path); u32 OpenFile(std::string filename, FileAccess access); void CloseFile(u32 handle); diff --git a/Core/FileSystems/MetaFileSystem.cpp b/Core/FileSystems/MetaFileSystem.cpp index 9d69f1d138..58ae33fae1 100644 --- a/Core/FileSystems/MetaFileSystem.cpp +++ b/Core/FileSystems/MetaFileSystem.cpp @@ -18,7 +18,7 @@ #include #include "MetaFileSystem.h" -bool applyPathStringToComponentsVector(std::vector &vector, const std::string &pathString) +static bool ApplyPathStringToComponentsVector(std::vector &vector, const std::string &pathString) { size_t len = pathString.length(); size_t start = 0; @@ -42,8 +42,8 @@ bool applyPathStringToComponentsVector(std::vector &vector, const s } else { - // what does the real PSP do for "/../filename"? - WARN_LOG(HLE, "RealPath: .. as first path component: \"%s\"", pathString.c_str()); + // The PSP silently ignores attempts to .. to parent of root directory + WARN_LOG(HLE, "RealPath: ignoring .. beyond root - root directory is its own parent: \"%s\"", pathString.c_str()); } } else @@ -61,9 +61,10 @@ bool applyPathStringToComponentsVector(std::vector &vector, const s /* * Changes relative paths to absolute, removes ".", "..", and trailing "/" + * "drive:./blah" is absolute (ignore the dot) and "/blah" is relative (because it's missing "drive:") * babel (and possibly other games) use "/directoryThatDoesNotExist/../directoryThatExists/filename" */ -bool RealPath(const std::string ¤tDirectory, const std::string &inPath, std::string &outPath) +static bool RealPath(const std::string ¤tDirectory, const std::string &inPath, std::string &outPath) { size_t inLen = inPath.length(); if (inLen == 0) @@ -81,64 +82,27 @@ bool RealPath(const std::string ¤tDirectory, const std::string &inPath, st return true; } - std::string curDirPrefix; - size_t curDirColon = std::string::npos, curDirLen = currentDirectory.length(); - if (curDirLen != 0) - { - curDirColon = currentDirectory.find(':'); - - if (curDirColon == std::string::npos) - { - DEBUG_LOG(HLE, "RealPath: currentDirectory has no prefix: \"%s\"", currentDirectory.c_str()); - } - else - { - if (curDirColon + 1 == curDirLen) - DEBUG_LOG(HLE, "RealPath: currentDirectory is all prefix and no path: \"%s\"", currentDirectory.c_str()); - - curDirPrefix = currentDirectory.substr(0, curDirColon + 1); - } - } - - std::string inPrefix, inAfter; - - if (inColon == std::string::npos) - { - inPrefix = curDirPrefix; - inAfter = inPath; - } - else - { - inPrefix = inPath.substr(0, inColon + 1); - inAfter = inPath.substr(inColon + 1); - } - + bool relative = (inColon == std::string::npos); + + std::string prefix, inAfterColon; std::vector cmpnts; // path components - size_t capacityGuess = inPath.length(); + size_t outPathCapacityGuess = inPath.length(); - // Special hack for strange root paths. - // Don't understand why this is needed. I don't think the current - // directory should be the root. - if (inAfter.substr(0, 11) == "./PSP_GAME/") - inAfter = inAfter.substr(1); - - if ((inAfter[0] != '/')) + if (relative) { + size_t curDirLen = currentDirectory.length(); if (curDirLen == 0) { ERROR_LOG(HLE, "RealPath: inPath \"%s\" is relative, but current directory is empty", inPath.c_str()); return false; } - if (curDirColon == std::string::npos || curDirPrefix.length() == 0) + size_t curDirColon = currentDirectory.find(':'); + if (curDirColon == std::string::npos) { ERROR_LOG(HLE, "RealPath: inPath \"%s\" is relative, but current directory \"%s\" has no prefix", inPath.c_str(), currentDirectory.c_str()); return false; } - - if (inPrefix != curDirPrefix) - WARN_LOG(HLE, "RealPath: inPath \"%s\" is relative, but specifies a different prefix than current directory \"%s\"", inPath.c_str(), currentDirectory.c_str()); - if (curDirColon + 1 == curDirLen) { ERROR_LOG(HLE, "RealPath: inPath \"%s\" is relative, but current directory \"%s\" is all prefix and no path. Using \"/\" as path for current directory.", inPath.c_str(), currentDirectory.c_str()); @@ -146,26 +110,34 @@ bool RealPath(const std::string ¤tDirectory, const std::string &inPath, st else { const std::string curDirAfter = currentDirectory.substr(curDirColon + 1); - if (! applyPathStringToComponentsVector(cmpnts, curDirAfter) ) + if (! ApplyPathStringToComponentsVector(cmpnts, curDirAfter) ) { ERROR_LOG(HLE,"RealPath: currentDirectory is not a valid path: \"%s\"", currentDirectory.c_str()); return false; } + + outPathCapacityGuess += curDirLen; } - capacityGuess += currentDirectory.length(); + prefix = currentDirectory.substr(0, curDirColon + 1); + inAfterColon = inPath; + } + else + { + prefix = inPath.substr(0, inColon + 1); + inAfterColon = inPath.substr(inColon + 1); } - if (! applyPathStringToComponentsVector(cmpnts, inAfter) ) + if (! ApplyPathStringToComponentsVector(cmpnts, inAfterColon) ) { - DEBUG_LOG(HLE, "RealPath: inPath is not a valid path: \"%s\"", inPath.c_str()); + WARN_LOG(HLE, "RealPath: inPath is not a valid path: \"%s\"", inPath.c_str()); return false; } outPath.clear(); - outPath.reserve(capacityGuess); + outPath.reserve(outPathCapacityGuess); - outPath.append(inPrefix); + outPath.append(prefix); size_t numCmpnts = cmpnts.size(); for (size_t i = 0; i < numCmpnts; i++) @@ -188,31 +160,37 @@ IFileSystem *MetaFileSystem::GetHandleOwner(u32 handle) return 0; } -bool MetaFileSystem::MapFilePath(std::string inpath, std::string &outpath, IFileSystem **system) +bool MetaFileSystem::MapFilePath(const std::string &_inpath, std::string &outpath, IFileSystem **system) { //TODO: implement current directory per thread (NOT per drive) - - //DEBUG_LOG(HLE, "MapFilePath: starting with \"%s\"", inpath.c_str()); + std::string realpath; - if ( RealPath(currentDirectory, inpath, inpath) ) + // Special handling: host0:command.txt (as seen in Super Monkey Ball Adventures, for example) + // appears to mean the current directory on the UMD. Let's just assume the current directory. + std::string inpath = _inpath; + if (inpath.substr(0, 6) == "host0:") { + INFO_LOG(HLE, "Host0 path detected, stripping: %s", inpath.c_str()); + inpath = inpath.substr(6); + } + + if ( RealPath(currentDirectory, inpath, realpath) ) { for (size_t i = 0; i < fileSystems.size(); i++) { size_t prefLen = fileSystems[i].prefix.size(); - if (fileSystems[i].prefix == inpath.substr(0, prefLen)) + if (fileSystems[i].prefix == realpath.substr(0, prefLen)) { - outpath = inpath.substr(prefLen); + outpath = realpath.substr(prefLen); *system = fileSystems[i].system; - DEBUG_LOG(HLE, "MapFilePath: mapped to prefix: \"%s\", path: \"%s\"", fileSystems[i].prefix.c_str(), outpath.c_str()); + DEBUG_LOG(HLE, "MapFilePath: mapped \"%s\" to prefix: \"%s\", path: \"%s\"", inpath.c_str(), fileSystems[i].prefix.c_str(), outpath.c_str()); return true; } } } - DEBUG_LOG(HLE, "MapFilePath: failed, returning false"); - + DEBUG_LOG(HLE, "MapFilePath: failed mapping \"%s\", returning false", inpath.c_str()); return false; } @@ -389,3 +367,22 @@ size_t MetaFileSystem::SeekFile(u32 handle, s32 position, FileMove type) return 0; } +void MetaFileSystem::DoState(PointerWrap &p) +{ + p.Do(current); + p.Do(currentDirectory); + + int n = (int) fileSystems.size(); + p.Do(n); + if (n != fileSystems.size()) + { + ERROR_LOG(FILESYS, "Savestate failure: number of filesystems doesn't match."); + return; + } + + for (int i = 0; i < n; ++i) + fileSystems[i].system->DoState(p); + + p.DoMarker("MetaFileSystem"); +} + diff --git a/Core/FileSystems/MetaFileSystem.h b/Core/FileSystems/MetaFileSystem.h index dee552e098..7fd0a96db2 100644 --- a/Core/FileSystems/MetaFileSystem.h +++ b/Core/FileSystems/MetaFileSystem.h @@ -36,8 +36,10 @@ public: u32 GetNewHandle() {return current++;} void FreeHandle(u32 handle) {} + virtual void DoState(PointerWrap &p); + IFileSystem *GetHandleOwner(u32 handle); - bool MapFilePath(std::string inpath, std::string &outpath, IFileSystem **system); + bool MapFilePath(const std::string &inpath, std::string &outpath, IFileSystem **system); std::vector GetDirListing(std::string path); u32 OpenFile(std::string filename, FileAccess access); diff --git a/Core/HLE/FunctionWrappers.h b/Core/HLE/FunctionWrappers.h index b6ffadc3ef..d88e99090c 100644 --- a/Core/HLE/FunctionWrappers.h +++ b/Core/HLE/FunctionWrappers.h @@ -136,7 +136,10 @@ template void WrapU_I() { RETURN(retval); } - +template void WrapU_IIU() { + u32 retval = func(PARAM(0), PARAM(1), PARAM(2)); + RETURN(retval); +} template void WrapI_I() { int retval = func(PARAM(0)); @@ -183,6 +186,11 @@ template void WrapU_UU() { RETURN(retval); } +template void WrapU_CUUU() { + u32 retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2), PARAM(3)); + RETURN(retval); +} + template void WrapV_UIUII() { func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4)); } @@ -234,6 +242,11 @@ template void WrapI_III() { RETURN(retval); } +template void WrapI_IIII() { + int retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3)); + RETURN(retval); +} + template void WrapI_IIU() { int retval = func(PARAM(0), PARAM(1), PARAM(2)); RETURN(retval); @@ -301,11 +314,31 @@ template void WrapU_III() { RETURN(retval); } +template void WrapU_II() { + u32 retval = func(PARAM(0), PARAM(1)); + RETURN(retval); +} + +template void WrapU_IIII() { + u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3)); + RETURN(retval); +} + template void WrapU_IUU() { u32 retval = func(PARAM(0), PARAM(1), PARAM(2)); RETURN(retval); } +template void WrapU_IUUU() { + u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3)); + RETURN(retval); +} + +template void WrapU_IUUUU() { + u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4)); + RETURN(retval); +} + template void WrapU_UUU() { u32 retval = func(PARAM(0), PARAM(1), PARAM(2)); RETURN(retval); @@ -415,6 +448,11 @@ template void WrapU_CI() { RETURN(retval); } +template void WrapU_CII() { + int retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2)); + RETURN(retval); +} + template void WrapU_CIUIU() { int retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2), PARAM(3), PARAM(4)); diff --git a/Core/HLE/HLE.cpp b/Core/HLE/HLE.cpp index 69af4be0c0..2ebb7b5149 100644 --- a/Core/HLE/HLE.cpp +++ b/Core/HLE/HLE.cpp @@ -17,6 +17,7 @@ #include "HLE.h" #include +#include #include "../MemMap.h" #include "HLETables.h" @@ -26,7 +27,9 @@ #include "sceAudio.h" #include "sceKernelMemory.h" #include "sceKernelThread.h" +#include "sceKernelInterrupt.h" #include "../MIPS/MIPSCodeUtils.h" +#include "../Host.h" enum { @@ -40,6 +43,10 @@ enum HLE_AFTER_ALL_CALLBACKS = 0x04, // Reschedule and process current thread's callbacks after the syscall. HLE_AFTER_RESCHED_CALLBACKS = 0x08, + // Run interrupts (and probably reschedule) after the syscall. + HLE_AFTER_RUN_INTERRUPTS = 0x10, + // Switch to CORE_STEPPING after the syscall (for debugging.) + HLE_AFTER_DEBUG_BREAK = 0x20, }; static std::vector moduleDB; @@ -52,9 +59,18 @@ void HLEInit() RegisterAllModules(); } +void HLEDoState(PointerWrap &p) +{ + Syscall sc = {0}; + p.Do(unresolvedSyscalls, sc); + p.DoMarker("HLE"); +} + void HLEShutdown() { + hleAfterSyscall = HLE_AFTER_NOTHING; moduleDB.clear(); + unresolvedSyscalls.clear(); } void RegisterModule(const char *name, int numFunctions, const HLEFunction *funcTable) @@ -208,7 +224,7 @@ void hleCheckCurrentCallbacks() void hleReSchedule(const char *reason) { _dbg_assert_msg_(HLE, reason != 0, "hleReSchedule: Expecting a valid reason."); - _dbg_assert_msg_(HLE, strlen(reason) < 256, "hleReSchedule: Not too long reason."); + _dbg_assert_msg_(HLE, reason != 0 && strlen(reason) < 256, "hleReSchedule: Not too long reason."); hleAfterSyscall |= HLE_AFTER_RESCHED; @@ -231,11 +247,42 @@ void hleReSchedule(bool callbacks, const char *reason) hleAfterSyscall |= HLE_AFTER_RESCHED_CALLBACKS; } -inline void hleFinishSyscall() +void hleRunInterrupts() +{ + hleAfterSyscall |= HLE_AFTER_RUN_INTERRUPTS; +} + +void hleDebugBreak() +{ + hleAfterSyscall |= HLE_AFTER_DEBUG_BREAK; +} + +// Pauses execution after an HLE call. +bool hleExecuteDebugBreak(const HLEFunction &func) +{ + const u32 NID_SUSPEND_INTR = 0x092968F4, NID_RESUME_INTR = 0x5F10D406; + + // Never break on these, they're noise. + u32 blacklistedNIDs[] = {NID_SUSPEND_INTR, NID_RESUME_INTR, NID_IDLE}; + for (int i = 0; i < ARRAY_SIZE(blacklistedNIDs); ++i) + { + if (func.ID == blacklistedNIDs[i]) + return false; + } + + Core_EnableStepping(true); + host->SetDebugMode(true); + return true; +} + +inline void hleFinishSyscall(int modulenum, int funcnum) { if ((hleAfterSyscall & HLE_AFTER_CURRENT_CALLBACKS) != 0) __KernelForceCallbacks(); + if ((hleAfterSyscall & HLE_AFTER_RUN_INTERRUPTS) != 0) + __RunOnePendingInterrupt(); + // Rescheduling will also do HLE_AFTER_ALL_CALLBACKS. if ((hleAfterSyscall & HLE_AFTER_RESCHED_CALLBACKS) != 0) __KernelReSchedule(true, hleAfterSyscallReschedReason); @@ -244,6 +291,17 @@ inline void hleFinishSyscall() else if ((hleAfterSyscall & HLE_AFTER_ALL_CALLBACKS) != 0) __KernelCheckCallbacks(); + if ((hleAfterSyscall & HLE_AFTER_DEBUG_BREAK) != 0) + { + if (!hleExecuteDebugBreak(moduleDB[modulenum].funcTable[funcnum])) + { + // We'll do it next syscall. + hleAfterSyscall = HLE_AFTER_DEBUG_BREAK; + hleAfterSyscallReschedReason[0] = 0; + return; + } + } + hleAfterSyscall = HLE_AFTER_NOTHING; hleAfterSyscallReschedReason[0] = 0; } @@ -253,7 +311,7 @@ void CallSyscall(u32 op) u32 callno = (op >> 6) & 0xFFFFF; //20 bits int funcnum = callno & 0xFFF; int modulenum = (callno & 0xFF000) >> 12; - if (funcnum == 0xfff) + if (funcnum == 0xfff || op == 0xffff) { _dbg_assert_msg_(HLE,0,"Unknown syscall"); ERROR_LOG(HLE,"Unknown syscall: Module: %s", moduleDB[modulenum].name); @@ -265,7 +323,7 @@ void CallSyscall(u32 op) func(); if (hleAfterSyscall != HLE_AFTER_NOTHING) - hleFinishSyscall(); + hleFinishSyscall(modulenum, funcnum); } else { diff --git a/Core/HLE/HLE.h b/Core/HLE/HLE.h index 83633b134f..8ff39c328c 100644 --- a/Core/HLE/HLE.h +++ b/Core/HLE/HLE.h @@ -81,8 +81,13 @@ void hleCheckAllCallbacks(); void hleReSchedule(const char *reason); // Reschedule and go into a callback processing state after the syscall finishes. void hleReSchedule(bool callbacks, const char *reason); +// Run interrupts after the syscall finishes. +void hleRunInterrupts(); +// Pause emulation after the syscall finishes. +void hleDebugBreak(); void HLEInit(); +void HLEDoState(PointerWrap &p); void HLEShutdown(); u32 GetNibByName(const char *module, const char *function); u32 GetSyscallOp(const char *module, u32 nib); @@ -90,7 +95,3 @@ void WriteSyscall(const char *module, u32 nib, u32 address); void CallSyscall(u32 op); void ResolveSyscall(const char *moduleName, u32 nib, u32 address); -// Need to be able to save entire kernel state -int GetStateSize(); -void SaveState(u8 *ptr); -void LoadState(const u8 *ptr); diff --git a/Core/HLE/HLETables.cpp b/Core/HLE/HLETables.cpp index a2e2f5a634..7a8aba3661 100644 --- a/Core/HLE/HLETables.cpp +++ b/Core/HLE/HLETables.cpp @@ -77,7 +77,7 @@ const HLEFunction UtilsForUser[] = {0x91E4F6A7, WrapU_V, "sceKernelLibcClock"}, {0x27CC57F0, sceKernelLibcTime, "sceKernelLibcTime"}, {0x71EC4271, sceKernelLibcGettimeofday, "sceKernelLibcGettimeofday"}, - {0xBFA98062, 0, "sceKernelDcacheInvalidateRange"}, + {0xBFA98062, WrapV_UI, "sceKernelDcacheInvalidateRange"}, {0xC8186A58, 0, "sceKernelUtilsMd5Digest"}, {0x9E5C5086, 0, "sceKernelUtilsMd5BlockInit"}, {0x61E1E525, 0, "sceKernelUtilsMd5BlockUpdate"}, @@ -92,8 +92,8 @@ const HLEFunction UtilsForUser[] = {0x6AD345D7, sceKernelSetGPO, "sceKernelSetGPO"}, {0x79D1C3FA, sceKernelDcacheWritebackAll, "sceKernelDcacheWritebackAll"}, {0xB435DEC5, sceKernelDcacheWritebackInvalidateAll, "sceKernelDcacheWritebackInvalidateAll"}, - {0x3EE30821, sceKernelDcacheWritebackRange, "sceKernelDcacheWritebackRange"}, - {0x34B9FA9E, sceKernelDcacheWritebackInvalidateRange, "sceKernelDcacheWritebackInvalidateRange"}, + {0x3EE30821, WrapV_UI, "sceKernelDcacheWritebackRange"}, + {0x34B9FA9E, WrapV_UI, "sceKernelDcacheWritebackInvalidateRange"}, {0xC2DF770E, 0, "sceKernelIcacheInvalidateRange"}, {0x80001C4C, 0, "sceKernelDcacheProbe"}, {0x16641D70, 0, "sceKernelDcacheReadTag"}, diff --git a/Core/HLE/__sceAudio.cpp b/Core/HLE/__sceAudio.cpp index bf22116aeb..48261e457b 100644 --- a/Core/HLE/__sceAudio.cpp +++ b/Core/HLE/__sceAudio.cpp @@ -28,20 +28,15 @@ #include "FixedSizeQueue.h" #include "Common/Thread.h" -// While buffers == MAX_BUFFERS, block on blocking write -// non-blocking writes will return busy, I guess - -#define MAX_BUFFERS 2 -#define MIN_BUFFERS 1 - std::recursive_mutex section; int eventAudioUpdate = -1; int eventHostAudioUpdate = -1; int mixFrequency = 44100; + const int hwSampleRate = 44100; -const int hwBlockSize = 480; -const int hostAttemptBlockSize = 64; +const int hwBlockSize = 60; +const int hostAttemptBlockSize = 256; const int audioIntervalUs = (int)(1000000ULL * hwBlockSize / hwSampleRate); const int audioHostIntervalUs = (int)(1000000ULL * hostAttemptBlockSize / hwSampleRate); @@ -49,20 +44,20 @@ const int audioHostIntervalUs = (int)(1000000ULL * hostAttemptBlockSize / hwSamp const int chanQueueMaxSizeFactor = 4; const int chanQueueMinSizeFactor = 1; -FixedSizeQueue outAudioQueue; +FixedSizeQueue outAudioQueue; void hleAudioUpdate(u64 userdata, int cyclesLate) { __AudioUpdate(); - CoreTiming::ScheduleEvent(usToCycles(audioIntervalUs), eventAudioUpdate, 0); + CoreTiming::ScheduleEvent(usToCycles(audioIntervalUs) - cyclesLate, eventAudioUpdate, 0); } void hleHostAudioUpdate(u64 userdata, int cyclesLate) { host->UpdateSound(); - CoreTiming::ScheduleEvent(usToCycles(audioHostIntervalUs), eventHostAudioUpdate, 0); + CoreTiming::ScheduleEvent(usToCycles(audioHostIntervalUs) - cyclesLate, eventHostAudioUpdate, 0); } void __AudioInit() @@ -78,6 +73,33 @@ void __AudioInit() chans[i].clear(); } +void __AudioDoState(PointerWrap &p) +{ + section.lock(); + + p.Do(eventAudioUpdate); + CoreTiming::RestoreRegisterEvent(eventAudioUpdate, "AudioUpdate", &hleAudioUpdate); + p.Do(eventHostAudioUpdate); + CoreTiming::RestoreRegisterEvent(eventHostAudioUpdate, "AudioUpdateHost", &hleAudioUpdate); + + p.Do(mixFrequency); + outAudioQueue.DoState(p); + + int chanCount = ARRAY_SIZE(chans); + p.Do(chanCount); + if (chanCount != ARRAY_SIZE(chans)) + { + ERROR_LOG(HLE, "Savestate failure: different number of audio channels."); + section.unlock(); + return; + } + for (int i = 0; i < chanCount; ++i) + chans[i].DoState(p); + + section.unlock(); + p.DoMarker("sceAudio"); +} + void __AudioShutdown() { for (int i = 0; i < 8; i++) @@ -137,12 +159,12 @@ void __AudioUpdate() s32 mixBuffer[hwBlockSize * 2]; memset(mixBuffer, 0, sizeof(mixBuffer)); - for (int i = 0; i < MAX_CHANNEL; i++) + for (int i = 0; i < PSP_AUDIO_CHANNEL_MAX; i++) { if (!chans[i].reserved) continue; if (!chans[i].sampleQueue.size()) { - // DEBUG_LOG(HLE, "No queued samples, skipping channel %i", i); + // ERROR_LOG(HLE, "No queued samples, skipping channel %i", i); continue; } @@ -178,14 +200,20 @@ void __AudioUpdate() section.lock(); - if (g_Config.bEnableSound && outAudioQueue.room() >= hwBlockSize * 2) { - // Push the mixed samples onto the output audio queue. - for (int i = 0; i < hwBlockSize; i++) { - s32 sampleL = mixBuffer[i * 2] >> 2; // TODO - what factor? - s32 sampleR = mixBuffer[i * 2 + 1] >> 2; + if (g_Config.bEnableSound) { + if (outAudioQueue.room() >= hwBlockSize * 2) { + // Push the mixed samples onto the output audio queue. + for (int i = 0; i < hwBlockSize; i++) { + s32 sampleL = mixBuffer[i * 2] >> 2; // TODO - what factor? + s32 sampleR = mixBuffer[i * 2 + 1] >> 2; - outAudioQueue.push((s16)sampleL); - outAudioQueue.push((s16)sampleR); + outAudioQueue.push((s16)sampleL); + outAudioQueue.push((s16)sampleR); + } + } else { + // This happens quite a lot. There's still something slightly off + // about the amount of audio we produce. + DEBUG_LOG(HLE, "Audio outbuffer overrun! room = %i / %i", outAudioQueue.room(), (u32)outAudioQueue.capacity()); } } @@ -225,7 +253,7 @@ int __AudioMix(short *outstereo, int numFrames) } } if (anythingToPlay && underrun >= 0) { - ERROR_LOG(HLE, "audio out buffer UNDERRUN at %i of %i", underrun, numFrames); + DEBUG_LOG(HLE, "audio out buffer UNDERRUN at %i of %i", underrun, numFrames); } else { // DEBUG_LOG(HLE, "No underrun, mixed %i samples fine", numFrames); } diff --git a/Core/HLE/__sceAudio.h b/Core/HLE/__sceAudio.h index 196c16a7b8..21a83ef953 100644 --- a/Core/HLE/__sceAudio.h +++ b/Core/HLE/__sceAudio.h @@ -22,6 +22,7 @@ // Easy interface for sceAudio to write to, to keep the complexity in check. void __AudioInit(); +void __AudioDoState(PointerWrap &p); void __AudioUpdate(); void __AudioShutdown(); void __AudioSetOutputFrequency(int freq); diff --git a/Core/HLE/sceAtrac.cpp b/Core/HLE/sceAtrac.cpp index 6b442be805..b996633261 100644 --- a/Core/HLE/sceAtrac.cpp +++ b/Core/HLE/sceAtrac.cpp @@ -28,13 +28,15 @@ #define ATRAC_ERROR_API_FAIL 0x80630002 #define ATRAC_ERROR_ALL_DATA_DECODED 0x80630024 -int sceAtracAddStreamData(int atracID, u32 bytesToAdd) + + +u32 sceAtracAddStreamData(int atracID, u32 bytesToAdd) { - ERROR_LOG(HLE, "UNIMPL sceAtracAddStreamData(%i, %i)", atracID, bytesToAdd); + ERROR_LOG(HLE, "UNIMPL sceAtracAddStreamData(%i, %08x)", atracID, bytesToAdd); return 0; } -int sceAtracDecodeData(int atracID, u32 outAddr, u32 numSamplesAddr, u32 finishFlagAddr, u32 remainAddr) +u32 sceAtracDecodeData(int atracID, u32 outAddr, u32 numSamplesAddr, u32 finishFlagAddr, u32 remainAddr) { ERROR_LOG(HLE, "FAKE sceAtracDecodeData(%i, %08x, %08x, %08x, %08x)", atracID, outAddr, numSamplesAddr, finishFlagAddr, remainAddr); @@ -48,76 +50,82 @@ int sceAtracDecodeData(int atracID, u32 outAddr, u32 numSamplesAddr, u32 finishF return 0; } -void sceAtracEndEntry() +u32 sceAtracEndEntry() { - ERROR_LOG(HLE, "UNIMPL sceAtracEndEntry"); - RETURN(0); -} - -void sceAtracGetAtracID() -{ - ERROR_LOG(HLE, "UNIMPL sceAtracGetAtracID"); - RETURN(0); -} - -void sceAtracGetBufferInfoForReseting() -{ - ERROR_LOG(HLE, "UNIMPL sceAtracGetBufferInfoForReseting"); - RETURN(0); -} - -int sceAtracGetBitrate(int atracID, u32 outBitrateAddr) -{ - ERROR_LOG(HLE, "UNIMPL sceAtracGetBitrate"); + ERROR_LOG(HLE, "UNIMPL sceAtracEndEntry(.)"); return 0; } -void sceAtracGetChannel() +u32 sceAtracGetAtracID(int codecType) { - ERROR_LOG(HLE, "UNIMPL sceAtracGetChannel"); - RETURN(0); + ERROR_LOG(HLE, "UNIMPL sceAtracGetAtracID(%i)", codecType); + return 1; } -void sceAtracGetLoopStatus() +u32 sceAtracGetBufferInfoForReseting(int atracID, int sample, u32 bufferInfoAddr) { - ERROR_LOG(HLE, "UNIMPL sceAtracGetLoopStatus"); - RETURN(0); + ERROR_LOG(HLE, "UNIMPL sceAtracGetBufferInfoForReseting(%i, %i, %08x)",atracID, sample, bufferInfoAddr); + return 0; } -void sceAtracGetInternalErrorInfo() +u32 sceAtracGetBitrate(int atracID, u32 outBitrateAddr) { - ERROR_LOG(HLE, "UNIMPL sceAtracGetInternalErrorInfo"); - RETURN(0); + ERROR_LOG(HLE, "UNIMPL sceAtracGetBitrate(%i, %08x)", atracID, outBitrateAddr); + if (Memory::IsValidAddress(outBitrateAddr)) + Memory::Write_U32(64, outBitrateAddr); + return 0; } -void sceAtracGetMaxSample() +u32 sceAtracGetChannel(int atracID, u32 channelAddr) { - ERROR_LOG(HLE, "UNIMPL sceAtracGetMaxSample"); - RETURN(0); + ERROR_LOG(HLE, "UNIMPL sceAtracGetChannel(%i, %08x)", atracID, channelAddr); + if (Memory::IsValidAddress(channelAddr)) + Memory::Write_U32(2, channelAddr); + return 0; } -int sceAtracGetNextDecodePosition(int atracID, u32 outposAddr) +u32 sceAtracGetLoopStatus(int atracID, u32 loopNbr, u32 statusAddr) +{ + ERROR_LOG(HLE, "UNIMPL sceAtracGetLoopStatus(%i, %08x, %08x)", atracID, loopNbr, statusAddr ); + return 0; +} + +u32 sceAtracGetInternalErrorInfo(int atracID, u32 errorAddr) +{ + ERROR_LOG(HLE, "UNIMPL sceAtracGetInternalErrorInfo(%i, %08x)", atracID, errorAddr); + return 0; +} + +u32 sceAtracGetMaxSample(int atracID, u32 maxSamplesAddr) +{ + ERROR_LOG(HLE, "UNIMPL sceAtracGetMaxSample(%i, %08x)", atracID, maxSamplesAddr); + if (Memory::IsValidAddress(maxSamplesAddr)) + Memory::Write_U32(1024, maxSamplesAddr); + return 0; +} + +u32 sceAtracGetNextDecodePosition(int atracID, u32 outposAddr) { ERROR_LOG(HLE, "UNIMPL sceAtracGetNextDecodePosition(%i, %08x)", atracID, outposAddr); Memory::Write_U32(1, outposAddr); // outpos return 0; } -int sceAtracGetNextSample(int atracID, u32 outNAddr) +u32 sceAtracGetNextSample(int atracID, u32 outNAddr) { ERROR_LOG(HLE, "FAKE sceAtracGetNextSample(%i, %08x)", atracID, outNAddr); Memory::Write_U32(0, outNAddr); return 0; } -int sceAtracGetRemainFrame(int atracID, u32 outposAddr) +u32 sceAtracGetRemainFrame(int atracID, u32 outposAddr) { ERROR_LOG(HLE, "sceAtracGetRemainFrame(%i, %08x)", atracID, outposAddr); Memory::Write_U32(12, outposAddr); // outpos return 0; } -int sceAtracGetSecondBufferInfo(int atracID, u32 outposAddr, u32 outBytesAddr) +u32 sceAtracGetSecondBufferInfo(int atracID, u32 outposAddr, u32 outBytesAddr) { ERROR_LOG(HLE, "sceAtracGetSecondBufferInfo(%i, %08x, %08x)", atracID, outposAddr, outBytesAddr); Memory::Write_U32(0, outposAddr); // outpos @@ -125,7 +133,7 @@ int sceAtracGetSecondBufferInfo(int atracID, u32 outposAddr, u32 outBytesAddr) return 0; } -int sceAtracGetSoundSample(int atracID, u32 outEndSampleAddr, u32 outLoopStartSampleAddr, u32 outLoopEndSampleAddr) +u32 sceAtracGetSoundSample(int atracID, u32 outEndSampleAddr, u32 outLoopStartSampleAddr, u32 outLoopEndSampleAddr) { ERROR_LOG(HLE, "UNIMPL sceAtracGetSoundSample(%i, %08x, %08x, %08x)", atracID, outEndSampleAddr, outLoopStartSampleAddr, outLoopEndSampleAddr); Memory::Write_U32(0x10000, outEndSampleAddr); // outEndSample @@ -134,7 +142,7 @@ int sceAtracGetSoundSample(int atracID, u32 outEndSampleAddr, u32 outLoopStartSa return 0; } -int sceAtracGetStreamDataInfo(int atracID, u32 writePointerAddr, u32 availableBytesAddr, u32 readOffsetAddr) +u32 sceAtracGetStreamDataInfo(int atracID, u32 writePointerAddr, u32 availableBytesAddr, u32 readOffsetAddr) { ERROR_LOG(HLE, "FAKE sceAtracGetStreamDataInfo(%i, %08x, %08x, %08x)", atracID, writePointerAddr, availableBytesAddr, readOffsetAddr); Memory::Write_U32(0, readOffsetAddr); @@ -143,58 +151,58 @@ int sceAtracGetStreamDataInfo(int atracID, u32 writePointerAddr, u32 availableBy return 0; } -void sceAtracReleaseAtracID() +u32 sceAtracReleaseAtracID(int atracID) { - ERROR_LOG(HLE, "UNIMPL sceAtracReleaseAtracID"); - RETURN(0); + ERROR_LOG(HLE, "UNIMPL sceAtracReleaseAtracID(%i)", atracID); + return 0; } -void sceAtracResetPlayPosition() +u32 sceAtracResetPlayPosition(int atracID, int sample, int bytesWrittenFirstBuf, int bytesWrittenSecondBuf) { - ERROR_LOG(HLE, "UNIMPL sceAtracResetPlayPosition"); - RETURN(0); + ERROR_LOG(HLE, "UNIMPL sceAtracResetPlayPosition(%i, %i, %i, %i)", atracID, sample, bytesWrittenFirstBuf, bytesWrittenSecondBuf); + return 0; } -void sceAtracSetHalfwayBuffer() +u32 sceAtracSetHalfwayBuffer(int atracID, u32 halfBuffer, u32 readSize, u32 halfBufferSize) { - ERROR_LOG(HLE, "UNIMPL sceAtracSetHalfwayBuffer"); - RETURN(0); + ERROR_LOG(HLE, "UNIMPL sceAtracSetHalfwayBuffer(%i, %08x, %8x, %8x)", atracID, halfBuffer, readSize, halfBufferSize); + return 0; } -void sceAtracSetSecondBuffer() +u32 sceAtracSetSecondBuffer(int atracID, u32 secondBuffer, u32 secondBufferSize) { - ERROR_LOG(HLE, "UNIMPL sceAtracSetSecondBuffer(%i, %08x, %i)", PARAM(0),PARAM(1),PARAM(2)); - RETURN(0); + ERROR_LOG(HLE, "UNIMPL sceAtracSetSecondBuffer(%i, %08x, %8x)", atracID, secondBuffer, secondBufferSize); + return 0; } -void sceAtracSetData() +u32 sceAtracSetData(int atracID, u32 buffer, u32 bufferSize) { - ERROR_LOG(HLE, "UNIMPL sceAtracSetData"); - RETURN(0); -} //? + ERROR_LOG(HLE, "UNIMPL sceAtracSetData(%i, %08x, %08x)", atracID, buffer, bufferSize); + return 0; +} -void sceAtracSetDataAndGetID() -{ - ERROR_LOG(HLE, "UNIMPL sceAtracSetDataAndGetID(%08x, %i)", PARAM(0), PARAM(1)); - RETURN(1); +int sceAtracSetDataAndGetID(u32 buffer, u32 bufferSize) +{ + ERROR_LOG(HLE, "UNIMPL sceAtracSetDataAndGetID(%08x, %08x)", buffer, bufferSize); + return 0; } -void sceAtracSetHalfwayBufferAndGetID() +int sceAtracSetHalfwayBufferAndGetID(int atracID, u32 halfBuffer, u32 readSize, u32 halfBufferSize) { - ERROR_LOG(HLE, "UNIMPL sceAtracSetHalfwayBufferAndGetID"); - RETURN(0); + ERROR_LOG(HLE, "UNIMPL sceAtracSetHalfwayBufferAndGetID(%i, %08x, %08x, %08x)", atracID, halfBuffer, readSize, halfBufferSize); + return 0; } -void sceAtracStartEntry() +u32 sceAtracStartEntry() { - ERROR_LOG(HLE, "UNIMPL sceAtracStartEntry"); - RETURN(0); + ERROR_LOG(HLE, "UNIMPL sceAtracStartEntry(.)"); + return 0; } -void sceAtracSetLoopNum() +u32 sceAtracSetLoopNum(int atracID, int loopNum) { - ERROR_LOG(HLE, "UNIMPL sceAtracSetLoopNum(%i, %i)", PARAM(0), PARAM(1)); - RETURN(0); + ERROR_LOG(HLE, "UNIMPL sceAtracSetLoopNum(%i, %i)", atracID, loopNum); + return 0; } int sceAtracReinit() @@ -203,37 +211,57 @@ int sceAtracReinit() return 0; } +int sceAtracGetOutputChannel(int atracID, u32 outputChanPtr) +{ + ERROR_LOG(HLE, "UNIMPL sceAtracGetOutputChannel(%i, %08x)", atracID, outputChanPtr); + if (Memory::IsValidAddress(outputChanPtr)) + Memory::Write_U32(2, outputChanPtr); + return 0; +} + +int sceAtracIsSecondBufferNeeded(int atracID) +{ + ERROR_LOG(HLE, "UNIMPL sceAtracIsSecondBufferNeeded(%i)", atracID); + return 0; +} const HLEFunction sceAtrac3plus[] = { - {0x7db31251,WrapI_IU,"sceAtracAddStreamData"}, - {0x6a8c3cd5,WrapI_IUUUU,"sceAtracDecodeData"}, - {0xd5c28cc0,sceAtracEndEntry,"sceAtracEndEntry"}, - {0x780f88d1,sceAtracGetAtracID,"sceAtracGetAtracID"}, - {0xca3ca3d2,sceAtracGetBufferInfoForReseting,"sceAtracGetBufferInfoForReseting"}, - {0xa554a158,WrapI_IU,"sceAtracGetBitrate"}, - {0x31668baa,sceAtracGetChannel,"sceAtracGetChannel"}, - {0xfaa4f89b,sceAtracGetLoopStatus,"sceAtracGetLoopStatus"}, - {0xe88f759b,sceAtracGetInternalErrorInfo,"sceAtracGetInternalErrorInfo"}, - {0xd6a5f2f7,sceAtracGetMaxSample,"sceAtracGetMaxSample"}, - {0xe23e3a35,WrapI_IU,"sceAtracGetNextDecodePosition"}, - {0x36faabfb,WrapI_IU,"sceAtracGetNextSample"}, - {0x9ae849a7,WrapI_IU,"sceAtracGetRemainFrame"}, - {0x83e85ea0,WrapI_IUU,"sceAtracGetSecondBufferInfo"}, - {0xa2bba8be,WrapI_IUUU,"sceAtracGetSoundSample"}, - {0x5d268707,WrapI_IUUU,"sceAtracGetStreamDataInfo"}, - {0x61eb33f5,sceAtracReleaseAtracID,"sceAtracReleaseAtracID"}, - {0x644e5607,sceAtracResetPlayPosition,"sceAtracResetPlayPosition"}, - {0x3f6e26b5,sceAtracSetHalfwayBuffer,"sceAtracSetHalfwayBuffer"}, - {0x83bf7afd,sceAtracSetSecondBuffer,"sceAtracSetSecondBuffer"}, - {0x0E2A73AB,sceAtracSetData,"sceAtracSetData"}, //? - {0x7a20e7af,sceAtracSetDataAndGetID,"sceAtracSetDataAndGetID"}, - {0xd1f59fdb,sceAtracStartEntry,"sceAtracStartEntry"}, - {0x868120b5,sceAtracSetLoopNum,"sceAtracSetLoopNum"}, + {0x7db31251,WrapU_IU,"sceAtracAddStreamData"}, + {0x6a8c3cd5,WrapU_IUUUU,"sceAtracDecodeData"}, + {0xd5c28cc0,WrapU_V,"sceAtracEndEntry"}, + {0x780f88d1,WrapU_I,"sceAtracGetAtracID"}, + {0xca3ca3d2,WrapU_IIU,"sceAtracGetBufferInfoForReseting"}, + {0xa554a158,WrapU_IU,"sceAtracGetBitrate"}, + {0x31668baa,WrapU_IU,"sceAtracGetChannel"}, + {0xfaa4f89b,WrapU_IUU,"sceAtracGetLoopStatus"}, + {0xe88f759b,WrapU_IU,"sceAtracGetInternalErrorInfo"}, + {0xd6a5f2f7,WrapU_IU,"sceAtracGetMaxSample"}, + {0xe23e3a35,WrapU_IU,"sceAtracGetNextDecodePosition"}, + {0x36faabfb,WrapU_IU,"sceAtracGetNextSample"}, + {0x9ae849a7,WrapU_IU,"sceAtracGetRemainFrame"}, + {0x83e85ea0,WrapU_IUU,"sceAtracGetSecondBufferInfo"}, + {0xa2bba8be,WrapU_IUUU,"sceAtracGetSoundSample"}, + {0x5d268707,WrapU_IUUU,"sceAtracGetStreamDataInfo"}, + {0x61eb33f5,WrapU_I,"sceAtracReleaseAtracID"}, + {0x644e5607,WrapU_IIII,"sceAtracResetPlayPosition"}, + {0x3f6e26b5,WrapU_IUUU,"sceAtracSetHalfwayBuffer"}, + {0x83bf7afd,WrapU_IUU,"sceAtracSetSecondBuffer"}, + {0x0E2A73AB,WrapU_IUU,"sceAtracSetData"}, //? + {0x7a20e7af,WrapI_UU,"sceAtracSetDataAndGetID"}, + {0xd1f59fdb,WrapU_V,"sceAtracStartEntry"}, + {0x868120b5,WrapU_II,"sceAtracSetLoopNum"}, {0x132f1eca,WrapI_V,"sceAtracReinit"}, - {0xeca32a99,0,"sceAtracIsSecondBufferNeeded"}, - {0x0fae370e,sceAtracSetHalfwayBufferAndGetID,"sceAtracSetHalfwayBufferAndGetID"}, - {0x2DD3E298,0,"sceAtrac3plus_2DD3E298"}, + {0xeca32a99,WrapI_I,"sceAtracIsSecondBufferNeeded"}, + {0x0fae370e,WrapI_IUUU,"sceAtracSetHalfwayBufferAndGetID"}, + {0x2DD3E298,0,"sceAtracGetBufferInfoForResetting"}, + {0x5CF9D852,0,"sceAtracSetMOutHalfwayBuffer"}, + {0xB3B5D042,WrapI_IU,"sceAtracGetOutputChannel"}, + {0xF6837A1A,0,"sceAtracSetMOutData"}, + {0x472E3825,0,"sceAtracSetMOutDataAndGetID"}, + {0x9CD7DE03,0,"sceAtracSetMOutHalfwayBufferAndGetID"}, + {0x5622B7C1,0,"sceAtracSetAA3DataAndGetID"}, + {0x5DD66588,0,"sceAtracSetAA3HalfwayBufferAndGetID"}, }; diff --git a/Core/HLE/sceAudio.cpp b/Core/HLE/sceAudio.cpp index bcfcb6300c..0bd420577d 100644 --- a/Core/HLE/sceAudio.cpp +++ b/Core/HLE/sceAudio.cpp @@ -41,7 +41,7 @@ u32 sceAudioOutputBlocking(u32 chan, u32 vol, u32 samplePtr) { ERROR_LOG(HLE, "sceAudioOutputBlocking - Sample pointer null"); return 0; } - if (chan < 0 || chan >= MAX_CHANNEL) { + if (chan >= PSP_AUDIO_CHANNEL_MAX) { ERROR_LOG(HLE,"sceAudioOutputBlocking() - BAD CHANNEL"); return SCE_ERROR_AUDIO_INVALID_CHANNEL; } else if (!chans[chan].reserved) { @@ -60,7 +60,7 @@ u32 sceAudioOutputPannedBlocking(u32 chan, u32 volume1, u32 volume2, u32 sampleP if (samplePtr == 0) { ERROR_LOG(HLE, "sceAudioOutputPannedBlocking - Sample pointer null"); return 0; - } else if (chan < 0 || chan >= MAX_CHANNEL) { + } else if (chan >= PSP_AUDIO_CHANNEL_MAX) { ERROR_LOG(HLE,"sceAudioOutputPannedBlocking() - BAD CHANNEL"); return SCE_ERROR_AUDIO_INVALID_CHANNEL; } else if (!chans[chan].reserved) { @@ -77,7 +77,7 @@ u32 sceAudioOutputPannedBlocking(u32 chan, u32 volume1, u32 volume2, u32 sampleP u32 sceAudioOutput(u32 chan, u32 vol, u32 samplePtr) { - if (chan < 0 || chan >= MAX_CHANNEL) { + if (chan >= PSP_AUDIO_CHANNEL_MAX) { ERROR_LOG(HLE,"sceAudioOutput() - BAD CHANNEL"); return SCE_ERROR_AUDIO_INVALID_CHANNEL; } @@ -99,7 +99,7 @@ u32 sceAudioOutput(u32 chan, u32 vol, u32 samplePtr) u32 sceAudioOutputPanned(u32 chan, u32 leftVol, u32 rightVol, u32 samplePtr) { - if (chan < 0 || chan >= MAX_CHANNEL) + if (chan >= PSP_AUDIO_CHANNEL_MAX) { ERROR_LOG(HLE,"sceAudioOutputPanned() - BAD CHANNEL"); return SCE_ERROR_AUDIO_INVALID_CHANNEL; @@ -122,7 +122,7 @@ u32 sceAudioOutputPanned(u32 chan, u32 leftVol, u32 rightVol, u32 samplePtr) int sceAudioGetChannelRestLen(u32 chan) { - if (chan < 0 || chan >= MAX_CHANNEL) + if (chan >= PSP_AUDIO_CHANNEL_MAX) { ERROR_LOG(HLE, "sceAudioGetChannelRestLen(%i) - BAD CHANNEL", chan); return SCE_ERROR_AUDIO_INVALID_CHANNEL; @@ -135,7 +135,7 @@ int sceAudioGetChannelRestLen(u32 chan) int sceAudioGetChannelRestLength(u32 chan) { - if (chan < 0 || chan >= MAX_CHANNEL) + if (chan >= PSP_AUDIO_CHANNEL_MAX) { ERROR_LOG(HLE, "sceAudioGetChannelRestLength(%i) - BAD CHANNEL", chan); return SCE_ERROR_AUDIO_INVALID_CHANNEL; @@ -148,13 +148,9 @@ int sceAudioGetChannelRestLength(u32 chan) static int GetFreeChannel() { - for (int i = 0; i < MAX_CHANNEL; i++) - { + for (int i = 0; i < PSP_AUDIO_CHANNEL_MAX ; i++) if (!chans[i].reserved) - { return i; - } - } return -1; } @@ -170,7 +166,7 @@ u32 sceAudioChReserve(u32 channel, u32 sampleCount, u32 format) //.Allocate soun return SCE_ERROR_AUDIO_NO_CHANNELS_AVAILABLE; } - if (channel < 0 || channel >= MAX_CHANNEL) + if (channel >= PSP_AUDIO_CHANNEL_MAX) { ERROR_LOG(HLE ,"sceAudioChReserve(channel = %d, sampleCount = %d, format = %d) - BAD CHANNEL", channel, sampleCount, format); return SCE_ERROR_AUDIO_INVALID_CHANNEL; @@ -186,7 +182,7 @@ u32 sceAudioChReserve(u32 channel, u32 sampleCount, u32 format) //.Allocate soun { WARN_LOG(HLE, "WARNING: Reserving already reserved channel. Error?"); } - DEBUG_LOG(HLE, "%i = sceAudioChReserve(%i, %i, %i)", channel, sampleCount, format); + DEBUG_LOG(HLE, "sceAudioChReserve(channel = %d, sampleCount = %d, format = %d)", channel, sampleCount, format); chans[channel].sampleCount = sampleCount; chans[channel].reserved = true; @@ -195,7 +191,7 @@ u32 sceAudioChReserve(u32 channel, u32 sampleCount, u32 format) //.Allocate soun u32 sceAudioChRelease(u32 chan) { - if (chan < 0 || chan >= MAX_CHANNEL) + if (chan >= PSP_AUDIO_CHANNEL_MAX) { ERROR_LOG(HLE, "sceAudioChRelease(%i) - BAD CHANNEL", chan); return SCE_ERROR_AUDIO_INVALID_CHANNEL; @@ -214,7 +210,7 @@ u32 sceAudioChRelease(u32 chan) u32 sceAudioSetChannelDataLen(u32 chan, u32 len) { - if (chan < 0 || chan >= MAX_CHANNEL) + if (chan >= PSP_AUDIO_CHANNEL_MAX) { ERROR_LOG(HLE,"sceAudioSetChannelDataLen(%i, %i) - BAD CHANNEL", chan, len); return SCE_ERROR_AUDIO_INVALID_CHANNEL; @@ -234,7 +230,7 @@ u32 sceAudioSetChannelDataLen(u32 chan, u32 len) u32 sceAudioChangeChannelConfig(u32 chan, u32 format) { - if (chan < 0 || chan >= MAX_CHANNEL) + if (chan >= PSP_AUDIO_CHANNEL_MAX) { ERROR_LOG(HLE,"sceAudioChangeChannelConfig(%i, %i) - invalid channel number", chan, format); return SCE_ERROR_AUDIO_INVALID_CHANNEL; @@ -254,7 +250,7 @@ u32 sceAudioChangeChannelConfig(u32 chan, u32 format) u32 sceAudioChangeChannelVolume(u32 chan, u32 lvolume, u32 rvolume) { - if (chan < 0 || chan >= MAX_CHANNEL) + if (chan >= PSP_AUDIO_CHANNEL_MAX) { ERROR_LOG(HLE,"sceAudioChangeChannelVolume(%i, %i, %i) - invalid channel number", chan, lvolume, rvolume); return SCE_ERROR_AUDIO_INVALID_CHANNEL; @@ -272,7 +268,7 @@ u32 sceAudioChangeChannelVolume(u32 chan, u32 lvolume, u32 rvolume) return 0; } } - + u32 sceAudioInit() { DEBUG_LOG(HLE,"sceAudioInit()"); @@ -288,7 +284,7 @@ u32 sceAudioEnd() u32 sceAudioOutput2Reserve(u32 sampleCount) { - ERROR_LOG(HLE,"sceAudioOutput2Reserve(%i)", sampleCount); + DEBUG_LOG(HLE,"sceAudioOutput2Reserve(%i)", sampleCount); chans[0].sampleCount = sampleCount; chans[0].reserved = true; return 0; @@ -305,20 +301,20 @@ u32 sceAudioOutput2OutputBlocking(u32 vol, u32 dataPtr) u32 sceAudioOutput2ChangeLength(u32 sampleCount) { - WARN_LOG(HLE,"sceAudioOutput2ChangeLength(%i)", sampleCount); + DEBUG_LOG(HLE,"sceAudioOutput2ChangeLength(%i)", sampleCount); chans[0].sampleCount = sampleCount; return 0; } u32 sceAudioOutput2GetRestSample() { - WARN_LOG(HLE,"UNTESTED sceAudioOutput2GetRestSample()"); + DEBUG_LOG(HLE,"UNTESTED sceAudioOutput2GetRestSample()"); return chans[0].sampleQueue.size() * 2; } u32 sceAudioOutput2Release() { - WARN_LOG(HLE,"sceAudioOutput2Release()"); + DEBUG_LOG(HLE,"sceAudioOutput2Release()"); chans[0].reserved = false; return 0; } diff --git a/Core/HLE/sceAudio.h b/Core/HLE/sceAudio.h index 6f1305b0d6..013b818bde 100644 --- a/Core/HLE/sceAudio.h +++ b/Core/HLE/sceAudio.h @@ -36,45 +36,57 @@ enum PspAudioFrequencies { PSP_AUDIO_FREQ_44K = 44100, PSP_AUDIO_FREQ_48K = 48 #define SCE_ERROR_AUDIO_CHANNEL_NOT_RESERVED 0x80260008 #define SCE_ERROR_AUDIO_NOT_OUTPUT 0x80260009 -#define MAX_CHANNEL 8 +#define PSP_AUDIO_CHANNEL_MAX 8 struct AudioChannel { - AudioChannel() { + AudioChannel() { clear(); } - // PSP side + // PSP side - bool reserved; + bool reserved; - // last sample address - u32 sampleAddress; - u32 sampleCount; // Number of samples written in each OutputBlocking + // last sample address + u32 sampleAddress; + u32 sampleCount; // Number of samples written in each OutputBlocking - int leftVolume; - int rightVolume; + int leftVolume; + int rightVolume; - int format; + int format; SceUID waitingThread; - // PC side - should probably split out + // PC side - should probably split out - // We copy samples as they are written into this simple ring buffer. - // Might try something more efficient later. - FixedSizeQueue sampleQueue; + // We copy samples as they are written into this simple ring buffer. + // Might try something more efficient later. + FixedSizeQueue sampleQueue; - void clear() { - reserved = false; + void DoState(PointerWrap &p) { + p.Do(reserved); + p.Do(sampleAddress); + p.Do(sampleCount); + p.Do(leftVolume); + p.Do(rightVolume); + p.Do(format); + p.Do(waitingThread); + sampleQueue.DoState(p); + p.DoMarker("AudioChannel"); + } + + void clear() { + reserved = false; waitingThread = 0; leftVolume = 0; rightVolume = 0; format = 0; sampleAddress = 0; sampleCount = 0; - sampleQueue.clear(); - } + sampleQueue.clear(); + } }; extern AudioChannel chans[8]; diff --git a/Core/HLE/sceCtrl.cpp b/Core/HLE/sceCtrl.cpp index 73e03def9d..234bb3dc5f 100644 --- a/Core/HLE/sceCtrl.cpp +++ b/Core/HLE/sceCtrl.cpp @@ -33,6 +33,7 @@ const int PSP_CTRL_ERROR_INVALID_MODE = 0x80000107; const int PSP_CTRL_ERROR_INVALID_NUM_BUFFERS = 0x80000104; +const int PSP_CTRL_ERROR_INVALID_IDLE_PTR = 0x80000023; const int NUM_CTRL_BUFFERS = 64; @@ -61,7 +62,6 @@ struct CtrlLatch { ////////////////////////////////////////////////////////////////////////// // STATE BEGIN -static bool ctrlInited = false; static bool analogEnabled = false; static int ctrlLatchBufs = 0; static u32 ctrlOldButtons = 0; @@ -72,9 +72,16 @@ static int ctrlBuf = 0; static int ctrlBufRead = 0; static CtrlLatch latch; +static int ctrlIdleReset = -1; +static int ctrlIdleBack = -1; + +static int ctrlCycle = 0; + static std::vector waitingThreads; static std::recursive_mutex ctrlMutex; +static int ctrlTimer = -1; + // STATE END ////////////////////////////////////////////////////////////////////////// @@ -125,6 +132,13 @@ u32 __CtrlPeekButtons() return ctrlCurrent.buttons; } +u32 __CtrlReadLatch() +{ + u32 ret = latch.btnMake; + __CtrlResetLatch(); + return ret; +} + // Functions so that the rest of the emulator can control what the sceCtrl interface should return // to the game: @@ -202,9 +216,9 @@ int __CtrlReadBuffer(u32 ctrlDataPtr, u32 nBufs, bool negative, bool peek) return done; } -void __CtrlVblank() +void __CtrlDoSample() { - // When in vblank sampling mode, this samples the ctrl data into the buffers and updates the latch. + // This samples the ctrl data into the buffers and updates the latch. __CtrlUpdateLatch(); // Wake up a single thread that was waiting for the buffer. @@ -226,15 +240,32 @@ retry: } } +void __CtrlVblank() +{ + // This always runs, so make sure we're in vblank mode. + if (ctrlCycle == 0) + __CtrlDoSample(); +} + +void __CtrlTimerUpdate(u64 userdata, int cyclesLate) +{ + // This only runs in timer mode (ctrlCycle > 0.) + _dbg_assert_msg_(HLE, ctrlCycle > 0, "Ctrl: sampling cycle should be > 0"); + + __CtrlDoSample(); + CoreTiming::ScheduleEvent(usToCycles(ctrlCycle), ctrlTimer, 0); +} + void __CtrlInit() { - std::lock_guard guard(ctrlMutex); + ctrlTimer = CoreTiming::RegisterEvent("CtrlSampleTimer", __CtrlTimerUpdate); + __DisplayListenVblank(__CtrlVblank); - if (!ctrlInited) - { - __DisplayListenVblank(__CtrlVblank); - ctrlInited = true; - } + ctrlIdleReset = -1; + ctrlIdleBack = -1; + ctrlCycle = 0; + + std::lock_guard guard(ctrlMutex); ctrlBuf = 1; ctrlBufRead = 0; @@ -253,31 +284,64 @@ void __CtrlInit() memcpy(&ctrlBufs[i], &ctrlCurrent, sizeof(_ctrl_data)); } -void sceCtrlInit() +void __CtrlDoState(PointerWrap &p) { - __CtrlInit(); + std::lock_guard guard(ctrlMutex); - DEBUG_LOG(HLE,"sceCtrlInit"); - RETURN(0); + p.Do(analogEnabled); + p.Do(ctrlLatchBufs); + p.Do(ctrlOldButtons); + + p.DoVoid(ctrlBufs, sizeof(ctrlBufs)); + p.Do(ctrlCurrent); + p.Do(ctrlBuf); + p.Do(ctrlBufRead); + p.Do(latch); + + p.Do(ctrlIdleReset); + p.Do(ctrlIdleBack); + + p.Do(ctrlCycle); + + SceUID dv = 0; + p.Do(waitingThreads, dv); + + p.Do(ctrlTimer); + CoreTiming::RestoreRegisterEvent(ctrlTimer, "CtrlSampleTimer", __CtrlTimerUpdate); + p.DoMarker("sceCtrl"); +} + +void __CtrlShutdown() +{ + waitingThreads.clear(); } u32 sceCtrlSetSamplingCycle(u32 cycle) { - if (cycle == 0) + DEBUG_LOG(HLE, "sceCtrlSetSamplingCycle(%u)", cycle); + + if ((cycle > 0 && cycle < 5555) || cycle > 20000) { - // TODO: Change to vblank when we support something else. - DEBUG_LOG(HLE, "sceCtrlSetSamplingCycle(%u)", cycle); + WARN_LOG(HLE, "SCE_KERNEL_ERROR_INVALID_VALUE=sceCtrlSetSamplingCycle(%u)", cycle); + return SCE_KERNEL_ERROR_INVALID_VALUE; } - else - { - ERROR_LOG(HLE, "UNIMPL sceCtrlSetSamplingCycle(%u)", cycle); - } - return 0; + + u32 prev = ctrlCycle; + ctrlCycle = cycle; + + if (prev > 0) + CoreTiming::UnscheduleEvent(ctrlTimer, 0); + if (cycle > 0) + CoreTiming::ScheduleEvent(usToCycles(ctrlCycle), ctrlTimer, 0); + + return prev; } int sceCtrlGetSamplingCycle(u32 cyclePtr) { - ERROR_LOG(HLE, "UNIMPL sceCtrlSetSamplingCycle(%08x)", cyclePtr); + DEBUG_LOG(HLE, "sceCtrlSetSamplingCycle(%08x)", cyclePtr); + if (Memory::IsValidAddress(cyclePtr)) + Memory::Write_U32(ctrlCycle, cyclePtr); return 0; } @@ -297,6 +361,7 @@ u32 sceCtrlSetSamplingMode(u32 mode) int sceCtrlGetSamplingMode(u32 modePtr) { u32 retVal = analogEnabled == true ? CTRL_MODE_ANALOG : CTRL_MODE_DIGITAL; + DEBUG_LOG(HLE, "%d=sceCtrlGetSamplingMode(%i)", retVal); if (Memory::IsValidAddress(modePtr)) Memory::Write_U32(retVal, modePtr); @@ -304,10 +369,33 @@ int sceCtrlGetSamplingMode(u32 modePtr) return 0; } -void sceCtrlSetIdleCancelThreshold() +int sceCtrlSetIdleCancelThreshold(int idleReset, int idleBack) { - ERROR_LOG(HLE,"UNIMPL sceCtrlSetIdleCancelThreshold"); - RETURN(0); + DEBUG_LOG(HLE, "FAKE sceCtrlSetIdleCancelThreshold(%d, %d)", idleReset, idleBack); + + if (idleReset < -1 || idleBack < -1 || idleReset > 128 || idleBack > 128) + return SCE_KERNEL_ERROR_INVALID_VALUE; + + ctrlIdleReset = idleReset; + ctrlIdleBack = idleBack; + return 0; +} + +int sceCtrlGetIdleCancelThreshold(u32 idleResetPtr, u32 idleBackPtr) +{ + DEBUG_LOG(HLE, "sceCtrlSetIdleCancelThreshold(%08x, %08x)", idleResetPtr, idleBackPtr); + + if (idleResetPtr && !Memory::IsValidAddress(idleResetPtr)) + return PSP_CTRL_ERROR_INVALID_IDLE_PTR; + if (idleBackPtr && !Memory::IsValidAddress(idleBackPtr)) + return PSP_CTRL_ERROR_INVALID_IDLE_PTR; + + if (idleResetPtr) + Memory::Write_U32(ctrlIdleReset, idleResetPtr); + if (idleBackPtr) + Memory::Write_U32(ctrlIdleBack, idleBackPtr); + + return 0; } void sceCtrlReadBufferPositive(u32 ctrlDataPtr, u32 nBufs) @@ -378,7 +466,7 @@ u32 sceCtrlReadLatch(u32 latchDataPtr) static const HLEFunction sceCtrl[] = { - {0x3E65A0EA, WrapV_V, "sceCtrlInit"}, //(int unknown), init with 0 + {0x3E65A0EA, 0, "sceCtrlInit"}, //(int unknown), init with 0 {0x1f4011e6, WrapU_U, "sceCtrlSetSamplingMode"}, //(int on); {0x6A2774F3, WrapU_U, "sceCtrlSetSamplingCycle"}, {0x02BAAD91, WrapI_U,"sceCtrlGetSamplingCycle"}, @@ -393,8 +481,8 @@ static const HLEFunction sceCtrl[] = {0xAF5960F3, 0, "sceCtrl_AF5960F3"}, {0xA68FD260, 0, "sceCtrlClearRapidFire"}, {0x6841BE1A, 0, "sceCtrlSetRapidFire"}, - {0xa7144800, WrapV_V, "sceCtrlSetIdleCancelThreshold"}, - {0x687660fa, 0, "sceCtrlGetIdleCancelThreshold"}, + {0xa7144800, WrapI_II, "sceCtrlSetIdleCancelThreshold"}, + {0x687660fa, WrapI_UU, "sceCtrlGetIdleCancelThreshold"}, }; void Register_sceCtrl() diff --git a/Core/HLE/sceCtrl.h b/Core/HLE/sceCtrl.h index 2f658d73c0..5f9b4e3e05 100644 --- a/Core/HLE/sceCtrl.h +++ b/Core/HLE/sceCtrl.h @@ -17,6 +17,8 @@ #pragma once +#include "../../Common/ChunkFile.h" + void Register_sceCtrl(); #define CTRL_SQUARE 0x8000 @@ -33,6 +35,8 @@ void Register_sceCtrl(); #define CTRL_RTRIGGER 0x0200 void __CtrlInit(); +void __CtrlDoState(PointerWrap &p); +void __CtrlShutdown(); void __CtrlButtonDown(u32 buttonBit); void __CtrlButtonUp(u32 buttonBit); @@ -40,4 +44,5 @@ void __CtrlButtonUp(u32 buttonBit); void __CtrlSetAnalog(float x, float y); // For use by internal UI like MsgDialog -u32 __CtrlPeekButtons(); \ No newline at end of file +u32 __CtrlPeekButtons(); +u32 __CtrlReadLatch(); \ No newline at end of file diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index df34fda23c..ddd793b43b 100644 --- a/Core/HLE/sceDisplay.cpp +++ b/Core/HLE/sceDisplay.cpp @@ -46,15 +46,19 @@ // Internal drawing library #include "../Util/PPGeDraw.h" -extern ShaderManager shaderManager; - -struct FrameBufferState -{ +struct FrameBufferState { u32 topaddr; PspDisplayPixelFormat pspFramebufFormat; int pspFramebufLinesize; }; +struct WaitVBlankInfo +{ + WaitVBlankInfo(u32 tid) : threadID(tid), vcountUnblock(0) {} + u32 threadID; + int vcountUnblock; // what was this for again? +}; + // STATE BEGIN static FrameBufferState framebuf; static FrameBufferState latchedFramebuf; @@ -63,15 +67,18 @@ static bool framebufIsLatched; static int enterVblankEvent = -1; static int leaveVblankEvent = -1; -static int hCount = 0; -static int hCountTotal = 0; //unused -static int vCount = 0; -static int isVblank = 0; -static bool hasSetMode = false; -double lastFrameTime = 0; +static int hCount; +static int hCountTotal; //unused +static int vCount; +static int isVblank; +static bool hasSetMode; +double lastFrameTime; + +std::vector vblankWaitingThreads; // STATE END +// Called when vblank happens (like an internal interrupt.) Not part of state, should be static. std::vector vblankListeners; // The vblank period is 731.5 us (0.7315 ms) @@ -83,20 +90,10 @@ enum { PSP_DISPLAY_SETBUF_NEXTFRAME = 1 }; -struct WaitVBlankInfo -{ - WaitVBlankInfo(u32 tid) : threadID(tid), vcountUnblock(0) {} - u32 threadID; - int vcountUnblock; // what was this for again? -}; - -std::vector vblankWaitingThreads; - void hleEnterVblank(u64 userdata, int cyclesLate); void hleLeaveVblank(u64 userdata, int cyclesLate); -void __DisplayInit() -{ +void __DisplayInit() { gpuStats.reset(); hasSetMode = false; framebufIsLatched = false; @@ -110,31 +107,53 @@ void __DisplayInit() CoreTiming::ScheduleEvent(msToCycles(frameMs - vblankMs), enterVblankEvent, 0); isVblank = 0; vCount = 0; + hCount = 0; + hCountTotal = 0; + hasSetMode = false; + lastFrameTime = 0; InitGfxState(); } -void __DisplayShutdown() -{ +void __DisplayDoState(PointerWrap &p) { + p.Do(framebuf); + p.Do(latchedFramebuf); + p.Do(framebufIsLatched); + p.Do(hCount); + p.Do(hCountTotal); + p.Do(vCount); + p.Do(isVblank); + p.Do(hasSetMode); + p.Do(lastFrameTime); + WaitVBlankInfo wvi(0); + p.Do(vblankWaitingThreads, wvi); + + p.Do(enterVblankEvent); + CoreTiming::RestoreRegisterEvent(enterVblankEvent, "EnterVBlank", &hleEnterVblank); + p.Do(leaveVblankEvent); + CoreTiming::RestoreRegisterEvent(leaveVblankEvent, "LeaveVBlank", &hleLeaveVblank); + + p.DoMarker("sceDisplay"); +} + +void __DisplayShutdown() { + vblankListeners.clear(); + vblankWaitingThreads.clear(); ShutdownGfxState(); } -void __DisplayListenVblank(VblankCallback callback) -{ +void __DisplayListenVblank(VblankCallback callback) { vblankListeners.push_back(callback); } -void __DisplayFireVblank() -{ - for (std::vector::iterator iter = vblankListeners.begin(), end = vblankListeners.end(); iter != end; ++iter) - { +void __DisplayFireVblank() { + for (std::vector::iterator iter = vblankListeners.begin(), end = vblankListeners.end(); iter != end; ++iter) { VblankCallback cb = *iter; cb(); } } -void hleEnterVblank(u64 userdata, int cyclesLate) -{ +void hleEnterVblank(u64 userdata, int cyclesLate) { int vbCount = userdata; DEBUG_LOG(HLE, "Enter VBlank %i", vbCount); @@ -145,19 +164,18 @@ void hleEnterVblank(u64 userdata, int cyclesLate) __DisplayFireVblank(); // Wake up threads waiting for VBlank - for (int i = 0; i < vblankWaitingThreads.size(); i++) { + for (size_t i = 0; i < vblankWaitingThreads.size(); i++) { __KernelResumeThreadFromWait(vblankWaitingThreads[i].threadID, 0); } vblankWaitingThreads.clear(); // Trigger VBlank interrupt handlers. - __TriggerInterrupt(PSP_VBLANK_INTR); + __TriggerInterrupt(PSP_INTR_IMMEDIATE | PSP_INTR_ONLY_IF_ENABLED, PSP_VBLANK_INTR); CoreTiming::ScheduleEvent(msToCycles(vblankMs) - cyclesLate, leaveVblankEvent, vbCount+1); // TODO: Should this be done here or in hleLeaveVblank? - if (framebufIsLatched) - { + if (framebufIsLatched) { DEBUG_LOG(HLE, "Setting latched framebuffer %08x (prev: %08x)", latchedFramebuf.topaddr, framebuf.topaddr); framebuf = latchedFramebuf; framebufIsLatched = false; @@ -175,33 +193,35 @@ void hleEnterVblank(u64 userdata, int cyclesLate) // Now we can subvert the Ge engine in order to draw custom overlays like stat counters etc. // Here we will be drawing to the non buffered front surface. - if (g_Config.bShowDebugStats) - { + if (g_Config.bShowDebugStats && gpuStats.numDrawCalls) { gpu->UpdateStats(); char stats[512]; sprintf(stats, "Frames: %i\n" "Draw calls: %i\n" + "Draw flushes: %i\n" "Vertices Transformed: %i\n" "Textures active: %i\n" + "Textures decoded: %i\n" "Vertex shaders loaded: %i\n" "Fragment shaders loaded: %i\n" "Combined shaders loaded: %i\n", gpuStats.numFrames, gpuStats.numDrawCalls, + gpuStats.numFlushes, gpuStats.numVertsTransformed, gpuStats.numTextures, + gpuStats.numTexturesDecoded, gpuStats.numVertexShaders, gpuStats.numFragmentShaders, gpuStats.numShaders ); - - float zoom = 0.7f * sqrtf(g_Config.iWindowZoom); + + float zoom = 0.7f; /// g_Config.iWindowZoom; PPGeBegin(); - PPGeDrawText(stats, 2, 2, 0, zoom, 0x90000000); - PPGeDrawText(stats, 0, 0, 0, zoom); + PPGeDrawText(stats, 0, 0, 0, zoom, 0xFFc0c0c0); PPGeEnd(); - + gpuStats.resetFrame(); } @@ -214,20 +234,23 @@ void hleEnterVblank(u64 userdata, int cyclesLate) if (lastFrameTime == 0.0) lastFrameTime = time_now_d(); if (!GetAsyncKeyState(VK_TAB)) { - while (time_now_d() < lastFrameTime + 1.0 / 60.0f) { + while (time_now_d() < lastFrameTime + 1.0 / 60.0) { Common::SleepCurrentThread(1); time_update(); } - lastFrameTime = time_now_d(); + // Advance lastFrameTime by a constant amount each frame, + // but don't let it get too far behind. + lastFrameTime = std::max(lastFrameTime + 1.0 / 60.0, time_now_d() - 1.5 / 60.0); } + + // We are going to have to do something about audio timing for platforms that + // are vsynced to something that's not exactly 60fps.. + #endif host->BeginFrame(); gpu->BeginFrame(); - shaderManager.DirtyShader(); - shaderManager.DirtyUniform(DIRTY_ALL); - // Tell the emu core that it's time to stop emulating // Win32 doesn't need this. #ifndef _WIN32 @@ -235,9 +258,7 @@ void hleEnterVblank(u64 userdata, int cyclesLate) #endif } - -void hleLeaveVblank(u64 userdata, int cyclesLate) -{ +void hleLeaveVblank(u64 userdata, int cyclesLate) { isVblank = 0; DEBUG_LOG(HLE,"Leave VBlank %i", (int)userdata - 1); vCount++; @@ -245,19 +266,16 @@ void hleLeaveVblank(u64 userdata, int cyclesLate) CoreTiming::ScheduleEvent(msToCycles(frameMs - vblankMs) - cyclesLate, enterVblankEvent, userdata); } -void sceDisplayIsVblank() -{ +void sceDisplayIsVblank() { DEBUG_LOG(HLE,"%i=sceDisplayIsVblank()",isVblank); RETURN(isVblank); } -u32 sceDisplaySetMode(u32 unknown, u32 xres, u32 yres) -{ +u32 sceDisplaySetMode(u32 unknown, u32 xres, u32 yres) { DEBUG_LOG(HLE,"sceDisplaySetMode(%d,%d,%d)",unknown,xres,yres); host->BeginFrame(); - if (!hasSetMode) - { + if (!hasSetMode) { gpu->InitClear(); hasSetMode = true; } @@ -265,9 +283,7 @@ u32 sceDisplaySetMode(u32 unknown, u32 xres, u32 yres) return 0; } -u32 sceDisplaySetFramebuf() -{ - //host->EndFrame(); +u32 sceDisplaySetFramebuf() { u32 topaddr = PARAM(0); int linesize = PARAM(1); int pixelformat = PARAM(2); @@ -275,25 +291,24 @@ u32 sceDisplaySetFramebuf() FrameBufferState fbstate; DEBUG_LOG(HLE,"sceDisplaySetFramebuf(topaddr=%08x,linesize=%d,pixelsize=%d,sync=%d)",topaddr,linesize,pixelformat,sync); - if (topaddr == 0) - { + if (topaddr == 0) { DEBUG_LOG(HLE,"- screen off"); - } - else - { + } else { fbstate.topaddr = topaddr; fbstate.pspFramebufFormat = (PspDisplayPixelFormat)pixelformat; fbstate.pspFramebufLinesize = linesize; } - if (sync == PSP_DISPLAY_SETBUF_IMMEDIATE) - { + if (sync == PSP_DISPLAY_SETBUF_IMMEDIATE) { // Write immediately to the current framebuffer parameters - framebuf = fbstate; - gpu->SetDisplayFramebuffer(framebuf.topaddr, framebuf.pspFramebufLinesize, framebuf.pspFramebufFormat); - } - else if (topaddr != 0) - { + if (topaddr != 0) + { + framebuf = fbstate; + gpu->SetDisplayFramebuffer(framebuf.topaddr, framebuf.pspFramebufLinesize, framebuf.pspFramebufFormat); + } + else + WARN_LOG(HLE, "%s: PSP_DISPLAY_SETBUF_IMMEDIATE without topaddr?", __FUNCTION__); + } else if (topaddr != 0) { // Delay the write until vblank latchedFramebuf = fbstate; framebufIsLatched = true; @@ -301,12 +316,11 @@ u32 sceDisplaySetFramebuf() return 0; } -u32 sceDisplayGetFramebuf(u32 topaddrPtr, u32 linesizePtr, u32 pixelFormatPtr, int mode) -{ +u32 sceDisplayGetFramebuf(u32 topaddrPtr, u32 linesizePtr, u32 pixelFormatPtr, int mode) { const FrameBufferState &fbState = mode == 1 ? latchedFramebuf : framebuf; DEBUG_LOG(HLE,"sceDisplayGetFramebuf(*%08x = %08x, *%08x = %08x, *%08x = %08x, %i)", topaddrPtr, fbState.topaddr, linesizePtr, fbState.pspFramebufLinesize, pixelFormatPtr, fbState.pspFramebufFormat, mode); - + if (Memory::IsValidAddress(topaddrPtr)) Memory::Write_U32(fbState.topaddr, topaddrPtr); if (Memory::IsValidAddress(linesizePtr)) @@ -317,85 +331,73 @@ u32 sceDisplayGetFramebuf(u32 topaddrPtr, u32 linesizePtr, u32 pixelFormatPtr, i return 0; } -void sceDisplayWaitVblankStart() -{ +void sceDisplayWaitVblankStart() { DEBUG_LOG(HLE,"sceDisplayWaitVblankStart()"); vblankWaitingThreads.push_back(WaitVBlankInfo(__KernelGetCurThread())); __KernelWaitCurThread(WAITTYPE_VBLANK, 0, 0, 0, false); } -void sceDisplayWaitVblank() -{ +void sceDisplayWaitVblank() { DEBUG_LOG(HLE,"sceDisplayWaitVblank()"); vblankWaitingThreads.push_back(WaitVBlankInfo(__KernelGetCurThread())); __KernelWaitCurThread(WAITTYPE_VBLANK, 0, 0, 0, false); } -void sceDisplayWaitVblankStartMulti() -{ +void sceDisplayWaitVblankStartMulti() { DEBUG_LOG(HLE,"sceDisplayWaitVblankStartMulti()"); vblankWaitingThreads.push_back(WaitVBlankInfo(__KernelGetCurThread())); __KernelWaitCurThread(WAITTYPE_VBLANK, 0, 0, 0, false); } -void sceDisplayWaitVblankCB() -{ - DEBUG_LOG(HLE,"sceDisplayWaitVblankCB()"); +void sceDisplayWaitVblankCB() { + DEBUG_LOG(HLE,"sceDisplayWaitVblankCB()"); vblankWaitingThreads.push_back(WaitVBlankInfo(__KernelGetCurThread())); __KernelWaitCurThread(WAITTYPE_VBLANK, 0, 0, 0, true); } -void sceDisplayWaitVblankStartCB() -{ - DEBUG_LOG(HLE,"sceDisplayWaitVblankStartCB()"); +void sceDisplayWaitVblankStartCB() { + DEBUG_LOG(HLE,"sceDisplayWaitVblankStartCB()"); vblankWaitingThreads.push_back(WaitVBlankInfo(__KernelGetCurThread())); __KernelWaitCurThread(WAITTYPE_VBLANK, 0, 0, 0, true); } -void sceDisplayWaitVblankStartMultiCB() -{ - DEBUG_LOG(HLE,"sceDisplayWaitVblankStartMultiCB()"); +void sceDisplayWaitVblankStartMultiCB() { + DEBUG_LOG(HLE,"sceDisplayWaitVblankStartMultiCB()"); vblankWaitingThreads.push_back(WaitVBlankInfo(__KernelGetCurThread())); __KernelWaitCurThread(WAITTYPE_VBLANK, 0, 0, 0, true); } -u32 sceDisplayGetVcount() -{ +u32 sceDisplayGetVcount() { // Too spammy - // DEBUG_LOG(HLE,"%i=sceDisplayGetVcount()", vCount); + // DEBUG_LOG(HLE,"%i=sceDisplayGetVcount()", vCount); - // Puyo Puyo Fever polls this as a substitute for waiting vblank. + // Puyo Puyo Fever polls this as a substitute for waiting for vblank. // As a result, the game never gets to reschedule so it doesn't mix audio and things break. - // I added this as a workaround until we figure out what call actually does reschedule - it doesn't call much though... + // Need to find a better hack as this breaks games like Project Diva. + // hleReSchedule("sceDisplayGetVcount hack"); // Puyo puyo hack? CoreTiming::Idle(1000000); - __KernelReSchedule(); // Puyo puyo hack? return vCount; } -void sceDisplayGetCurrentHcount() -{ +void sceDisplayGetCurrentHcount() { RETURN(hCount++); } - -void sceDisplayGetAccumulatedHcount() -{ +void sceDisplayGetAccumulatedHcount() { // Just do an estimate u32 accumHCount = CoreTiming::GetTicks() / (222000000 / 60 / 272); - DEBUG_LOG(HLE,"%i=sceDisplayGetAccumulatedHcount()", accumHCount); + DEBUG_LOG(HLE,"%i=sceDisplayGetAccumulatedHcount()", accumHCount); RETURN(accumHCount); } -float sceDisplayGetFramePerSec() -{ +float sceDisplayGetFramePerSec() { float fps = 59.9400599f; DEBUG_LOG(HLE,"%f=sceDisplayGetFramePerSec()", fps); - return fps; // (9MHz * 1)/(525 * 286) + return fps; // (9MHz * 1)/(525 * 286) } -const HLEFunction sceDisplay[] = -{ +const HLEFunction sceDisplay[] = { {0x0E20F177,WrapU_UUU, "sceDisplaySetMode"}, {0x289D82FE,WrapU_V, "sceDisplaySetFramebuf"}, {0xEEDA2E54,WrapU_UUUI,"sceDisplayGetFrameBuf"}, @@ -415,10 +417,8 @@ const HLEFunction sceDisplay[] = {0xB4F378FA,0,"sceDisplayIsForeground"}, {0x31C4BAA8,0,"sceDisplayGetBrightness"}, {0x4D4E10EC,sceDisplayIsVblank,"sceDisplayIsVblank"}, - }; -void Register_sceDisplay() -{ +void Register_sceDisplay() { RegisterModule("sceDisplay", ARRAY_SIZE(sceDisplay), sceDisplay); } diff --git a/Core/HLE/sceDisplay.h b/Core/HLE/sceDisplay.h index 003b3164b9..c75faf93f7 100644 --- a/Core/HLE/sceDisplay.h +++ b/Core/HLE/sceDisplay.h @@ -18,6 +18,8 @@ #pragma once void __DisplayInit(); +void __DisplayDoState(PointerWrap &p); +void __DisplayShutdown(); void Register_sceDisplay(); @@ -25,4 +27,5 @@ void Register_sceDisplay(); bool __DisplayFrameDone(); typedef void (*VblankCallback)(); +// Listen for vblank events. Only register during init. void __DisplayListenVblank(VblankCallback callback); diff --git a/Core/HLE/sceFont.cpp b/Core/HLE/sceFont.cpp index dc1e6ffa82..8c026e1913 100644 --- a/Core/HLE/sceFont.cpp +++ b/Core/HLE/sceFont.cpp @@ -12,19 +12,19 @@ typedef u32 FontLibraryHandle; typedef u32 FontHandle; typedef struct { - u32* userDataAddr; - u32 numFonts; - u32* cacheDataAddr; + u32 userDataAddr; + u32 numFonts; + u32 cacheDataAddr; // Driver callbacks. - void *(*allocFuncAddr)(void *, u32); - void (*freeFuncAddr )(void *, void *); - u32* openFuncAddr; - u32* closeFuncAddr; - u32* readFuncAddr; - u32* seekFuncAddr; - u32* errorFuncAddr; - u32* ioFinishFuncAddr; + u32 allocFuncAddr; + u32 freeFuncAddr; + u32 openFuncAddr; + u32 closeFuncAddr; + u32 readFuncAddr; + u32 seekFuncAddr; + u32 errorFuncAddr; + u32 ioFinishFuncAddr; } FontNewLibParams; typedef enum { @@ -138,11 +138,22 @@ typedef struct { u16 bufHeight; u16 bytesPerLine; u16 pad; - void *buffer; + u32 bufferPtr; } GlyphImage; FontNewLibParams fontLib; +void __FontInit() +{ + memset(&fontLib, 0, sizeof(fontLib)); +} + +void __FontDoState(PointerWrap &p) +{ + p.Do(fontLib); + p.DoMarker("sceFont"); +} + u32 sceFontNewLib(u32 FontNewLibParamsPtr, u32 errorCodePtr) { ERROR_LOG(HLE, "sceFontNewLib %x, %x", FontNewLibParamsPtr, errorCodePtr); @@ -235,32 +246,31 @@ int sceFontGetFontInfo(u32 fontHandle, u32 fontInfoPtr) memset (&fi, 0, sizeof(fi)); if (Memory::IsValidAddress(fontInfoPtr)) { - fi.BPP =4; + fi.BPP = 4; fi.charMapLength = 255; - // fi.fontStyle =1; fi.maxGlyphAdvanceXF = 2.0; - fi.maxGlyphAdvanceXI =2; + fi.maxGlyphAdvanceXI = 2; fi.maxGlyphAdvanceYF = 2.0; fi.maxGlyphAdvanceYI = 32 << 6; - fi.maxGlyphAscenderF =32 << 6; + fi.maxGlyphAscenderF = 32 << 6; fi.maxGlyphAscenderI = 32 << 6; - fi.maxGlyphBaseYF= 0.0; - fi.maxGlyphBaseYI=0.0; - fi.maxGlyphDescenderF =0; - fi.maxGlyphDescenderI =0; + fi.maxGlyphBaseYF = 0.0; + fi.maxGlyphBaseYI = 0; + fi.maxGlyphDescenderF = 0; + fi.maxGlyphDescenderI = 0; fi.maxGlyphHeight = 32; - fi.maxGlyphHeightF= 32; + fi.maxGlyphHeightF = 32; fi.maxGlyphHeightI = 32; - fi.maxGlyphLeftXF= 0; + fi.maxGlyphLeftXF = 0; fi.maxGlyphLeftXI = 0; - fi.maxGlyphTopYF =0; + fi.maxGlyphTopYF = 0; fi.maxGlyphTopYI = 0; - fi.maxGlyphWidth =32; + fi.maxGlyphWidth = 32; fi.maxGlyphWidthF = 32; - fi.maxGlyphWidthI= 32; + fi.maxGlyphWidthI = 32; fi.minGlyphCenterXF = 16; - fi.minGlyphCenterXI= 16; - fi.shadowMapLength=0; + fi.minGlyphCenterXI = 16; + fi.shadowMapLength = 0; Memory::WriteStruct(fontInfoPtr, &fi); } @@ -318,6 +328,25 @@ int sceFontGetCharGlyphImage_Clip(u32 libHandler, u32 charCode, u32 glyphImagePt return 0; } +int sceFontGetFontList(u32 fontLibHandle, u32 fontStylePtr, u32 numFonts) +{ + ERROR_LOG(HLE, "sceFontGetFontList %x, %x, %x", fontLibHandle, fontStylePtr, numFonts); + + FontStyle style; + memset(&style, 0, sizeof (style)); + + style.fontH = 20 / 64.f; + style.fontV = 20 / 64.f; + style.fontHRes = 20 / 64.f; + style.fontVRes = 20 / 64.f; + style.fontStyle = 1; + + for (u32 i = 0; i < numFonts; i++) + { + Memory::WriteStruct(fontStylePtr+ (sizeof(style)), &style); + } + return 0; +} const HLEFunction sceLibFont[] = { @@ -325,7 +354,7 @@ const HLEFunction sceLibFont[] = {0x574b6fbc, WrapI_U, "sceFontDoneLib"}, {0x48293280, 0, "sceFontSetResolution"}, {0x27f6e642, WrapI_UU, "sceFontGetNumFontList"}, - {0xbc75d85b, 0, "sceFontGetFontList"}, + {0xbc75d85b, WrapI_UUU, "sceFontGetFontList"}, {0x099ef33c, WrapI_UUU, "sceFontFindOptimumFont"}, {0x681e61a7, WrapI_UUU, "sceFontFindFont"}, {0x2f67356a, 0, "sceFontCalcMemorySize"}, diff --git a/Core/HLE/sceFont.h b/Core/HLE/sceFont.h index d5ff49a150..9ca1bcd6d6 100644 --- a/Core/HLE/sceFont.h +++ b/Core/HLE/sceFont.h @@ -1,3 +1,8 @@ #pragma once +#include "../../Common/ChunkFile.h" + void Register_sceFont(); + +void __FontInit(); +void __FontDoState(PointerWrap &p); diff --git a/Core/HLE/sceGe.cpp b/Core/HLE/sceGe.cpp index dd41e6e1db..023ac3aaa0 100644 --- a/Core/HLE/sceGe.cpp +++ b/Core/HLE/sceGe.cpp @@ -33,6 +33,17 @@ void __GeInit() state = 0; } +void __GeDoState(PointerWrap &p) +{ + p.Do(state); + p.Do(gstate); + p.Do(gstate_c); + + ReapplyGfxState(); + gpu->InvalidateCache(0, -1); + p.DoMarker("sceGe"); +} + void __GeShutdown() { @@ -166,7 +177,7 @@ void sceGeUnsetCallback(u32 cbID) { u32 sceGeSaveContext(u32 ctxAddr) { DEBUG_LOG(HLE, "sceGeSaveContext(%08x)", ctxAddr); - + gpu->Flush(); if (sizeof(gstate) > 512 * 4) { ERROR_LOG(HLE, "AARGH! sizeof(gstate) has grown too large!"); @@ -187,6 +198,7 @@ u32 sceGeSaveContext(u32 ctxAddr) u32 sceGeRestoreContext(u32 ctxAddr) { DEBUG_LOG(HLE, "sceGeRestoreContext(%08x)", ctxAddr); + gpu->Flush(); if (sizeof(gstate) > 512 * 4) { @@ -225,12 +237,12 @@ const HLEFunction sceGe_user[] = {0xE0D68148,&WrapV_UU, "sceGeListUpdateStallAddr"}, {0x03444EB4,&WrapI_UU, "sceGeListSync"}, {0xB287BD61,&WrapU_U, "sceGeDrawSync"}, - {0xB448EC0D,&WrapV_U, "sceGeBreak"}, + {0xB448EC0D,&WrapV_U, "sceGeBreak"}, {0x4C06E472,sceGeContinue, "sceGeContinue"}, {0xA4FC06A4,&WrapU_U, "sceGeSetCallback"}, {0x05DB22CE,&WrapV_U, "sceGeUnsetCallback"}, {0x1F6752AD,&WrapU_V, "sceGeEdramGetSize"}, - {0xB77905EA,&WrapU_I,"sceGeEdramSetAddrTranslation"}, + {0xB77905EA,&WrapU_I,"sceGeEdramSetAddrTranslation"}, {0xDC93CFEF,0,"sceGeGetCmd"}, {0x57C8945B,&sceGeGetMtx,"sceGeGetMtx"}, {0x438A385A,&WrapU_U,"sceGeSaveContext"}, diff --git a/Core/HLE/sceGe.h b/Core/HLE/sceGe.h index 7b92c863b8..d6a19a0767 100644 --- a/Core/HLE/sceGe.h +++ b/Core/HLE/sceGe.h @@ -37,6 +37,7 @@ typedef struct PspGeCallbackData void Register_sceGe_user(); void __GeInit(); +void __GeDoState(PointerWrap &p); void __GeShutdown(); diff --git a/Core/HLE/sceImpose.cpp b/Core/HLE/sceImpose.cpp index 93a417a088..2f201c0bcf 100644 --- a/Core/HLE/sceImpose.cpp +++ b/Core/HLE/sceImpose.cpp @@ -33,9 +33,24 @@ const int PSP_LANGUAGE_KOREAN = 9; const int PSP_LANGUAGE_TRADITIONAL_CHINESE = 10; const int PSP_LANGUAGE_SIMPLIFIED_CHINESE = 11; -static u32 iLanguage = PSP_LANGUAGE_ENGLISH; -static u32 iButtonValue = 0; +static u32 language = PSP_LANGUAGE_ENGLISH; +static u32 buttonValue = 0; +static u32 umdPopup = 0; +void __ImposeInit() +{ + language = PSP_LANGUAGE_ENGLISH; + buttonValue = 0; + umdPopup = 0; +} + +void __ImposeDoState(PointerWrap &p) +{ + p.Do(language); + p.Do(buttonValue); + p.Do(umdPopup); + p.DoMarker("sceImpose"); +} u32 sceImposeGetBatteryIconStatus(u32 chargingPtr, u32 iconStatusPtr) { @@ -50,8 +65,8 @@ u32 sceImposeGetBatteryIconStatus(u32 chargingPtr, u32 iconStatusPtr) u32 sceImposeSetLanguageMode(u32 languageVal, u32 buttonVal) { DEBUG_LOG(HLE, "sceImposeSetLanguageMode(%08x, %08x)", languageVal, buttonVal); - iLanguage = languageVal; - iButtonValue = buttonVal; + language = languageVal; + buttonValue = buttonVal; return 0; } @@ -59,20 +74,32 @@ u32 sceImposeGetLanguageMode(u32 languagePtr, u32 btnPtr) { DEBUG_LOG(HLE, "sceImposeGetLanguageMode(%08x, %08x)", languagePtr, btnPtr); if (Memory::IsValidAddress(languagePtr)) - Memory::Write_U32(iLanguage, languagePtr); + Memory::Write_U32(language, languagePtr); if (Memory::IsValidAddress(btnPtr)) - Memory::Write_U32(iButtonValue, btnPtr); + Memory::Write_U32(buttonValue, btnPtr); return 0; } +u32 sceImposeSetUMDPopup(int value) { + DEBUG_LOG(HLE, "sceImposeSetUMDPopup(%i)", value); + umdPopup = value; + return 0; +} + +u32 sceImposeGetUMDPopup() { + DEBUG_LOG(HLE, "sceImposeGetUMDPopup()"); + return umdPopup; +} + //OSD stuff? home button? const HLEFunction sceImpose[] = { - {0x36aa6e91, &WrapU_UU, "sceImposeSetLanguageMode"}, // Seen + {0x36aa6e91, WrapU_UU, "sceImposeSetLanguageMode"}, // Seen {0x381bd9e7, 0, "sceImposeHomeButton"}, - {0x24fd7bcf, &WrapU_UU, "sceImposeGetLanguageMode"}, - {0x8c943191, &WrapU_UU, "sceImposeGetBatteryIconStatus"}, - {0x72189C48, 0, "sceImposeSetUMDPopup"}, + {0x24fd7bcf, WrapU_UU, "sceImposeGetLanguageMode"}, + {0x8c943191, WrapU_UU, "sceImposeGetBatteryIconStatus"}, + {0x72189C48, WrapU_I, "sceImposeSetUMDPopup"}, + {0xE0887BC8, WrapU_V, "sceImposeGetUMDPopup"}, }; void Register_sceImpose() diff --git a/Core/HLE/sceImpose.h b/Core/HLE/sceImpose.h index 1de2aea93e..f340cd5722 100644 --- a/Core/HLE/sceImpose.h +++ b/Core/HLE/sceImpose.h @@ -17,4 +17,8 @@ #pragma once +#include "../../Common/ChunkFile.h" + void Register_sceImpose(); +void __ImposeInit(); +void __ImposeDoState(PointerWrap &p); diff --git a/Core/HLE/sceIo.cpp b/Core/HLE/sceIo.cpp index 17817b760b..a7f8e00564 100644 --- a/Core/HLE/sceIo.cpp +++ b/Core/HLE/sceIo.cpp @@ -22,6 +22,7 @@ #include "../System.h" #include "../Config.h" +#include "../SaveState.h" #include "HLE.h" #include "../MIPS/MIPS.h" #include "../HW/MemoryStick.h" @@ -84,7 +85,7 @@ const std::string &EmuDebugOutput() { typedef u32 (*DeferredAction)(SceUID id, int param); DeferredAction defAction = 0; -u32 defParam; +u32 defParam = 0; #define SCE_STM_FDIR 0x1000 #define SCE_STM_FREG 0x2000 @@ -137,7 +138,18 @@ public: sprintf(ptr, "Seekpos: %08x", (u32)pspFileSystem.GetSeekPos(handle)); } static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_BADF; } - int GetIDType() const { return 0; } + int GetIDType() const { return PPSSPP_KERNEL_TMID_File; } + + virtual void DoState(PointerWrap &p) { + p.Do(fullpath); + p.Do(handle); + p.Do(callbackID); + p.Do(callbackArg); + p.Do(asyncResult); + p.Do(pendingAsyncResult); + p.Do(sectorBlockMode); + p.DoMarker("File"); + } std::string fullpath; u32 handle; @@ -154,6 +166,8 @@ public: void __IoInit() { INFO_LOG(HLE, "Starting up I/O..."); + MemoryStick_SetFatState(PSP_FAT_MEMORYSTICK_STATE_ASSIGNED); + #ifdef _WIN32 char path_buffer[_MAX_PATH], drive[_MAX_DRIVE] ,dir[_MAX_DIR], file[_MAX_FNAME], ext[_MAX_EXT]; @@ -191,8 +205,17 @@ void __IoInit() { pspFileSystem.Mount("flash1:", flash); } -void __IoShutdown() { +void __IoDoState(PointerWrap &p) { + // TODO: defAction is hard to save, and not the right way anyway. + // Should probbly be an enum and on the FileNode anyway. + if (defAction != NULL) { + WARN_LOG(HLE, "FIXME: Savestate failure: deferred IO not saved yet."); + } +} +void __IoShutdown() { + defAction = 0; + defParam = 0; } u32 sceIoAssign(const char *aliasname, const char *physname, const char *devname, u32 flag) { @@ -221,7 +244,7 @@ void __IoCompleteAsyncIO(SceUID id) { FileNode *f = kernelObjects.Get < FileNode > (id, error); if (f) { if (f->callbackID) { - // __KernelNotifyCallbackType(THREAD_CALLBACK_IO, __KernelGetCurThread(), f->callbackID, f->callbackArg); + __KernelNotifyCallback(THREAD_CALLBACK_IO, f->callbackID, f->callbackArg); } } } @@ -245,13 +268,16 @@ void __IoGetStat(SceIoStat *stat, PSPFileInfo &info) { u32 sceIoGetstat(const char *filename, u32 addr) { SceIoStat stat; PSPFileInfo info = pspFileSystem.GetFileInfo(filename); - __IoGetStat(&stat, info); - Memory::WriteStruct(addr, &stat); - - DEBUG_LOG(HLE, "sceIoGetstat(%s, %08x) : sector = %08x", filename, addr, + if (info.exists) { + __IoGetStat(&stat, info); + Memory::WriteStruct(addr, &stat); + DEBUG_LOG(HLE, "sceIoGetstat(%s, %08x) : sector = %08x", filename, addr, info.startSector); - - return 0; + return 0; + } else { + DEBUG_LOG(HLE, "sceIoGetstat(%s, %08x) : FILE NOT FOUND", filename, addr); + return SCE_KERNEL_ERROR_NOFILE; + } } //Not sure about wrapping it or not, since the log seems to take the address of the data var @@ -364,24 +390,24 @@ u32 sceIoLseek32(int id, int offset, int whence) { } } -u32 sceIoOpen(const char* filename, int mode) { +u32 sceIoOpen(const char* filename, int flags, int mode) { //memory stick filename int access = FILEACCESS_NONE; - if (mode & O_RDONLY) + if (flags & O_RDONLY) access |= FILEACCESS_READ; - if (mode & O_WRONLY) + if (flags & O_WRONLY) access |= FILEACCESS_WRITE; - if (mode & O_APPEND) + if (flags & O_APPEND) access |= FILEACCESS_APPEND; - if (mode & O_CREAT) + if (flags & O_CREAT) access |= FILEACCESS_CREATE; u32 h = pspFileSystem.OpenFile(filename, (FileAccess) access); if (h == 0) { ERROR_LOG(HLE, - "ERROR_ERRNO_FILE_NOT_FOUND=sceIoOpen(%s, %08x) - file not found", - filename, mode); + "ERROR_ERRNO_FILE_NOT_FOUND=sceIoOpen(%s, %08x, %08x) - file not found", + filename, flags, mode); return ERROR_ERRNO_FILE_NOT_FOUND; } @@ -390,7 +416,7 @@ u32 sceIoOpen(const char* filename, int mode) { f->handle = h; f->fullpath = filename; f->asyncResult = id; - DEBUG_LOG(HLE, "%i=sceIoOpen(%s, %08x)", id, filename, mode); + DEBUG_LOG(HLE, "%i=sceIoOpen(%s, %08x, %08x)", id, filename, flags, mode); return id; } @@ -428,11 +454,11 @@ void sceIoSync() { } struct DeviceSize { + u32 maxClusters; + u32 freeClusters; u32 maxSectors; u32 sectorSize; - u32 sectorsPerCluster; - u32 totalClusters; - u32 freeClusters; + u32 sectorCount; }; u32 sceIoDevctl(const char *name, int cmd, u32 argAddr, int argLen, u32 outPtr, int outLen) { @@ -494,9 +520,16 @@ u32 sceIoDevctl(const char *name, int cmd, u32 argAddr, int argLen, u32 outPtr, } break; - case 0x02025806: // Memory stick inserted? case 0x02025801: // Memstick Driver status? - if (Memory::IsValidAddress(outPtr)) { + if (Memory::IsValidAddress(outPtr) && outLen >= 4) { + Memory::Write_U32(4, outPtr); // JPSCP: The right return value is 4 for some reason + return 0; + } else { + return ERROR_MEMSTICK_DEVCTL_BAD_PARAMS; + } + + case 0x02025806: // Memory stick inserted? + if (Memory::IsValidAddress(outPtr) && outLen >= 4) { Memory::Write_U32(1, outPtr); return 0; } else { @@ -505,20 +538,23 @@ u32 sceIoDevctl(const char *name, int cmd, u32 argAddr, int argLen, u32 outPtr, case 0x02425818: // Get memstick size etc // Pretend we have a 2GB memory stick. - if (Memory::IsValidAddress(argAddr)) { // "Should" be outPtr but isn't + if (Memory::IsValidAddress(argAddr) && argLen >= 4) { // "Should" be outPtr but isn't u32 pointer = Memory::Read_U32(argAddr); - - u64 totalSize = (u32)2 * 1024 * 1024 * 1024; - u64 freeSize = 1 * 1024 * 1024 * 1024; + u32 sectorSize = 0x200; + u32 memStickSectorSize = 32 * 1024; + u32 sectorCount = memStickSectorSize / sectorSize; + u64 freeSize = 1 * 1024 * 1024 * 1024; DeviceSize deviceSize; - deviceSize.maxSectors = 512; - deviceSize.sectorSize = 0x200; - deviceSize.sectorsPerCluster = 0x08; - deviceSize.totalClusters = (u32)((totalSize * 95 / 100) / (deviceSize.sectorSize * deviceSize.sectorsPerCluster)); - deviceSize.freeClusters = (u32)((freeSize * 95 / 100) / (deviceSize.sectorSize * deviceSize.sectorsPerCluster)); + deviceSize.maxClusters = (u32)((freeSize * 95 / 100) / (sectorSize * sectorCount)); + deviceSize.freeClusters = deviceSize.maxClusters; + deviceSize.maxSectors = deviceSize.maxClusters; + deviceSize.sectorSize = sectorSize; + deviceSize.sectorCount = sectorCount; Memory::WriteStruct(pointer, &deviceSize); + DEBUG_LOG(HLE, "Returned memstick size: maxSectors=%i", deviceSize.maxSectors); return 0; } else { + ERROR_LOG(HLE, "memstick size query: bad params"); return ERROR_MEMSTICK_DEVCTL_BAD_PARAMS; } } @@ -573,17 +609,18 @@ u32 sceIoDevctl(const char *name, int cmd, u32 argAddr, int argLen, u32 outPtr, case 0x02425818: // Get memstick size etc // Pretend we have a 2GB memory stick. { - if (Memory::IsValidAddress(argAddr)) { // "Should" be outPtr but isn't + if (Memory::IsValidAddress(argAddr) && argLen >= 4) { // NOTE: not outPtr u32 pointer = Memory::Read_U32(argAddr); - - u64 totalSize = (u32)2 * 1024 * 1024 * 1024; - u64 freeSize = 1 * 1024 * 1024 * 1024; + u32 sectorSize = 0x200; + u32 memStickSectorSize = 32 * 1024; + u32 sectorCount = memStickSectorSize / sectorSize; + u64 freeSize = 1 * 1024 * 1024 * 1024; DeviceSize deviceSize; - deviceSize.maxSectors = 512; - deviceSize.sectorSize = 0x200; - deviceSize.sectorsPerCluster = 0x08; - deviceSize.totalClusters = (u32)((totalSize * 95 / 100) / (deviceSize.sectorSize * deviceSize.sectorsPerCluster)); - deviceSize.freeClusters = (u32)((freeSize * 95 / 100) / (deviceSize.sectorSize * deviceSize.sectorsPerCluster)); + deviceSize.maxClusters = (u32)((freeSize * 95 / 100) / (sectorSize * sectorCount)); + deviceSize.freeClusters = deviceSize.maxClusters; + deviceSize.maxSectors = deviceSize.maxClusters; + deviceSize.sectorSize = sectorSize; + deviceSize.sectorCount = sectorCount; Memory::WriteStruct(pointer, &deviceSize); return 0; } else { @@ -622,7 +659,12 @@ u32 sceIoDevctl(const char *name, int cmd, u32 argAddr, int argLen, u32 outPtr, } case 3: // EMULATOR_DEVCTL__IS_EMULATOR if (Memory::IsValidAddress(outPtr)) - Memory::Write_U32(1, outPtr); // TODO: Make a headless mode for running tests! + Memory::Write_U32(1, outPtr); + return 0; + case 4: // EMULATOR_DEVCTL__VERIFY_STATE + // Note that this is async, and makes sure the save state matches up. + SaveState::Verify(); + // TODO: Maybe save/load to a file just to be sure? return 0; } @@ -656,26 +698,27 @@ u32 sceIoChdir(const char *dirname) { return 1; } -void sceIoChangeAsyncPriority() +int sceIoChangeAsyncPriority(int id, int priority) { - ERROR_LOG(HLE, "UNIMPL sceIoChangeAsyncPriority(%d)", PARAM(0)); - RETURN(0); + ERROR_LOG(HLE, "UNIMPL sceIoChangeAsyncPriority(%d, %d)", id, priority); + return 0; } -u32 __IoClose(SceUID id, int param) +u32 __IoClose(SceUID actedFd, int closedFd) { - DEBUG_LOG(HLE, "Deferred IoClose(%d)", id); - __IoCompleteAsyncIO(id); - return kernelObjects.Destroy < FileNode > (id); + DEBUG_LOG(HLE, "Deferred IoClose(%d, %d)", actedFd, closedFd); + __IoCompleteAsyncIO(closedFd); + return kernelObjects.Destroy < FileNode > (closedFd); } -//TODO Not really sure if this should be wrapped nor how -void sceIoCloseAsync() +int sceIoCloseAsync(SceUID id) { - DEBUG_LOG(HLE, "sceIoCloseAsync(%d)", PARAM(0)); + DEBUG_LOG(HLE, "sceIoCloseAsync(%d)", id); //sceIoClose(); + // TODO: Not sure this is a good solution. Seems like you can defer one per fd. defAction = &__IoClose; - RETURN(0); + defParam = id; + return 0; } u32 sceIoLseekAsync(int id, s64 offset, int whence) @@ -694,6 +737,7 @@ u32 sceIoSetAsyncCallback(int id, u32 clbckId, u32 clbckArg) FileNode *f = kernelObjects.Get < FileNode > (id, error); if (f) { + // TODO: Check replacing / updating? f->callbackID = clbckId; f->callbackArg = clbckArg; return 0; @@ -712,13 +756,14 @@ u32 sceIoLseek32Async(int id, int offset, int whence) return 0; } -void sceIoOpenAsync(const char *filename, int mode) +u32 sceIoOpenAsync(const char *filename, int flags, int mode) { DEBUG_LOG(HLE, "sceIoOpenAsync() sorta implemented"); - RETURN(sceIoOpen(filename, mode)); -// __IoCompleteAsyncIO(currentMIPS->r[2]); // The return value - // We have to return a UID here, which may have been destroyed when we reach Wait if it failed. - // Now that we're just faking it, we just don't RETURN(0) here. + u32 fd = sceIoOpen(filename, flags, mode); + // TODO: This can't actually have a callback yet, but if it's set before waiting, it should be called. + __IoCompleteAsyncIO(fd); + // We have to return an fd here, which may have been destroyed when we reach Wait if it failed. + return fd; } u32 sceIoReadAsync(int id, u32 data_addr, int size) @@ -729,7 +774,7 @@ u32 sceIoReadAsync(int id, u32 data_addr, int size) return 0; } -u32 sceIoGetAsyncStat(int id, u32 address, u32 uknwn) +u32 sceIoGetAsyncStat(int id, u32 poll, u32 address) { u32 error; FileNode *f = kernelObjects.Get < FileNode > (id, error); @@ -737,7 +782,9 @@ u32 sceIoGetAsyncStat(int id, u32 address, u32 uknwn) { Memory::Write_U64(f->asyncResult, address); DEBUG_LOG(HLE, "%i = sceIoGetAsyncStat(%i, %i, %08x) (HACK)", - (u32) f->asyncResult, id, address, uknwn); + (u32) f->asyncResult, id, poll, address); + if (!poll) + hleReSchedule("io waited"); return 0; //completed } else @@ -747,7 +794,7 @@ u32 sceIoGetAsyncStat(int id, u32 address, u32 uknwn) } } -void sceIoWaitAsync(int id, u32 address, u32 uknwn) { +int sceIoWaitAsync(int id, u32 address) { u32 error; FileNode *f = kernelObjects.Get < FileNode > (id, error); if (f) { @@ -758,15 +805,16 @@ void sceIoWaitAsync(int id, u32 address, u32 uknwn) { } Memory::Write_U64(res, address); DEBUG_LOG(HLE, "%i = sceIoWaitAsync(%i, %08x) (HACK)", (u32) res, id, - uknwn); - RETURN(0); //completed + address); + hleReSchedule("io waited"); + return 0; //completed } else { ERROR_LOG(HLE, "ERROR - sceIoWaitAsync waiting for invalid id %i", id); - RETURN(-1); + return -1; } } -void sceIoWaitAsyncCB(int id, u32 address) { +int sceIoWaitAsyncCB(int id, u32 address) { // Should process callbacks here u32 error; FileNode *f = kernelObjects.Get < FileNode > (id, error); @@ -779,10 +827,13 @@ void sceIoWaitAsyncCB(int id, u32 address) { Memory::Write_U64(res, address); DEBUG_LOG(HLE, "%i = sceIoWaitAsyncCB(%i, %08x) (HACK)", (u32) res, id, address); - RETURN(0); //completed + hleCheckCurrentCallbacks(); + hleReSchedule(true, "io waited"); + return 0; //completed } else { ERROR_LOG(HLE, "ERROR - sceIoWaitAsyncCB waiting for invalid id %i", id); + return -1; } } @@ -810,7 +861,20 @@ public: const char *GetName() {return name.c_str();} const char *GetTypeName() {return "DirListing";} static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_BADF; } - int GetIDType() const { return 0; } + int GetIDType() const { return PPSSPP_KERNEL_TMID_DirList; } + + virtual void DoState(PointerWrap &p) { + p.Do(name); + + // TODO: Is this the right way for it to wake up? + int count = listing.size(); + p.Do(count); + listing.resize(count); + for (int i = 0; i < count; ++i) { + listing[i].DoState(p); + } + p.DoMarker("DirListing"); + } std::string name; std::vector listing; @@ -869,8 +933,37 @@ u32 sceIoDclose(int id) { u32 sceIoIoctl(u32 id, u32 cmd, u32 indataPtr, u32 inlen, u32 outdataPtr, u32 outlen) { - ERROR_LOG(HLE, "UNIMPL 0=sceIoIoctrl id: %08x, cmd %08x, indataPtr %08x, inlen %08x, outdataPtr %08x, outLen %08x", id,cmd,indataPtr,inlen,outdataPtr,outlen); - return 0; + ERROR_LOG(HLE, "UNIMPL PARTIAL 0=sceIoIoctl id: %08x, cmd %08x, indataPtr %08x, inlen %08x, outdataPtr %08x, outLen %08x", id,cmd,indataPtr,inlen,outdataPtr,outlen); + + u32 error; + FileNode *f = kernelObjects.Get(id, error); + if (error) { + return error; + } + + //KD Hearts: + //56:46:434 HLE\sceIo.cpp:886 E[HLE]: UNIMPL 0=sceIoIoctrl id: 0000011f, cmd 04100001, indataPtr 08b313d8, inlen 00000010, outdataPtr 00000000, outLen 0 + // 0000000 + switch (cmd) { + case 0x04100001: // Define decryption key (amctrl.prx DRM) + if (Memory::IsValidAddress(indataPtr) && inlen == 16) { + u8 keybuf[16]; + memcpy(keybuf, Memory::GetPointer(indataPtr), 16); + ERROR_LOG(HLE, "PGD DRM not yet supported, sorry."); + } + break; + + } + + return 0; +} + +KernelObject *__KernelFileNodeObject() { + return new FileNode; +} + +KernelObject *__KernelDirListingObject() { + return new DirListing; } const HLEFunction IoFileMgrForUser[] = { @@ -885,17 +978,17 @@ const HLEFunction IoFileMgrForUser[] = { { 0x08bd7374, 0, "sceIoGetDevType" }, { 0xB2A628C1, &WrapU_CCCU, "sceIoAssign" }, { 0xe8bc6571, 0, "sceIoCancel" }, - { 0xb293727f, sceIoChangeAsyncPriority, "sceIoChangeAsyncPriority" }, + { 0xb293727f, &WrapI_II, "sceIoChangeAsyncPriority" }, { 0x810C4BC3, &WrapU_I, "sceIoClose" }, //(int fd); - { 0xff5940b6, sceIoCloseAsync, "sceIoCloseAsync" }, + { 0xff5940b6, &WrapI_I, "sceIoCloseAsync" }, { 0x54F5FB11, &WrapU_CIUIUI, "sceIoDevctl" }, //(const char *name int cmd, void *arg, size_t arglen, void *buf, size_t *buflen); { 0xcb05f8d6, &WrapU_IUU, "sceIoGetAsyncStat" }, { 0x27EB27B8, &WrapI64_II64I, "sceIoLseek" }, //(int fd, int offset, int whence); { 0x68963324, &WrapU_III, "sceIoLseek32" }, { 0x1b385d8f, &WrapU_III, "sceIoLseek32Async" }, { 0x71b19e77, &WrapU_II64I, "sceIoLseekAsync" }, - { 0x109F50BC, &WrapU_CI, "sceIoOpen" }, //(const char* file, int mode); - { 0x89AA9906, &WrapV_CI, "sceIoOpenAsync" }, + { 0x109F50BC, &WrapU_CII, "sceIoOpen" }, //(const char* file, int mode); + { 0x89AA9906, &WrapU_CII, "sceIoOpenAsync" }, { 0x06A70004, &WrapU_CI, "sceIoMkdir" }, //(const char *dir, int mode); { 0x3251ea56, &WrapU_IU, "sceIoPollAsync" }, { 0x6A638D83, &WrapU_IUI, "sceIoRead" }, //(int fd, void *data, int size); @@ -908,8 +1001,8 @@ const HLEFunction IoFileMgrForUser[] = { { 0x6d08a871, 0, "sceIoUnassign" }, { 0x42EC03AC, &WrapU_IVI, "sceIoWrite" }, //(int fd, void *data, int size); { 0x0facab19, 0, "sceIoWriteAsync" }, - { 0x35dbd746, &WrapV_IU, "sceIoWaitAsyncCB" }, - { 0xe23eec33, &WrapV_IUU, "sceIoWaitAsync" }, + { 0x35dbd746, &WrapI_IU, "sceIoWaitAsyncCB" }, + { 0xe23eec33, &WrapI_IU, "sceIoWaitAsync" }, }; void Register_IoFileMgrForUser() { diff --git a/Core/HLE/sceIo.h b/Core/HLE/sceIo.h index 907fd6046b..0f87cde613 100644 --- a/Core/HLE/sceIo.h +++ b/Core/HLE/sceIo.h @@ -19,9 +19,13 @@ #include #include "HLE.h" +#include "sceKernel.h" void __IoInit(); +void __IoDoState(PointerWrap &p); void __IoShutdown(); +KernelObject *__KernelFileNodeObject(); +KernelObject *__KernelDirListingObject(); void Register_IoFileMgrForUser(); void Register_StdioForUser(); diff --git a/Core/HLE/sceKernel.cpp b/Core/HLE/sceKernel.cpp index 2b7f1595c7..7c6b529017 100644 --- a/Core/HLE/sceKernel.cpp +++ b/Core/HLE/sceKernel.cpp @@ -24,13 +24,17 @@ #include "../FileSystems/MetaFileSystem.h" #include "../PSPLoaders.h" #include "../../Core/CoreTiming.h" +#include "../../Core/SaveState.h" #include "../../Core/System.h" +#include "../../GPU/GPUInterface.h" +#include "../../GPU/GPUState.h" #include "__sceAudio.h" #include "sceAudio.h" #include "sceCtrl.h" #include "sceDisplay.h" +#include "sceFont.h" #include "sceGe.h" #include "sceIo.h" #include "sceKernel.h" @@ -53,11 +57,11 @@ #include "sceSsl.h" #include "sceSas.h" #include "scePsmf.h" +#include "sceImpose.h" +#include "sceUsb.h" #include "../Util/PPGeDraw.h" -extern MetaFileSystem pspFileSystem; - /* 17: [MIPS32 R4K 00000000 ]: Loader: Type: 1 Vaddr: 00000000 Filesz: 2856816 Memsz: 2856816 18: [MIPS32 R4K 00000000 ]: Loader: Loadable Segment Copied to 0898dab0, size 002b9770 @@ -66,6 +70,7 @@ extern MetaFileSystem pspFileSystem; */ static bool kernelRunning = false; +KernelObjectPool kernelObjects; void __KernelInit() { @@ -75,13 +80,19 @@ void __KernelInit() return; } + SaveState::Init(); + __InterruptsInit(); __KernelMemoryInit(); __KernelThreadingInit(); + __KernelAlarmInit(); + __KernelEventFlagInit(); + __KernelMbxInit(); + __KernelMutexInit(); + __KernelSemaInit(); __IoInit(); __AudioInit(); __SasInit(); __DisplayInit(); - __InterruptsInit(); __GeInit(); __PowerInit(); __UtilityInit(); @@ -90,6 +101,9 @@ void __KernelInit() __PsmfInit(); __CtrlInit(); __SslInit(); + __ImposeInit(); + __UsbInit(); + __FontInit(); // "Internal" PSP libraries __PPGeInit(); @@ -113,13 +127,17 @@ void __KernelShutdown() __PsmfShutdown(); __PPGeShutdown(); + __CtrlShutdown(); + __UtilityShutdown(); __GeShutdown(); __SasShutdown(); + __DisplayShutdown(); __AudioShutdown(); __IoShutdown(); - __InterruptsShutdown(); + __KernelMutexShutdown(); __KernelThreadingShutdown(); __KernelMemoryShutdown(); + __InterruptsShutdown(); CoreTiming::ClearPendingEvents(); CoreTiming::UnregisterAllEvents(); @@ -127,6 +145,42 @@ void __KernelShutdown() kernelRunning = false; } +void __KernelDoState(PointerWrap &p) +{ + p.Do(kernelRunning); + kernelObjects.DoState(p); + p.DoMarker("KernelObjects"); + + __InterruptsDoState(p); + __KernelMemoryDoState(p); + __KernelThreadingDoState(p); + __KernelAlarmDoState(p); + __KernelEventFlagDoState(p); + __KernelMbxDoState(p); + __KernelModuleDoState(p); + __KernelMutexDoState(p); + __KernelSemaDoState(p); + + __AudioDoState(p); + __CtrlDoState(p); + __DisplayDoState(p); + __FontDoState(p); + __GeDoState(p); + __ImposeDoState(p); + __IoDoState(p); + __PowerDoState(p); + __SasDoState(p); + __SslDoState(p); + __UmdDoState(p); + __UtilityDoState(p); + __UsbDoState(p); + + __PPGeDoState(p); + + __InterruptsDoStateLate(p); + __KernelThreadingDoStateLate(p); +} + bool __KernelIsRunning() { return kernelRunning; } @@ -189,22 +243,27 @@ void sceKernelGetGPI() } // Don't even log these, they're spammy and we probably won't -// need to emulate them. +// need to emulate them. Might be useful for invalidating cached +// textures, and in the future display lists, in some cases though. +void sceKernelDcacheInvalidateRange(u32 addr, int size) +{ + gpu->InvalidateCache(addr, size); +} void sceKernelDcacheWritebackAll() { } -void sceKernelDcacheWritebackRange() +void sceKernelDcacheWritebackRange(u32 addr, int size) { } -void sceKernelDcacheWritebackInvalidateRange() +void sceKernelDcacheWritebackInvalidateRange(u32 addr, int size) { + gpu->InvalidateCache(addr, size); } void sceKernelDcacheWritebackInvalidateAll() { + gpu->InvalidateCache(0, -1); } -KernelObjectPool kernelObjects; - KernelObjectPool::KernelObjectPool() { memset(occupied, 0, sizeof(bool)*maxCount); @@ -267,12 +326,12 @@ void KernelObjectPool::List() if (pool[i]) { pool[i]->GetQuickInfo(buffer,256); + INFO_LOG(HLE, "KO %i: %s \"%s\": %s", i + handleOffset, pool[i]->GetTypeName(), pool[i]->GetName(), buffer); } else { strcpy(buffer,"WTF? Zero Pointer"); } - INFO_LOG(HLE, "KO %i: %s \"%s\": %s", i + handleOffset, pool[i]->GetTypeName(), pool[i]->GetName(), buffer); } } } @@ -288,6 +347,88 @@ int KernelObjectPool::GetCount() return count; } +void KernelObjectPool::DoState(PointerWrap &p) +{ + int _maxCount = maxCount; + p.Do(_maxCount); + + if (_maxCount != maxCount) + ERROR_LOG(HLE, "Unable to load state: different kernel object storage."); + + if (p.mode == p.MODE_READ) + kernelObjects.Clear(); + + p.DoArray(occupied, maxCount); + for (int i = 0; i < maxCount; ++i) + { + if (!occupied[i]) + continue; + + int type; + if (p.mode == p.MODE_READ) + { + p.Do(type); + pool[i] = CreateByIDType(type); + pool[i]->uid = i + handleOffset; + + // Already logged an error. + if (pool[i] == NULL) + return; + } + else + { + type = pool[i]->GetIDType(); + p.Do(type); + } + pool[i]->DoState(p); + } + p.DoMarker("KernelObjectPool"); +} + +KernelObject *KernelObjectPool::CreateByIDType(int type) +{ + // Used for save states. This is ugly, but what other way is there? + switch (type) + { + case SCE_KERNEL_TMID_Alarm: + return __KernelAlarmObject(); + case SCE_KERNEL_TMID_EventFlag: + return __KernelEventFlagObject(); + case SCE_KERNEL_TMID_Mbox: + return __KernelMbxObject(); + case SCE_KERNEL_TMID_Fpl: + return __KernelMemoryFPLObject(); + case SCE_KERNEL_TMID_Vpl: + return __KernelMemoryVPLObject(); + case PPSSPP_KERNEL_TMID_PMB: + return __KernelMemoryPMBObject(); + case PPSSPP_KERNEL_TMID_Module: + return __KernelModuleObject(); + case SCE_KERNEL_TMID_Mpipe: + return __KernelMsgPipeObject(); + case SCE_KERNEL_TMID_Mutex: + return __KernelMutexObject(); + case SCE_KERNEL_TMID_LwMutex: + return __KernelLwMutexObject(); + case SCE_KERNEL_TMID_Semaphore: + return __KernelSemaphoreObject(); + case SCE_KERNEL_TMID_Callback: + return __KernelCallbackObject(); + case SCE_KERNEL_TMID_Thread: + return __KernelThreadObject(); + case SCE_KERNEL_TMID_VTimer: + return __KernelVTimerObject(); + case PPSSPP_KERNEL_TMID_File: + return __KernelFileNodeObject(); + case PPSSPP_KERNEL_TMID_DirList: + return __KernelDirListingObject(); + + default: + ERROR_LOG(COMMON, "Unable to load state: could not find object type %d.", type); + return NULL; + } +} + void sceKernelIcacheInvalidateAll() { DEBUG_LOG(CPU, "Icache invalidated - should clear JIT someday"); @@ -418,10 +559,10 @@ const HLEFunction ThreadManForUser[] = {0x64D4540E,0,"sceKernelReferThreadProfiler"}, //Fifa Street 2 uses alarms - {0x6652b8ca,sceKernelSetAlarm,"sceKernelSetAlarm"}, - {0xB2C25152,sceKernelSetSysClockAlarm,"sceKernelSetSysClockAlarm"}, - {0x7e65b999,sceKernelCancelAlarm,"sceKernelCancelAlarm"}, - {0xDAA3F564,sceKernelReferAlarmStatus,"sceKernelReferAlarmStatus"}, + {0x6652b8ca,WrapI_UUU,"sceKernelSetAlarm"}, + {0xB2C25152,WrapI_UUU,"sceKernelSetSysClockAlarm"}, + {0x7e65b999,WrapI_I,"sceKernelCancelAlarm"}, + {0xDAA3F564,WrapI_IU,"sceKernelReferAlarmStatus"}, {0xba6b92e2,sceKernelSysClock2USec,"sceKernelSysClock2USec"}, {0x110DEC9A,0,"sceKernelUSec2SysClock"}, diff --git a/Core/HLE/sceKernel.h b/Core/HLE/sceKernel.h index 2876906894..90835442d1 100644 --- a/Core/HLE/sceKernel.h +++ b/Core/HLE/sceKernel.h @@ -18,6 +18,7 @@ #pragma once #include "../../Globals.h" +#include "../../Common/ChunkFile.h" #include enum @@ -223,6 +224,7 @@ enum SCE_KERNEL_ERROR_ERRORMAX = 0x8002044d, }; +// If you add to this, make sure to check KernelObjectPool::CreateByIDType(). enum TMIDPurpose { SCE_KERNEL_TMID_Thread = 1, @@ -242,6 +244,12 @@ enum TMIDPurpose SCE_KERNEL_TMID_DelayThread = 65, SCE_KERNEL_TMID_SuspendThread = 66, SCE_KERNEL_TMID_DormantThread = 67, + + // Not official, but need ids for save states. + PPSSPP_KERNEL_TMID_Module = 0x100001, + PPSSPP_KERNEL_TMID_PMB = 0x100002, + PPSSPP_KERNEL_TMID_File = 0x100003, + PPSSPP_KERNEL_TMID_DirList = 0x100004, }; typedef int SceUID; @@ -263,6 +271,7 @@ struct SceKernelLoadExecParam void __KernelInit(); void __KernelShutdown(); +void __KernelDoState(PointerWrap &p); bool __KernelIsRunning(); bool __KernelLoadExec(const char *filename, SceKernelLoadExecParam *param); @@ -283,9 +292,10 @@ void sceKernelFindModuleByName(); void sceKernelSetGPO(); void sceKernelGetGPI(); +void sceKernelDcacheInvalidateRange(u32 addr, int size); void sceKernelDcacheWritebackAll(); -void sceKernelDcacheWritebackRange(); -void sceKernelDcacheWritebackInvalidateRange(); +void sceKernelDcacheWritebackRange(u32 addr, int size); +void sceKernelDcacheWritebackInvalidateRange(u32 addr, int size); void sceKernelDcacheWritebackInvalidateAll(); void sceKernelGetThreadStackFreeSize(); void sceKernelIcacheInvalidateAll(); @@ -310,9 +320,10 @@ public: // Implement this in all subclasses: // static u32 GetMissingErrorCode() - // Future - // void Serialize(ChunkFile) - // void DeSerialize(ChunkFile) + virtual void DoState(PointerWrap &p) + { + _dbg_assert_msg_(HLE, false, "Unable to save state: bad kernel object."); + } }; @@ -324,7 +335,8 @@ public: // Allocates a UID within the range and inserts the object into the map. SceUID Create(KernelObject *obj, int rangeBottom = 16, int rangeTop = 0x7fffffff); - // TODO: How will we ever save/restore this pool? + void DoState(PointerWrap &p); + static KernelObject *CreateByIDType(int type); template u32 Destroy(SceUID handle) diff --git a/Core/HLE/sceKernelAlarm.cpp b/Core/HLE/sceKernelAlarm.cpp index 8f4cd961b2..bb29e077e6 100644 --- a/Core/HLE/sceKernelAlarm.cpp +++ b/Core/HLE/sceKernelAlarm.cpp @@ -17,28 +17,212 @@ #include "sceKernel.h" #include "sceKernelAlarm.h" +#include "sceKernelInterrupt.h" #include "HLE.h" +#include "../../Core/CoreTiming.h" -void sceKernelSetAlarm() +const int NATIVEALARM_SIZE = 20; + +struct NativeAlarm { - ERROR_LOG(HLE,"UNIMPL sceKernelSetAlarm"); - RETURN(-1); + SceSize size; + u64 schedule; + u32 handlerPtr; + u32 commonPtr; +}; + +struct Alarm : public KernelObject +{ + const char *GetName() {return "[Alarm]";} + const char *GetTypeName() {return "Alarm";} + static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_ALMID; } + int GetIDType() const { return SCE_KERNEL_TMID_Alarm; } + + virtual void DoState(PointerWrap &p) + { + p.Do(alm); + p.DoMarker("Alarm"); + } + + NativeAlarm alm; +}; + +void __KernelScheduleAlarm(Alarm *alarm, u64 ticks); + +class AlarmIntrHandler : public SubIntrHandler +{ +public: + static SubIntrHandler *Create() + { + return new AlarmIntrHandler(); + } + + void setAlarm(Alarm *alarm) + { + alarmID = alarm->GetUID(); + handlerAddress = alarm->alm.handlerPtr; + enabled = true; + } + + virtual void copyArgsToCPU(const PendingInterrupt &pend) + { + SubIntrHandler::copyArgsToCPU(pend); + + u32 error; + Alarm *alarm = kernelObjects.Get(alarmID, error); + if (alarm) + currentMIPS->r[MIPS_REG_A0] = alarm->alm.commonPtr; + else + ERROR_LOG(HLE, "sceKernelAlarm: Unable to send interrupt args: alarm deleted?"); + } + + virtual void handleResult(int result) + { + // A non-zero result means to reschedule. + if (result > 0) + { + u32 error; + Alarm *alarm = kernelObjects.Get(alarmID, error); + __KernelScheduleAlarm(alarm, (u64) usToCycles(result)); + } + else + { + if (result < 0) + WARN_LOG(HLE, "Alarm requested reschedule for negative value %u, ignoring", (unsigned) result); + + // Delete the alarm if it's not rescheduled. + kernelObjects.Destroy(alarmID); + __ReleaseSubIntrHandler(PSP_SYSTIMER0_INTR, alarmID); + } + } + + virtual void DoState(PointerWrap &p) + { + SubIntrHandler::DoState(p); + p.Do(alarmID); + p.DoMarker("AlarmIntrHandler"); + } + + SceUID alarmID; +}; + +static int alarmTimer = 0; + +void __KernelTriggerAlarm(u64 userdata, int cyclesLate) +{ + int uid = (int) userdata; + + u32 error; + Alarm *alarm = kernelObjects.Get(uid, error); + if (alarm) + __TriggerInterrupt(PSP_INTR_IMMEDIATE, PSP_SYSTIMER0_INTR, uid); } -void sceKernelSetSysClockAlarm() +void __KernelAlarmInit() { - ERROR_LOG(HLE,"UNIMPL sceKernelSetSysClockAlarm"); - RETURN(-1); + alarmTimer = CoreTiming::RegisterEvent("Alarm", __KernelTriggerAlarm); + __RegisterSubIntrCreator(PSP_SYSTIMER0_INTR, AlarmIntrHandler::Create); } -void sceKernelCancelAlarm() +void __KernelAlarmDoState(PointerWrap &p) { - ERROR_LOG(HLE,"UNIMPL sceKernelCancelAlarm"); - RETURN(-1); + p.Do(alarmTimer); + CoreTiming::RestoreRegisterEvent(alarmTimer, "Alarm", __KernelTriggerAlarm); + p.DoMarker("sceKernelAlarm"); } -void sceKernelReferAlarmStatus() +KernelObject *__KernelAlarmObject() { - ERROR_LOG(HLE,"UNIMPL sceKernelReferAlarmStatus"); - RETURN(-1); + // Default object to load from state. + return new Alarm; +} + +void __KernelScheduleAlarm(Alarm *alarm, u64 ticks) +{ + alarm->alm.schedule = (CoreTiming::GetTicks() + ticks) / (u64) CoreTiming::GetClockFrequencyMHz(); + CoreTiming::ScheduleEvent((int) ticks, alarmTimer, alarm->GetUID()); +} + +SceUID __KernelSetAlarm(u64 ticks, u32 handlerPtr, u32 commonPtr) +{ + if (!Memory::IsValidAddress(handlerPtr)) + return SCE_KERNEL_ERROR_ILLEGAL_ADDR; + + Alarm *alarm = new Alarm; + SceUID uid = kernelObjects.Create(alarm); + + alarm->alm.size = NATIVEALARM_SIZE; + alarm->alm.handlerPtr = handlerPtr; + alarm->alm.commonPtr = commonPtr; + + u32 error; + AlarmIntrHandler *handler = (AlarmIntrHandler *) __RegisterSubIntrHandler(PSP_SYSTIMER0_INTR, uid, error); + if (error != 0) + { + kernelObjects.Destroy(uid); + return error; + } + + handler->setAlarm(alarm); + __KernelScheduleAlarm(alarm, ticks); + return uid; +} + +SceUID sceKernelSetAlarm(SceUInt micro, u32 handlerPtr, u32 commonPtr) +{ + DEBUG_LOG(HLE, "sceKernelSetAlarm(%d, %08x, %08x)", micro, handlerPtr, commonPtr); + return __KernelSetAlarm(usToCycles((u64) micro), handlerPtr, commonPtr); +} + +SceUID sceKernelSetSysClockAlarm(u32 microPtr, u32 handlerPtr, u32 commonPtr) +{ + u64 micro; + + if (Memory::IsValidAddress(microPtr)) + micro = Memory::Read_U64(microPtr); + else + return -1; + + DEBUG_LOG(HLE, "sceKernelSetSysClockAlarm(%lld, %08x, %08x)", micro, handlerPtr, commonPtr); + return __KernelSetAlarm(usToCycles(micro), handlerPtr, commonPtr); +} + +int sceKernelCancelAlarm(SceUID uid) +{ + DEBUG_LOG(HLE, "sceKernelCancelAlarm(%08x)", uid); + + CoreTiming::UnscheduleEvent(alarmTimer, uid); + __ReleaseSubIntrHandler(PSP_SYSTIMER0_INTR, uid); + + return kernelObjects.Destroy(uid); +} + +int sceKernelReferAlarmStatus(SceUID uid, u32 infoPtr) +{ + u32 error; + Alarm *alarm = kernelObjects.Get(uid, error); + if (!alarm) + { + ERROR_LOG(HLE, "sceKernelReferAlarmStatus(%08x, %08x): invalid alarm", uid, infoPtr); + return error; + } + + DEBUG_LOG(HLE, "sceKernelReferAlarmStatus(%08x, %08x)", uid, infoPtr); + + if (!Memory::IsValidAddress(infoPtr)) + return -1; + + u32 size = Memory::Read_U32(infoPtr); + + // Alarms actually respect size and write (kinda) what it can hold. + if (size > 0) + Memory::Write_U32(alarm->alm.size, infoPtr); + if (size > 4) + Memory::Write_U64(alarm->alm.schedule, infoPtr + 4); + if (size > 12) + Memory::Write_U32(alarm->alm.handlerPtr, infoPtr + 12); + if (size > 16) + Memory::Write_U32(alarm->alm.commonPtr, infoPtr + 16); + + return 0; } \ No newline at end of file diff --git a/Core/HLE/sceKernelAlarm.h b/Core/HLE/sceKernelAlarm.h index 56b004f0fc..4b286eb87a 100644 --- a/Core/HLE/sceKernelAlarm.h +++ b/Core/HLE/sceKernelAlarm.h @@ -17,7 +17,11 @@ #pragma once -void sceKernelSetAlarm(); -void sceKernelSetSysClockAlarm(); -void sceKernelCancelAlarm(); -void sceKernelReferAlarmStatus(); \ No newline at end of file +SceUID sceKernelSetAlarm(SceUInt clock, u32 handlerPtr, u32 commonPtr); +SceUID sceKernelSetSysClockAlarm(u32 sysClockPtr, u32 handlerPtr, u32 commonPtr); +int sceKernelCancelAlarm(SceUID uid); +int sceKernelReferAlarmStatus(SceUID uid, u32 infoPtr); + +void __KernelAlarmInit(); +void __KernelAlarmDoState(PointerWrap &p); +KernelObject *__KernelAlarmObject(); diff --git a/Core/HLE/sceKernelEventFlag.cpp b/Core/HLE/sceKernelEventFlag.cpp index 2ba4f23cf3..8f62b722c0 100644 --- a/Core/HLE/sceKernelEventFlag.cpp +++ b/Core/HLE/sceKernelEventFlag.cpp @@ -57,12 +57,20 @@ public: nef.currentPattern, nef.numWaitThreads); } - + static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_EVFID; } int GetIDType() const { return SCE_KERNEL_TMID_EventFlag; } + virtual void DoState(PointerWrap &p) + { + p.Do(nef); + EventFlagTh eft = {0}; + p.Do(waitingThreads, eft); + p.DoMarker("EventFlag"); + } + NativeEventFlag nef; std::vector waitingThreads; }; @@ -90,13 +98,24 @@ enum PspEventFlagWaitTypes PSP_EVENT_WAITKNOWN = PSP_EVENT_WAITCLEAR | PSP_EVENT_WAITCLEARALL | PSP_EVENT_WAITOR, }; -bool eventFlagInitComplete = false; int eventFlagWaitTimer = 0; void __KernelEventFlagInit() { - eventFlagWaitTimer = CoreTiming::RegisterEvent("EventFlagTimeout", &__KernelEventFlagTimeout); - eventFlagInitComplete = true; + eventFlagWaitTimer = CoreTiming::RegisterEvent("EventFlagTimeout", __KernelEventFlagTimeout); +} + +void __KernelEventFlagDoState(PointerWrap &p) +{ + p.Do(eventFlagWaitTimer); + CoreTiming::RestoreRegisterEvent(eventFlagWaitTimer, "EventFlagTimeout", __KernelEventFlagTimeout); + p.DoMarker("sceKernelEventFlag"); +} + +KernelObject *__KernelEventFlagObject() +{ + // Default object to load from state. + return new EventFlag; } bool __KernelEventFlagMatches(u32 *pattern, u32 bits, u8 wait, u32 outAddr) @@ -168,9 +187,6 @@ bool __KernelClearEventFlagThreads(EventFlag *e, int reason) //SceUID sceKernelCreateEventFlag(const char *name, int attr, int bits, SceKernelEventFlagOptParam *opt); int sceKernelCreateEventFlag(const char *name, u32 flag_attr, u32 flag_initPattern, u32 optPtr) { - if (!eventFlagInitComplete) - __KernelEventFlagInit(); - if (!name) { WARN_LOG(HLE, "%08x=sceKernelCreateEventFlag(): invalid name", SCE_KERNEL_ERROR_ERROR); @@ -397,7 +413,7 @@ int sceKernelWaitEventFlag(SceUID id, u32 bits, u32 wait, u32 outBitsPtr, u32 ti th.bits = bits; th.wait = wait; // If < 5ms, sometimes hardware doesn't write this, but it's unpredictable. - th.outAddr = timeout == 0 ? NULL : outBitsPtr; + th.outAddr = timeout == 0 ? 0 : outBitsPtr; e->waitingThreads.push_back(th); __KernelSetEventFlagTimeout(e, timeoutPtr); @@ -450,7 +466,7 @@ int sceKernelWaitEventFlagCB(SceUID id, u32 bits, u32 wait, u32 outBitsPtr, u32 th.bits = bits; th.wait = wait; // If < 5ms, sometimes hardware doesn't write this, but it's unpredictable. - th.outAddr = timeout == 0 ? NULL : outBitsPtr; + th.outAddr = timeout == 0 ? 0 : outBitsPtr; e->waitingThreads.push_back(th); __KernelSetEventFlagTimeout(e, timeoutPtr); diff --git a/Core/HLE/sceKernelEventFlag.h b/Core/HLE/sceKernelEventFlag.h index b47b43c53b..151efa25f7 100644 --- a/Core/HLE/sceKernelEventFlag.h +++ b/Core/HLE/sceKernelEventFlag.h @@ -26,3 +26,7 @@ int sceKernelWaitEventFlagCB(SceUID id, u32 bits, u32 wait, u32 outBitsPtr, u32 int sceKernelPollEventFlag(SceUID id, u32 bits, u32 wait, u32 outBitsPtr, u32 timeoutPtr); u32 sceKernelReferEventFlagStatus(SceUID id, u32 statusPtr); u32 sceKernelCancelEventFlag(SceUID uid, u32 pattern, u32 numWaitThreadsPtr); + +void __KernelEventFlagInit(); +void __KernelEventFlagDoState(PointerWrap &p); +KernelObject *__KernelEventFlagObject(); diff --git a/Core/HLE/sceKernelInterrupt.cpp b/Core/HLE/sceKernelInterrupt.cpp index bf298e4d40..77e7e707a9 100644 --- a/Core/HLE/sceKernelInterrupt.cpp +++ b/Core/HLE/sceKernelInterrupt.cpp @@ -28,40 +28,9 @@ #include "sceKernelInterrupt.h" #include "sceKernelMutex.h" -struct Interrupt -{ - PSPInterrupt intno; -}; - -// Yeah, this bit is a bit silly. -static int interruptsEnabled = 1; - -static bool inInterrupt; - -void __InterruptsInit() -{ - interruptsEnabled = 1; -} - -void __InterruptsShutdown() -{ -} - -void __DisableInterrupts() -{ - interruptsEnabled = 0; -} - -void __EnableInterrupts() -{ - interruptsEnabled = 1; -} - -bool __InterruptsEnabled() -{ - return interruptsEnabled != 0; -} - +void __DisableInterrupts(); +void __EnableInterrupts(); +bool __InterruptsEnabled(); // InterruptsManager ////////////////////////////////////////////////////////////////////////// @@ -89,6 +58,7 @@ void sceKernelCpuResumeIntr(u32 enable) if (enable) { __EnableInterrupts(); + hleRunInterrupts(); } else { @@ -115,86 +85,31 @@ void sceKernelCpuResumeIntrWithSync(u32 enable) sceKernelCpuResumeIntr(enable); } - - -bool __IsInInterrupt() -{ - return inInterrupt; -} - -bool __CanExecuteInterrupt() -{ - return !inInterrupt; -} - -class AllegrexInterruptHandler; - -struct PendingInterrupt { - AllegrexInterruptHandler *handler; - int arg; - bool hasArg; -}; - - -class AllegrexInterruptHandler -{ -public: - virtual ~AllegrexInterruptHandler() {} - virtual void copyArgsToCPU(const PendingInterrupt &pend) = 0; - virtual void queueUp() = 0; - virtual void queueUpWithArg(int arg) = 0; -}; - -std::list pendingInterrupts; - -class SubIntrHandler : public AllegrexInterruptHandler -{ -public: - SubIntrHandler() {} - virtual void queueUp() - { - if (!enabled) - return; - PendingInterrupt pend; - pend.handler = this; - pend.hasArg = false; - pendingInterrupts.push_back(pend); - } - virtual void queueUpWithArg(int arg) - { - if (!enabled) - return; - PendingInterrupt pend; - pend.handler = this; - pend.arg = arg; - pend.hasArg = true; - pendingInterrupts.push_back(pend); - } - - virtual void copyArgsToCPU(const PendingInterrupt &pend) - { - DEBUG_LOG(CPU, "Entering interrupt handler %08x", handlerAddress); - currentMIPS->pc = handlerAddress; - currentMIPS->r[MIPS_REG_A0] = pend.hasArg ? pend.arg : number; - currentMIPS->r[MIPS_REG_A1] = handlerArg; - // RA is already taken care of - } - - bool enabled; - int number; - u32 handlerAddress; - u32 handlerArg; -}; - class IntrHandler { public: - void add(int subIntrNum, SubIntrHandler handler) + IntrHandler() + : subIntrCreator(NULL) { + } + + SubIntrHandler *add(int subIntrNum) + { + SubIntrHandler *handler; + if (subIntrCreator != NULL) + handler = subIntrCreator(); + else + handler = new SubIntrHandler(); + subIntrHandlers[subIntrNum] = handler; + return handler; } void remove(int subIntrNum) { - subIntrHandlers.erase(subIntrNum); + if (has(subIntrNum)) + { + delete subIntrHandlers[subIntrNum]; + subIntrHandlers.erase(subIntrNum); + } } bool has(int subIntrNum) const { @@ -203,20 +118,26 @@ public: SubIntrHandler *get(int subIntrNum) { if (has(subIntrNum)) - return &subIntrHandlers[subIntrNum]; + return subIntrHandlers[subIntrNum]; else - return 0; - // what to do, what to do... + return NULL; + } + void clear() + { + std::map::iterator it, end; + for (it = subIntrHandlers.begin(), end = subIntrHandlers.end(); it != end; ++it) + delete it->second; + subIntrHandlers.clear(); } void queueUp(int subintr) { // Just call execute on all the subintr handlers for this interrupt. // They will get queued up. - for (std::map::iterator iter = subIntrHandlers.begin(); iter != subIntrHandlers.end(); ++iter) + for (std::map::iterator iter = subIntrHandlers.begin(); iter != subIntrHandlers.end(); ++iter) { if (subintr == -1 || iter->first == subintr) - iter->second.queueUp(); + iter->second->queueUp(); } } @@ -224,31 +145,71 @@ public: { // Just call execute on all the subintr handlers for this interrupt. // They will get queued up. - for (std::map::iterator iter = subIntrHandlers.begin(); iter != subIntrHandlers.end(); ++iter) + for (std::map::iterator iter = subIntrHandlers.begin(); iter != subIntrHandlers.end(); ++iter) { if (subintr == -1 || iter->first == subintr) - iter->second.queueUpWithArg(arg); + iter->second->queueUpWithArg(arg); + } + } + + void setCreator(SubIntrCreator creator) + { + subIntrCreator = creator; + } + + void DoState(PointerWrap &p) + { + // We assume that the same creator has already been registered. + bool hasCreator = subIntrCreator != NULL; + p.Do(hasCreator); + if (hasCreator != (subIntrCreator != NULL)) + { + ERROR_LOG(HLE, "Savestate failure: incompatible sub interrupt handler."); + return; + } + + int n = (int) subIntrHandlers.size(); + p.Do(n); + + if (p.mode == p.MODE_READ) + { + clear(); + for (int i = 0; i < n; ++i) + { + int subIntrNum; + p.Do(subIntrNum); + SubIntrHandler *handler = add(subIntrNum); + handler->DoState(p); + } + } + else + { + std::map::iterator it, end; + for (it = subIntrHandlers.begin(), end = subIntrHandlers.end(); it != end; ++it) + { + p.Do(it->first); + it->second->DoState(p); + } } } private: - std::map subIntrHandlers; + SubIntrCreator subIntrCreator; + std::map subIntrHandlers; }; - class InterruptState { public: - void save() - { - insideInterrupt = __IsInInterrupt(); - __KernelSaveContext(&savedCpu); - } + void save(); + void restore(); + void clear(); - void restore() + void DoState(PointerWrap &p) { - ::inInterrupt = insideInterrupt; - __KernelLoadContext(&savedCpu); + p.Do(insideInterrupt); + p.Do(savedCpu); + p.DoMarker("InterruptState"); } bool insideInterrupt; @@ -261,27 +222,152 @@ public: InterruptState intState; IntrHandler intrHandlers[PSP_NUMBER_INTERRUPTS]; +std::list pendingInterrupts; + +// Yeah, this bit is a bit silly. +static int interruptsEnabled = 1; +static bool inInterrupt; + + +void __InterruptsInit() +{ + interruptsEnabled = 1; + inInterrupt = false; + intState.clear(); +} + +void __InterruptsDoState(PointerWrap &p) +{ + int numInterrupts = PSP_NUMBER_INTERRUPTS; + p.Do(numInterrupts); + if (numInterrupts != PSP_NUMBER_INTERRUPTS) + { + ERROR_LOG(HLE, "Savestate failure: wrong number of interrupts, can't load."); + return; + } + + intState.DoState(p); + PendingInterrupt pi(0, 0); + p.Do(pendingInterrupts, pi); + p.Do(interruptsEnabled); + p.Do(inInterrupt); + p.DoMarker("sceKernelInterrupt"); +} + +void __InterruptsDoStateLate(PointerWrap &p) +{ + // We do these later to ensure the handlers have been registered. + for (int i = 0; i < PSP_NUMBER_INTERRUPTS; ++i) + intrHandlers[i].DoState(p); + p.DoMarker("sceKernelInterrupt Late"); +} + +void __InterruptsShutdown() +{ + for (int i = 0; i < PSP_NUMBER_INTERRUPTS; ++i) + intrHandlers[i].clear(); + pendingInterrupts.clear(); +} + +void __DisableInterrupts() +{ + interruptsEnabled = 0; +} + +void __EnableInterrupts() +{ + interruptsEnabled = 1; +} + +bool __InterruptsEnabled() +{ + return interruptsEnabled != 0; +} + +bool __IsInInterrupt() +{ + return inInterrupt; +} + +bool __CanExecuteInterrupt() +{ + return !inInterrupt; +} + +void InterruptState::save() +{ + insideInterrupt = __IsInInterrupt(); + __KernelSaveContext(&savedCpu); +} + +void InterruptState::restore() +{ + ::inInterrupt = insideInterrupt; + __KernelLoadContext(&savedCpu); +} + +void InterruptState::clear() +{ + insideInterrupt = false; +} // http://forums.ps2dev.org/viewtopic.php?t=5687 // http://www.google.se/url?sa=t&rct=j&q=&esrc=s&source=web&cd=7&ved=0CFYQFjAG&url=http%3A%2F%2Fdev.psnpt.com%2Fredmine%2Fprojects%2Fuofw%2Frepository%2Frevisions%2F65%2Fraw%2Ftrunk%2Finclude%2Finterruptman.h&ei=J4pCUKvyK4nl4QSu-YC4Cg&usg=AFQjCNFxJcgzQnv6dK7aiQlht_BM9grfQQ&sig2=GGk5QUEWI6qouYDoyE07YQ +void SubIntrHandler::queueUp() +{ + if (!enabled) + return; + + pendingInterrupts.push_back(PendingInterrupt(intrNumber, number)); +}; + +void SubIntrHandler::queueUpWithArg(int arg) +{ + if (!enabled) + return; + + pendingInterrupts.push_back(PendingInterrupt(intrNumber, number, arg)); +} + +void SubIntrHandler::copyArgsToCPU(const PendingInterrupt &pend) +{ + DEBUG_LOG(CPU, "Entering interrupt handler %08x", handlerAddress); + currentMIPS->pc = handlerAddress; + currentMIPS->r[MIPS_REG_A0] = pend.hasArg ? pend.arg : number; + currentMIPS->r[MIPS_REG_A1] = handlerArg; + // RA is already taken care of +} + // Returns true if anything was executed. bool __RunOnePendingInterrupt() { - if (inInterrupt) + if (inInterrupt || !interruptsEnabled) { // Already in an interrupt! We'll keep going when it's done. return false; } // Can easily prioritize between different kinds of interrupts if necessary. +retry: if (pendingInterrupts.size()) { + // If we came from CoreTiming::Advance(), we might've come from a waiting thread's callback. + // To avoid "injecting" return values into our saved state, we context switch here. + __KernelSwitchOffThread("interrupt"); + PendingInterrupt pend = pendingInterrupts.front(); - pendingInterrupts.pop_front(); + SubIntrHandler *handler = intrHandlers[pend.intr].get(pend.subintr); + if (handler == NULL) + { + WARN_LOG(HLE, "Ignoring interrupt, already been released."); + pendingInterrupts.pop_front(); + goto retry; + } + intState.save(); - pend.handler->copyArgsToCPU(pend); + handler->copyArgsToCPU(pend); currentMIPS->r[MIPS_REG_RA] = __KernelInterruptReturnAddress(); inInterrupt = true; @@ -294,37 +380,92 @@ bool __RunOnePendingInterrupt() } } -void __TriggerInterrupt(PSPInterrupt intno, int subintr) +void __TriggerRunInterrupts(int type) { - intrHandlers[intno].queueUp(subintr); - DEBUG_LOG(HLE, "Triggering subinterrupts for interrupt %i sub %i (%i in queue)", intno, subintr, pendingInterrupts.size()); - if (!inInterrupt) - __RunOnePendingInterrupt(); + // If interrupts aren't enabled, we run them later. + if (interruptsEnabled && !inInterrupt) + { + if ((type & PSP_INTR_HLE) != 0) + hleRunInterrupts(); + else + __RunOnePendingInterrupt(); + } } -void __TriggerInterruptWithArg(PSPInterrupt intno, int subintr, int arg) +void __TriggerInterrupt(int type, PSPInterrupt intno, int subintr) { - intrHandlers[intno].queueUpWithArg(subintr, arg); - DEBUG_LOG(HLE, "Triggering subinterrupts for interrupt %i sub %i with arg %i (%i in queue)", intno, subintr, arg, pendingInterrupts.size()); - if (!inInterrupt) - __RunOnePendingInterrupt(); + if (interruptsEnabled || (type & PSP_INTR_ONLY_IF_ENABLED) == 0) + { + intrHandlers[intno].queueUp(subintr); + DEBUG_LOG(HLE, "Triggering subinterrupts for interrupt %i sub %i (%i in queue)", intno, subintr, (u32)pendingInterrupts.size()); + __TriggerRunInterrupts(type); + } +} + +void __TriggerInterruptWithArg(int type, PSPInterrupt intno, int subintr, int arg) +{ + if (interruptsEnabled || (type & PSP_INTR_ONLY_IF_ENABLED) == 0) + { + intrHandlers[intno].queueUpWithArg(subintr, arg); + DEBUG_LOG(HLE, "Triggering subinterrupts for interrupt %i sub %i with arg %i (%i in queue)", intno, subintr, arg, + (u32)pendingInterrupts.size()); + __TriggerRunInterrupts(type); + } } void __KernelReturnFromInterrupt() { DEBUG_LOG(CPU, "Left interrupt handler at %08x", currentMIPS->pc); inInterrupt = false; + + // This is what we just ran. + PendingInterrupt pend = pendingInterrupts.front(); + pendingInterrupts.pop_front(); + + SubIntrHandler *handler = intrHandlers[pend.intr].get(pend.subintr); + if (handler != NULL) + handler->handleResult(currentMIPS->r[MIPS_REG_V0]); + else + ERROR_LOG(HLE, "Interrupt released itself? Should not happen."); + // Restore context after running the interrupt. intState.restore(); // All should now be back to normal, including PC. // Alright, let's see if there's any more interrupts queued... - if (!__RunOnePendingInterrupt()) + __KernelReSchedule("return from interrupt"); +} + +void __RegisterSubIntrCreator(u32 intrNumber, SubIntrCreator creator) +{ + intrHandlers[intrNumber].setCreator(creator); +} + +SubIntrHandler *__RegisterSubIntrHandler(u32 intrNumber, u32 subIntrNumber, u32 &error) +{ + SubIntrHandler *subIntrHandler = intrHandlers[intrNumber].add(subIntrNumber); + subIntrHandler->number = subIntrNumber; + subIntrHandler->intrNumber = intrNumber; + error = 0; + return subIntrHandler; +} + +u32 __ReleaseSubIntrHandler(u32 intrNumber, u32 subIntrNumber) +{ + if (!intrHandlers[intrNumber].has(subIntrNumber)) + return -1; + + for (std::list::iterator it = pendingInterrupts.begin(); it != pendingInterrupts.end(); ) { - // Hmmm... - //__KernelReSchedule("return from interrupt"); + if (it->intr == intrNumber && it->subintr == subIntrNumber) + pendingInterrupts.erase(it++); + else + ++it; } + + intrHandlers[intrNumber].remove(subIntrNumber); + return 0; } u32 sceKernelRegisterSubIntrHandler(u32 intrNumber, u32 subIntrNumber, u32 handler, u32 handlerArg) @@ -334,35 +475,31 @@ u32 sceKernelRegisterSubIntrHandler(u32 intrNumber, u32 subIntrNumber, u32 handl if (intrNumber >= PSP_NUMBER_INTERRUPTS) return -1; - SubIntrHandler subIntrHandler; - subIntrHandler.number = subIntrNumber; - subIntrHandler.enabled = false; - subIntrHandler.handlerAddress = handler; - subIntrHandler.handlerArg = handlerArg; - intrHandlers[intrNumber].add(subIntrNumber, subIntrHandler); - return 0; + u32 error; + SubIntrHandler *subIntrHandler = __RegisterSubIntrHandler(intrNumber, subIntrNumber, error); + if (subIntrHandler) + { + subIntrHandler->enabled = false; + subIntrHandler->handlerAddress = handler; + subIntrHandler->handlerArg = handlerArg; + } + return error; } u32 sceKernelReleaseSubIntrHandler(u32 intrNumber, u32 subIntrNumber) { DEBUG_LOG(HLE,"sceKernelReleaseSubIntrHandler(%i, %i)", PARAM(0), PARAM(1)); - // TODO: should check if it's pending and remove it from pending list! (although that's probably unlikely) - if (intrNumber >= PSP_NUMBER_INTERRUPTS) return -1; - if (!intrHandlers[intrNumber].has(subIntrNumber)) - return -1; - - intrHandlers[intrNumber].remove(subIntrNumber); - return 0; + return __ReleaseSubIntrHandler(intrNumber, subIntrNumber); } u32 sceKernelEnableSubIntr(u32 intrNumber, u32 subIntrNumber) { DEBUG_LOG(HLE,"sceKernelEnableSubIntr(%i, %i)", intrNumber, subIntrNumber); - if (intrNumber < 0 || intrNumber >= PSP_NUMBER_INTERRUPTS) + if (intrNumber >= PSP_NUMBER_INTERRUPTS) return -1; if (!intrHandlers[intrNumber].has(subIntrNumber)) @@ -375,7 +512,7 @@ u32 sceKernelEnableSubIntr(u32 intrNumber, u32 subIntrNumber) u32 sceKernelDisableSubIntr(u32 intrNumber, u32 subIntrNumber) { DEBUG_LOG(HLE,"sceKernelDisableSubIntr(%i, %i)", intrNumber, subIntrNumber); - if (intrNumber < 0 || intrNumber >= PSP_NUMBER_INTERRUPTS) + if (intrNumber >= PSP_NUMBER_INTERRUPTS) return -1; if (!intrHandlers[intrNumber].has(subIntrNumber)) @@ -410,15 +547,14 @@ void QueryIntrHandlerInfo() RETURN(0); } -void sceKernelMemset() +// TODO: speedup +u32 sceKernelMemset(u32 addr, u32 fillc, u32 n) { - u32 addr = PARAM(0); - u8 c = PARAM(1) & 0xff; - u32 n = PARAM(2); + u8 c = fillc & 0xff; DEBUG_LOG(HLE, "sceKernelMemset(ptr = %08x, c = %02x, n = %08x)", addr, c, n); for (size_t i = 0; i < n; i++) Memory::Write_U8((u8)c, addr + i); - RETURN(0); /* TODO: verify it should return this */ + return 0; // TODO: verify it should return this } u32 sceKernelMemcpy(u32 dst, u32 src, u32 size) @@ -431,21 +567,21 @@ u32 sceKernelMemcpy(u32 dst, u32 src, u32 size) return 0; } -const HLEFunction Kernel_Library[] = +const HLEFunction Kernel_Library[] = { {0x092968F4,sceKernelCpuSuspendIntr,"sceKernelCpuSuspendIntr"}, {0x5F10D406,WrapV_U, "sceKernelCpuResumeIntr"}, //int oldstat {0x3b84732d,WrapV_U, "sceKernelCpuResumeIntrWithSync"}, {0x47a0b729,sceKernelIsCpuIntrSuspended, "sceKernelIsCpuIntrSuspended"}, //flags - {0xb55249d2,sceKernelIsCpuIntrEnable, "sceKernelIsCpuIntrEnable"}, - {0xa089eca4,sceKernelMemset, "sceKernelMemset"}, - {0xDC692EE3,&WrapI_UI, "sceKernelTryLockLwMutex"}, - {0x37431849,&WrapI_UI, "sceKernelTryLockLwMutex_600"}, - {0xbea46419,&WrapI_UIU, "sceKernelLockLwMutex"}, - {0x1FC64E09,&WrapI_UIU, "sceKernelLockLwMutexCB"}, - {0x15b6446b,&WrapI_UI, "sceKernelUnlockLwMutex"}, - {0x293b45b8,sceKernelGetThreadId, "sceKernelGetThreadId"}, - {0x1839852A,&WrapU_UUU,"sce_paf_private_memcpy"}, + {0xb55249d2,sceKernelIsCpuIntrEnable, "sceKernelIsCpuIntrEnable"}, + {0xa089eca4,WrapU_UUU, "sceKernelMemset"}, + {0xDC692EE3,WrapI_UI, "sceKernelTryLockLwMutex"}, + {0x37431849,WrapI_UI, "sceKernelTryLockLwMutex_600"}, + {0xbea46419,WrapI_UIU, "sceKernelLockLwMutex"}, + {0x1FC64E09,WrapI_UIU, "sceKernelLockLwMutexCB"}, + {0x15b6446b,WrapI_UI, "sceKernelUnlockLwMutex"}, + {0x293b45b8,sceKernelGetThreadId, "sceKernelGetThreadId"}, + {0x1839852A,WrapU_UUU,"sce_paf_private_memcpy"}, }; void Register_Kernel_Library() @@ -454,7 +590,7 @@ void Register_Kernel_Library() } -const HLEFunction InterruptManager[] = +const HLEFunction InterruptManager[] = { {0xCA04A2B9, WrapU_UUUU, "sceKernelRegisterSubIntrHandler"}, {0xD61E6961, WrapU_UU, "sceKernelReleaseSubIntrHandler"}, @@ -463,7 +599,7 @@ const HLEFunction InterruptManager[] = {0x5CB5A78B, 0, "sceKernelSuspendSubIntr"}, {0x7860E0DC, 0, "sceKernelResumeSubIntr"}, {0xFC4374B8, 0, "sceKernelIsSubInterruptOccurred"}, - {0xD2E8363F, 0, "QueryIntrHandlerInfo"}, // No sce prefix for some reason + {0xD2E8363F, QueryIntrHandlerInfo, "QueryIntrHandlerInfo"}, // No sce prefix for some reason {0xEEE43F47, 0, "sceKernelRegisterUserSpaceIntrStack"}, }; diff --git a/Core/HLE/sceKernelInterrupt.h b/Core/HLE/sceKernelInterrupt.h index adc13e5bcd..6b60a7870f 100644 --- a/Core/HLE/sceKernelInterrupt.h +++ b/Core/HLE/sceKernelInterrupt.h @@ -17,6 +17,8 @@ #pragma once +#include "../../Common/ChunkFile.h" + enum PSPInterrupt { PSP_GPIO_INTR = 4, PSP_ATA_INTR = 5, @@ -54,14 +56,70 @@ enum PSPGeSubInterrupts { PSP_GE_SUBINTR_SIGNAL = 15 }; +enum PSPInterruptTriggerType { + // Trigger immediately, for CoreTiming events. + PSP_INTR_IMMEDIATE = 0x0, + // Trigger after the HLE syscall finishes. + PSP_INTR_HLE = 0x1, + // Only trigger (as above) if interrupts are not suspended. + PSP_INTR_ONLY_IF_ENABLED = 0x2, +}; + +struct PendingInterrupt { + PendingInterrupt(int intr_, int subintr_) + : intr(intr_), subintr(subintr_), hasArg(false) {} + PendingInterrupt(int intr_, int subintr_, int arg_) + : intr(intr_), subintr(subintr_), hasArg(true), arg(arg_) {} + + int arg; + bool hasArg; + int intr; + int subintr; +}; + +class SubIntrHandler +{ +public: + SubIntrHandler() {} + virtual ~SubIntrHandler() {} + virtual void queueUp(); + virtual void queueUpWithArg(int arg); + virtual void copyArgsToCPU(const PendingInterrupt &pend); + virtual void handleResult(int result) {} + + virtual void DoState(PointerWrap &p) + { + p.Do(enabled); + p.Do(intrNumber); + p.Do(number); + p.Do(handlerAddress); + p.Do(handlerArg); + p.DoMarker("SubIntrHandler"); + } + + bool enabled; + int intrNumber; + int number; + u32 handlerAddress; + u32 handlerArg; +}; + +typedef SubIntrHandler *(*SubIntrCreator)(); + bool __IsInInterrupt(); void __InterruptsInit(); +void __InterruptsDoState(PointerWrap &p); +void __InterruptsDoStateLate(PointerWrap &p); void __InterruptsShutdown(); -void __TriggerInterrupt(PSPInterrupt intno, int subInterrupts = -1); -void __TriggerInterruptWithArg(PSPInterrupt intno, int subintr, int arg); // For GE "callbacks" +void __TriggerInterrupt(int type, PSPInterrupt intno, int subInterrupts = -1); +void __TriggerInterruptWithArg(int type, PSPInterrupt intno, int subintr, int arg); // For GE "callbacks" bool __RunOnePendingInterrupt(); void __KernelReturnFromInterrupt(); +void __RegisterSubIntrCreator(u32 intrNumber, SubIntrCreator creator); +SubIntrHandler *__RegisterSubIntrHandler(u32 intrNumber, u32 subIntrNumber, u32 &error); +u32 __ReleaseSubIntrHandler(u32 intrNumber, u32 subIntrNumber); + u32 sceKernelRegisterSubIntrHandler(u32 intrNumber, u32 subIntrNumber, u32 handler, u32 handlerArg); u32 sceKernelReleaseSubIntrHandler(u32 intrNumber, u32 subIntrNumber); u32 sceKernelEnableSubIntr(u32 intrNumber, u32 subIntrNumber); diff --git a/Core/HLE/sceKernelMbx.cpp b/Core/HLE/sceKernelMbx.cpp index bcd3b74d84..a36183ecca 100644 --- a/Core/HLE/sceKernelMbx.cpp +++ b/Core/HLE/sceKernelMbx.cpp @@ -30,8 +30,7 @@ const int PSP_MBX_ERROR_DUPLICATE_MSG = 0x800201C9; typedef std::pair MbxWaitingThread; void __KernelMbxTimeout(u64 userdata, int cyclesLate); -bool mbxInitComplete = false; -int mbxWaitTimer = 0; +static int mbxWaitTimer = 0; struct NativeMbx { @@ -154,6 +153,14 @@ struct Mbx : public KernelObject return 0; } + virtual void DoState(PointerWrap &p) + { + p.Do(nmb); + MbxWaitingThread mwt(0,0); + p.Do(waitingThreads, mwt); + p.DoMarker("Mbx"); + } + NativeMbx nmb; std::vector waitingThreads; @@ -161,9 +168,19 @@ struct Mbx : public KernelObject void __KernelMbxInit() { - mbxWaitTimer = CoreTiming::RegisterEvent("MbxTimeout", &__KernelMbxTimeout); + mbxWaitTimer = CoreTiming::RegisterEvent("MbxTimeout", __KernelMbxTimeout); +} - mbxInitComplete = true; +void __KernelMbxDoState(PointerWrap &p) +{ + p.Do(mbxWaitTimer); + CoreTiming::RestoreRegisterEvent(mbxWaitTimer, "MbxTimeout", __KernelMbxTimeout); + p.DoMarker("sceKernelMbx"); +} + +KernelObject *__KernelMbxObject() +{ + return new Mbx; } bool __KernelUnlockMbxForThread(Mbx *m, MbxWaitingThread &th, u32 &error, int result, bool &wokeThreads) @@ -263,9 +280,6 @@ std::vector::iterator __KernelMbxFindPriority(std::vectorAlloc(size, fromEnd, "PMB"); - alloc->ListBlocks(); + + // 0 is used for save states to wake up. + if (size != 0) + { + address = alloc->Alloc(size, fromEnd, "PMB"); + alloc->ListBlocks(); + } } ~PartitionMemoryBlock() { @@ -358,6 +394,14 @@ public: } bool IsValid() {return address != (u32)-1;} BlockAllocator *alloc; + + virtual void DoState(PointerWrap &p) + { + p.Do(address); + p.Do(name); + p.DoMarker("PMB"); + } + u32 address; char name[32]; }; @@ -439,17 +483,169 @@ void sceKernelPrintf() RETURN(0); } -void sceKernelSetCompiledSdkVersion() +void sceKernelSetCompiledSdkVersion(int sdkVersion) { - //pretty sure this only takes one arg - ERROR_LOG(HLE,"UNIMPL sceKernelSetCompiledSdkVersion(%08x)", PARAM(0)); - RETURN(0); + int sdkMainVersion = sdkVersion & 0xFFFF0000; + bool valiSDK = false; + switch(sdkMainVersion) + { + case 0x1000000: + case 0x1050000: + case 0x2000000: + case 0x2050000: + case 0x2060000: + case 0x2070000: + case 0x2080000: + case 0x3000000: + case 0x3010000: + case 0x3030000: + case 0x3040000: + case 0x3050000: + case 0x3060000: + valiSDK = true; + break; + default: + valiSDK = false; + break; + } + + if(valiSDK) + { + sdkVersion_ = sdkVersion; + flags_ |= SCE_KERNEL_HASCOMPILEDSDKVERSION; + } + else + { + ERROR_LOG(HLE,"sceKernelSetCompiledSdkVersion unknown SDK : %x\n",sdkVersion); + } + return; } -void sceKernelSetCompilerVersion() +void sceKernelSetCompiledSdkVersion370(int sdkVersion) { - ERROR_LOG(HLE,"UNIMPL sceKernelSetCompilerVersion(%08x, %08x, %08x, %08x)", PARAM(0),PARAM(1),PARAM(2),PARAM(3)); - RETURN(0); + int sdkMainVersion = sdkVersion & 0xFFFF0000; + if(sdkMainVersion == 0x3070000) + { + sdkVersion_ = sdkVersion; + flags_ |= SCE_KERNEL_HASCOMPILEDSDKVERSION; + } + else + { + ERROR_LOG(HLE,"sceKernelSetCompiledSdkVersion370 unknown SDK : %x\n",sdkVersion); + } + return; +} + +void sceKernelSetCompiledSdkVersion380_390(int sdkVersion) +{ + int sdkMainVersion = sdkVersion & 0xFFFF0000; + if(sdkMainVersion == 0x3080000 || sdkMainVersion == 0x3090000) + { + sdkVersion_ = sdkVersion; + flags_ |= SCE_KERNEL_HASCOMPILEDSDKVERSION; + } + else + { + ERROR_LOG(HLE,"sceKernelSetCompiledSdkVersion380_390 unknown SDK : %x\n",sdkVersion); + } + return; +} + +void sceKernelSetCompiledSdkVersion395(int sdkVersion) +{ + int sdkMainVersion = sdkVersion & 0xFFFFFF00; + if(sdkMainVersion == 0x4000000 + || sdkMainVersion == 0x4000100 + || sdkMainVersion == 0x4000500 + || sdkMainVersion == 0x3090500 + || sdkMainVersion == 0x3090600) + { + sdkVersion_ = sdkVersion; + flags_ |= SCE_KERNEL_HASCOMPILEDSDKVERSION; + } + else + { + ERROR_LOG(HLE,"sceKernelSetCompiledSdkVersion395 unknown SDK : %x\n",sdkVersion); + } + return; +} + +void sceKernelSetCompiledSdkVersion600_602(int sdkVersion) +{ + int sdkMainVersion = sdkVersion & 0xFFFF0000; + if(sdkMainVersion == 0x6010000 + || sdkMainVersion == 0x6000000 + || sdkMainVersion == 0x6020000) + { + sdkVersion_ = sdkVersion; + flags_ |= SCE_KERNEL_HASCOMPILEDSDKVERSION; + } + else + { + ERROR_LOG(HLE,"sceKernelSetCompiledSdkVersion600_602 unknown SDK : %x\n",sdkVersion); + } + return; +} + +void sceKernelSetCompiledSdkVersion603_605(int sdkVersion) +{ + int sdkMainVersion = sdkVersion & 0xFFFF0000; + if(sdkMainVersion == 0x6040000 + || sdkMainVersion == 0x6030000 + || sdkMainVersion == 0x6050000) + { + sdkVersion_ = sdkVersion; + flags_ |= SCE_KERNEL_HASCOMPILEDSDKVERSION; + } + else + { + ERROR_LOG(HLE,"sceKernelSetCompiledSdkVersion603_605 unknown SDK : %x\n",sdkVersion); + } + return; +} + +void sceKernelSetCompiledSdkVersion606(int sdkVersion) +{ + int sdkMainVersion = sdkVersion & 0xFFFF0000; + if(sdkMainVersion == 0x6060000) + { + sdkVersion_ = sdkVersion; + flags_ |= SCE_KERNEL_HASCOMPILEDSDKVERSION; + } + else + { + ERROR_LOG(HLE,"sceKernelSetCompiledSdkVersion606 unknown SDK : %x\n",sdkVersion); + } + return; +} + +int sceKernelGetCompiledSdkVersion() +{ + if(!(flags_ & SCE_KERNEL_HASCOMPILEDSDKVERSION)) + return 0; + return sdkVersion_; +} + +void sceKernelSetCompilerVersion(int version) +{ + compilerVersion_ = version; + flags_ |= SCE_KERNEL_HASCOMPILERVERSION; +} + +KernelObject *__KernelMemoryFPLObject() +{ + return new FPL; +} + +KernelObject *__KernelMemoryVPLObject() +{ + return new VPL; +} + +KernelObject *__KernelMemoryPMBObject() +{ + // TODO: We could theoretically handle kernelMemory too, but we don't support that now anyway. + return new PartitionMemoryBlock(&userMemory, 0, true); } // VPL = variable length memory pool @@ -642,58 +838,54 @@ void sceKernelReferVplStatus() RETURN(0); } -void AllocMemoryBlock() { - const char *pname = Memory::GetCharPointer(PARAM(0)); - int type = PARAM(1); - u32 size = PARAM(2); - int paramsAddr = PARAM(3); - - DEBUG_LOG(HLE,"AllocMemoryBlock(SysMemUserForUser_FE707FDF)(%s, %i, %i, %08x)", pname, type, size, paramsAddr); + +// TODO: Make proper kernel objects for these instead of using the UID as a pointer. + +u32 AllocMemoryBlock(const char *pname, u32 type, u32 size, u32 paramsAddr) { // Just support allocating a block in the user region. - u32 blockPtr = userMemory.Alloc(size, false, pname); + u32 blockPtr = userMemory.Alloc(size, type == 1, pname); + INFO_LOG(HLE,"%08x=AllocMemoryBlock(SysMemUserForUser_FE707FDF)(%s, %i, %08x, %08x)", blockPtr, pname, type, size, paramsAddr); // Create a UID object??? Nah, let's just us the UID itself (hack!) - RETURN(blockPtr); + return blockPtr; } -void FreeMemoryBlock() { - SceUID uid = PARAM(0); - DEBUG_LOG(HLE, "FreeMemoryBlock(%i)", uid); +u32 FreeMemoryBlock(u32 uid) { + INFO_LOG(HLE, "FreeMemoryBlock(%08x)", uid); userMemory.Free(uid); - RETURN(0); + return 0; } -void GetMemoryBlockPtr() { - SceUID uid = PARAM(0); - DEBUG_LOG(HLE, "GetMemoryBlockPtr(%i)", uid); - RETURN(uid); +u32 GetMemoryBlockPtr(u32 uid, u32 addr) { + INFO_LOG(HLE, "GetMemoryBlockPtr(%08x, %08x)", uid, addr); + Memory::Write_U32(uid, addr); + return 0; } -const HLEFunction SysMemUserForUser[] = -{ +const HLEFunction SysMemUserForUser[] = { {0xA291F107,sceKernelMaxFreeMemSize, "sceKernelMaxFreeMemSize"}, {0xF919F628,sceKernelTotalFreeMemSize,"sceKernelTotalFreeMemSize"}, - {0x3FC9AE6A,sceKernelDevkitVersion, "sceKernelDevkitVersion"}, + {0x3FC9AE6A,sceKernelDevkitVersion, "sceKernelDevkitVersion"}, {0x237DBD4F,sceKernelAllocPartitionMemory,"sceKernelAllocPartitionMemory"}, //(int size) ? {0xB6D61D02,sceKernelFreePartitionMemory,"sceKernelFreePartitionMemory"}, //(void *ptr) ? {0x9D9A5BA1,sceKernelGetBlockHeadAddr,"sceKernelGetBlockHeadAddr"}, //(void *ptr) ? {0x13a5abef,sceKernelPrintf,"sceKernelPrintf 0x13a5abef"}, - {0x7591c7db,sceKernelSetCompiledSdkVersion,"sceKernelSetCompiledSdkVersion"}, - {0x342061E5,0,"sceKernelSetCompiledSdkVersion370"}, - {0x315AD3A0,0,"sceKernelSetCompiledSdkVersion380_390"}, - {0xEBD5C3E6,0,"sceKernelSetCompiledSdkVersion395"}, - {0xf77d77cb,sceKernelSetCompilerVersion,"sceKernelSetCompilerVersion"}, - {0x35669d4c,0,"sceKernelSetCompiledSdkVersion600_602"}, //?? - {0x1b4217bc,0,"sceKernelSetCompiledSdkVersion603_605"}, - {0x358ca1bb,0,"sceKernelSetCompiledSdkVersion606"}, + {0x7591c7db,&WrapV_I,"sceKernelSetCompiledSdkVersion"}, + {0x342061E5,&WrapV_I,"sceKernelSetCompiledSdkVersion370"}, + {0x315AD3A0,&WrapV_I,"sceKernelSetCompiledSdkVersion380_390"}, + {0xEBD5C3E6,&WrapV_I,"sceKernelSetCompiledSdkVersion395"}, + {0xf77d77cb,&WrapV_I,"sceKernelSetCompilerVersion"}, + {0x35669d4c,&WrapV_I,"sceKernelSetCompiledSdkVersion600_602"}, //?? + {0x1b4217bc,&WrapV_I,"sceKernelSetCompiledSdkVersion603_605"}, + {0x358ca1bb,&WrapV_I,"sceKernelSetCompiledSdkVersion606"}, + {0xfc114573,&WrapI_V,"sceKernelGetCompiledSdkVersion"}, // Obscure raw block API - {0xDB83A952,GetMemoryBlockPtr,"SysMemUserForUser_DB83A952"}, // GetMemoryBlockAddr - {0x91DE343C,0,"SysMemUserForUser_91DE343C"}, - {0x50F61D8A,FreeMemoryBlock,"SysMemUserForUser_50F61D8A"}, // FreeMemoryBlock - {0xFE707FDF,AllocMemoryBlock,"SysMemUserForUser_FE707FDF"}, // AllocMemoryBlock + {0xDB83A952,WrapU_UU,"SysMemUserForUser_DB83A952"}, // GetMemoryBlockAddr + {0x50F61D8A,WrapU_U,"SysMemUserForUser_50F61D8A"}, // FreeMemoryBlock + {0xFE707FDF,WrapU_CUUU,"SysMemUserForUser_FE707FDF"}, // AllocMemoryBlock }; diff --git a/Core/HLE/sceKernelMemory.h b/Core/HLE/sceKernelMemory.h index 247f683d7f..0b6602d756 100644 --- a/Core/HLE/sceKernelMemory.h +++ b/Core/HLE/sceKernelMemory.h @@ -18,6 +18,7 @@ #pragma once #include "../Util/BlockAllocator.h" +#include "sceKernel.h" //todo: "real" memory block allocator, @@ -29,7 +30,11 @@ extern BlockAllocator userMemory; extern BlockAllocator kernelMemory; void __KernelMemoryInit(); +void __KernelMemoryDoState(PointerWrap &p); void __KernelMemoryShutdown(); +KernelObject *__KernelMemoryFPLObject(); +KernelObject *__KernelMemoryVPLObject(); +KernelObject *__KernelMemoryPMBObject(); void sceKernelCreateVpl(); void sceKernelDeleteVpl(); @@ -49,5 +54,6 @@ void sceKernelFreeFpl(); void sceKernelCancelFpl(); void sceKernelReferFplStatus(); +int sceKernelGetCompiledSdkVersion(); void Register_SysMemUserForUser(); diff --git a/Core/HLE/sceKernelModule.cpp b/Core/HLE/sceKernelModule.cpp index 3624d5f523..8f35526859 100644 --- a/Core/HLE/sceKernelModule.cpp +++ b/Core/HLE/sceKernelModule.cpp @@ -117,13 +117,45 @@ public: nm.entry_addr); } static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_MODULE; } - int GetIDType() const { return 0; } + int GetIDType() const { return PPSSPP_KERNEL_TMID_Module; } + + virtual void DoState(PointerWrap &p) + { + p.Do(nm); + p.Do(memoryBlockAddr); + p.DoMarker("Module"); + } NativeModule nm; u32 memoryBlockAddr; }; +KernelObject *__KernelModuleObject() +{ + return new Module; +} + +class AfterModuleEntryCall : public Action { +public: + AfterModuleEntryCall() {} + SceUID moduleID_; + u32 retValAddr; + virtual void run(); + virtual void DoState(PointerWrap &p) { + p.Do(moduleID_); + p.Do(retValAddr); + p.DoMarker("AfterModuleEntryCall"); + } + static Action *Create() { + return new AfterModuleEntryCall; + } +}; + +void AfterModuleEntryCall::run() { + Memory::Write_U32(retValAddr, currentMIPS->r[2]); +} + ////////////////////////////////////////////////////////////////////////// // MODULES ////////////////////////////////////////////////////////////////////////// @@ -156,10 +188,24 @@ struct SceKernelSMOption { ////////////////////////////////////////////////////////////////////////// // STATE BEGIN +static int actionAfterModule; static SceUID mainModuleID; // hack // STATE END ////////////////////////////////////////////////////////////////////////// +void __KernelModuleInit() +{ + actionAfterModule = __KernelRegisterActionType(AfterModuleEntryCall::Create); +} + +void __KernelModuleDoState(PointerWrap &p) +{ + p.Do(mainModuleID); + p.Do(actionAfterModule); + __KernelRestoreActionType(actionAfterModule, AfterModuleEntryCall::Create); + p.DoMarker("sceKernelModule"); +} + Module *__KernelLoadELFFromPtr(const u8 *ptr, u32 loadAddress, std::string *error_string) { Module *module = new Module; @@ -249,7 +295,7 @@ Module *__KernelLoadELFFromPtr(const u8 *ptr, u32 loadAddress, std::string *erro if (sceModuleInfoSection != -1) modinfo = (PspModuleInfo *)Memory::GetPointer(reader.GetSectionAddr(sceModuleInfoSection)); else - modinfo = (PspModuleInfo *)Memory::GetPointer(reader.GetVaddr() + (reader.GetSegmentPaddr(0) & 0x7FFFFFFF) - reader.GetSegmentOffset(0)); + modinfo = (PspModuleInfo *)Memory::GetPointer(reader.GetSegmentVaddr(0) + (reader.GetSegmentPaddr(0) & 0x7FFFFFFF) - reader.GetSegmentOffset(0)); // Check for module blacklist - we don't allow games to load these modules from disc // as we have HLE implementations and the originals won't run in the emu because they @@ -448,13 +494,13 @@ bool __KernelLoadPBP(const char *filename, std::string *error_string) in.seekg(offsets[5]); //in.read((char*)&id,4); { - u8 *temp = new u8[1024*1024*8]; - in.read((char*)temp, 1024*1024*8); - Module *module = __KernelLoadELFFromPtr(temp, PSP_GetDefaultLoadAddress(), error_string); + u8 *elftemp = new u8[1024*1024*8]; + in.read((char*)elftemp, 1024*1024*8); + Module *module = __KernelLoadELFFromPtr(elftemp, PSP_GetDefaultLoadAddress(), error_string); if (!module) return false; mipsr4k.pc = module->nm.entry_addr; - delete [] temp; + delete [] elftemp; } in.close(); return true; @@ -515,6 +561,7 @@ bool __KernelLoadExec(const char *filename, SceKernelLoadExecParam *param, std:: if (__KernelIsRunning()) __KernelShutdown(); + __KernelModuleInit(); __KernelInit(); PSPFileInfo info = pspFileSystem.GetFileInfo(filename); @@ -599,8 +646,7 @@ u32 sceKernelLoadModule(const char *name, u32 flags) return SCE_KERNEL_ERROR_NOFILE; } - if (!size) - { + if (!size) { ERROR_LOG(LOADER, "sceKernelLoadModule(%s, %08x): Module file is size 0", name, flags); return SCE_KERNEL_ERROR_ILLEGAL_OBJECT; } @@ -610,10 +656,8 @@ u32 sceKernelLoadModule(const char *name, u32 flags) SceKernelLMOption *lmoption = 0; int position = 0; // TODO: Use position to decide whether to load high or low - if (PARAM(2)) - { - SceKernelLMOption *lmoption = (SceKernelLMOption *)Memory::GetPointer(PARAM(2)); - + if (PARAM(2)) { + lmoption = (SceKernelLMOption *)Memory::GetPointer(PARAM(2)); } Module *module = 0; @@ -635,27 +679,13 @@ u32 sceKernelLoadModule(const char *name, u32 flags) INFO_LOG(HLE,"%i=sceKernelLoadModule(name=%s,flag=%08x,%08x,%08x,%08x,position = %08x)", module->GetUID(),name,flags, lmoption->size,lmoption->mpidtext,lmoption->mpiddata,lmoption->position); - } - else - { + } else { INFO_LOG(HLE,"%i=sceKernelLoadModule(name=%s,flag=%08x,(...))", module->GetUID(), name, flags); } return module->GetUID(); } -class AfterModuleEntryCall : public Action { -public: - AfterModuleEntryCall() {} - Module *module_; - u32 retValAddr; - virtual void run(); -}; - -void AfterModuleEntryCall::run() { - Memory::Write_U32(retValAddr, currentMIPS->r[2]); -} - void sceKernelStartModule(u32 moduleId, u32 argsize, u32 argAddr, u32 returnValueAddr, u32 optionAddr) { ERROR_LOG(HLE,"UNIMPL sceKernelStartModule(%d,asize=%08x,aptr=%08x,retptr=%08x,%08x)", diff --git a/Core/HLE/sceKernelModule.h b/Core/HLE/sceKernelModule.h index 2652e1e55f..732bd43173 100644 --- a/Core/HLE/sceKernelModule.h +++ b/Core/HLE/sceKernelModule.h @@ -20,6 +20,9 @@ #include "sceKernel.h" #include "HLE.h" +KernelObject *__KernelModuleObject(); +void __KernelModuleDoState(PointerWrap &p); + u32 __KernelGetModuleGP(SceUID module); bool __KernelLoadExec(const char *filename, SceKernelLoadExecParam *param, std::string *error_string); diff --git a/Core/HLE/sceKernelMsgPipe.cpp b/Core/HLE/sceKernelMsgPipe.cpp index ca8fa76a73..2f820a3a3a 100644 --- a/Core/HLE/sceKernelMsgPipe.cpp +++ b/Core/HLE/sceKernelMsgPipe.cpp @@ -56,10 +56,12 @@ struct MsgPipe : public KernelObject static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_MPPID; } int GetIDType() const { return SCE_KERNEL_TMID_Mpipe; } - NativeMsgPipe nmp; - - std::vector sendWaitingThreads; - std::vector receiveWaitingThreads; + MsgPipe() : buffer(NULL) {} + ~MsgPipe() + { + if (buffer != NULL) + delete [] buffer; + } void AddWaitingThread(std::vector &list, SceUID id, u32 addr, u32 size, int waitMode, u32 transferredBytesAddr, bool usePrio) { @@ -98,7 +100,7 @@ struct MsgPipe : public KernelObject if (sendWaitingThreads.empty()) return; MsgPipeWaitingThread *thread = &sendWaitingThreads.front(); - if (nmp.freeSize >= thread->bufSize) + if ((u32) nmp.freeSize >= thread->bufSize) { // Put all the data to the buffer memcpy(buffer + (nmp.bufSize - nmp.freeSize), Memory::GetPointer(thread->bufAddr), thread->bufSize); @@ -126,7 +128,7 @@ struct MsgPipe : public KernelObject if (receiveWaitingThreads.empty()) return; MsgPipeWaitingThread *thread = &receiveWaitingThreads.front(); - if (nmp.bufSize - nmp.freeSize >= thread->bufSize) + if ((u32) nmp.bufSize - (u32) nmp.freeSize >= thread->bufSize) { // Get the needed data from the buffer Memory::Memcpy(thread->bufAddr, buffer, thread->bufSize); @@ -150,9 +152,36 @@ struct MsgPipe : public KernelObject } } + virtual void DoState(PointerWrap &p) + { + p.Do(nmp); + MsgPipeWaitingThread mpwt1 = {0}, mpwt2 = {0}; + p.Do(sendWaitingThreads, mpwt1); + p.Do(receiveWaitingThreads, mpwt2); + bool hasBuffer = buffer != NULL; + p.Do(hasBuffer); + if (hasBuffer) + { + if (buffer == NULL) + buffer = new u8[nmp.bufSize]; + p.DoArray(buffer, nmp.bufSize); + } + p.DoMarker("MsgPipe"); + } + + NativeMsgPipe nmp; + + std::vector sendWaitingThreads; + std::vector receiveWaitingThreads; + u8 *buffer; }; +KernelObject *__KernelMsgPipeObject() +{ + return new MsgPipe; +} + void sceKernelCreateMsgPipe() { const char *name = Memory::GetCharPointer(PARAM(0)); @@ -194,10 +223,6 @@ void sceKernelDeleteMsgPipe() RETURN(error); return; } - if (m->buffer != 0) - { - delete [] m->buffer; - } for (u32 i = 0; i < m->sendWaitingThreads.size(); i++) { __KernelResumeThreadFromWait(m->sendWaitingThreads[i].id); @@ -271,7 +296,7 @@ void __KernelSendMsgPipe(MsgPipe *m, u32 sendBufAddr, u32 sendSize, int waitMode } else { - if (sendSize <= m->nmp.freeSize) + if (sendSize <= (u32) m->nmp.freeSize) { memcpy(m->buffer + (m->nmp.bufSize - m->nmp.freeSize), Memory::GetPointer(sendBufAddr), sendSize); m->nmp.freeSize -= sendSize; @@ -445,7 +470,7 @@ void __KernelReceiveMsgPipe(MsgPipe *m, u32 receiveBufAddr, u32 receiveSize, int else { // Enough data in the buffer: copy just the needed amount of data - if (receiveSize <= m->nmp.bufSize - m->nmp.freeSize) + if (receiveSize <= (u32) m->nmp.bufSize - (u32) m->nmp.freeSize) { Memory::Memcpy(receiveBufAddr, m->buffer, receiveSize); m->nmp.freeSize += receiveSize; diff --git a/Core/HLE/sceKernelMsgPipe.h b/Core/HLE/sceKernelMsgPipe.h index 0f262038b8..46f812f4ee 100644 --- a/Core/HLE/sceKernelMsgPipe.h +++ b/Core/HLE/sceKernelMsgPipe.h @@ -26,4 +26,6 @@ void sceKernelReceiveMsgPipe(); void sceKernelReceiveMsgPipeCB(); void sceKernelTryReceiveMsgPipe(); void sceKernelCancelMsgPipe(); -void sceKernelReferMsgPipeStatus(); \ No newline at end of file +void sceKernelReferMsgPipeStatus(); + +KernelObject *__KernelMsgPipeObject(); diff --git a/Core/HLE/sceKernelMutex.cpp b/Core/HLE/sceKernelMutex.cpp index 77d2a957c6..a591e4ce6d 100644 --- a/Core/HLE/sceKernelMutex.cpp +++ b/Core/HLE/sceKernelMutex.cpp @@ -62,6 +62,15 @@ struct Mutex : public KernelObject const char *GetTypeName() {return "Mutex";} static u32 GetMissingErrorCode() { return PSP_MUTEX_ERROR_NO_SUCH_MUTEX; } int GetIDType() const { return SCE_KERNEL_TMID_Mutex; } + + virtual void DoState(PointerWrap &p) + { + p.Do(nm); + SceUID dv = 0; + p.Do(waitingThreads, dv); + p.DoMarker("Mutex"); + } + NativeMutex nm; std::vector waitingThreads; }; @@ -103,31 +112,61 @@ struct LwMutex : public KernelObject const char *GetTypeName() {return "LwMutex";} static u32 GetMissingErrorCode() { return PSP_LWMUTEX_ERROR_NO_SUCH_LWMUTEX; } int GetIDType() const { return SCE_KERNEL_TMID_LwMutex; } + + virtual void DoState(PointerWrap &p) + { + p.Do(nm); + SceUID dv = 0; + p.Do(waitingThreads, dv); + p.DoMarker("LwMutex"); + } + NativeLwMutex nm; std::vector waitingThreads; }; -bool mutexInitComplete = false; -int mutexWaitTimer = 0; -int lwMutexWaitTimer = 0; +static int mutexWaitTimer = 0; +static int lwMutexWaitTimer = 0; // Thread -> Mutex locks for thread end. typedef std::multimap MutexMap; -MutexMap mutexHeldLocks; +static MutexMap mutexHeldLocks; void __KernelMutexInit() { - mutexWaitTimer = CoreTiming::RegisterEvent("MutexTimeout", &__KernelMutexTimeout); - lwMutexWaitTimer = CoreTiming::RegisterEvent("LwMutexTimeout", &__KernelLwMutexTimeout); + mutexWaitTimer = CoreTiming::RegisterEvent("MutexTimeout", __KernelMutexTimeout); + lwMutexWaitTimer = CoreTiming::RegisterEvent("LwMutexTimeout", __KernelLwMutexTimeout); - // TODO: Install on first mutex (if it's slow?) __KernelListenThreadEnd(&__KernelMutexThreadEnd); +} - mutexInitComplete = true; +void __KernelMutexDoState(PointerWrap &p) +{ + p.Do(mutexWaitTimer); + CoreTiming::RestoreRegisterEvent(mutexWaitTimer, "MutexTimeout", __KernelMutexTimeout); + p.Do(lwMutexWaitTimer); + CoreTiming::RestoreRegisterEvent(lwMutexWaitTimer, "LwMutexTimeout", __KernelLwMutexTimeout); + p.Do(mutexHeldLocks); + p.DoMarker("sceKernelMutex"); +} + +KernelObject *__KernelMutexObject() +{ + return new Mutex; +} + +KernelObject *__KernelLwMutexObject() +{ + return new LwMutex; +} + +void __KernelMutexShutdown() +{ + mutexHeldLocks.clear(); } void __KernelMutexAcquireLock(Mutex *mutex, int count, SceUID thread) { -#if _DEBUG +#if defined(_DEBUG) std::pair locked = mutexHeldLocks.equal_range(thread); for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) _dbg_assert_msg_(HLE, (*iter).second != mutex->GetUID(), "Thread %d / mutex %d wasn't removed from mutexHeldLocks properly.", thread, mutex->GetUID()); @@ -184,9 +223,6 @@ std::vector::iterator __KernelMutexFindPriority(std::vector &wai int sceKernelCreateMutex(const char *name, u32 attr, int initialCount, u32 optionsPtr) { - if (!mutexInitComplete) - __KernelMutexInit(); - if (!name) { WARN_LOG(HLE, "%08x=sceKernelCreateMutex(): invalid name", SCE_KERNEL_ERROR_ERROR); @@ -500,9 +536,6 @@ int sceKernelUnlockMutex(SceUID id, int count) int sceKernelCreateLwMutex(u32 workareaPtr, const char *name, u32 attr, int initialCount, u32 optionsPtr) { - if (!mutexInitComplete) - __KernelMutexInit(); - if (!name) { WARN_LOG(HLE, "%08x=sceKernelCreateLwMutex(): invalid name", SCE_KERNEL_ERROR_ERROR); @@ -856,4 +889,4 @@ int sceKernelUnlockLwMutex(u32 workareaPtr, int count) Memory::WriteStruct(workareaPtr, &workarea); return 0; -} \ No newline at end of file +} diff --git a/Core/HLE/sceKernelMutex.h b/Core/HLE/sceKernelMutex.h index 8d7c09e9f2..18eeb4fafb 100644 --- a/Core/HLE/sceKernelMutex.h +++ b/Core/HLE/sceKernelMutex.h @@ -34,4 +34,10 @@ int sceKernelUnlockLwMutex(u32 workareaPtr, int count); void __KernelMutexTimeout(u64 userdata, int cyclesLate); void __KernelLwMutexTimeout(u64 userdata, int cyclesLate); -void __KernelMutexThreadEnd(SceUID thread); \ No newline at end of file +void __KernelMutexThreadEnd(SceUID thread); + +void __KernelMutexInit(); +void __KernelMutexDoState(PointerWrap &p); +void __KernelMutexShutdown(); +KernelObject *__KernelMutexObject(); +KernelObject *__KernelLwMutexObject(); diff --git a/Core/HLE/sceKernelSemaphore.cpp b/Core/HLE/sceKernelSemaphore.cpp index 5b9bdd3d7a..f9e5cd4e0b 100644 --- a/Core/HLE/sceKernelSemaphore.cpp +++ b/Core/HLE/sceKernelSemaphore.cpp @@ -57,17 +57,35 @@ struct Semaphore : public KernelObject static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_SEMID; } int GetIDType() const { return SCE_KERNEL_TMID_Semaphore; } + virtual void DoState(PointerWrap &p) + { + p.Do(ns); + SceUID dv = 0; + p.Do(waitingThreads, dv); + p.DoMarker("Semaphore"); + } + NativeSemaphore ns; std::vector waitingThreads; }; -bool semaInitComplete = false; -int semaWaitTimer = 0; +static int semaWaitTimer = 0; void __KernelSemaInit() { - semaWaitTimer = CoreTiming::RegisterEvent("SemaphoreTimeout", &__KernelSemaTimeout); - semaInitComplete = true; + semaWaitTimer = CoreTiming::RegisterEvent("SemaphoreTimeout", __KernelSemaTimeout); +} + +void __KernelSemaDoState(PointerWrap &p) +{ + p.Do(semaWaitTimer); + CoreTiming::RestoreRegisterEvent(semaWaitTimer, "SemaphoreTimeout", __KernelSemaTimeout); + p.DoMarker("sceKernelSema"); +} + +KernelObject *__KernelSemaphoreObject() +{ + return new Semaphore; } // Returns whether the thread should be removed. @@ -137,7 +155,6 @@ std::vector::iterator __KernelSemaFindPriority(std::vector &wait return best; } -// int sceKernelCancelSema(SceUID id, int newCount, int *numWaitThreads); int sceKernelCancelSema(SceUID id, int newCount, u32 numWaitThreadsPtr) { DEBUG_LOG(HLE, "sceKernelCancelSema(%i)", id); @@ -170,12 +187,8 @@ int sceKernelCancelSema(SceUID id, int newCount, u32 numWaitThreadsPtr) } } -//SceUID sceKernelCreateSema(const char *name, SceUInt attr, int initVal, int maxVal, SceKernelSemaOptParam *option); int sceKernelCreateSema(const char* name, u32 attr, int initVal, int maxVal, u32 optionPtr) { - if (!semaInitComplete) - __KernelSemaInit(); - if (!name) { WARN_LOG(HLE, "%08x=sceKernelCreateSema(): invalid name", SCE_KERNEL_ERROR_ERROR); @@ -209,7 +222,6 @@ int sceKernelCreateSema(const char* name, u32 attr, int initVal, int maxVal, u32 return id; } -//int sceKernelDeleteSema(SceUID semaid); int sceKernelDeleteSema(SceUID id) { DEBUG_LOG(HLE, "sceKernelDeleteSema(%i)", id); @@ -231,7 +243,6 @@ int sceKernelDeleteSema(SceUID id) } } -//int sceKernelDeleteSema(SceUID semaid, SceKernelSemaInfo *info); int sceKernelReferSemaStatus(SceUID id, u32 infoPtr) { u32 error; @@ -248,8 +259,7 @@ int sceKernelReferSemaStatus(SceUID id, u32 infoPtr) return error; } } - -//int sceKernelSignalSema(SceUID semaid, int signal); + int sceKernelSignalSema(SceUID id, int signal) { u32 error; @@ -364,15 +374,13 @@ int __KernelWaitSema(SceUID id, int wantedCount, u32 timeoutPtr, const char *bad } } -//int sceKernelWaitSema(SceUID semaid, int signal, SceUInt *timeout); int sceKernelWaitSema(SceUID id, int wantedCount, u32 timeoutPtr) { DEBUG_LOG(HLE, "sceKernelWaitSema(%i, %i, %i)", id, wantedCount, timeoutPtr); return __KernelWaitSema(id, wantedCount, timeoutPtr, "sceKernelWaitSema: Trying to wait for invalid semaphore %i", false); -} +} -//int sceKernelWaitSemaCB(SceUID semaid, int signal, SceUInt *timeout); int sceKernelWaitSemaCB(SceUID id, int wantedCount, u32 timeoutPtr) { DEBUG_LOG(HLE, "sceKernelWaitSemaCB(%i, %i, %i)", id, wantedCount, timeoutPtr); diff --git a/Core/HLE/sceKernelSemaphore.h b/Core/HLE/sceKernelSemaphore.h index 32150c6307..14df34cbed 100644 --- a/Core/HLE/sceKernelSemaphore.h +++ b/Core/HLE/sceKernelSemaphore.h @@ -27,3 +27,7 @@ int sceKernelWaitSema(SceUID semaid, int signal, u32 timeoutPtr); int sceKernelWaitSemaCB(SceUID semaid, int signal, u32 timeoutPtr); void __KernelSemaTimeout(u64 userdata, int cycleslate); + +void __KernelSemaInit(); +void __KernelSemaDoState(PointerWrap &p); +KernelObject *__KernelSemaphoreObject(); diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index a1e0976b82..b247736056 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -37,43 +37,42 @@ enum { - ERROR_KERNEL_THREAD_ALREADY_DORMANT = 0x800201a2, - ERROR_KERNEL_THREAD_ALREADY_SUSPEND = 0x800201a3, - ERROR_KERNEL_THREAD_IS_NOT_DORMANT = 0x800201a4, - ERROR_KERNEL_THREAD_IS_NOT_SUSPEND = 0x800201a5, - ERROR_KERNEL_THREAD_IS_NOT_WAIT = 0x800201a6, + ERROR_KERNEL_THREAD_ALREADY_DORMANT = 0x800201a2, + ERROR_KERNEL_THREAD_ALREADY_SUSPEND = 0x800201a3, + ERROR_KERNEL_THREAD_IS_NOT_DORMANT = 0x800201a4, + ERROR_KERNEL_THREAD_IS_NOT_SUSPEND = 0x800201a5, + ERROR_KERNEL_THREAD_IS_NOT_WAIT = 0x800201a6, }; enum { - PSP_THREAD_ATTR_USER = 0x80000000, - PSP_THREAD_ATTR_USBWLAN = 0xa0000000, - PSP_THREAD_ATTR_VSH = 0xc0000000, - PSP_THREAD_ATTR_KERNEL = 0x00001000, - PSP_THREAD_ATTR_VFPU = 0x00004000, // TODO: Should not bother saving VFPU context except when switching between two thread that has this attribute - PSP_THREAD_ATTR_SCRATCH_SRAM = 0x00008000, // Save/restore scratch as part of context??? - PSP_THREAD_ATTR_NO_FILLSTACK = 0x00100000, // TODO: No filling of 0xff - PSP_THREAD_ATTR_CLEAR_STACK = 0x00200000, // TODO: Clear thread stack when deleted + PSP_THREAD_ATTR_USER = 0x80000000, + PSP_THREAD_ATTR_USBWLAN = 0xa0000000, + PSP_THREAD_ATTR_VSH = 0xc0000000, + PSP_THREAD_ATTR_KERNEL = 0x00001000, + PSP_THREAD_ATTR_VFPU = 0x00004000, // TODO: Should not bother saving VFPU context except when switching between two thread that has this attribute + PSP_THREAD_ATTR_SCRATCH_SRAM = 0x00008000, // Save/restore scratch as part of context??? + PSP_THREAD_ATTR_NO_FILLSTACK = 0x00100000, // TODO: No filling of 0xff + PSP_THREAD_ATTR_CLEAR_STACK = 0x00200000, // TODO: Clear thread stack when deleted }; -const char *waitTypeStrings[] = -{ - "NONE", - "Sleep", - "Delay", - "Sema", - "EventFlag", - "Mbx", - "Vpl", - "Fpl", - "", - "ThreadEnd", // These are nonstandard wait types - "AudioChannel", - "Umd", - "Vblank", - "Mutex", - "LwMutex", - "Ctrl", +const char *waitTypeStrings[] = { + "NONE", + "Sleep", + "Delay", + "Sema", + "EventFlag", + "Mbx", + "Vpl", + "Fpl", + "", + "ThreadEnd", // These are nonstandard wait types + "AudioChannel", + "Umd", + "Vblank", + "Mutex", + "LwMutex", + "Ctrl", }; struct SceKernelSysClock { @@ -81,7 +80,6 @@ struct SceKernelSysClock { u32 hi; }; - struct NativeCallback { SceUInt size; @@ -115,6 +113,18 @@ public: static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_CBID; } int GetIDType() const { return SCE_KERNEL_TMID_Callback; } + virtual void DoState(PointerWrap &p) + { + p.Do(nc); + p.Do(savedPC); + p.Do(savedRA); + p.Do(savedV0); + p.Do(savedV1); + p.Do(savedIdRegister); + p.Do(forceDelete); + p.DoMarker("Callback"); + } + NativeCallback nc; u32 savedPC; @@ -164,7 +174,155 @@ struct ThreadWaitInfo { u32 timeoutPtr; }; -class ActionAfterMipsCall; +// Owns outstanding MIPS calls and provides a way to get them by ID. +class MipsCallManager { +public: + MipsCallManager() : idGen_(0) {} + int add(MipsCall *call) { + int id = genId(); + calls_.insert(std::pair(id, call)); + return id; + } + MipsCall *get(int id) { + return calls_[id]; + } + MipsCall *pop(int id) { + MipsCall *temp = calls_[id]; + calls_.erase(id); + return temp; + } + void clear() { + std::map::iterator it, end; + for (it = calls_.begin(), end = calls_.end(); it != end; ++it) { + delete it->second; + } + calls_.clear(); + idGen_ = 0; + } + + int registerActionType(ActionCreator creator) { + types_.push_back(creator); + return types_.size() - 1; + } + + void restoreActionType(int actionType, ActionCreator creator) { + if (actionType >= (int) types_.size()) + types_.resize(actionType + 1, NULL); + types_[actionType] = creator; + } + + Action *createActionByType(int actionType) { + if (actionType < (int) types_.size() && types_[actionType] != NULL) { + Action *a = types_[actionType](); + a->actionTypeID = actionType; + return a; + } + return NULL; + } + + void DoState(PointerWrap &p) { + + int n = (int) calls_.size(); + p.Do(n); + + if (p.mode == p.MODE_READ) { + clear(); + for (int i = 0; i < n; ++i) { + int k; + p.Do(k); + MipsCall *call = new MipsCall(); + call->DoState(p); + calls_[k] = call; + } + } else { + std::map::iterator it, end; + for (it = calls_.begin(), end = calls_.end(); it != end; ++it) { + p.Do(it->first); + it->second->DoState(p); + } + } + + p.Do(idGen_); + p.DoMarker("MipsCallManager"); + } + +private: + int genId() { return ++idGen_; } + std::map calls_; + std::vector types_; + int idGen_; +}; + +class ActionAfterMipsCall : public Action +{ +public: + virtual void run(); + + static Action *Create() + { + return new ActionAfterMipsCall; + } + + virtual void DoState(PointerWrap &p) + { + p.Do(threadID); + p.Do(status); + p.Do(waitType); + p.Do(waitID); + p.Do(waitInfo); + p.Do(isProcessingCallbacks); + + p.DoMarker("ActionAfterMipsCall"); + + int chainedActionType = 0; + if (chainedAction != NULL) + chainedActionType = chainedAction->actionTypeID; + p.Do(chainedActionType); + + if (chainedActionType != 0) + { + if (p.mode == p.MODE_READ) + chainedAction = __KernelCreateAction(chainedActionType); + chainedAction->DoState(p); + } + } + + SceUID threadID; + + // Saved thread state + int status; + WaitType waitType; + int waitID; + ThreadWaitInfo waitInfo; + bool isProcessingCallbacks; + + Action *chainedAction; +}; + +class ActionAfterCallback : public Action +{ +public: + ActionAfterCallback() {} + virtual void run(); + + static Action *Create() + { + return new ActionAfterCallback; + } + + void setCallback(SceUID cbId_) + { + cbId = cbId_; + } + + void DoState(PointerWrap &p) + { + p.Do(cbId); + p.DoMarker("ActionAfterCallback"); + } + + SceUID cbId; +}; class Thread : public KernelObject { @@ -185,9 +343,9 @@ public: nt.waitID, waitInfo.waitValue); } - + static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_THID; } - + int GetIDType() const { return SCE_KERNEL_TMID_Thread; } bool AllocateStack(u32 &stackSize) @@ -255,7 +413,34 @@ public: bool isReady() const { return (nt.status & THREADSTATUS_DORMANT) != 0; } bool isWaiting() const { return (nt.status & THREADSTATUS_WAIT) != 0; } bool isSuspended() const { return (nt.status & THREADSTATUS_SUSPEND) != 0; } - + + virtual void DoState(PointerWrap &p) + { + p.Do(nt); + p.Do(waitInfo); + p.Do(sleeping); + p.Do(moduleId); + p.Do(isProcessingCallbacks); + p.Do(currentCallbackId); + p.Do(context); + + u32 numCallbacks = THREAD_CALLBACK_NUM_TYPES; + p.Do(numCallbacks); + if (numCallbacks != THREAD_CALLBACK_NUM_TYPES) + ERROR_LOG(HLE, "Unable to load state: different kernel object storage."); + + for (size_t i = 0; i < THREAD_CALLBACK_NUM_TYPES; ++i) + { + p.Do(registeredCallbacks[i]); + p.Do(readyCallbacks[i]); + } + + p.Do(pendingMipsCalls); + p.Do(stackBlock); + + p.DoMarker("Thread"); + } + NativeThread nt; ThreadWaitInfo waitInfo; @@ -269,7 +454,7 @@ public: std::set registeredCallbacks[THREAD_CALLBACK_NUM_TYPES]; std::list readyCallbacks[THREAD_CALLBACK_NUM_TYPES]; - + std::list pendingMipsCalls; u32 stackBlock; @@ -278,8 +463,6 @@ public: void __KernelExecuteMipsCallOnCurrentThread(int callId, bool reschedAfter); -int g_inCbCount = 0; - Thread *__KernelCreateThread(SceUID &id, SceUID moduleID, const char *name, u32 entryPoint, u32 priority, int stacksize, u32 attr); void __KernelResetThread(Thread *t); void __KernelCancelWakeup(SceUID threadID); @@ -288,12 +471,13 @@ bool __KernelCheckThreadCallbacks(Thread *thread, bool force); ////////////////////////////////////////////////////////////////////////// //STATE BEGIN ////////////////////////////////////////////////////////////////////////// -Thread *currentThread; +int g_inCbCount = 0; +SceUID currentThread; u32 idleThreadHackAddr; u32 threadReturnHackAddr; u32 cbReturnHackAddr; u32 intReturnHackAddr; -std::vector threadqueue; //Change to SceUID +std::vector threadqueue; std::vector threadEndListeners; SceUID threadIdleID[2]; @@ -302,6 +486,9 @@ int eventScheduledWakeup; bool dispatchEnabled = true; +MipsCallManager mipsCalls; +int actionAfterCallback; +int actionAfterMipsCall; // This seems nasty SceUID curModule; @@ -310,61 +497,154 @@ SceUID curModule; //STATE END ////////////////////////////////////////////////////////////////////////// +int __KernelRegisterActionType(ActionCreator creator) +{ + return mipsCalls.registerActionType(creator); +} + +void __KernelRestoreActionType(int actionType, ActionCreator creator) +{ + mipsCalls.restoreActionType(actionType, creator); +} + +Action *__KernelCreateAction(int actionType) +{ + return mipsCalls.createActionByType(actionType); +} + +void MipsCall::DoState(PointerWrap &p) +{ + p.Do(entryPoint); + p.Do(cbId); + p.DoArray(args, ARRAY_SIZE(args)); + p.Do(numArgs); + p.Do(savedIdRegister); + p.Do(savedRa); + p.Do(savedPc); + p.Do(savedV0); + p.Do(savedV1); + p.Do(returnVoid); + p.Do(tag); + p.Do(savedId); + p.Do(reschedAfter); + + p.DoMarker("MipsCall"); + + int actionTypeID = 0; + if (doAfter != NULL) + actionTypeID = doAfter->actionTypeID; + p.Do(actionTypeID); + if (actionTypeID != 0) + { + if (p.mode == p.MODE_READ) + doAfter = __KernelCreateAction(actionTypeID); + doAfter->DoState(p); + } +} // TODO: Should move to this wrapper so we can keep the current thread as a SceUID instead // of a dangerous raw pointer. Thread *__GetCurrentThread() { - return currentThread; + u32 error; + if (currentThread != 0) + return kernelObjects.Get(currentThread, error); + else + return NULL; } u32 __KernelMipsCallReturnAddress() { - return cbReturnHackAddr; + return cbReturnHackAddr; } u32 __KernelInterruptReturnAddress() { - return intReturnHackAddr; + return intReturnHackAddr; } void hleScheduledWakeup(u64 userdata, int cyclesLate); void __KernelThreadingInit() { - u32 blockSize = 4 * 4 + 4 * 2 * 3; // One 16-byte thread plus 3 8-byte "hacks" + u32 blockSize = 4 * 4 + 4 * 2 * 3; // One 16-byte thread plus 3 8-byte "hacks" dispatchEnabled = true; + g_inCbCount = 0; idleThreadHackAddr = kernelMemory.Alloc(blockSize, false, "threadrethack"); - // Make sure it got allocated where we expect it... at the very start of kernel RAM - //CHECK_EQ(idleThreadHackAddr & 0x3FFFFFFF, 0x08000000); + // Make sure it got allocated where we expect it... at the very start of kernel RAM + //CHECK_EQ(idleThreadHackAddr & 0x3FFFFFFF, 0x08000000); - // Yeah, this is straight out of JPCSP, I should be ashamed. - Memory::Write_U32(MIPS_MAKE_ADDIU(MIPS_REG_A0, MIPS_REG_ZERO, 0), idleThreadHackAddr); - Memory::Write_U32(MIPS_MAKE_LUI(MIPS_REG_RA, 0x0800), idleThreadHackAddr + 4); - Memory::Write_U32(MIPS_MAKE_JR_RA(), idleThreadHackAddr + 8); - //Memory::Write_U32(MIPS_MAKE_SYSCALL("ThreadManForUser", "sceKernelDelayThread"), idleThreadHackAddr + 12); - Memory::Write_U32(MIPS_MAKE_SYSCALL("FakeSysCalls", "_sceKernelIdle"), idleThreadHackAddr + 12); - Memory::Write_U32(MIPS_MAKE_BREAK(), idleThreadHackAddr + 16); + // Yeah, this is straight out of JPCSP, I should be ashamed. + Memory::Write_U32(MIPS_MAKE_ADDIU(MIPS_REG_A0, MIPS_REG_ZERO, 0), idleThreadHackAddr); + Memory::Write_U32(MIPS_MAKE_LUI(MIPS_REG_RA, 0x0800), idleThreadHackAddr + 4); + Memory::Write_U32(MIPS_MAKE_JR_RA(), idleThreadHackAddr + 8); + //Memory::Write_U32(MIPS_MAKE_SYSCALL("ThreadManForUser", "sceKernelDelayThread"), idleThreadHackAddr + 12); + Memory::Write_U32(MIPS_MAKE_SYSCALL("FakeSysCalls", "_sceKernelIdle"), idleThreadHackAddr + 12); + Memory::Write_U32(MIPS_MAKE_BREAK(), idleThreadHackAddr + 16); - threadReturnHackAddr = idleThreadHackAddr + 20; + threadReturnHackAddr = idleThreadHackAddr + 20; WriteSyscall("FakeSysCalls", NID_THREADRETURN, threadReturnHackAddr); - cbReturnHackAddr = threadReturnHackAddr + 8; - WriteSyscall("FakeSysCalls", NID_CALLBACKRETURN, cbReturnHackAddr); + cbReturnHackAddr = threadReturnHackAddr + 8; + WriteSyscall("FakeSysCalls", NID_CALLBACKRETURN, cbReturnHackAddr); - intReturnHackAddr = cbReturnHackAddr + 8; - WriteSyscall("FakeSysCalls", NID_INTERRUPTRETURN, intReturnHackAddr); + intReturnHackAddr = cbReturnHackAddr + 8; + WriteSyscall("FakeSysCalls", NID_INTERRUPTRETURN, intReturnHackAddr); eventScheduledWakeup = CoreTiming::RegisterEvent("ScheduledWakeup", &hleScheduledWakeup); + actionAfterMipsCall = __KernelRegisterActionType(ActionAfterMipsCall::Create); + actionAfterCallback = __KernelRegisterActionType(ActionAfterCallback::Create); - // Create the two idle threads, as well. With the absolute minimal possible priority. - // 4096 stack size - don't know what the right value is. Hm, if callbacks are ever to run on these threads... - __KernelResetThread(__KernelCreateThread(threadIdleID[0], 0, "idle0", idleThreadHackAddr, 0x7f, 4096, PSP_THREAD_ATTR_KERNEL)); - __KernelResetThread(__KernelCreateThread(threadIdleID[1], 0, "idle1", idleThreadHackAddr, 0x7f, 4096, PSP_THREAD_ATTR_KERNEL)); - // These idle threads are later started in LoadExec, which calls __KernelStartIdleThreads below. + // Create the two idle threads, as well. With the absolute minimal possible priority. + // 4096 stack size - don't know what the right value is. Hm, if callbacks are ever to run on these threads... + __KernelResetThread(__KernelCreateThread(threadIdleID[0], 0, "idle0", idleThreadHackAddr, 0x7f, 4096, PSP_THREAD_ATTR_KERNEL)); + __KernelResetThread(__KernelCreateThread(threadIdleID[1], 0, "idle1", idleThreadHackAddr, 0x7f, 4096, PSP_THREAD_ATTR_KERNEL)); + // These idle threads are later started in LoadExec, which calls __KernelStartIdleThreads below. - __KernelListenThreadEnd(__KernelCancelWakeup); + __KernelListenThreadEnd(__KernelCancelWakeup); +} + +void __KernelThreadingDoState(PointerWrap &p) +{ + p.Do(g_inCbCount); + p.Do(idleThreadHackAddr); + p.Do(threadReturnHackAddr); + p.Do(cbReturnHackAddr); + p.Do(intReturnHackAddr); + + p.Do(currentThread); + SceUID dv = 0; + p.Do(threadqueue, dv); + p.DoArray(threadIdleID, ARRAY_SIZE(threadIdleID)); + p.Do(dispatchEnabled); + p.Do(curModule); + + p.Do(eventScheduledWakeup); + CoreTiming::RestoreRegisterEvent(eventScheduledWakeup, "ScheduledWakeup", &hleScheduledWakeup); + p.Do(actionAfterMipsCall); + __KernelRestoreActionType(actionAfterMipsCall, ActionAfterMipsCall::Create); + p.Do(actionAfterCallback); + __KernelRestoreActionType(actionAfterCallback, ActionAfterCallback::Create); + + p.DoMarker("sceKernelThread"); +} + +void __KernelThreadingDoStateLate(PointerWrap &p) +{ + // We do this late to give modules time to register actions. + mipsCalls.DoState(p); + p.DoMarker("sceKernelThread Late"); +} + +KernelObject *__KernelThreadObject() +{ + return new Thread; +} + +KernelObject *__KernelCallbackObject() +{ + return new Callback; } void __KernelListenThreadEnd(ThreadCallback callback) @@ -384,38 +664,65 @@ void __KernelFireThreadEnd(Thread *thread) void __KernelStartIdleThreads() { - for (int i = 0; i < 2; i++) - { - u32 error; - Thread *t = kernelObjects.Get(threadIdleID[i], error); - t->nt.gpreg = __KernelGetModuleGP(curModule); - t->context.r[MIPS_REG_GP] = t->nt.gpreg; - //t->context.pc += 4; // ADJUSTPC - t->nt.status = THREADSTATUS_READY; - } + for (int i = 0; i < 2; i++) + { + u32 error; + Thread *t = kernelObjects.Get(threadIdleID[i], error); + t->nt.gpreg = __KernelGetModuleGP(curModule); + t->context.r[MIPS_REG_GP] = t->nt.gpreg; + //t->context.pc += 4; // ADJUSTPC + t->nt.status = THREADSTATUS_READY; + } +} + +bool __KernelSwitchOffThread(const char *reason) +{ + if (!reason) + reason = "switch off thread"; + + SceUID threadID = currentThread; + + if (threadID != threadIdleID[0] && threadID != threadIdleID[1]) + { + u32 error; + // Idle 0 chosen entirely arbitrarily. + Thread *t = kernelObjects.Get(threadIdleID[0], error); + if (t) + { + __KernelSwitchContext(t, reason); + return true; + } + else + ERROR_LOG(HLE, "Unable to switch to idle thread."); + } + + return false; } void __KernelIdle() { - CoreTiming::Idle(); - // Advance must happen between Idle and Reschedule, so that threads that were waiting for something - // that was triggered at the end of the Idle period must get a chance to be scheduled. - CoreTiming::Advance(); + CoreTiming::Idle(); + // Advance must happen between Idle and Reschedule, so that threads that were waiting for something + // that was triggered at the end of the Idle period must get a chance to be scheduled. + CoreTiming::Advance(); - // In Advance, we might trigger an interrupt such as vblank. - // If we end up in an interrupt, we don't want to reschedule. - // However, we have to reschedule... damn. - __KernelReSchedule("idle"); + // In Advance, we might trigger an interrupt such as vblank. + // If we end up in an interrupt, we don't want to reschedule. + // However, we have to reschedule... damn. + __KernelReSchedule("idle"); } void __KernelThreadingShutdown() { kernelMemory.Free(threadReturnHackAddr); + threadqueue.clear(); + threadEndListeners.clear(); + mipsCalls.clear(); threadReturnHackAddr = 0; - cbReturnHackAddr = 0; + cbReturnHackAddr = 0; currentThread = 0; intReturnHackAddr = 0; - threadqueue.clear(); + curModule = 0; } const char *__KernelGetThreadName(SceUID threadID) @@ -497,29 +804,29 @@ void sceKernelReferThreadStatus() void sceKernelGetThreadExitStatus() { - SceUID threadID = PARAM(0); - if (threadID == 0) - threadID = __KernelGetCurThread(); + SceUID threadID = PARAM(0); + if (threadID == 0) + threadID = __KernelGetCurThread(); - u32 error; - Thread *t = kernelObjects.Get(threadID, error); - if (t) - { - if (t->nt.status == THREADSTATUS_DORMANT) // TODO: can be dormant before starting, too, need to avoid that - { - DEBUG_LOG(HLE,"sceKernelGetThreadExitStatus(%i)", threadID); - RETURN(t->nt.exitStatus); - } - else - { - RETURN(SCE_KERNEL_ERROR_NOT_DORMANT); - } - } - else - { - ERROR_LOG(HLE,"sceKernelGetThreadExitStatus Error %08x", error); - RETURN(SCE_KERNEL_ERROR_UNKNOWN_THID); - } + u32 error; + Thread *t = kernelObjects.Get(threadID, error); + if (t) + { + if (t->nt.status == THREADSTATUS_DORMANT) // TODO: can be dormant before starting, too, need to avoid that + { + DEBUG_LOG(HLE,"sceKernelGetThreadExitStatus(%i)", threadID); + RETURN(t->nt.exitStatus); + } + else + { + RETURN(SCE_KERNEL_ERROR_NOT_DORMANT); + } + } + else + { + ERROR_LOG(HLE,"sceKernelGetThreadExitStatus Error %08x", error); + RETURN(SCE_KERNEL_ERROR_UNKNOWN_THID); + } } u32 sceKernelGetThreadmanIdType(u32 uid) { @@ -545,9 +852,9 @@ u32 sceKernelGetThreadmanIdList(u32 type, u32 readBufPtr, u32 readBufSize, u32 i return SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT; } - for (size_t i = 0; i < std::min(readBufSize, threadqueue.size()); i++) + for (size_t i = 0; i < std::min((size_t)readBufSize, threadqueue.size()); i++) { - Memory::Write_U32(threadqueue[i]->GetUID(), readBufPtr + i * 4); + Memory::Write_U32(threadqueue[i], readBufPtr + i * 4); } Memory::Write_U32(threadqueue.size(), idCountPtr); return 0; @@ -573,10 +880,10 @@ void __KernelSaveContext(ThreadContext *ctx) ctx->lo = currentMIPS->lo; ctx->pc = currentMIPS->pc; ctx->fpcond = currentMIPS->fpcond; - // ctx->fcr0 = currentMIPS->fcr0; - // ctx->fcr31 = currentMIPS->fcr31; + // ctx->fcr0 = currentMIPS->fcr0; + // ctx->fcr31 = currentMIPS->fcr31; - // TODO: Make VFPU saving optional/delayed, only necessary between VFPU-attr-marked threads + // TODO: Make VFPU saving optional/delayed, only necessary between VFPU-attr-marked threads } // Loads a CPU context @@ -599,8 +906,8 @@ void __KernelLoadContext(ThreadContext *ctx) currentMIPS->lo = ctx->lo; currentMIPS->pc = ctx->pc; currentMIPS->fpcond = ctx->fpcond; - // currentMIPS->fcr0 = ctx->fcr0; - // currentMIPS->fcr31 = ctx->fcr31; + // currentMIPS->fcr0 = ctx->fcr0; + // currentMIPS->fcr31 = ctx->fcr31; } u32 __KernelResumeThreadFromWait(SceUID threadID) @@ -643,10 +950,11 @@ bool __KernelTriggerWait(WaitType type, int id, bool useRetVal, int retVal, bool { bool doneAnything = false; - for (std::vector::iterator iter = threadqueue.begin(); iter != threadqueue.end(); iter++) + u32 error; + for (std::vector::iterator iter = threadqueue.begin(); iter != threadqueue.end(); iter++) { - Thread *t = *iter; - if (t->isWaitingFor(type, id)) + Thread *t = kernelObjects.Get(*iter, error); + if (t && t->isWaitingFor(type, id)) { // This thread was waiting for the triggered object. t->resumeFromWait(); @@ -686,12 +994,13 @@ void __KernelWaitCurThread(WaitType type, SceUID waitID, u32 waitValue, u32 time if (g_inCbCount > 0) WARN_LOG(HLE, "UNTESTED - waiting within a callback, probably bad mojo."); - currentThread->nt.waitID = waitID; - currentThread->nt.waitType = type; - __KernelChangeThreadState(currentThread, THREADSTATUS_WAIT); - currentThread->nt.numReleases++; - currentThread->waitInfo.waitValue = waitValue; - currentThread->waitInfo.timeoutPtr = timeoutPtr; + Thread *thread = __GetCurrentThread(); + thread->nt.waitID = waitID; + thread->nt.waitType = type; + __KernelChangeThreadState(thread, THREADSTATUS_WAIT); + thread->nt.numReleases++; + thread->waitInfo.waitValue = waitValue; + thread->waitInfo.timeoutPtr = timeoutPtr; // TODO: Remove this once all callers are cleaned up. RETURN(0); //pretend all went OK @@ -699,7 +1008,7 @@ void __KernelWaitCurThread(WaitType type, SceUID waitID, u32 waitValue, u32 time // TODO: time waster char temp[256]; sprintf(temp, "started wait %s", waitTypeStrings[(int)type]); - + hleReSchedule(processCallbacks, temp); // TODO: Remove thread from Ready queue? } @@ -722,21 +1031,21 @@ void __KernelCancelWakeup(SceUID threadID) void __KernelRemoveFromThreadQueue(Thread *t) { - for (size_t i = 0; i < threadqueue.size(); i++) - { - if (threadqueue[i] == t) - { + for (size_t i = 0; i < threadqueue.size(); i++) + { + if (threadqueue[i] == t->GetUID()) + { DEBUG_LOG(HLE, "Deleted thread %p (%i) from thread queue", t, t->GetUID()); - threadqueue.erase(threadqueue.begin() + i); - return; - } - } + threadqueue.erase(threadqueue.begin() + i); + return; + } + } } Thread *__KernelNextThread() { // round-robin scheduler // seems to work ? - // not accurate! + // not accurate! int bestthread = -1; int prio = 0xffffff; @@ -750,12 +1059,13 @@ Thread *__KernelNextThread() { } } + u32 error; for (size_t i = 0; i < threadqueue.size(); i++) { next = (next + 1) % threadqueue.size(); - Thread *t = threadqueue[next]; - if (t->nt.currentPriority < prio) + Thread *t = kernelObjects.Get(threadqueue[next], error); + if (t && t->nt.currentPriority < prio) { if (t->nt.status & THREADSTATUS_READY) { @@ -766,7 +1076,7 @@ Thread *__KernelNextThread() { } if (bestthread != -1) - return threadqueue[bestthread]; + return kernelObjects.Get(threadqueue[bestthread], error); else return 0; } @@ -814,67 +1124,65 @@ retry: void __KernelReSchedule(bool doCallbacks, const char *reason) { - Thread *thread = currentThread; + Thread *thread = __GetCurrentThread(); if (doCallbacks) { if (thread) thread->isProcessingCallbacks = doCallbacks; } __KernelReSchedule(reason); - if (doCallbacks && thread == currentThread) { + if (doCallbacks && thread != NULL && thread->GetUID() == currentThread) { if (thread->isRunning()) { thread->isProcessingCallbacks = false; } } } - - ////////////////////////////////////////////////////////////////////////// // Thread Management ////////////////////////////////////////////////////////////////////////// void sceKernelCheckThreadStack() { - u32 error; - Thread *t = kernelObjects.Get(__KernelGetCurThread(), error); - u32 diff = abs((long)((s64)t->stackBlock - (s64)currentMIPS->r[MIPS_REG_SP])); - ERROR_LOG(HLE, "%i=sceKernelCheckThreadStack()", diff); + u32 error; + Thread *t = kernelObjects.Get(__KernelGetCurThread(), error); + u32 diff = abs((long)((s64)t->stackBlock - (s64)currentMIPS->r[MIPS_REG_SP])); + ERROR_LOG(HLE, "%i=sceKernelCheckThreadStack()", diff); RETURN(diff); //Blatant lie } void ThreadContext::reset() { - for (int i = 0; i<32; i++) - { - r[i] = 0; - f[i] = 0.0f; - } - for (int i = 0; i<128; i++) - { - v[i] = 0.0f; - } - for (int i = 0; i<15; i++) - { - vfpuCtrl[i] = 0x00000000; - } - vfpuCtrl[VFPU_CTRL_SPREFIX] = 0xe4; // neutral - vfpuCtrl[VFPU_CTRL_TPREFIX] = 0xe4; // neutral - vfpuCtrl[VFPU_CTRL_DPREFIX] = 0x0; // neutral - vfpuCtrl[VFPU_CTRL_CC] = 0x3f; - vfpuCtrl[VFPU_CTRL_INF4] = 0; - vfpuCtrl[VFPU_CTRL_RCX0] = 0x3f800001; - vfpuCtrl[VFPU_CTRL_RCX1] = 0x3f800002; - vfpuCtrl[VFPU_CTRL_RCX2] = 0x3f800004; - vfpuCtrl[VFPU_CTRL_RCX3] = 0x3f800008; - vfpuCtrl[VFPU_CTRL_RCX4] = 0x3f800000; - vfpuCtrl[VFPU_CTRL_RCX5] = 0x3f800000; - vfpuCtrl[VFPU_CTRL_RCX6] = 0x3f800000; - vfpuCtrl[VFPU_CTRL_RCX7] = 0x3f800000; - fpcond = 0; - fcr0 = 0; - fcr31 = 0; - hi = 0; - lo = 0; + for (int i = 0; i<32; i++) + { + r[i] = 0; + f[i] = 0.0f; + } + for (int i = 0; i<128; i++) + { + v[i] = 0.0f; + } + for (int i = 0; i<15; i++) + { + vfpuCtrl[i] = 0x00000000; + } + vfpuCtrl[VFPU_CTRL_SPREFIX] = 0xe4; // neutral + vfpuCtrl[VFPU_CTRL_TPREFIX] = 0xe4; // neutral + vfpuCtrl[VFPU_CTRL_DPREFIX] = 0x0; // neutral + vfpuCtrl[VFPU_CTRL_CC] = 0x3f; + vfpuCtrl[VFPU_CTRL_INF4] = 0; + vfpuCtrl[VFPU_CTRL_RCX0] = 0x3f800001; + vfpuCtrl[VFPU_CTRL_RCX1] = 0x3f800002; + vfpuCtrl[VFPU_CTRL_RCX2] = 0x3f800004; + vfpuCtrl[VFPU_CTRL_RCX3] = 0x3f800008; + vfpuCtrl[VFPU_CTRL_RCX4] = 0x3f800000; + vfpuCtrl[VFPU_CTRL_RCX5] = 0x3f800000; + vfpuCtrl[VFPU_CTRL_RCX6] = 0x3f800000; + vfpuCtrl[VFPU_CTRL_RCX7] = 0x3f800000; + fpcond = 0; + fcr0 = 0; + fcr31 = 0; + hi = 0; + lo = 0; } void __KernelResetThread(Thread *t) @@ -903,7 +1211,7 @@ Thread *__KernelCreateThread(SceUID &id, SceUID moduleId, const char *name, u32 Thread *t = new Thread; id = kernelObjects.Create(t); - threadqueue.push_back(t); + threadqueue.push_back(id); memset(&t->nt, 0xCD, sizeof(t->nt)); @@ -935,19 +1243,21 @@ void __KernelSetupRootThread(SceUID moduleID, int args, const char *argp, int pr curModule = moduleID; //grab mips regs SceUID id; - currentThread = __KernelCreateThread(id, moduleID, "root", currentMIPS->pc, prio, stacksize, attr); - __KernelResetThread(currentThread); - currentThread->nt.status = THREADSTATUS_READY; // do not schedule + Thread *thread = __KernelCreateThread(id, moduleID, "root", currentMIPS->pc, prio, stacksize, attr); + __KernelResetThread(thread); - strcpy(currentThread->nt.name, "root"); + currentThread = id; + thread->nt.status = THREADSTATUS_READY; // do not schedule - __KernelLoadContext(¤tThread->context); + strcpy(thread->nt.name, "root"); + + __KernelLoadContext(&thread->context); mipsr4k.r[MIPS_REG_A0] = args; mipsr4k.r[MIPS_REG_SP] -= 256; u32 location = mipsr4k.r[MIPS_REG_SP]; mipsr4k.r[MIPS_REG_A1] = location; for (int i = 0; i < args; i++) - Memory::Write_U8(argp[i], location + i); + Memory::Write_U8(argp[i], location + i); } @@ -957,7 +1267,7 @@ int sceKernelCreateThread(const char *threadName, u32 entry, u32 prio, int stack __KernelCreateThread(id, curModule, threadName, entry, prio, stacksize, attr); INFO_LOG(HLE, "%i = sceKernelCreateThread(name=\"%s\", entry=%08x, prio=%x, stacksize=%i)", id, threadName, entry, prio, stacksize); if (optionAddr != 0) - WARN_LOG(HLE, "sceKernelCreateThread: unsupported options parameter.", threadName); + WARN_LOG(HLE, "sceKernelCreateThread(name=\"%s\"): unsupported options parameter %08x", threadName, optionAddr); return id; } @@ -965,7 +1275,7 @@ int sceKernelCreateThread(const char *threadName, u32 entry, u32 prio, int stack // int sceKernelStartThread(SceUID threadToStartID, SceSize argSize, void *argBlock) int sceKernelStartThread(SceUID threadToStartID, u32 argSize, u32 argBlockPtr) { - if (threadToStartID != currentThread->GetUID()) + if (threadToStartID != currentThread) { u32 error; Thread *startThread = kernelObjects.Get(threadToStartID, error); @@ -992,12 +1302,12 @@ int sceKernelStartThread(SceUID threadToStartID, u32 argSize, u32 argBlockPtr) if (argBlockPtr && argSize > 0) { startThread->context.r[MIPS_REG_A0] = argSize; - startThread->context.r[MIPS_REG_A1] = sp; + startThread->context.r[MIPS_REG_A1] = sp; } else { startThread->context.r[MIPS_REG_A0] = 0; - startThread->context.r[MIPS_REG_A1] = 0; + startThread->context.r[MIPS_REG_A1] = 0; } startThread->context.r[MIPS_REG_GP] = startThread->nt.gpreg; @@ -1027,7 +1337,7 @@ void sceKernelGetThreadStackFreeSize() INFO_LOG(HLE,"sceKernelGetThreadStackFreeSize(%i)", threadID); if (threadID == 0) - thread = currentThread; + thread = __GetCurrentThread(); else { u32 error; @@ -1040,30 +1350,33 @@ void sceKernelGetThreadStackFreeSize() } } - // Scan the stack for 0xFF - int sz = 0; - for (u32 addr = thread->stackBlock; addr < thread->stackBlock + thread->nt.stackSize; addr++) - { - if (Memory::Read_U8(addr) != 0xFF) - break; - sz++; - } + // Scan the stack for 0xFF + int sz = 0; + for (u32 addr = thread->stackBlock; addr < thread->stackBlock + thread->nt.stackSize; addr++) + { + if (Memory::Read_U8(addr) != 0xFF) + break; + sz++; + } - RETURN(sz & ~3); + RETURN(sz & ~3); } // Internal function void __KernelReturnFromThread() { - INFO_LOG(HLE,"__KernelReturnFromThread : %s", currentThread->GetName()); + Thread *thread = __GetCurrentThread(); + _dbg_assert_msg_(HLE, thread != NULL, "Returned from a NULL thread."); + + INFO_LOG(HLE,"__KernelReturnFromThread : %s", thread->GetName()); // TEMPORARY HACK: kill the stack of the root thread early: - if (!strcmp(currentThread->GetName(), "root")) { - currentThread->FreeStack(); + if (!strcmp(thread->GetName(), "root")) { + thread->FreeStack(); } - currentThread->nt.exitStatus = currentThread->context.r[2]; - currentThread->nt.status = THREADSTATUS_DORMANT; - __KernelFireThreadEnd(currentThread); + thread->nt.exitStatus = thread->context.r[2]; + thread->nt.status = THREADSTATUS_DORMANT; + __KernelFireThreadEnd(thread); // TODO: Need to remove the thread from any ready queues. @@ -1077,10 +1390,13 @@ void __KernelReturnFromThread() void sceKernelExitThread() { + Thread *thread = __GetCurrentThread(); + _dbg_assert_msg_(HLE, thread != NULL, "Exited from a NULL thread."); + ERROR_LOG(HLE,"sceKernelExitThread FAKED"); - currentThread->nt.status = THREADSTATUS_DORMANT; - currentThread->nt.exitStatus = PARAM(0); - __KernelFireThreadEnd(currentThread); + thread->nt.status = THREADSTATUS_DORMANT; + thread->nt.exitStatus = PARAM(0); + __KernelFireThreadEnd(thread); //Find threads that waited for me // Wake them @@ -1092,46 +1408,49 @@ void sceKernelExitThread() void _sceKernelExitThread() { - ERROR_LOG(HLE,"_sceKernelExitThread FAKED"); - currentThread->nt.status = THREADSTATUS_DORMANT; - currentThread->nt.exitStatus = PARAM(0); - __KernelFireThreadEnd(currentThread); + Thread *thread = __GetCurrentThread(); + _dbg_assert_msg_(HLE, thread != NULL, "_Exited from a NULL thread."); - //Find threads that waited for this one - // Wake them - if (!__KernelTriggerWait(WAITTYPE_THREADEND, __KernelGetCurThread())) - hleReSchedule("exit-deleted thread"); + ERROR_LOG(HLE,"_sceKernelExitThread FAKED"); + thread->nt.status = THREADSTATUS_DORMANT; + thread->nt.exitStatus = PARAM(0); + __KernelFireThreadEnd(thread); + + //Find threads that waited for this one + // Wake them + if (!__KernelTriggerWait(WAITTYPE_THREADEND, __KernelGetCurThread())) + hleReSchedule("_exit thread"); // The stack will be deallocated when the thread is deleted. } void sceKernelExitDeleteThread() { - int threadHandle = __KernelGetCurThread(); - u32 error; - Thread *t = kernelObjects.Get(threadHandle, error); - if (t) - { - ERROR_LOG(HLE,"sceKernelExitDeleteThread()"); - currentThread->nt.status = THREADSTATUS_DORMANT; - currentThread->nt.exitStatus = PARAM(0); - __KernelFireThreadEnd(currentThread); + int threadHandle = __KernelGetCurThread(); + u32 error; + Thread *t = kernelObjects.Get(threadHandle, error); + if (t) + { + INFO_LOG(HLE,"sceKernelExitDeleteThread()"); + t->nt.status = THREADSTATUS_DORMANT; + t->nt.exitStatus = PARAM(0); + __KernelFireThreadEnd(t); //userMemory.Free(currentThread->stackBlock); - currentThread->stackBlock = 0; + t->stackBlock = 0; - __KernelRemoveFromThreadQueue(t); - currentThread = 0; + __KernelRemoveFromThreadQueue(t); + currentThread = 0; - RETURN(kernelObjects.Destroy(threadHandle)); + RETURN(kernelObjects.Destroy(threadHandle)); - __KernelTriggerWait(WAITTYPE_THREADEND, threadHandle); - } - else - { - ERROR_LOG(HLE,"sceKernelExitDeleteThread() ERROR - could not find myself!"); - RETURN(error); - } -} + __KernelTriggerWait(WAITTYPE_THREADEND, threadHandle); + } + else + { + ERROR_LOG(HLE,"sceKernelExitDeleteThread() ERROR - could not find myself!"); + RETURN(error); + } +} u32 sceKernelSuspendDispatchThread() { @@ -1157,11 +1476,11 @@ void sceKernelRotateThreadReadyQueue() int sceKernelDeleteThread(int threadHandle) { - if (threadHandle != currentThread->GetUID()) + if (threadHandle != currentThread) { //TODO: remove from threadqueue! DEBUG_LOG(HLE,"sceKernelDeleteThread(%i)",threadHandle); - + u32 error; Thread *t = kernelObjects.Get(threadHandle, error); if (t) @@ -1182,14 +1501,14 @@ int sceKernelDeleteThread(int threadHandle) } else { - ERROR_LOG(HLE, "Thread \"%s\" tries to delete itself! :(",currentThread->GetName()); + ERROR_LOG(HLE, "Thread \"%s\" tries to delete itself! :(", __GetCurrentThread() ? __GetCurrentThread()->GetName() : "NULL"); return -1; } } int sceKernelTerminateDeleteThread(int threadno) { - if (threadno != currentThread->GetUID()) + if (threadno != currentThread) { //TODO: remove from threadqueue! INFO_LOG(HLE, "sceKernelTerminateDeleteThread(%i)", threadno); @@ -1198,18 +1517,19 @@ int sceKernelTerminateDeleteThread(int threadno) if (!__KernelTriggerWait(WAITTYPE_THREADEND, threadno)) hleReSchedule("termdeletethread"); + // TODO: Why not delete? return 0; //kernelObjects.Destroy(threadno)); } else { - ERROR_LOG(HLE, "Thread \"%s\" trying to delete itself! :(", currentThread->GetName()); + ERROR_LOG(HLE, "Thread \"%s\" trying to delete itself! :(", __GetCurrentThread() ? __GetCurrentThread()->GetName() : "NULL"); return -1; } } int sceKernelTerminateThread(u32 threadID) { - if (threadID != currentThread->GetUID()) + if (threadID != currentThread) { INFO_LOG(HLE, "sceKernelTerminateThread(%i)", threadID); @@ -1226,25 +1546,28 @@ int sceKernelTerminateThread(u32 threadID) } else { - ERROR_LOG(HLE, "Thread \"%s\" trying to delete itself! :(", currentThread->GetName()); + ERROR_LOG(HLE, "Thread \"%s\" trying to delete itself! :(", __GetCurrentThread() ? __GetCurrentThread()->GetName() : "NULL"); return -1; } } SceUID __KernelGetCurThread() { - return currentThread->GetUID(); + return currentThread; } SceUID __KernelGetCurThreadModuleId() { - return currentThread->moduleId; + Thread *t = __GetCurrentThread(); + if (t) + return t->moduleId; + return 0; } void sceKernelGetThreadId() { - u32 retVal = currentThread->GetUID(); + u32 retVal = currentThread; // DEBUG_LOG(HLE,"%i = sceKernelGetThreadId()", retVal); RETURN(retVal); } @@ -1261,14 +1584,18 @@ void sceKernelChangeCurrentThreadAttr() int clearAttr = PARAM(0); int setAttr = PARAM(1); DEBUG_LOG(HLE,"0 = sceKernelChangeCurrentThreadAttr(clear = %08x, set = %08x", clearAttr, setAttr); - currentThread->nt.attr = (currentThread->nt.attr & ~clearAttr) | setAttr; + Thread *t = __GetCurrentThread(); + if (t) + t->nt.attr = (t->nt.attr & ~clearAttr) | setAttr; + else + ERROR_LOG(HLE, "%s(): No current thread?", __FUNCTION__); RETURN(0); } void sceKernelChangeThreadPriority() { int id = PARAM(0); - if (id == 0) id = currentThread->GetUID(); //special + if (id == 0) id = currentThread; //special u32 error; Thread *thread = kernelObjects.Get(id, error); @@ -1351,7 +1678,7 @@ void sceKernelCancelWakeupThread() t->nt.wakeupCount = 0; DEBUG_LOG(HLE,"sceKernelCancelWakeupThread(%i) - wakeupCount reset from %i", uid, wCount); RETURN(wCount); - } + } else { ERROR_LOG(HLE,"sceKernelCancelWakeupThread(%i) - bad thread id", uid); RETURN(error); @@ -1359,9 +1686,16 @@ void sceKernelCancelWakeupThread() } static void __KernelSleepThread(bool doCallbacks) { - DEBUG_LOG(HLE,"sceKernelSleepThread() - wakeupCount decremented to %i", currentThread->nt.wakeupCount); - if (currentThread->nt.wakeupCount > 0) { - currentThread->nt.wakeupCount--; + Thread *thread = __GetCurrentThread(); + if (!thread) + { + ERROR_LOG(HLE, "sceKernelSleepThread*(): bad current thread"); + return; + } + + DEBUG_LOG(HLE,"sceKernelSleepThread() - wakeupCount decremented to %i", thread->nt.wakeupCount); + if (thread->nt.wakeupCount > 0) { + thread->nt.wakeupCount--; RETURN(0); } else { RETURN(0); @@ -1405,24 +1739,24 @@ void sceKernelWaitThreadEnd() void sceKernelWaitThreadEndCB() { - SceUID id = PARAM(0); - DEBUG_LOG(HLE,"sceKernelWaitThreadEnd(%i)",id); - u32 error; - Thread *t = kernelObjects.Get(id, error); - if (t) - { - if (t->nt.status != THREADSTATUS_DORMANT) { - __KernelWaitCurThread(WAITTYPE_THREADEND, id, 0, 0, true); - } else { + SceUID id = PARAM(0); + DEBUG_LOG(HLE,"sceKernelWaitThreadEnd(%i)",id); + u32 error; + Thread *t = kernelObjects.Get(id, error); + if (t) + { + if (t->nt.status != THREADSTATUS_DORMANT) { + __KernelWaitCurThread(WAITTYPE_THREADEND, id, 0, 0, true); + } else { DEBUG_LOG(HLE,"sceKernelWaitThreadEnd - thread %i already ended. Doing nothing.", id); } __KernelCheckCallbacks(); - } - else - { - ERROR_LOG(HLE,"sceKernelWaitThreadEnd - bad thread %i", id); - } - RETURN(0); + } + else + { + ERROR_LOG(HLE,"sceKernelWaitThreadEnd - bad thread %i", id); + } + RETURN(0); } void sceKernelSuspendThread() @@ -1451,14 +1785,14 @@ u32 __KernelCreateCallback(const char *name, u32 entrypoint, u32 commonArg) SceUID id = kernelObjects.Create(cb); cb->nc.size = sizeof(NativeCallback); - strncpy(cb->nc.name, name, 32); - + strncpy(cb->nc.name, name, 32); + cb->nc.entrypoint = entrypoint; cb->nc.threadId = __KernelGetCurThread(); cb->nc.commonArgument = commonArg; cb->nc.notifyCount = 0; cb->nc.notifyArg = 0; - + cb->forceDelete = false; return id; @@ -1507,7 +1841,7 @@ void sceKernelCancelCallback() Callback *cb = kernelObjects.Get(cbId, error); if (cb) { // This is what JPCSP does. Huh? - cb->nc.notifyArg = 0; + cb->nc.notifyArg = 0; RETURN(0); } else { ERROR_LOG(HLE,"sceKernelCancelCallback(%i) - bad cbId", cbId); @@ -1547,56 +1881,16 @@ void sceKernelReferCallbackStatus() } } -// Owns outstanding MIPS calls and provides a way to get them by ID. -// TODO: MipsCall structs are kinda big, try to cut down on the copying by owning pointers instead. -class MipsCallManager { -public: - MipsCallManager() : idGen_(0) {} - int add(MipsCall *call) { - int id = genId(); - calls_.insert(std::pair(id, call)); - return id; - } - MipsCall *get(int id) { - return calls_[id]; - } - MipsCall *pop(int id) { - MipsCall *temp = calls_[id]; - calls_.erase(id); - return temp; - } - -private: - int genId() { return ++idGen_; } - std::map calls_; - int idGen_; -}; - -MipsCallManager mipsCalls; - - -class ActionAfterMipsCall : public Action -{ -public: - virtual void run(); - Thread *thread; - - // Saved thread state - int status; - WaitType waitType; - int waitID; - ThreadWaitInfo waitInfo; - bool isProcessingCallbacks; - - Action *chainedAction; -}; - void ActionAfterMipsCall::run() { - thread->nt.status = status; - thread->nt.waitType = waitType; - thread->nt.waitID = waitID; - thread->waitInfo = waitInfo; - thread->isProcessingCallbacks = isProcessingCallbacks; + u32 error; + Thread *thread = kernelObjects.Get(threadID, error); + if (thread) { + thread->nt.status = status; + thread->nt.waitType = waitType; + thread->nt.waitID = waitID; + thread->waitInfo = waitInfo; + thread->isProcessingCallbacks = isProcessingCallbacks; + } if (chainedAction) { chainedAction->run(); @@ -1604,10 +1898,9 @@ void ActionAfterMipsCall::run() { } } - ActionAfterMipsCall *Thread::getRunningCallbackAction() { - if (this == currentThread && g_inCbCount > 0) + if (this->GetUID() == currentThread && g_inCbCount > 0) { MipsCall *call = mipsCalls.get(this->currentCallbackId); ActionAfterMipsCall *action; @@ -1628,7 +1921,7 @@ ActionAfterMipsCall *Thread::getRunningCallbackAction() void Thread::setReturnValue(u32 retval) { - if (this == currentThread) { + if (this->GetUID() == currentThread) { if (g_inCbCount) { int callId = this->currentCallbackId; MipsCall *call = mipsCalls.get(callId); @@ -1715,18 +2008,28 @@ ThreadWaitInfo Thread::getWaitInfo() void __KernelSwitchContext(Thread *target, const char *reason) { - if (currentThread) // It might just have been deleted. + u32 oldPC = 0; + u32 oldUID = 0; + const char *oldName = "(none)"; + + Thread *cur = __GetCurrentThread(); + if (cur) // It might just have been deleted. { - __KernelSaveContext(¤tThread->context); - DEBUG_LOG(HLE,"Context saved (%s): %i - %s - pc: %08x", reason, currentThread->GetUID(), currentThread->GetName(), currentMIPS->pc); + __KernelSaveContext(&cur->context); + oldPC = currentMIPS->pc; + oldUID = cur->GetUID(); + oldName = cur->GetName(); } - currentThread = target; - __KernelLoadContext(¤tThread->context); - DEBUG_LOG(HLE,"Context loaded (%s): %i - %s - pc: %08x", reason, currentThread->GetUID(), currentThread->GetName(), currentMIPS->pc); + currentThread = target->GetUID(); + __KernelLoadContext(&target->context); + DEBUG_LOG(HLE,"Context switched: %s -> %s (%s) (%i - pc: %08x -> %i - pc: %08x)", + oldName, target->GetName(), + reason, + oldUID, oldPC, target->GetUID(), currentMIPS->pc); // No longer waiting. - currentThread->nt.waitType = WAITTYPE_NONE; - currentThread->nt.waitID = 0; + target->nt.waitType = WAITTYPE_NONE; + target->nt.waitID = 0; __KernelExecutePendingMipsCalls(true); } @@ -1735,7 +2038,7 @@ void __KernelChangeThreadState(Thread *thread, ThreadStatus newStatus) { if (!thread || thread->nt.status == newStatus) return; - if (!dispatchEnabled && thread == currentThread && newStatus != THREADSTATUS_RUNNING) { + if (!dispatchEnabled && thread == __GetCurrentThread() && newStatus != THREADSTATUS_RUNNING) { ERROR_LOG(HLE, "Dispatching suspended, not changing thread state"); return; } @@ -1762,9 +2065,9 @@ bool __CanExecuteCallbackNow(Thread *thread) { void __KernelCallAddress(Thread *thread, u32 entryPoint, Action *afterAction, bool returnVoid, std::vector args, bool reschedAfter) { if (thread) { - ActionAfterMipsCall *after = new ActionAfterMipsCall(); + ActionAfterMipsCall *after = (ActionAfterMipsCall *) __KernelCreateAction(actionAfterMipsCall); after->chainedAction = afterAction; - after->thread = thread; + after->threadID = thread->GetUID(); after->status = thread->nt.status; after->waitType = thread->nt.waitType; after->waitID = thread->nt.waitID; @@ -1787,13 +2090,13 @@ void __KernelCallAddress(Thread *thread, u32 entryPoint, Action *afterAction, bo call->numArgs = args.size(); call->doAfter = afterAction; call->tag = "callAddress"; - + int callId = mipsCalls.add(call); bool called = false; - if (!thread || thread == currentThread) { + if (!thread || thread == __GetCurrentThread()) { if (__CanExecuteCallbackNow(thread)) { - thread = currentThread; + thread = __GetCurrentThread(); __KernelChangeThreadState(thread, THREADSTATUS_RUNNING); __KernelExecuteMipsCallOnCurrentThread(callId, reschedAfter); called = true; @@ -1801,8 +2104,12 @@ void __KernelCallAddress(Thread *thread, u32 entryPoint, Action *afterAction, bo } if (!called) { - DEBUG_LOG(HLE, "Making mipscall pending on thread"); - thread->pendingMipsCalls.push_back(callId); + if (thread) { + DEBUG_LOG(HLE, "Making mipscall pending on thread"); + thread->pendingMipsCalls.push_back(callId); + } else { + WARN_LOG(HLE, "Ignoring mispcall on NULL/deleted thread"); + } } } @@ -1813,24 +2120,31 @@ void __KernelDirectMipsCall(u32 entryPoint, Action *afterAction, bool returnVoid for (int i = 0; i < numargs; i++) argsv.push_back(args[i]); - __KernelCallAddress(currentThread, entryPoint, afterAction, returnVoid, argsv, reschedAfter); + __KernelCallAddress(__GetCurrentThread(), entryPoint, afterAction, returnVoid, argsv, reschedAfter); } void __KernelExecuteMipsCallOnCurrentThread(int callId, bool reschedAfter) { + Thread *cur = __GetCurrentThread(); + if (cur == NULL) + { + ERROR_LOG(HLE, "__KernelExecuteMipsCallOnCurrentThread(): Bad current thread"); + return; + } + if (g_inCbCount > 0) { - WARN_LOG(HLE, "__KernelExecuteMipsCallOnCurrentThread: Already in a callback!"); + WARN_LOG(HLE, "__KernelExecuteMipsCallOnCurrentThread(): Already in a callback!"); } DEBUG_LOG(HLE, "Executing mipscall %i", callId); MipsCall *call = mipsCalls.get(callId); - + // Save the few regs that need saving call->savedPc = currentMIPS->pc; call->savedRa = currentMIPS->r[MIPS_REG_RA]; call->savedV0 = currentMIPS->r[MIPS_REG_V0]; call->savedV1 = currentMIPS->r[MIPS_REG_V1]; call->savedIdRegister = currentMIPS->r[MIPS_REG_CALL_ID]; - call->savedId = currentThread->currentCallbackId; + call->savedId = cur->currentCallbackId; call->returnVoid = false; call->reschedAfter = reschedAfter; @@ -1840,7 +2154,7 @@ void __KernelExecuteMipsCallOnCurrentThread(int callId, bool reschedAfter) // We put this two places in case the game overwrites it. // We may want it later to "inject" return values. currentMIPS->r[MIPS_REG_CALL_ID] = callId; - currentThread->currentCallbackId = callId; + cur->currentCallbackId = callId; for (int i = 0; i < call->numArgs; i++) { currentMIPS->r[MIPS_REG_A0 + i] = call->args[i]; } @@ -1850,7 +2164,14 @@ void __KernelExecuteMipsCallOnCurrentThread(int callId, bool reschedAfter) void __KernelReturnFromMipsCall() { - int callId = currentThread->currentCallbackId; + Thread *cur = __GetCurrentThread(); + if (cur == NULL) + { + ERROR_LOG(HLE, "__KernelReturnFromMipsCall(): Bad current thread"); + return; + } + + int callId = cur->currentCallbackId; if (currentMIPS->r[MIPS_REG_CALL_ID] != callId) WARN_LOG(HLE, "__KernelReturnFromMipsCall(): s0 is %08x != %08x", currentMIPS->r[MIPS_REG_CALL_ID], callId); @@ -1862,26 +2183,31 @@ void __KernelReturnFromMipsCall() // Should also save/restore wait state here. if (call->doAfter) + { call->doAfter->run(); + delete call->doAfter; + } currentMIPS->pc = call->savedPc; currentMIPS->r[MIPS_REG_RA] = call->savedRa; currentMIPS->r[MIPS_REG_V0] = call->savedV0; currentMIPS->r[MIPS_REG_V1] = call->savedV1; currentMIPS->r[MIPS_REG_CALL_ID] = call->savedIdRegister; - currentThread->currentCallbackId = call->savedId; + cur->currentCallbackId = call->savedId; g_inCbCount--; // yeah! back in the real world, let's keep going. Should we process more callbacks? - __KernelCheckThreadCallbacks(currentThread, !call->reschedAfter); + __KernelCheckThreadCallbacks(cur, !call->reschedAfter); if (!__KernelExecutePendingMipsCalls(call->reschedAfter)) { // Sometimes, we want to stay on the thread. - int threadReady = currentThread->nt.status & (THREADSTATUS_READY | THREADSTATUS_RUNNING); + int threadReady = cur->nt.status & (THREADSTATUS_READY | THREADSTATUS_RUNNING); if (call->reschedAfter || threadReady == 0) __KernelReSchedule("return from callback"); } + + delete call; } bool __KernelExecutePendingMipsCalls(bool reschedAfter) @@ -1904,15 +2230,6 @@ bool __KernelExecutePendingMipsCalls(bool reschedAfter) return false; } - -class ActionAfterCallback : public Action -{ -public: - ActionAfterCallback(SceUID cbId_) : cbId(cbId_) {} - virtual void run(); - SceUID cbId; -}; - // Executes the callback, when it next is context switched to. void __KernelRunCallbackOnThread(SceUID cbId, Thread *thread, bool reschedAfter) { @@ -1937,7 +2254,12 @@ void __KernelRunCallbackOnThread(SceUID cbId, Thread *thread, bool reschedAfter) cb->nc.notifyCount = 0; cb->nc.notifyArg = 0; - Action *action = new ActionAfterCallback(cbId); + ActionAfterCallback *action = (ActionAfterCallback *) __KernelCreateAction(actionAfterCallback); + if (action != NULL) + action->setCallback(cbId); + else + ERROR_LOG(HLE, "Something went wrong creating a restore action for a callback."); + __KernelCallAddress(thread, cb->nc.entrypoint, action, false, args, reschedAfter); } @@ -1988,13 +2310,14 @@ bool __KernelCheckThreadCallbacks(Thread *thread, bool force) // Checks for callbacks on all threads bool __KernelCheckCallbacks() { // SceUID currentThread = __KernelGetCurThread(); - // currentThread->isProcessingCallbacks = true; + // __GetCurrentThread()->isProcessingCallbacks = true; // do { bool processed = false; - for (std::vector::iterator iter = threadqueue.begin(); iter != threadqueue.end(); iter++) { - Thread *thread = *iter; - if (__KernelCheckThreadCallbacks(thread, false)) { + u32 error; + for (std::vector::iterator iter = threadqueue.begin(); iter != threadqueue.end(); iter++) { + Thread *thread = kernelObjects.Get(*iter, error); + if (thread && __KernelCheckThreadCallbacks(thread, false)) { processed = true; } } @@ -2024,7 +2347,7 @@ void sceKernelCheckCallback() bool callbacksProcessed = __KernelForceCallbacks(); if (callbacksProcessed) { - ERROR_LOG(HLE,"sceKernelCheckCallback() - processed a callback."); + DEBUG_LOG(HLE,"sceKernelCheckCallback() - processed a callback."); } else { RETURN(0); } @@ -2079,8 +2402,12 @@ void __KernelNotifyCallback(RegisteredCallbackType type, SceUID cbId, int notify // TODO: If cbId == -1, notify the callback ID on all threads that have it. u32 __KernelNotifyCallbackType(RegisteredCallbackType type, SceUID cbId, int notifyArg) { - for (std::vector::iterator iter = threadqueue.begin(); iter != threadqueue.end(); iter++) { - Thread *t = *iter; + u32 error; + for (std::vector::iterator iter = threadqueue.begin(); iter != threadqueue.end(); iter++) { + Thread *t = kernelObjects.Get(*iter, error); + if (!t) + continue; + for (std::set::iterator citer = t->registeredCallbacks[type].begin(); citer != t->registeredCallbacks[type].end(); citer++) { if (cbId == -1 || cbId == *citer) { __KernelNotifyCallback(type, *citer, notifyArg); diff --git a/Core/HLE/sceKernelThread.h b/Core/HLE/sceKernelThread.h index 8ecbb1eede..5264456709 100644 --- a/Core/HLE/sceKernelThread.h +++ b/Core/HLE/sceKernelThread.h @@ -97,7 +97,11 @@ struct ThreadContext // Internal API, used by implementations of kernel functions void __KernelThreadingInit(); +void __KernelThreadingDoState(PointerWrap &p); +void __KernelThreadingDoStateLate(PointerWrap &p); void __KernelThreadingShutdown(); +KernelObject *__KernelThreadObject(); +KernelObject *__KernelCallbackObject(); void __KernelScheduleWakeup(int usFromNow, int threadnumber); SceUID __KernelGetCurThread(); @@ -181,8 +185,18 @@ void __KernelSwitchContext(Thread *target, const char *reason); bool __KernelExecutePendingMipsCalls(bool reschedAfter); void __KernelNotifyCallback(RegisteredCallbackType type, SceUID cbId, int notifyArg); +// Switch to an idle / non-user thread, if not already on one. +// Returns whether a switch occurred. +bool __KernelSwitchOffThread(const char *reason); + // A call into game code. These can be pending on a thread. // Similar to Callback-s (NOT CallbackInfos) in JPCSP. +class Action; +typedef Action *(*ActionCreator)(); +Action *__KernelCreateAction(int actionType); +int __KernelRegisterActionType(ActionCreator creator); +void __KernelRestoreActionType(int actionType, ActionCreator creator); + struct MipsCall { u32 entryPoint; u32 cbId; @@ -195,9 +209,11 @@ struct MipsCall { u32 savedV0; u32 savedV1; bool returnVoid; - const char *tag; + std::string tag; u32 savedId; bool reschedAfter; + + void DoState(PointerWrap &p); }; enum ThreadStatus { diff --git a/Core/HLE/sceKernelVTimer.cpp b/Core/HLE/sceKernelVTimer.cpp index 299e47fd6b..35174f34f1 100644 --- a/Core/HLE/sceKernelVTimer.cpp +++ b/Core/HLE/sceKernelVTimer.cpp @@ -30,6 +30,18 @@ struct VTimer : public KernelObject static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_VTID; } int GetIDType() const { return SCE_KERNEL_TMID_VTimer; } + virtual void DoState(PointerWrap &p) + { + p.Do(size); + p.Do(name); + p.Do(startTime); + p.Do(running); + p.Do(handler); + p.Do(handlerTime); + p.Do(argument); + p.DoMarker("VTimer"); + } + SceSize size; char name[KERNELOBJECT_MAX_NAME_LENGTH+1]; u64 startTime; @@ -39,6 +51,11 @@ struct VTimer : public KernelObject u32 argument; }; +KernelObject *__KernelVTimerObject() +{ + return new VTimer; +} + void sceKernelCreateVTimer() { DEBUG_LOG(HLE,"sceKernelCreateVTimer"); diff --git a/Core/HLE/sceKernelVTimer.h b/Core/HLE/sceKernelVTimer.h index 260341b214..3597dfeb18 100644 --- a/Core/HLE/sceKernelVTimer.h +++ b/Core/HLE/sceKernelVTimer.h @@ -23,3 +23,5 @@ void sceKernelSetVTimerHandler(); // TODO void _sceKernelReturnFromTimerHandler(); + +KernelObject *__KernelVTimerObject(); diff --git a/Core/HLE/scePower.cpp b/Core/HLE/scePower.cpp index bfe1943bc1..f486c064f9 100644 --- a/Core/HLE/scePower.cpp +++ b/Core/HLE/scePower.cpp @@ -22,16 +22,21 @@ #include "scePower.h" #include "sceKernelThread.h" -static bool volatileMemLocked; - const int POWER_CB_AUTO = -1; - const int numberOfCBPowerSlots = 16; -static int powerCbSlots[numberOfCBPowerSlots]; +static bool volatileMemLocked; +static int powerCbSlots[numberOfCBPowerSlots]; void __PowerInit() { memset(powerCbSlots, 0, sizeof(powerCbSlots)); + volatileMemLocked = false; +} + +void __PowerDoState(PointerWrap &p) { + p.DoArray(powerCbSlots, ARRAY_SIZE(powerCbSlots)); + p.Do(volatileMemLocked); + p.DoMarker("scePower"); } int scePowerGetBatteryLifePercent() { @@ -163,6 +168,17 @@ void scePowerSetClockFrequency(u32 cpufreq, u32 busfreq, u32 gpufreq) { INFO_LOG(HLE,"scePowerSetClockFrequency(%i,%i,%i)", cpufreq, busfreq, gpufreq); } +u32 scePowerSetCpuClockFrequency(u32 cpufreq) { + CoreTiming::SetClockFrequencyMHz(cpufreq); + DEBUG_LOG(HLE,"scePowerSetCpuClockFrequency(%i)", cpufreq); + return 0; +} + +u32 scePowerSetBusClockFrequency(u32 busfreq) { + DEBUG_LOG(HLE,"scePowerSetBusClockFrequency(%i)", busfreq); + return 0; +} + u32 scePowerGetCpuClockFrequencyInt() { int freq = CoreTiming::GetClockFrequencyMHz(); INFO_LOG(HLE,"%i=scePowerGetCpuClockFrequencyInt()", freq); @@ -218,8 +234,8 @@ static const HLEFunction scePower[] = { {0x0074EF9B,0,"scePowerGetResumeCount"}, {0xDFA8BAF8,WrapI_I,"scePowerUnregisterCallback"}, {0xDB9D28DD,WrapI_I,"scePowerUnregitserCallback"}, //haha - {0x843FBF43,0,"scePowerSetCpuClockFrequency"}, - {0xB8D7B3FB,0,"scePowerSetBusClockFrequency"}, + {0x843FBF43,WrapU_U,"scePowerSetCpuClockFrequency"}, + {0xB8D7B3FB,WrapU_U,"scePowerSetBusClockFrequency"}, {0xFEE03A2F,0,"scePowerGetCpuClockFrequency"}, {0x478FE6F5,0,"scePowerGetBusClockFrequency"}, {0xFDB5BFE9,WrapU_V,"scePowerGetCpuClockFrequencyInt"}, diff --git a/Core/HLE/scePower.h b/Core/HLE/scePower.h index ac99c6a769..49a1b8b444 100644 --- a/Core/HLE/scePower.h +++ b/Core/HLE/scePower.h @@ -17,7 +17,10 @@ #pragma once +#include "../../Common/ChunkFile.h" + void __PowerInit(); +void __PowerDoState(PointerWrap &p); void Register_scePower(); void Register_sceSuspendForUser(); diff --git a/Core/HLE/sceRtc.cpp b/Core/HLE/sceRtc.cpp index 8d1f3256c7..5692a12f73 100644 --- a/Core/HLE/sceRtc.cpp +++ b/Core/HLE/sceRtc.cpp @@ -154,13 +154,13 @@ void __RtcTicksToPspTime(ScePspDateTime &t, u64 ticks) t.microsecond = ticks % 1000000; } -u64 JumpYMD(u64 year, u64 month, u64 day) { - return 367*year - 7*(year+(month+9)/12)/4 + 275*month/9 + day; +u64 JumpYMD(u64 year, u64 month, u64 day) { + return 367*year - 7*(year+(month+9)/12)/4 + 275*month/9 + day; } u64 JumpSeconds(u64 year, u64 month, u64 day, u64 hour, u64 minute, u64 second) { - static const u64 secs_per_day = 24 * 60 * 60; - return JumpYMD(year, month, day) * secs_per_day + hour * 3600 + minute * 60 + second; + static const u64 secs_per_day = 24 * 60 * 60; + return JumpYMD(year, month, day) * secs_per_day + hour * 3600 + minute * 60 + second; } u64 __RtcPspTimeToTicks(ScePspDateTime &t) @@ -299,7 +299,7 @@ u32 sceRtcGetDayOfWeek(u32 year, u32 month, u32 day) return 0; } year -= month < 3; - return ( year + year/4 - year/100 + year/400 + t[month-1] + day) % 7; + return (year + year/4 - year/100 + year/400 + t[month-1] + day) % 7; } u32 sceRtcGetDaysInMonth(u32 year, u32 month) @@ -307,7 +307,7 @@ u32 sceRtcGetDaysInMonth(u32 year, u32 month) DEBUG_LOG(HLE, "sceRtcGetDaysInMonth(%d, %d)", year, month); u32 numberOfDays; - if (year <= 0 || month <= 0 || month > 12) + if (year == 0 || month == 0 || month > 12) return SCE_KERNEL_ERROR_INVALID_ARGUMENT; switch (month) @@ -336,7 +336,7 @@ u32 sceRtcGetDaysInMonth(u32 year, u32 month) u32 sceRtcIsLeapYear(u32 year) { ERROR_LOG(HLE, "sceRtcIsLeapYear(%d)", year); - return (year % 4 == 0) && !(year % 100 == 0)|| (year % 400 == 0); + return (year % 4 == 0) && (!(year % 100 == 0) || (year % 400 == 0)); } int sceRtcConvertLocalTimeToUTC(u32 tickLocalPtr,u32 tickUTCPtr) @@ -374,55 +374,56 @@ int sceRtcConvertUtcToLocalTime(u32 tickUTCPtr,u32 tickLocalPtr) int sceRtcCheckValid(u32 datePtr) { DEBUG_LOG(HLE, "sceRtcCheckValid(%d)", datePtr); - int ret = 0; if (Memory::IsValidAddress(datePtr)) { ScePspDateTime pt; Memory::ReadStruct(datePtr, &pt); - if (pt.year < 1 || pt.year > 9999) - { - ret = PSP_TIME_INVALID_YEAR; - } - else if (pt.month < 1 || pt.month > 12) + if (pt.year < 1 || pt.year > 9999) { - ret = PSP_TIME_INVALID_MONTH; - } - else if (pt.day < 1 || pt.day > 31) + return PSP_TIME_INVALID_YEAR; + } + else if (pt.month < 1 || pt.month > 12) { - ret = PSP_TIME_INVALID_DAY; - } - else if (pt.day < 0 || pt.day > 31) // TODO: Needs to check actual days in month, including leaps - { - ret = PSP_TIME_INVALID_DAY; - } - else if (pt.hour < 0 || pt.hour > 23) + return PSP_TIME_INVALID_MONTH; + } + else if (pt.day < 1 || pt.day > 31) { - ret = PSP_TIME_INVALID_HOUR; - } - else if (pt.minute < 0 || pt.minute > 59) + return PSP_TIME_INVALID_DAY; + } + else if (pt.day > 31) // TODO: Needs to check actual days in month, including leaps { - ret = PSP_TIME_INVALID_MINUTES; - } - else if (pt.second < 0 || pt.second > 59) + return PSP_TIME_INVALID_DAY; + } + else if (pt.hour > 23) { - ret = PSP_TIME_INVALID_SECONDS; - } - else if (pt.microsecond < 0 || pt.microsecond >= 1000000) + return PSP_TIME_INVALID_HOUR; + } + else if (pt.minute > 59) { - ret = PSP_TIME_INVALID_MICROSECONDS; - } + return PSP_TIME_INVALID_MINUTES; + } + else if (pt.second > 59) + { + return PSP_TIME_INVALID_SECONDS; + } + else if (pt.microsecond >= 1000000) + { + return PSP_TIME_INVALID_MICROSECONDS; + } + else { + return 0; + } } else { - ret=-1; + return -1; } - return ret; } int sceRtcSetTime_t(u32 datePtr, u32 time) { - ERROR_LOG(HLE, "HACK sceRtcSetTime_t(%d,%d)", datePtr, time); + ERROR_LOG(HLE, "HACK sceRtcSetTime_t(%08x,%d)", datePtr, time); if (Memory::IsValidAddress(datePtr)) { ScePspDateTime pt; @@ -439,7 +440,7 @@ int sceRtcSetTime_t(u32 datePtr, u32 time) int sceRtcSetTime64_t(u32 datePtr, u64 time) { - ERROR_LOG(HLE, "HACK sceRtcSetTime64_t(%d,%d)", datePtr, time); + ERROR_LOG(HLE, "HACK sceRtcSetTime64_t(%08x,%lld)", datePtr, time); if (Memory::IsValidAddress(datePtr)) { ScePspDateTime pt; @@ -454,16 +455,15 @@ int sceRtcSetTime64_t(u32 datePtr, u64 time) return 0; } - int sceRtcGetTime_t(u32 datePtr, u32 timePtr) { - ERROR_LOG(HLE, "HACK sceRtcGetTime_t(%d,%d)", datePtr, time); + ERROR_LOG(HLE, "HACK sceRtcGetTime_t(%08x,%08x)", datePtr, timePtr); if (Memory::IsValidAddress(datePtr)&&Memory::IsValidAddress(timePtr)) { ScePspDateTime pt; Memory::ReadStruct(datePtr, &pt); pt.year-=1969; - u64 result = __RtcPspTimeToTicks(pt)/1000000ULL; + u32 result = (u32) (__RtcPspTimeToTicks(pt)/1000000ULL); Memory::Write_U32(result, timePtr); } else @@ -473,10 +473,9 @@ int sceRtcGetTime_t(u32 datePtr, u32 timePtr) return 0; } - int sceRtcGetTime64_t(u32 datePtr, u32 timePtr) { - ERROR_LOG(HLE, "HACK sceRtcGetTime64_t(%d,%d)", datePtr, time); + ERROR_LOG(HLE, "HACK sceRtcGetTime64_t(%08x,%08x)", datePtr, timePtr); if (Memory::IsValidAddress(datePtr)&&Memory::IsValidAddress(timePtr)) { ScePspDateTime pt; @@ -492,8 +491,6 @@ int sceRtcGetTime64_t(u32 datePtr, u32 timePtr) return 0; } - - int sceRtcSetDosTime(u32 datePtr, u32 dosTime) { ERROR_LOG(HLE, "HACK sceRtcSetDosTime(%d,%d)", datePtr, dosTime); @@ -544,20 +541,14 @@ int sceRtcGetWin32FileTime(u32 datePtr, u32 win32TimePtr) int sceRtcCompareTick(u32 tick1Ptr, u32 tick2Ptr) { ERROR_LOG(HLE, "HACK sceRtcCompareTick(%d,%d)", tick1Ptr, tick2Ptr); - if (Memory::IsValidAddress(tick1Ptr)&&Memory::IsValidAddress(tick1Ptr)) + if (Memory::IsValidAddress(tick1Ptr) && Memory::IsValidAddress(tick2Ptr)) { u64 tick1 = Memory::Read_U64(tick1Ptr); u64 tick2 = Memory::Read_U64(tick2Ptr); - - if (tick1 > tick2) - { + if (tick1 > tick2) return 1; - } - - if (tick1 < tick2) - { + if (tick1 < tick2) return -1; - } } return 0; } @@ -572,7 +563,7 @@ int sceRtcTickAddTicks(u32 destTickPtr, u32 srcTickPtr, u64 numTicks) Memory::Write_U64(srcTick, destTickPtr); } - DEBUG_LOG(HLE, "sceRtcTickAddTicks(%d,%d,%d)", destTickPtr, srcTickPtr, numTicks); + DEBUG_LOG(HLE, "sceRtcTickAddTicks(%x,%x,%llu)", destTickPtr, srcTickPtr, numTicks); return 0; } @@ -586,7 +577,7 @@ int sceRtcTickAddMicroseconds(u32 destTickPtr,u32 srcTickPtr, u64 numMS) Memory::Write_U64(srcTick, destTickPtr); } - ERROR_LOG(HLE, "HACK sceRtcTickAddMicroseconds(%d,%d,%d)", destTickPtr, srcTickPtr, numMS); + ERROR_LOG(HLE, "HACK sceRtcTickAddMicroseconds(%x,%x,%llu)", destTickPtr, srcTickPtr, numMS); return 0; } @@ -599,7 +590,7 @@ int sceRtcTickAddSeconds(u32 destTickPtr, u32 srcTickPtr, u64 numSecs) srcTick += numSecs * 1000000UL; Memory::Write_U64(srcTick, destTickPtr); } - ERROR_LOG(HLE, "HACK sceRtcTickAddSeconds(%d,%d,%d)", destTickPtr, srcTickPtr, numSecs); + ERROR_LOG(HLE, "HACK sceRtcTickAddSeconds(%x,%x,%llu)", destTickPtr, srcTickPtr, numSecs); return 0; } @@ -612,7 +603,7 @@ int sceRtcTickAddMinutes(u32 destTickPtr, u32 srcTickPtr, u64 numMins) srcTick += numMins*60000000UL; Memory::Write_U64(srcTick, destTickPtr); } - ERROR_LOG(HLE, "HACK sceRtcTickAddMinutes(%d,%d,%d)", destTickPtr, srcTickPtr, numMins); + ERROR_LOG(HLE, "HACK sceRtcTickAddMinutes(%x,%x,%llu)", destTickPtr, srcTickPtr, numMins); return 0; } @@ -621,7 +612,6 @@ int sceRtcTickAddHours(u32 destTickPtr, u32 srcTickPtr, int numHours) if (Memory::IsValidAddress(destTickPtr) && Memory::IsValidAddress(srcTickPtr)) { s64 srcTick = (s64)Memory::Read_U64(srcTickPtr); - srcTick += numHours*3600000000UL; Memory::Write_U64(srcTick, destTickPtr); } @@ -660,7 +650,7 @@ int sceRtcTickAddMonths(u32 destTickPtr, u32 srcTickPtr, int numMonths) if (Memory::IsValidAddress(destTickPtr) && Memory::IsValidAddress(srcTickPtr)) { u64 srcTick = Memory::Read_U64(srcTickPtr); - + // slightly bodgy but we need to add months to a pt and then convert to ticks to cover different day count in months and leapyears ScePspDateTime pt; memset(&pt, 0, sizeof(pt)); @@ -669,7 +659,7 @@ int sceRtcTickAddMonths(u32 destTickPtr, u32 srcTickPtr, int numMonths) numMonths = -numMonths;; int years = numMonths /12; int realmonths = numMonths % 12; - + pt.year = years; pt.month = realmonths; u64 monthTicks =__RtcPspTimeToTicks(pt); @@ -688,7 +678,6 @@ int sceRtcTickAddMonths(u32 destTickPtr, u32 srcTickPtr, int numMonths) { int years = numMonths /12; int realmonths = numMonths % 12; - pt.year = years; pt.month = realmonths; srcTick +=__RtcPspTimeToTicks(pt); @@ -700,7 +689,7 @@ int sceRtcTickAddMonths(u32 destTickPtr, u32 srcTickPtr, int numMonths) return 0; } -//TODO: off by 6 days every 2000 years. +// TODO: off by 6 days every 2000 years. int sceRtcTickAddYears(u32 destTickPtr, u32 srcTickPtr, int numYears) { if (Memory::IsValidAddress(destTickPtr) && Memory::IsValidAddress(srcTickPtr)) @@ -743,10 +732,10 @@ int sceRtcParseDateTime(u32 destTickPtr, u32 dateStringPtr) return 0; } -const HLEFunction sceRtc[] = +const HLEFunction sceRtc[] = { {0xC41C2853, WrapU_V, "sceRtcGetTickResolution"}, - {0x3f7ad767, WrapU_U, "sceRtcGetCurrentTick"}, + {0x3f7ad767, WrapU_U, "sceRtcGetCurrentTick"}, {0x011F03C1, WrapU64_V, "sceRtcGetAccumulativeTime"}, {0x029CA3B3, WrapU64_V, "sceRtcGetAccumlativeTime"}, {0x4cfa57b0, WrapU_UI, "sceRtcGetCurrentClock"}, @@ -767,7 +756,7 @@ const HLEFunction sceRtc[] = {0x6FF40ACC, WrapU_UU, "sceRtcGetTick"}, {0x9ED0AE87, WrapI_UU, "sceRtcCompareTick"}, {0x44F45E05, WrapI_UUU64, "sceRtcTickAddTicks"}, - {0x26D25A5D, WrapI_UUU64, "sceRtcTickAddMicroseconds"}, + {0x26D25A5D, WrapI_UUU64, "sceRtcTickAddMicroseconds"}, {0xF2A4AFE5, WrapI_UUU64, "sceRtcTickAddSeconds"}, {0xE6605BCA, WrapI_UUU64, "sceRtcTickAddMinutes"}, {0x26D7A24A, WrapI_UUI, "sceRtcTickAddHours"}, @@ -785,8 +774,6 @@ const HLEFunction sceRtc[] = {0x1909c99b, WrapI_UU64, "sceRtcSetTime64_t"}, }; - - void Register_sceRtc() { RegisterModule("sceRtc", ARRAY_SIZE(sceRtc), sceRtc); diff --git a/Core/HLE/sceSas.cpp b/Core/HLE/sceSas.cpp index c19ae2c5bd..45693a80e5 100644 --- a/Core/HLE/sceSas.cpp +++ b/Core/HLE/sceSas.cpp @@ -50,6 +50,13 @@ void __SasInit() { sas = new SasInstance(); } +void __SasDoState(PointerWrap &p) { + if (sas != NULL) { + sas->DoState(p); + } + p.DoMarker("sceSas"); +} + void __SasShutdown() { delete sas; sas = 0; @@ -114,6 +121,11 @@ u32 sceSasSetVoice(u32 core, int voiceNum, u32 vagAddr, int size, int loop) return ERROR_SAS_INVALID_VOICE; } + if (!Memory::IsValidAddress(vagAddr)) { + ERROR_LOG(HLE, "Ignoring invalid VAG audio address %08x", vagAddr); + return 0; + } + //Real VAG header is 0x30 bytes behind the vagAddr SasVoice &v = sas->voices[voiceNum]; v.type = VOICETYPE_VAG; @@ -126,7 +138,7 @@ u32 sceSasSetVoice(u32 core, int voiceNum, u32 vagAddr, int size, int loop) u32 sceSasSetVoicePCM(u32 core, int voiceNum, u32 pcmAddr, int size, int loop) { - DEBUG_LOG(HLE,"0=sceSasSetVoicePCM(core=%08x, voicenum=%i, pcmAddr=%08x, size=%i, loop=%i)",core, voiceNum, pcmAddr, size, loop); + DEBUG_LOG(HLE,"0=sceSasSetVoicePCM(core=%08x, voicenum=%i, pcmAddr=%08x, size=%i, loop=%i)", core, voiceNum, pcmAddr, size, loop); SasVoice &v = sas->voices[voiceNum]; v.type = VOICETYPE_PCM; v.pcmAddr = pcmAddr; @@ -310,7 +322,7 @@ u32 sceSasGetEnvelopeHeight(u32 core, u32 voiceNum) { DEBUG_LOG(HLE,"UNIMPL 0=sceSasGetEnvelopeHeight(core=%08x, voicenum=%i)", core, voiceNum); } - if (voiceNum >= PSP_SAS_VOICES_MAX || voiceNum < 0) + if (voiceNum >= PSP_SAS_VOICES_MAX) { WARN_LOG(HLE, "%s: invalid voicenum %d", __FUNCTION__, voiceNum); return ERROR_SAS_INVALID_VOICE; diff --git a/Core/HLE/sceSas.h b/Core/HLE/sceSas.h index 58efddd75b..bf798dee45 100644 --- a/Core/HLE/sceSas.h +++ b/Core/HLE/sceSas.h @@ -18,6 +18,7 @@ #pragma once void __SasInit(); +void __SasDoState(PointerWrap &p); void __SasShutdown(); void Register_sceSasCore(); diff --git a/Core/HLE/sceSsl.cpp b/Core/HLE/sceSsl.cpp index 0a762af122..b588135b88 100644 --- a/Core/HLE/sceSsl.cpp +++ b/Core/HLE/sceSsl.cpp @@ -35,6 +35,14 @@ void __SslInit() currentMemSize = 0; } +void __SslDoState(PointerWrap &p) +{ + p.Do(isSslInit); + p.Do(maxMemSize); + p.Do(currentMemSize); + p.DoMarker("sceSsl"); +} + int sceSslInit(int heapSize) { DEBUG_LOG(HLE, "sceSslInit %d", heapSize); diff --git a/Core/HLE/sceSsl.h b/Core/HLE/sceSsl.h index c8e46f976b..6a1239f892 100644 --- a/Core/HLE/sceSsl.h +++ b/Core/HLE/sceSsl.h @@ -17,6 +17,9 @@ #pragma once +#include "../../Common/ChunkFile.h" + void Register_sceSsl(); -void __SslInit(); \ No newline at end of file +void __SslInit(); +void __SslDoState(PointerWrap &p); diff --git a/Core/HLE/sceUmd.cpp b/Core/HLE/sceUmd.cpp index fbe74b1ce8..4d3b425b51 100644 --- a/Core/HLE/sceUmd.cpp +++ b/Core/HLE/sceUmd.cpp @@ -31,11 +31,11 @@ const int PSP_ERROR_UMD_INVALID_PARAM = 0x80010016; #define UMD_READABLE 0x20 -u8 umdActivated = 1; -u32 umdStatus = 0; -u32 umdErrorStat = 0; -static int driveCBId= -1; -int umdStatTimer = 0; +static u8 umdActivated = 1; +static u32 umdStatus = 0; +static u32 umdErrorStat = 0; +static int driveCBId = -1; +static int umdStatTimer = 0; #define PSP_UMD_TYPE_GAME 0x10 @@ -47,14 +47,28 @@ struct PspUmdInfo { u32 type; }; +void __UmdStatTimeout(u64 userdata, int cyclesLate); -void __UmdInit() { +void __UmdInit() +{ + umdStatTimer = CoreTiming::RegisterEvent("UmdTimeout", __UmdStatTimeout); umdActivated = 1; umdStatus = 0; umdErrorStat = 0; driveCBId = -1; } +void __UmdDoState(PointerWrap &p) +{ + p.Do(umdActivated); + p.Do(umdStatus); + p.Do(umdErrorStat); + p.Do(driveCBId); + p.Do(umdStatTimer); + CoreTiming::RestoreRegisterEvent(umdStatTimer, "UmdTimeout", __UmdStatTimeout); + p.DoMarker("sceUmd"); +} + u8 __KernelUmdGetState() { u8 state = UMD_PRESENT; @@ -134,7 +148,7 @@ int sceUmdActivate(u32 unknown, const char *name) int sceUmdDeactivate(u32 unknown, const char *name) { // Why 18? No idea. - if (unknown < 0 || unknown > 18) + if (unknown > 18) return PSP_ERROR_UMD_INVALID_PARAM; bool changed = umdActivated != 0; @@ -214,9 +228,6 @@ void __UmdStatTimeout(u64 userdata, int cyclesLate) void __UmdWaitStat(u32 timeout) { - if (umdStatTimer == 0) - umdStatTimer = CoreTiming::RegisterEvent("UmdTimeout", &__UmdStatTimeout); - // This happens to be how the hardware seems to time things. if (timeout <= 4) timeout = 15; diff --git a/Core/HLE/sceUmd.h b/Core/HLE/sceUmd.h index ceaa832a42..b0026e6eb0 100644 --- a/Core/HLE/sceUmd.h +++ b/Core/HLE/sceUmd.h @@ -27,5 +27,6 @@ enum pspUmdState { }; void __UmdInit(); +void __UmdDoState(PointerWrap &p); void Register_sceUmdUser(); diff --git a/Core/HLE/sceUsb.cpp b/Core/HLE/sceUsb.cpp index faec5397a9..a76751cf2e 100644 --- a/Core/HLE/sceUsb.cpp +++ b/Core/HLE/sceUsb.cpp @@ -22,6 +22,17 @@ bool usbActivated = false; +void __UsbInit() +{ + usbActivated = false; +} + +void __UsbDoState(PointerWrap &p) +{ + p.Do(usbActivated); + p.DoMarker("sceUsb"); +} + u32 sceUsbActivate() { ERROR_LOG(HLE, "UNIMPL sceUsbActivate"); usbActivated = true; diff --git a/Core/HLE/sceUsb.h b/Core/HLE/sceUsb.h index 009f44ec96..d1b280be76 100644 --- a/Core/HLE/sceUsb.h +++ b/Core/HLE/sceUsb.h @@ -18,3 +18,6 @@ #pragma once void Register_sceUsb(); + +void __UsbInit(); +void __UsbDoState(PointerWrap &p); diff --git a/Core/HLE/sceUtility.cpp b/Core/HLE/sceUtility.cpp index a2530c7f04..c5dbc84934 100644 --- a/Core/HLE/sceUtility.cpp +++ b/Core/HLE/sceUtility.cpp @@ -39,17 +39,33 @@ void __UtilityInit() SavedataParam::Init(); } +void __UtilityDoState(PointerWrap &p) +{ + saveDialog.DoState(p); + msgDialog.DoState(p); + oskDialog.DoState(p); + netDialog.DoState(p); + p.DoMarker("sceUtility"); +} + +void __UtilityShutdown() +{ + saveDialog.Shutdown(); + msgDialog.Shutdown(); + oskDialog.Shutdown(); + netDialog.Shutdown(); +} + int sceUtilitySavedataInitStart(u32 paramAddr) { - saveDialog.Init(paramAddr); - return 0; + DEBUG_LOG(HLE,"sceUtilitySavedataInitStart(%08x)", paramAddr); + return saveDialog.Init(paramAddr); } int sceUtilitySavedataShutdownStart() { DEBUG_LOG(HLE,"sceUtilitySavedataShutdownStart()"); - saveDialog.Shutdown(); - return 0; + return saveDialog.Shutdown(); } int sceUtilitySavedataGetStatus() @@ -57,13 +73,10 @@ int sceUtilitySavedataGetStatus() return saveDialog.GetStatus(); } -void sceUtilitySavedataUpdate(u32 unknown) +int sceUtilitySavedataUpdate(int animSpeed) { - DEBUG_LOG(HLE,"sceUtilitySavedataUpdate()"); - - saveDialog.Update(); - - return; + DEBUG_LOG(HLE,"sceUtilitySavedataUpdate(%d)", animSpeed); + return saveDialog.Update(); } #define PSP_AV_MODULE_AVCODEC 0 @@ -91,26 +104,27 @@ void sceUtilityLoadModule(u32 module) __KernelReSchedule("utilityloadmodule"); } -void sceUtilityMsgDialogInitStart(u32 structAddr) +int sceUtilityMsgDialogInitStart(u32 structAddr) { DEBUG_LOG(HLE,"sceUtilityMsgDialogInitStart(%i)", structAddr); - msgDialog.Init(structAddr); + return msgDialog.Init(structAddr); } -void sceUtilityMsgDialogShutdownStart(u32 unknown) +int sceUtilityMsgDialogShutdownStart(u32 unknown) { - DEBUG_LOG(HLE,"FAKE sceUtilityMsgDialogShutdownStart(%i)", unknown); - msgDialog.Shutdown(); + DEBUG_LOG(HLE,"sceUtilityMsgDialogShutdownStart(%i)", unknown); + return msgDialog.Shutdown(); } -void sceUtilityMsgDialogUpdate(int animSpeed) +int sceUtilityMsgDialogUpdate(int animSpeed) { DEBUG_LOG(HLE,"sceUtilityMsgDialogUpdate(%i)", animSpeed); - msgDialog.Update(); + return msgDialog.Update(); } -u32 sceUtilityMsgDialogGetStatus() +int sceUtilityMsgDialogGetStatus() { + DEBUG_LOG(HLE,"sceUtilityMsgDialogGetStatus()"); return msgDialog.GetStatus(); } @@ -119,26 +133,25 @@ u32 sceUtilityMsgDialogGetStatus() int sceUtilityOskInitStart(u32 oskPtr) { - ERROR_LOG(HLE,"FAKE sceUtilityOskInitStart(%i)", PARAM(0)); + DEBUG_LOG(HLE,"sceUtilityOskInitStart(%i)", PARAM(0)); return oskDialog.Init(oskPtr); } int sceUtilityOskShutdownStart() { - ERROR_LOG(HLE,"FAKE sceUtilityOskShutdownStart(%i)", PARAM(0)); - oskDialog.Shutdown(); - return 0; + DEBUG_LOG(HLE,"sceUtilityOskShutdownStart(%i)", PARAM(0)); + return oskDialog.Shutdown(); } -void sceUtilityOskUpdate(unsigned int unknown) +int sceUtilityOskUpdate(unsigned int unknown) { - ERROR_LOG(HLE,"FAKE sceUtilityOskUpdate(%i)", unknown); - oskDialog.Update(); + DEBUG_LOG(HLE,"FAKE sceUtilityOskUpdate(%i)", unknown); + return oskDialog.Update(); } int sceUtilityOskGetStatus() { - int status = oskDialog.GetStatus(); + int status = oskDialog.GetStatus(); // Seems that 4 is the cancelled status for OSK? if (status == 4) { @@ -148,25 +161,25 @@ int sceUtilityOskGetStatus() } -void sceUtilityNetconfInitStart(unsigned int unknown) +int sceUtilityNetconfInitStart(u32 structAddr) { - DEBUG_LOG(HLE,"FAKE sceUtilityNetconfInitStart(%i)", unknown); - netDialog.Init(); + DEBUG_LOG(HLE,"FAKE sceUtilityNetconfInitStart(%08x)", structAddr); + return netDialog.Init(); } -void sceUtilityNetconfShutdownStart(unsigned int unknown) +int sceUtilityNetconfShutdownStart(unsigned int unknown) { DEBUG_LOG(HLE,"FAKE sceUtilityNetconfShutdownStart(%i)", unknown); - netDialog.Shutdown(); + return netDialog.Shutdown(); } -void sceUtilityNetconfUpdate(int unknown) +int sceUtilityNetconfUpdate(int animSpeed) { - DEBUG_LOG(HLE,"FAKE sceUtilityNetconfUpdate(%i)", unknown); - netDialog.Update(); + DEBUG_LOG(HLE,"FAKE sceUtilityNetconfUpdate(%i)", animSpeed); + return netDialog.Update(); } -unsigned int sceUtilityNetconfGetStatus() +int sceUtilityNetconfGetStatus() { DEBUG_LOG(HLE,"sceUtilityNetconfGetStatus()"); return netDialog.GetStatus(); @@ -179,10 +192,15 @@ int sceUtilityScreenshotGetStatus() return retval; } +void sceUtilityGamedataInstallInitStart(u32 unkown) +{ + DEBUG_LOG(HLE,"UNIMPL sceUtilityGamedataInstallInitStart(%i)", unkown); +} + int sceUtilityGamedataInstallGetStatus() { u32 retval = 0;//__UtilityGetStatus(); - DEBUG_LOG(HLE,"%i=sceUtilityGamedataInstallGetStatus()", retval); + DEBUG_LOG(HLE,"UNIMPL %i=sceUtilityGamedataInstallGetStatus()", retval); return retval; } @@ -332,34 +350,39 @@ u32 sceUtilityUnloadNetModule(u32 module) return 0; } +void sceUtilityInstallInitStart(u32 unknown) +{ + DEBUG_LOG(HLE,"FAKE sceUtilityInstallInitStart()"); +} + const HLEFunction sceUtility[] = { {0x1579a159, &WrapU_U, "sceUtilityLoadNetModule"}, {0x64d50c56, &WrapU_U, "sceUtilityUnloadNetModule"}, - {0xf88155f6, &WrapV_U, "sceUtilityNetconfShutdownStart"}, - {0x4db1e739, &WrapV_U, "sceUtilityNetconfInitStart"}, - {0x91e70e35, &WrapV_I, "sceUtilityNetconfUpdate"}, - {0x6332aa39, &WrapU_V, "sceUtilityNetconfGetStatus"}, + {0xf88155f6, &WrapI_U, "sceUtilityNetconfShutdownStart"}, + {0x4db1e739, &WrapI_U, "sceUtilityNetconfInitStart"}, + {0x91e70e35, &WrapI_I, "sceUtilityNetconfUpdate"}, + {0x6332aa39, &WrapI_V, "sceUtilityNetconfGetStatus"}, {0x5eee6548, 0, "sceUtilityCheckNetParam"}, {0x434d4b3a, 0, "sceUtilityGetNetParam"}, {0x4FED24D8, 0, "sceUtilityGetNetParamLatestID"}, - {0x67af3428, &WrapV_U, "sceUtilityMsgDialogShutdownStart"}, - {0x2ad8e239, &WrapV_U, "sceUtilityMsgDialogInitStart"}, - {0x95fc253b, &WrapV_I, "sceUtilityMsgDialogUpdate"}, - {0x9a1c91d7, &WrapU_V, "sceUtilityMsgDialogGetStatus"}, + {0x67af3428, &WrapI_U, "sceUtilityMsgDialogShutdownStart"}, + {0x2ad8e239, &WrapI_U, "sceUtilityMsgDialogInitStart"}, + {0x95fc253b, &WrapI_I, "sceUtilityMsgDialogUpdate"}, + {0x9a1c91d7, &WrapI_V, "sceUtilityMsgDialogGetStatus"}, {0x4928bd96, 0, "sceUtilityMsgDialogAbort"}, {0x9790b33c, &WrapI_V, "sceUtilitySavedataShutdownStart"}, {0x50c4cd57, &WrapI_U, "sceUtilitySavedataInitStart"}, - {0xd4b95ffb, &WrapV_U, "sceUtilitySavedataUpdate"}, + {0xd4b95ffb, &WrapI_I, "sceUtilitySavedataUpdate"}, {0x8874dbe0, &WrapI_V, "sceUtilitySavedataGetStatus"}, {0x3dfaeba9, &WrapI_V, "sceUtilityOskShutdownStart"}, {0xf6269b82, &WrapI_U, "sceUtilityOskInitStart"}, - {0x4b85c861, &WrapV_U, "sceUtilityOskUpdate"}, + {0x4b85c861, &WrapI_U, "sceUtilityOskUpdate"}, {0xf3f76017, &WrapI_V, "sceUtilityOskGetStatus"}, {0x41e30674, &WrapU_UU, "sceUtilitySetSystemParamString"}, @@ -398,7 +421,7 @@ const HLEFunction sceUtility[] = {0x0D5BC6D2, 0, "sceUtilityLoadUsbModule"}, {0xF64910F0, 0, "sceUtilityUnloadUsbModule"}, - {0x24AC31EB, 0, "sceUtilityGamedataInstallInitStart"}, + {0x24AC31EB, &WrapV_U, "sceUtilityGamedataInstallInitStart"}, {0x32E32DCB, 0, "sceUtilityGamedataInstallShutdownStart"}, {0x4AECD179, 0, "sceUtilityGamedataInstallUpdate"}, {0xB57E95D9, &WrapI_V, "sceUtilityGamedataInstallGetStatus"}, @@ -409,7 +432,7 @@ const HLEFunction sceUtility[] = {0xF3FBC572, 0, "sceUtilityNpSigninUpdate"}, {0x86ABDB1B, 0, "sceUtilityNpSigninGetStatus"}, - {0x1281DA8E, 0, "sceUtilityInstallInitStart"}, + {0x1281DA8E, &WrapV_U, "sceUtilityInstallInitStart"}, {0x5EF1C24A, 0, "sceUtilityInstallShutdownStart"}, {0xA03D29BA, 0, "sceUtilityInstallUpdate"}, {0xC4700FA3, 0, "sceUtilityInstallGetStatus"}, diff --git a/Core/HLE/sceUtility.h b/Core/HLE/sceUtility.h index 3eefa42b8e..1518332972 100644 --- a/Core/HLE/sceUtility.h +++ b/Core/HLE/sceUtility.h @@ -18,5 +18,7 @@ #pragma once void __UtilityInit(); +void __UtilityDoState(PointerWrap &p); +void __UtilityShutdown(); void Register_sceUtility(); diff --git a/Core/HLE/sceVaudio.cpp b/Core/HLE/sceVaudio.cpp index 8b7168e631..68493f583d 100644 --- a/Core/HLE/sceVaudio.cpp +++ b/Core/HLE/sceVaudio.cpp @@ -19,13 +19,13 @@ #include "FunctionWrappers.h" #include "sceVaudio.h" -u32 sceVaudioOutputBlocking() { - ERROR_LOG(HLE, "UNIMPL sceVaudioOutputBlocking(...)"); +u32 sceVaudioOutputBlocking(int vol, u32 buffer) { + ERROR_LOG(HLE, "UNIMPL sceVaudioOutputBlocking(%i, %08x)", vol, buffer); return 0; } -u32 sceVaudioChReserve() { - ERROR_LOG(HLE, "UNIMPL sceVaudioChReserve(...)"); +u32 sceVaudioChReserve(int sampleCount, int freq, int format) { + ERROR_LOG(HLE, "UNIMPL sceVaudioChReserve(%i, %i, %i)", sampleCount, freq, format); return 0; } @@ -34,10 +34,22 @@ u32 sceVaudioChRelease() { return 0; } +u32 sceVaudioSetEffectType(int effectType, int vol) { + ERROR_LOG(HLE, "UNIMPL sceVaudioSetEffectType(%i, %i)", effectType, vol); + return 0; +} + +u32 sceVaudioSetAlcMode(int alcMode) { + ERROR_LOG(HLE, "UNIMPL sceVaudioSetAlcMode(%i)", alcMode); + return 0; +} + const HLEFunction sceVaudio[] = { - {0x03b6807d, WrapU_V, "sceVaudioOutputBlockingFunction"}, - {0x67585dfd, WrapU_V, "sceVaudioChReserveFunction"}, + {0x03b6807d, WrapU_IU, "sceVaudioOutputBlockingFunction"}, + {0x67585dfd, WrapU_III, "sceVaudioChReserveFunction"}, {0x8986295e, WrapU_V, "sceVaudioChReleaseFunction"}, + {0x346FBE94, WrapU_II, "sceVaudioSetEffectType"}, + {0xCBD4AC51, WrapU_I, "sceVaudioSetAlcMode"}, }; void Register_sceVaudio() { diff --git a/Core/HW/MemoryStick.cpp b/Core/HW/MemoryStick.cpp index 4d83e4783c..b2a746b0af 100644 --- a/Core/HW/MemoryStick.cpp +++ b/Core/HW/MemoryStick.cpp @@ -4,6 +4,13 @@ static MemStickState memStickState = PSP_MEMORYSTICK_STATE_DRIVER_READY; static MemStickFatState memStickFatState = PSP_FAT_MEMORYSTICK_STATE_ASSIGNED; +void MemoryStick_DoState(PointerWrap &p) +{ + p.Do(memStickState); + p.Do(memStickFatState); + p.DoMarker("MemoryStick"); +} + MemStickState MemoryStick_State() { return memStickState; diff --git a/Core/HW/MemoryStick.h b/Core/HW/MemoryStick.h index 1ccd9b64fa..fbd42b827b 100644 --- a/Core/HW/MemoryStick.h +++ b/Core/HW/MemoryStick.h @@ -1,4 +1,5 @@ #include "../../Globals.h" +#include "../../Common/ChunkFile.h" // mscmhc0 states enum MemStickState { @@ -14,6 +15,7 @@ enum MemStickFatState { PSP_FAT_MEMORYSTICK_STATE_ASSIGNED = 1, }; +void MemoryStick_DoState(PointerWrap &p); MemStickState MemoryStick_State(); MemStickFatState MemoryStick_FatState(); diff --git a/Core/HW/SasAudio.cpp b/Core/HW/SasAudio.cpp index c9413b3308..3845922fbe 100644 --- a/Core/HW/SasAudio.cpp +++ b/Core/HW/SasAudio.cpp @@ -20,18 +20,18 @@ #include "../MemMap.h" #include "SasAudio.h" -static const double f[5][2] = +static const double f[5][2] = { { 0.0, 0.0 }, { 60.0 / 64.0, 0.0 }, { 115.0 / 64.0, -52.0 / 64.0 }, { 98.0 / 64.0, -55.0 / 64.0 }, { 122.0 / 64.0, -60.0 / 64.0 } }; -void VagDecoder::Start(u8 *data, bool loopEnabled) -{ +void VagDecoder::Start(u32 data, int vagSize, bool loopEnabled) { loopEnabled_ = loopEnabled; loopAtNextBlock_ = false; loopStartBlock_ = 0; + numBlocks_ = vagSize / 16; end_ = false; data_ = data; read_ = data; @@ -41,16 +41,14 @@ void VagDecoder::Start(u8 *data, bool loopEnabled) s_2 = 0.0; } -bool VagDecoder::DecodeBlock() -{ - int predict_nr = GetByte(); +void VagDecoder::DecodeBlock(u8 *&readp) { + int predict_nr = *readp++; int shift_factor = predict_nr & 0xf; predict_nr >>= 4; - int flags = GetByte(); - if (flags == 7) - { + int flags = *readp++; + if (flags == 7) { end_ = true; - return false; + return; } else if (flags == 6) { loopStartBlock_ = curBlock_; @@ -58,23 +56,23 @@ bool VagDecoder::DecodeBlock() else if (flags == 3 && loopEnabled_) { loopAtNextBlock_ = true; } - for (int i = 0; i < 28; i += 2) - { - int d = GetByte(); + for (int i = 0; i < 28; i += 2) { + int d = *readp++; int s = (short)((d & 0xf) << 12); samples[i] = (double)(s >> shift_factor); s = (short)((d & 0xf0) << 8); samples[i + 1] = (double)(s >> shift_factor); } - for (int i = 0; i < 28; i++) - { + for (int i = 0; i < 28; i++) { samples[i] = samples[i] + s_1 * f[predict_nr][0] + s_2 * f[predict_nr][1]; s_2 = s_1; s_1 = samples[i]; } curSample = 0; curBlock_++; - return true; + if (curBlock_ == numBlocks_) { + end_ = true; + } } void VagDecoder::GetSamples(s16 *outSamples, int numSamples) { @@ -82,18 +80,31 @@ void VagDecoder::GetSamples(s16 *outSamples, int numSamples) { memset(outSamples, 0, numSamples * sizeof(s16)); return; } + u8 *readp = Memory::GetPointer(read_); + u8 *origp = readp; for (int i = 0; i < numSamples; i++) { if (curSample == 28) { if (loopAtNextBlock_) { read_ = data_ + 16 * loopStartBlock_; + readp = Memory::GetPointer(read_); + origp = readp; curBlock_ = loopStartBlock_; s_1 = 0.0; s_2 = 0.0; } - DecodeBlock(); + DecodeBlock(readp); + if (end_) { + // Clear the rest of the buffer and return. + memset(&outSamples[i], 0, (numSamples - i) * sizeof(s16)); + return; + } } outSamples[i] = end_ ? 0 : samples[curSample++]; } + + if (readp > origp) { + read_ += readp - origp; + } } // http://code.google.com/p/jpcsp/source/browse/trunk/src/jpcsp/HLE/modules150/sceSasCore.java @@ -168,19 +179,36 @@ void ADSREnvelope::SetSimpleEnvelope(u32 ADSREnv1, u32 ADSREnv2) { sustainLevel = getSustainLevel(ADSREnv1); } -SasInstance::SasInstance() - : mixBuffer(0), sendBuffer(0), resampleBuffer(0), grainSize(0) { +SasInstance::SasInstance() + : maxVoices(PSP_SAS_VOICES_MAX), + sampleRate(44100), + outputMode(0), + mixBuffer(0), + sendBuffer(0), + resampleBuffer(0), + grainSize(0) { } SasInstance::~SasInstance() { - delete [] mixBuffer; - delete [] sendBuffer; - delete [] resampleBuffer; + ClearGrainSize(); +} + +void SasInstance::ClearGrainSize() { + if (mixBuffer) + delete [] mixBuffer; + if (sendBuffer) + delete [] sendBuffer; + if (resampleBuffer) + delete [] resampleBuffer; + mixBuffer = NULL; + sendBuffer = NULL; + resampleBuffer = NULL; } void SasInstance::SetGrainSize(int newGrainSize) { grainSize = newGrainSize; + // If you change the sizes here, don't forget DoState(). if (mixBuffer) delete [] mixBuffer; if (sendBuffer) @@ -213,9 +241,7 @@ void SasInstance::Mix(u32 outAddr) { resampleBuffer[1] = voice.resampleHist[1]; // Figure out number of samples to read. - int curSample = voice.samplePos / PSP_SAS_PITCH_BASE; - int lastSample = (voice.samplePos + grainSize * voice.pitch) / PSP_SAS_PITCH_BASE; - u32 numSamples = lastSample - curSample; + u32 numSamples = (voice.sampleFrac + grainSize * voice.pitch) / PSP_SAS_PITCH_BASE; if (numSamples > grainSize * 4) { ERROR_LOG(SAS, "numSamples too large, clamping: %i vs %i", numSamples, grainSize * 4); numSamples = grainSize * 4; @@ -234,7 +260,7 @@ void SasInstance::Mix(u32 outAddr) { voice.resampleHist[1] = resampleBuffer[2 + numSamples - 1]; // Resample to the correct pitch, writing exactly "grainSize" samples. - u32 bufferPos = (voice.samplePos & (PSP_SAS_PITCH_BASE - 1)) + 2 * PSP_SAS_PITCH_BASE; + u32 bufferPos = voice.sampleFrac + 2 * PSP_SAS_PITCH_BASE; for (int i = 0; i < grainSize; i++) { // For now: nearest neighbour, not even using the resample history at all. int sample = resampleBuffer[bufferPos / PSP_SAS_PITCH_BASE]; @@ -250,13 +276,14 @@ void SasInstance::Mix(u32 outAddr) { // We mix into this 32-bit temp buffer and clip in a second loop // Ideally, the shift right should be there too but for now I'm concerned about // not overflowing. - mixBuffer[i * 2] += sample * voice.volumeLeft >> 15; - mixBuffer[i * 2 + 1] += sample * voice.volumeRight >> 15; - sendBuffer[i * 2] += sample * voice.volumeLeftSend >> 15; - sendBuffer[i * 2 + 1] += sample * voice.volumeRightSend >> 15; + mixBuffer[i * 2] += sample * voice.volumeLeft >> 12; + mixBuffer[i * 2 + 1] += sample * voice.volumeRight >> 12; + sendBuffer[i * 2] += sample * voice.volumeLeftSend >> 12; + sendBuffer[i * 2 + 1] += sample * voice.volumeRightSend >> 12; voice.envelope.Step(); } - voice.samplePos += voice.pitch * grainSize; + voice.sampleFrac += voice.pitch * grainSize; + voice.sampleFrac &= (PSP_SAS_PITCH_BASE - 1); if (voice.envelope.HasEnded()) { // NOTICE_LOG(SAS, "Hit end of envelope"); @@ -295,6 +322,44 @@ void SasInstance::Mix(u32 outAddr) { } } +void SasInstance::DoState(PointerWrap &p) { + p.Do(grainSize); + if (p.mode == p.MODE_READ) { + if (grainSize > 0) { + SetGrainSize(grainSize); + } else { + ClearGrainSize(); + } + } + + p.Do(maxVoices); + p.Do(sampleRate); + p.Do(outputMode); + + // SetGrainSize() / ClearGrainSize() should've made our buffers match. + if (mixBuffer != NULL && grainSize > 0) { + p.DoArray(mixBuffer, grainSize * 2); + } + if (sendBuffer != NULL && grainSize > 0) { + p.DoArray(sendBuffer, grainSize * 2); + } + if (resampleBuffer != NULL && grainSize > 0) { + p.DoArray(resampleBuffer, grainSize * 4 + 2); + } + + int n = PSP_SAS_VOICES_MAX; + p.Do(n); + if (n != PSP_SAS_VOICES_MAX) + { + ERROR_LOG(HLE, "Savestate failure: wrong number of SAS voices"); + return; + } + p.DoArray(voices, ARRAY_SIZE(voices)); + p.Do(waveformEffect); + + p.DoMarker("SasInstance"); +} + void SasVoice::Reset() { resampleHist[0] = 0; resampleHist[1] = 0; @@ -305,7 +370,7 @@ void SasVoice::KeyOn() { switch (type) { case VOICETYPE_VAG: if (Memory::IsValidAddress(vagAddr)) { - vag.Start(Memory::GetPointer(vagAddr), loop); + vag.Start(vagAddr, vagSize, loop); } else { ERROR_LOG(SAS, "Invalid VAG address %08x", vagAddr); return; @@ -328,7 +393,7 @@ void SasVoice::ChangedParams(bool changedVag) { if (!playing && on) { playing = true; if (changedVag) - vag.Start(Memory::GetPointer(vagAddr), loop); + vag.Start(vagAddr, vagSize, loop); } // TODO: restart VAG somehow } @@ -409,6 +474,12 @@ static int getExpCurveAt(int index, int duration) { return (short)(sample); } +ADSREnvelope::ADSREnvelope() + : steps_(0), + state_(STATE_OFF), + height_(0) { +} + void ADSREnvelope::WalkCurve(int rate, int type) { short expFactor; int duration; @@ -491,4 +562,4 @@ void ADSREnvelope::KeyOn() { void ADSREnvelope::KeyOff() { SetState(STATE_RELEASE); height_ = sustainLevel; -} \ No newline at end of file +} diff --git a/Core/HW/SasAudio.h b/Core/HW/SasAudio.h index 3b776f5c6d..1c204782fa 100644 --- a/Core/HW/SasAudio.h +++ b/Core/HW/SasAudio.h @@ -23,6 +23,7 @@ #pragma once #include "../Globals.h" +#include "../../Common/ChunkFile.h" enum { PSP_SAS_VOICES_MAX = 32, @@ -74,29 +75,25 @@ enum VoiceType { // It compresses 28 16-bit samples into a block of 16 bytes. // TODO: Get rid of the doubles, making sure it does not impact sound quality. // Doubles are pretty fast on Android devices these days though. -class VagDecoder -{ +class VagDecoder { public: VagDecoder() : data_(0), read_(0) {} - void Start(u8 *data, bool loopEnabled); + void Start(u32 dataPtr, int vagSize, bool loopEnabled); void GetSamples(s16 *outSamples, int numSamples); - bool DecodeBlock(); + void DecodeBlock(u8 *&readp); bool End() const { return end_; } - u8 GetByte() { - return *read_++; - } - private: double samples[28]; int curSample; - u8 *data_; - u8 *read_; + u32 data_; + u32 read_; int curBlock_; int loopStartBlock_; + int numBlocks_; // rolling state. start at 0, should probably reset to 0 on loops? double s_1; @@ -111,6 +108,7 @@ private: class ADSREnvelope { public: + ADSREnvelope(); void SetSimpleEnvelope(u32 ADSREnv1, u32 ADSREnv2); void WalkCurve(int rate, int type); @@ -121,7 +119,7 @@ public: void Step(); int GetHeight() const { - return height_ > PSP_SAS_ENVELOPE_HEIGHT_MAX ? PSP_SAS_ENVELOPE_HEIGHT_MAX : height_; + return height_ > PSP_SAS_ENVELOPE_HEIGHT_MAX ? PSP_SAS_ENVELOPE_HEIGHT_MAX : height_; } bool HasEnded() const { return state_ == STATE_OFF; @@ -158,20 +156,26 @@ struct SasVoice { SasVoice() : playing(false), paused(false), on(false), - type(VOICETYPE_OFF), + type(VOICETYPE_OFF), vagAddr(0), vagSize(0), pcmAddr(0), pcmSize(0), sampleRate(44100), - samplePos(0), + sampleFrac(0), pitch(PSP_SAS_PITCH_BASE), loop(false), noiseFreq(0), volumeLeft(0), volumeRight(0), volumeLeftSend(0), - volumeRightSend(0) {} + volumeRightSend(0) { + } + + void Reset(); + void KeyOn(); + void KeyOff(); + void ChangedParams(bool changedVag); bool playing; bool paused; // a voice can be playing AND paused. In that case, it won't play. @@ -185,7 +189,7 @@ struct SasVoice int pcmSize; int sampleRate; - int samplePos; + int sampleFrac; int pitch; bool loop; @@ -195,14 +199,6 @@ struct SasVoice int volumeRight; int volumeLeftSend; // volume to "Send" (audio-lingo) to the effects processing engine, like reverb int volumeRightSend; - - void Reset(); - - void KeyOn(); - void KeyOff(); - - void ChangedParams(bool changedVag); - s16 resampleHist[2]; ADSREnvelope envelope; @@ -216,6 +212,7 @@ public: SasInstance(); ~SasInstance(); + void ClearGrainSize(); void SetGrainSize(int newGrainSize); int GetGrainSize() const { return grainSize; } @@ -229,6 +226,8 @@ public: void Mix(u32 outAddr); + void DoState(PointerWrap &p); + SasVoice voices[PSP_SAS_VOICES_MAX]; WaveformEffect waveformEffect; diff --git a/Core/Host.h b/Core/Host.h index 9936e8db8f..5600e01393 100644 --- a/Core/Host.h +++ b/Core/Host.h @@ -26,6 +26,7 @@ class PMixer { public: PMixer() {} + virtual ~PMixer() {} virtual int Mix(short *stereoout, int numSamples) {memset(stereoout,0,numSamples*2*sizeof(short)); return numSamples;} }; diff --git a/Core/MIPS/ARM/Jit.h b/Core/MIPS/ARM/Jit.h index d3c9cade66..567d77d7c9 100644 --- a/Core/MIPS/ARM/Jit.h +++ b/Core/MIPS/ARM/Jit.h @@ -94,8 +94,10 @@ public: JitBlockCache *GetBlockCache() { return &blocks; } AsmRoutineManager &Asm() { return asm_; } -private: + void ClearCache(); + +private: void FlushAll(); void WriteExit(u32 destination, int exit_num); diff --git a/Core/MIPS/MIPS.cpp b/Core/MIPS/MIPS.cpp index 73dc9e129b..d784419212 100644 --- a/Core/MIPS/MIPS.cpp +++ b/Core/MIPS/MIPS.cpp @@ -52,7 +52,13 @@ MIPSState::~MIPSState() void MIPSState::Reset() { - if (!MIPSComp::jit && PSP_CoreParameter().cpuCore == CPU_JIT) + if (MIPSComp::jit) + { + delete MIPSComp::jit; + MIPSComp::jit = 0; + } + + if (PSP_CoreParameter().cpuCore == CPU_JIT) MIPSComp::jit = new MIPSComp::Jit(this); memset(r, 0, sizeof(r)); @@ -93,6 +99,33 @@ void MIPSState::Reset() rng.Init(0x1337); } +void MIPSState::DoState(PointerWrap &p) +{ + // Reset the jit if we're loading. + if (p.mode == p.MODE_READ) + Reset(); + + p.DoArray(r, sizeof(r) / sizeof(r[0])); + p.DoArray(f, sizeof(f) / sizeof(f[0])); + p.DoArray(v, sizeof(v) / sizeof(v[0])); + p.DoArray(vfpuCtrl, sizeof(vfpuCtrl) / sizeof(vfpuCtrl[0])); + p.DoArray(vfpuWriteMask, sizeof(vfpuWriteMask) / sizeof(vfpuWriteMask[0])); + p.Do(pc); + p.Do(nextPC); + p.Do(hi); + p.Do(lo); + p.Do(fpcond); + p.Do(fcr0); + p.Do(fcr31); + rng.DoState(p); + p.Do(inDelaySlot); + p.Do(llBit); + p.Do(cpuType); + p.Do(exceptions); + p.Do(debugCount); + p.DoMarker("MIPSState"); +} + void MIPSState::SetWriteMask(const bool wm[4]) { for (int i = 0; i < 4; i++) diff --git a/Core/MIPS/MIPS.h b/Core/MIPS/MIPS.h index cbab48edba..1e8bd29a6f 100644 --- a/Core/MIPS/MIPS.h +++ b/Core/MIPS/MIPS.h @@ -18,6 +18,7 @@ #pragma once #include "../../Globals.h" +#include "../../Common/ChunkFile.h" #include "../CPU.h" enum @@ -92,6 +93,12 @@ public: return (m_z << 16) + m_w; } + void DoState(PointerWrap &p) { + p.Do(m_w); + p.Do(m_z); + p.DoMarker("GMRng"); + } + private: u32 m_w; u32 m_z; @@ -104,6 +111,7 @@ public: ~MIPSState(); void Reset(); + void DoState(PointerWrap &p); u32 r[32]; float f[32]; diff --git a/Core/MIPS/MIPSAnalyst.cpp b/Core/MIPS/MIPSAnalyst.cpp index a97b168d8b..b05f70ed8d 100644 --- a/Core/MIPS/MIPSAnalyst.cpp +++ b/Core/MIPS/MIPSAnalyst.cpp @@ -301,13 +301,13 @@ namespace MIPSAnalyst { if (addr >= furthestBranch) { - u32 target = GetSureBranchTarget(addr); - if (target != INVALIDTARGET && target < addr) + u32 sureTarget = GetSureBranchTarget(addr); + if (sureTarget != INVALIDTARGET && sureTarget < addr) { end = true; } - target = GetJumpTarget(addr); - if (target != INVALIDTARGET && target < addr && ((op&0xFC000000)==0x08000000)) + sureTarget = GetJumpTarget(addr); + if (sureTarget != INVALIDTARGET && sureTarget < addr && ((op&0xFC000000)==0x08000000)) { end = true; } diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index b3045b9a4b..b552e0bd82 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -506,6 +506,29 @@ namespace MIPSInt PC += 4; EatPrefixes(); } + + void Int_Vsocp(u32 op) + { + float s[4], d[4]; + int vd = _VD; + int vs = _VS; + VectorSize sz = GetVecSize(op); + ReadVector(s, sz, vs); + ApplySwizzleS(s, sz); + int n=GetNumVectorElements(sz); + float x = s[0]; + d[0] = std::min(std::max(0.0f, 1.0f - x), 1.0f); + d[1] = std::min(std::max(0.0f, x), 1.0f); + if (n > 1) { + float y = s[1]; + d[2] = std::min(std::max(0.0f, 1.0f - y), 1.0f); + d[3] = std::min(std::max(0.0f, y), 1.0f); + } + ApplyPrefixD(d, sz); + WriteVector(d, sz, vd); + PC += 4; + EatPrefixes(); + } void Int_Vsgn(u32 op) { @@ -851,6 +874,31 @@ namespace MIPSInt EatPrefixes(); } + void Int_VHdp(u32 op) + { + float s[4], t[4]; + float d; + int vd = _VD; + int vs = _VS; + int vt = _VT; + VectorSize sz = GetVecSize(op); + ReadVector(s, sz, vs); + ApplySwizzleS(s, sz); + ReadVector(t, sz, vt); + ApplySwizzleT(t, sz); + float sum = 0.0f; + int n = GetNumVectorElements(sz); + for (int i = 0; i < n; i++) + { + sum += (i == n - 1) ? t[i] : s[i]*t[i]; + } + d = sum; + ApplyPrefixD(&d,V_Single); + V(vd) = d; + PC += 4; + EatPrefixes(); + } + void Int_Vbfy(u32 op) { float s[4]; @@ -882,7 +930,99 @@ namespace MIPSInt PC += 4; EatPrefixes(); } + + void Int_Vsrt1(u32 op) + { + float s[4]; + float d[4]; + int vd = _VD; + int vs = _VS; + VectorSize sz = GetVecSize(op); + ReadVector(s, sz, vs); + ApplySwizzleS(s, sz); + float x = s[0]; + float y = s[1]; + float z = s[2]; + float w = s[3]; + d[0] = std::min(x, y); + d[1] = std::max(x, y); + d[2] = std::min(z, w); + d[3] = std::max(z, w); + ApplyPrefixD(d, sz); + WriteVector(d, sz, vd); + PC += 4; + EatPrefixes(); + } + void Int_Vsrt2(u32 op) + { + float s[4]; + float d[4]; + int vd = _VD; + int vs = _VS; + VectorSize sz = GetVecSize(op); + ReadVector(s, sz, vs); + ApplySwizzleS(s, sz); + float x = s[0]; + float y = s[1]; + float z = s[2]; + float w = s[3]; + d[0] = std::min(x, w); + d[1] = std::min(y, z); + d[2] = std::max(y, z); + d[3] = std::max(x, w); + ApplyPrefixD(d, sz); + WriteVector(d, sz, vd); + PC += 4; + EatPrefixes(); + } + + void Int_Vsrt3(u32 op) + { + float s[4]; + float d[4]; + int vd = _VD; + int vs = _VS; + VectorSize sz = GetVecSize(op); + ReadVector(s, sz, vs); + ApplySwizzleS(s, sz); + float x = s[0]; + float y = s[1]; + float z = s[2]; + float w = s[3]; + d[0] = std::max(x, y); + d[1] = std::min(x, y); + d[2] = std::max(z, w); + d[3] = std::min(z, w); + ApplyPrefixD(d, sz); + WriteVector(d, sz, vd); + PC += 4; + EatPrefixes(); + } + + void Int_Vsrt4(u32 op) + { + float s[4]; + float d[4]; + int vd = _VD; + int vs = _VS; + VectorSize sz = GetVecSize(op); + ReadVector(s, sz, vs); + ApplySwizzleS(s, sz); + float x = s[0]; + float y = s[1]; + float z = s[2]; + float w = s[3]; + d[0] = std::max(x, w); + d[1] = std::max(y, z); + d[2] = std::min(y, z); + d[3] = std::min(x, w); + ApplyPrefixD(d, sz); + WriteVector(d, sz, vd); + PC += 4; + EatPrefixes(); + } + void Int_Vcrs(u32 op) { //half a cross product @@ -906,7 +1046,26 @@ namespace MIPSInt PC += 4; EatPrefixes(); } - + + void Int_Vdet(u32 op) + { + float s[4], t[4]; + float d[4]; + int vd = _VD; + int vs = _VS; + int vt = _VT; + VectorSize sz = GetVecSize(op); + if (sz != V_Pair) + _dbg_assert_msg_(CPU,0,"Trying to interpret instruction that can't be interpreted"); + ReadVector(s, sz, vs); + ReadVector(t, sz, vt); + d[0] = s[0] * t[1] - s[1] * t[0]; + ApplyPrefixD(d, sz); + WriteVector(d, sz, vd); + PC += 4; + EatPrefixes(); + } + void Int_Vfad(u32 op) { float s[4]; @@ -1293,7 +1452,7 @@ namespace MIPSInt int vd = _VD; int cond = op&15; VectorSize sz = GetVecSize(op); - int n = GetNumVectorElements(sz); + int numElements = GetNumVectorElements(sz); float s[4]; float t[4]; float d[4]; @@ -1321,14 +1480,37 @@ namespace MIPSInt PC += 4; EatPrefixes(); } - + + void Int_Vscmp(u32 op) { + int vt = _VT; + int vs = _VS; + int vd = _VD; + VectorSize sz = GetVecSize(op); + float s[4]; + float t[4]; + float d[4]; + ReadVector(s, sz, vs); + ApplySwizzleS(s, sz); + ReadVector(t, sz, vt); + ApplySwizzleT(t, sz); + int n = GetNumVectorElements(sz); + for (int i = 0; i < n ; i++) { + float a = s[i] - t[i]; + d[i] = (float) ((0.0 < a) - (a < 0.0)); + } + ApplyPrefixD(d, sz); + WriteVector(d, sz, vd); + PC += 4; + EatPrefixes(); + } + void Int_Vsge(u32 op) { int vt = _VT; int vs = _VS; int vd = _VD; int cond = op&15; VectorSize sz = GetVecSize(op); - int n = GetNumVectorElements(sz); + int numElements = GetNumVectorElements(sz); float s[4]; float t[4]; float d[4]; @@ -1353,7 +1535,7 @@ namespace MIPSInt int vd = _VD; int cond = op&15; VectorSize sz = GetVecSize(op); - int n = GetNumVectorElements(sz); + int numElements = GetNumVectorElements(sz); float s[4]; float t[4]; float d[4]; diff --git a/Core/MIPS/MIPSIntVFPU.h b/Core/MIPS/MIPSIntVFPU.h index 50e7229366..873a9110cc 100644 --- a/Core/MIPS/MIPSIntVFPU.h +++ b/Core/MIPS/MIPSIntVFPU.h @@ -39,9 +39,11 @@ namespace MIPSInt void Int_VV2Op(u32 op); void Int_Vrot(u32 op); void Int_VDot(u32 op); + void Int_VHdp(u32 op); void Int_Vavg(u32 op); void Int_Vfad(u32 op); void Int_Vocp(u32 op); + void Int_Vsocp(u32 op); void Int_Vsgn(u32 op); void Int_Vtfm(u32 op); void Int_Viim(u32 op); @@ -49,12 +51,18 @@ namespace MIPSInt void Int_Vidt(u32 op); void Int_Vcmp(u32 op); void Int_Vminmax(u32 op); + void Int_Vscmp(u32 op); void Int_Vcrs(u32 op); + void Int_Vdet(u32 op); void Int_Vcmov(u32 op); void Int_CrossQuat(u32 op); void Int_VPFX(u32 op); void Int_Vflush(u32 op); void Int_Vbfy(u32 op); + void Int_Vsrt1(u32 op); + void Int_Vsrt2(u32 op); + void Int_Vsrt3(u32 op); + void Int_Vsrt4(u32 op); void Int_Vf2i(u32 op); void Int_Vi2f(u32 op); void Int_Vi2x(u32 op); diff --git a/Core/MIPS/MIPSTables.cpp b/Core/MIPS/MIPSTables.cpp index 67d9b70e92..f6b4bdc3e3 100644 --- a/Core/MIPS/MIPSTables.cpp +++ b/Core/MIPS/MIPSTables.cpp @@ -492,10 +492,10 @@ MIPSInstruction tableVFPU1[8] = INSTR("vmul",&Jit::Comp_Generic, Dis_VectorSet3, Int_VecDo3, IS_VFPU), INSTR("vdot",&Jit::Comp_Generic, Dis_VectorDot, Int_VDot, IS_VFPU), INSTR("vscl",&Jit::Comp_Generic, Dis_VScl, Int_VScl, IS_VFPU), - INSTR("vhdp",&Jit::Comp_Generic, Dis_Generic, 0, IS_VFPU), - {-2}, + {-2}, + INSTR("vhdp",&Jit::Comp_Generic, Dis_Generic, Int_VHdp, IS_VFPU), INSTR("vcrs",&Jit::Comp_Generic, Dis_Vcrs, Int_Vcrs, IS_VFPU), - INSTR("vdet",&Jit::Comp_Generic, Dis_Generic, 0, IS_VFPU), + INSTR("vdet",&Jit::Comp_Generic, Dis_Generic, Int_Vdet, IS_VFPU), {-2}, }; @@ -506,7 +506,7 @@ MIPSInstruction tableVFPU3[8] = //011011 xxx INSTR("vmin",&Jit::Comp_Generic, Dis_VectorSet3, Int_Vminmax, IS_VFPU), INSTR("vmax",&Jit::Comp_Generic, Dis_VectorSet3, Int_Vminmax, IS_VFPU), {-2}, - INSTR("vscmp",&Jit::Comp_Generic, Dis_Generic, 0, IS_VFPU), + INSTR("vscmp",&Jit::Comp_Generic, Dis_VectorSet3, Int_Vscmp, IS_VFPU), INSTR("vsge",&Jit::Comp_Generic, Dis_VectorSet3, Int_Vsge, IS_VFPU), INSTR("vslt",&Jit::Comp_Generic, Dis_VectorSet3, Int_Vslt, IS_VFPU), }; @@ -685,18 +685,18 @@ MIPSInstruction tableVFPUMatrixSet1[16] = //111100 11100 0xxxx (rm x is 16) MIPSInstruction tableVFPU9[32] = //110100 00010 xxxxx { - INSTR("vsrt1", &Jit::Comp_Generic, Dis_Generic, 0, IS_VFPU), - INSTR("vsrt2", &Jit::Comp_Generic, Dis_Generic, 0, IS_VFPU), + INSTR("vsrt1", &Jit::Comp_Generic, Dis_Vbfy, Int_Vsrt1, IS_VFPU), + INSTR("vsrt2", &Jit::Comp_Generic, Dis_Vbfy, Int_Vsrt2, IS_VFPU), INSTR("vbfy1", &Jit::Comp_Generic, Dis_Vbfy, Int_Vbfy, IS_VFPU), INSTR("vbfy2", &Jit::Comp_Generic, Dis_Vbfy, Int_Vbfy, IS_VFPU), //4 INSTR("vocp", &Jit::Comp_Generic, Dis_Vbfy, Int_Vocp, IS_VFPU), // one's complement - INSTR("vsocp", &Jit::Comp_Generic, Dis_Generic, 0, IS_VFPU), + INSTR("vsocp", &Jit::Comp_Generic, Dis_Vbfy, Int_Vsocp, IS_VFPU), INSTR("vfad", &Jit::Comp_Generic, Dis_Vfad, Int_Vfad, IS_VFPU), INSTR("vavg", &Jit::Comp_Generic, Dis_Vfad, Int_Vavg, IS_VFPU), //8 - INSTR("vsrt3", &Jit::Comp_Generic, Dis_Generic, 0, IS_VFPU), - INSTR("vsrt4", &Jit::Comp_Generic, Dis_Generic, 0, IS_VFPU), + INSTR("vsrt3", &Jit::Comp_Generic, Dis_Vbfy, Int_Vsrt3, IS_VFPU), + INSTR("vsrt4", &Jit::Comp_Generic, Dis_Vbfy, Int_Vsrt4, IS_VFPU), INSTR("vsgn", &Jit::Comp_Generic, Dis_Vbfy, Int_Vsgn, IS_VFPU), {-2}, //12 @@ -848,13 +848,10 @@ const MIPSInstruction *MIPSGetInstruction(u32 op) instr = &table[subop]; if (encoding == Rese) return 0; //invalid instruction -// if (encoding == Spe3) -// __asm int 3 if (!instr) return 0; if (instr->altEncoding == -2) { - //BAD!! //ERROR_LOG(CPU, "Invalid instruction %08x in table %i, entry %i", op, (int)encoding, subop); return 0; //invalid instruction } @@ -924,7 +921,7 @@ void MIPSInterpret(u32 op) //only for those rare ones instr->interpret(op); else { - ERROR_LOG(CPU,"Unknown instruction %08x", op); + ERROR_LOG(CPU,"Unknown instruction %08x at %08x", op, currentMIPS->pc); // Try to disassemble it char disasm[256]; MIPSDisAsm(op, currentMIPS->pc, disasm); diff --git a/Core/MIPS/x86/Asm.cpp b/Core/MIPS/x86/Asm.cpp index 8aca44f08e..67a6611e64 100644 --- a/Core/MIPS/x86/Asm.cpp +++ b/Core/MIPS/x86/Asm.cpp @@ -78,7 +78,12 @@ void AsmRoutineManager::Generate(MIPSState *mips, MIPSComp::Jit *jit) outerLoop = GetCodePtr(); ABI_CallFunction(reinterpret_cast(&CoreTiming::Advance)); FixupBranch skipToRealDispatch = J(); //skip the sync and compare first time - + + dispatcherCheckCoreState = GetCodePtr(); + + CMP(32, M((void*)&coreState), Imm32(0)); + FixupBranch badCoreState = J_CC(CC_NZ, true); + dispatcher = GetCodePtr(); // The result of slice decrementation should be in flags if somebody jumped here // IMPORTANT - We jump on negative, not carry!!! @@ -144,10 +149,11 @@ void AsmRoutineManager::Generate(MIPSState *mips, MIPSComp::Jit *jit) JMP(dispatcherNoCheck); // Let's just dispatch again, we'll enter the block since we know it's there. SetJumpTarget(bail); - - CMP(32, M((void*)&coreState), Imm8(0)); + + CMP(32, M((void*)&coreState), Imm32(0)); J_CC(CC_Z, outerLoop, true); + SetJumpTarget(badCoreState); //Landing pad for drec space ABI_PopAllCalleeSavedRegsAndAdjustStack(); RET(); diff --git a/Core/MIPS/x86/Asm.h b/Core/MIPS/x86/Asm.h index 22b93e174e..226332c676 100644 --- a/Core/MIPS/x86/Asm.h +++ b/Core/MIPS/x86/Asm.h @@ -65,6 +65,7 @@ public: const u8 *outerLoop; const u8 *dispatcher; + const u8 *dispatcherCheckCoreState; const u8 *dispatcherNoCheck; const u8 *fpException; diff --git a/Core/MIPS/x86/CompBranch.cpp b/Core/MIPS/x86/CompBranch.cpp index 85a47125e5..a3cd04f889 100644 --- a/Core/MIPS/x86/CompBranch.cpp +++ b/Core/MIPS/x86/CompBranch.cpp @@ -61,6 +61,10 @@ static u64 saved_flags; void Jit::BranchRSRTComp(u32 op, Gen::CCFlags cc, bool likely) { + if (js.inDelaySlot) { + ERROR_LOG(JIT, "Branch in delay slot at %08x", js.compilerPC); + return; + } int offset = (signed short)(op&0xFFFF)<<2; int rt = _RT; int rs = _RS; @@ -119,6 +123,10 @@ void Jit::BranchRSRTComp(u32 op, Gen::CCFlags cc, bool likely) void Jit::BranchRSZeroComp(u32 op, Gen::CCFlags cc, bool likely) { + if (js.inDelaySlot) { + ERROR_LOG(JIT, "Branch in delay slot at %08x", js.compilerPC); + return; + } int offset = (signed short)(op&0xFFFF)<<2; int rs = _RS; u32 targetAddr = js.compilerPC + offset + 4; @@ -208,6 +216,10 @@ void Jit::Comp_RelBranchRI(u32 op) // If likely is set, discard the branch slot if NOT taken. void Jit::BranchFPFlag(u32 op, Gen::CCFlags cc, bool likely) { + if (js.inDelaySlot) { + ERROR_LOG(JIT, "Branch in delay slot at %08x", js.compilerPC); + return; + } int offset = (signed short)(op & 0xFFFF) << 2; u32 targetAddr = js.compilerPC + offset + 4; @@ -273,6 +285,10 @@ void Jit::Comp_FPUBranch(u32 op) // If likely is set, discard the branch slot if NOT taken. void Jit::BranchVFPUFlag(u32 op, Gen::CCFlags cc, bool likely) { + if (js.inDelaySlot) { + ERROR_LOG(JIT, "Branch in delay slot at %08x", js.compilerPC); + return; + } int offset = (signed short)(op & 0xFFFF) << 2; u32 targetAddr = js.compilerPC + offset + 4; @@ -346,10 +362,14 @@ void Jit::Comp_VBranch(u32 op) void Jit::Comp_Jump(u32 op) { + if (js.inDelaySlot) { + ERROR_LOG(JIT, "Branch in delay slot at %08x", js.compilerPC); + return; + } u32 off = ((op & 0x3FFFFFF) << 2); u32 targetAddr = (js.compilerPC & 0xF0000000) | off; //Delay slot - CompileAt(js.compilerPC + 4); + CompileAt(js.compilerPC + 4); FlushAll(); switch (op >> 26) @@ -374,6 +394,10 @@ static u32 savedPC; void Jit::Comp_JumpReg(u32 op) { + if (js.inDelaySlot) { + ERROR_LOG(JIT, "Branch in delay slot at %08x", js.compilerPC); + return; + } int rs = _RS; u32 delaySlotOp = Memory::ReadUnchecked_U32(js.compilerPC + 4); diff --git a/Core/MIPS/x86/CompFPU.cpp b/Core/MIPS/x86/CompFPU.cpp index f14ac8911d..b3bbf7a694 100644 --- a/Core/MIPS/x86/CompFPU.cpp +++ b/Core/MIPS/x86/CompFPU.cpp @@ -17,6 +17,7 @@ #include "../MIPS.h" +#include "../../Config.h" #include "Common/Common.h" #include "Jit.h" #include "RegCache.h" @@ -86,6 +87,10 @@ void Jit::Comp_FPU3op(u32 op) void Jit::Comp_FPULS(u32 op) { CONDITIONAL_DISABLE; + if (!g_Config.bFastMemory) { + DISABLE; + } + s32 offset = (s16)(op&0xFFFF); int ft = ((op>>16)&0x1f); diff --git a/Core/MIPS/x86/CompLoadStore.cpp b/Core/MIPS/x86/CompLoadStore.cpp index 69560307f8..631dc803aa 100644 --- a/Core/MIPS/x86/CompLoadStore.cpp +++ b/Core/MIPS/x86/CompLoadStore.cpp @@ -17,6 +17,7 @@ #include "../../MemMap.h" #include "../MIPSAnalyst.h" +#include "../../Config.h" #include "Jit.h" #include "RegCache.h" @@ -42,7 +43,11 @@ namespace MIPSComp { void Jit::Comp_ITypeMem(u32 op) { - // OLDD + if (!g_Config.bFastMemory) + { + DISABLE; + } + int offset = (signed short)(op&0xFFFF); int rt = _RT; int rs = _RS; diff --git a/Core/MIPS/x86/Jit.cpp b/Core/MIPS/x86/Jit.cpp index f046de4453..e79e624e2d 100644 --- a/Core/MIPS/x86/Jit.cpp +++ b/Core/MIPS/x86/Jit.cpp @@ -76,6 +76,7 @@ void Jit::RunLoopUntil(u64 globalticks) { // TODO: copy globalticks somewhere ((void (*)())asm_.enterCode)(); + // NOTICE_LOG(HLE, "Exited jitted code at %i, corestate=%i, dc=%i", CoreTiming::GetTicks() / 1000, (int)coreState, CoreTiming::downcount); } const u8 *Jit::DoJit(u32 em_address, JitBlock *b) @@ -168,7 +169,7 @@ void Jit::WriteExitDestInEAX() void Jit::WriteSyscallExit() { SUB(32, M(&CoreTiming::downcount), js.downcountAmount > 127 ? Imm32(js.downcountAmount) : Imm8(js.downcountAmount)); - JMP(asm_.dispatcher, true); + JMP(asm_.dispatcherCheckCoreState, true); } } // namespace diff --git a/Core/MIPS/x86/Jit.h b/Core/MIPS/x86/Jit.h index 23abe06467..cd5a511efc 100644 --- a/Core/MIPS/x86/Jit.h +++ b/Core/MIPS/x86/Jit.h @@ -93,8 +93,9 @@ public: JitBlockCache *GetBlockCache() { return &blocks; } AsmRoutineManager &Asm() { return asm_; } -private: + void ClearCache(); +private: void FlushAll(); void WriteExit(u32 destination, int exit_num); diff --git a/Core/MIPS/x86/JitCache.cpp b/Core/MIPS/x86/JitCache.cpp index 7cae2b5639..c710923972 100644 --- a/Core/MIPS/x86/JitCache.cpp +++ b/Core/MIPS/x86/JitCache.cpp @@ -104,7 +104,6 @@ JitBlockCache::~JitBlockCache() Shutdown(); } - // This clears the JIT cache. It's called from JitCache.cpp when the JIT cache // is full and when saving and loading states. void JitBlockCache::Clear() diff --git a/Core/MIPS/x86/RegCache.cpp b/Core/MIPS/x86/RegCache.cpp index 2cc65533da..2f0b99100f 100644 --- a/Core/MIPS/x86/RegCache.cpp +++ b/Core/MIPS/x86/RegCache.cpp @@ -287,6 +287,7 @@ void GPRRegCache::BindToRegister(int i, bool doLoad, bool makeDirty) { if (i != j && regs[j].location.IsSimpleReg() && regs[j].location.GetSimpleReg() == xr) { + ERROR_LOG(JIT, "BindToRegister: Strange condition"); Crash(); } } diff --git a/Core/MemMap.cpp b/Core/MemMap.cpp index 38a5027173..662f081cdc 100644 --- a/Core/MemMap.cpp +++ b/Core/MemMap.cpp @@ -160,6 +160,11 @@ void Memcpy(const u32 address, const void *data, const u32 len) memcpy(GetPointer(address), data, len); } +void Memcpy(void *data, const u32 address, const u32 len) +{ + memcpy(data,GetPointer(address),len); +} + void GetString(std::string& _string, const u32 em_address) { char stringBuffer[2048]; diff --git a/Core/MemMap.h b/Core/MemMap.h index 6f9a13b91e..2179bf9c9e 100644 --- a/Core/MemMap.h +++ b/Core/MemMap.h @@ -255,6 +255,7 @@ inline const char* GetCharPointer(const u32 address) { void Memset(const u32 _Address, const u8 _Data, const u32 _iLength); void Memcpy(const u32 _Address, const void *_Data, const u32 _iLength); +void Memcpy(void *data, const u32 address, const u32 len); template void ReadStruct(u32 address, T *ptr) diff --git a/Core/MemMapFunctions.cpp b/Core/MemMapFunctions.cpp index e468e79a51..faa0e5fa18 100644 --- a/Core/MemMapFunctions.cpp +++ b/Core/MemMapFunctions.cpp @@ -20,6 +20,7 @@ #include "MemMap.h" #include "Config.h" +#include "Host.h" #include "MIPS/MIPS.h" @@ -84,8 +85,8 @@ inline void ReadFromHardware(T &var, const u32 address) { WARN_LOG(MEMMAP, "ReadFromHardware: Invalid address %08x PC %08x LR %08x", address, currentMIPS->pc, currentMIPS->r[MIPS_REG_RA]); if (!g_Config.bIgnoreBadMemAccess) { - // TODO: Not sure what the best way to crash is... - exit(0); + Core_EnableStepping(true); + host->SetDebugMode(true); } var = 0; } @@ -112,8 +113,8 @@ inline void WriteToHardware(u32 address, const T data) { WARN_LOG(MEMMAP, "WriteToHardware: Invalid address %08x PC %08x LR %08x", address, currentMIPS->pc, currentMIPS->r[MIPS_REG_RA]); if (!g_Config.bIgnoreBadMemAccess) { - // TODO: Not sure what the best way to crash is... - exit(0); + Core_EnableStepping(true); + host->SetDebugMode(true); } } } diff --git a/Core/SaveState.cpp b/Core/SaveState.cpp new file mode 100644 index 0000000000..aa74d0cebe --- /dev/null +++ b/Core/SaveState.cpp @@ -0,0 +1,169 @@ +// Copyright (c) 2012- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "../Common/StdMutex.h" +#include + +#include "SaveState.h" +#include "Core.h" +#include "CoreTiming.h" +#include "HLE/HLE.h" +#include "HLE/sceKernel.h" +#include "HW/MemoryStick.h" +#include "MemMap.h" +#include "MIPS/MIPS.h" +#include "MIPS/JitCommon/JitCommon.h" +#include "System.h" + +namespace SaveState +{ + struct SaveStart + { + void DoState(PointerWrap &p); + }; + + enum OperationType + { + SAVESTATE_SAVE, + SAVESTATE_LOAD, + SAVESTATE_VERIFY, + }; + + struct Operation + { + Operation(OperationType t, const std::string &f, Callback cb) + : type(t), filename(f), callback(cb) + { + } + + OperationType type; + std::string filename; + Callback callback; + }; + + static int timer; + static std::vector pending; + static std::recursive_mutex mutex; + + void Process(u64 userdata, int cyclesLate); + + // This is where the magic happens. + void SaveStart::DoState(PointerWrap &p) + { + // Gotta do CoreTiming first since we'll restore into it. + CoreTiming::DoState(p); + + // This save state even saves its own state. + p.Do(timer); + CoreTiming::RestoreRegisterEvent(timer, "SaveState", Process); + p.DoMarker("SaveState"); + + Memory::DoState(p); + MemoryStick_DoState(p); + currentMIPS->DoState(p); + pspFileSystem.DoState(p); + HLEDoState(p); + __KernelDoState(p); + } + + void Enqueue(SaveState::Operation op) + { + std::lock_guard guard(mutex); + pending.push_back(op); + + // Don't actually run it until next CoreTiming::Advance(). + // It's possible there might be a duplicate but it won't hurt us. + if (Core_IsStepping()) + { + // Warning: this may run on a different thread. + Process(0, 0); + } + else + CoreTiming::ScheduleEvent_Threadsafe(0, timer); + } + + void Load(const std::string &filename, Callback callback) + { + Enqueue(Operation(SAVESTATE_LOAD, filename, callback)); + } + + void Save(const std::string &filename, Callback callback) + { + Enqueue(Operation(SAVESTATE_SAVE, filename, callback)); + } + + void Verify(Callback callback) + { + Enqueue(Operation(SAVESTATE_VERIFY, std::string(""), callback)); + } + + std::vector Flush() + { + std::lock_guard guard(mutex); + std::vector copy = pending; + pending.clear(); + + return copy; + } + + void Process(u64 userdata, int cyclesLate) + { + std::vector operations = Flush(); + SaveStart state; + + for (size_t i = 0, n = operations.size(); i < n; ++i) + { + Operation &op = operations[i]; + bool result; + + switch (op.type) + { + case SAVESTATE_LOAD: + if (MIPSComp::jit) + MIPSComp::jit->ClearCache(); + INFO_LOG(COMMON, "Loading state from %s", op.filename.c_str()); + result = CChunkFileReader::Load(op.filename, REVISION, state); + break; + + case SAVESTATE_SAVE: + if (MIPSComp::jit) + MIPSComp::jit->ClearCache(); + INFO_LOG(COMMON, "Saving state to %s", op.filename.c_str()); + result = CChunkFileReader::Save(op.filename, REVISION, state); + break; + + case SAVESTATE_VERIFY: + INFO_LOG(COMMON, "Verifying save state system"); + result = CChunkFileReader::Verify(state); + break; + + default: + ERROR_LOG(COMMON, "Savestate failure: unknown operation type %d", op.type); + result = false; + break; + } + + if (op.callback != NULL) + op.callback(result); + } + } + + void Init() + { + timer = CoreTiming::RegisterEvent("SaveState", Process); + } +} diff --git a/Core/SaveState.h b/Core/SaveState.h new file mode 100644 index 0000000000..82a7021040 --- /dev/null +++ b/Core/SaveState.h @@ -0,0 +1,39 @@ +// Copyright (c) 2012- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "../Common/ChunkFile.h" +#include + +namespace SaveState +{ + typedef void (*Callback)(bool status); + + // TODO: Better place for this? + const int REVISION = 1; + + void Init(); + + // Load the specified file into the current state (async.) + // Warning: callback will be called on a different thread. + void Load(const std::string &filename, Callback callback = NULL); + // Save the current state to the specified file (async.) + // Warning: callback will be called on a different thread. + void Save(const std::string &filename, Callback callback = NULL); + // For testing / automated tests. Runs a save state verification pass (async.) + // Warning: callback will be called on a different thread. + void Verify(Callback callback = NULL); +}; diff --git a/Core/System.cpp b/Core/System.cpp index 18c2b94687..9110dcc245 100644 --- a/Core/System.cpp +++ b/Core/System.cpp @@ -42,7 +42,6 @@ MetaFileSystem pspFileSystem; static CoreParameter coreParameter; -extern ShaderManager shaderManager; bool PSP_Init(const CoreParameter &coreParam, std::string *error_string) { @@ -76,9 +75,6 @@ bool PSP_Init(const CoreParameter &coreParam, std::string *error_string) return false; } - shaderManager.DirtyShader(); - shaderManager.DirtyUniform(DIRTY_ALL); - // Setup JIT here. if (coreParameter.startPaused) coreState = CORE_STEPPING; @@ -97,7 +93,6 @@ void PSP_Shutdown() pspFileSystem.UnmountAll(); TextureCache_Clear(true); - shaderManager.ClearCache(true); CoreTiming::ClearPendingEvents(); CoreTiming::UnregisterAllEvents(); diff --git a/Core/Util/BlockAllocator.cpp b/Core/Util/BlockAllocator.cpp index 27183cbff8..c8e7cbbe3e 100644 --- a/Core/Util/BlockAllocator.cpp +++ b/Core/Util/BlockAllocator.cpp @@ -297,3 +297,13 @@ u32 BlockAllocator::GetTotalFreeBytes() } return sum; } + +void BlockAllocator::DoState(PointerWrap &p) +{ + Block b(0, 0, false); + p.Do(blocks, b); + p.Do(rangeStart_); + p.Do(rangeSize_); + p.Do(grain_); + p.DoMarker("BlockAllocator"); +} diff --git a/Core/Util/BlockAllocator.h b/Core/Util/BlockAllocator.h index 7ed26d3fc6..dc75a8292e 100644 --- a/Core/Util/BlockAllocator.h +++ b/Core/Util/BlockAllocator.h @@ -2,6 +2,7 @@ #pragma once #include "../../Globals.h" +#include "../../Common/ChunkFile.h" #include #include @@ -22,7 +23,7 @@ public: void ListBlocks(); - // WARNING: size can be modified upwards! + // WARNING: size can be modified upwards! u32 Alloc(u32 &size, bool fromTop = false, const char *tag = 0); u32 AllocAt(u32 position, u32 size, const char *tag = 0); @@ -42,6 +43,8 @@ public: u32 GetLargestFreeBlockSize(); u32 GetTotalFreeBytes(); + void DoState(PointerWrap &p); + private: void CheckBlocks(); diff --git a/Core/Util/PPGeDraw.cpp b/Core/Util/PPGeDraw.cpp index 57e79195e6..bd8032dd0b 100644 --- a/Core/Util/PPGeDraw.cpp +++ b/Core/Util/PPGeDraw.cpp @@ -39,21 +39,21 @@ struct PPGeVertex { s16 x, y; u16 z; }; -u32 savedContextPtr; -u32 savedContextSize = 512 * 4; +static u32 savedContextPtr; +static u32 savedContextSize = 512 * 4; // Display list writer -u32 dlPtr; -u32 dlWritePtr; -u32 dlSize = 0x10000; // should be enough for a frame of gui... +static u32 dlPtr; +static u32 dlWritePtr; +static u32 dlSize = 0x10000; // should be enough for a frame of gui... -u32 dataPtr; -u32 dataWritePtr; -u32 dataSize = 0x10000; // should be enough for a frame of gui... +static u32 dataPtr; +static u32 dataWritePtr; +static u32 dataSize = 0x10000; // should be enough for a frame of gui... // Vertex collector -u32 vertexStart; -u32 vertexCount; +static u32 vertexStart; +static u32 vertexCount; //only 0xFFFFFF of data is used static void WriteCmd(u8 cmd, u32 data) { @@ -141,6 +141,29 @@ void __PPGeInit() dlPtr, dataPtr, atlasPtr, atlasSize, savedContextPtr); } +void __PPGeDoState(PointerWrap &p) +{ + p.Do(atlasPtr); + p.Do(atlasWidth); + p.Do(atlasHeight); + + p.Do(savedContextPtr); + p.Do(savedContextSize); + + p.Do(dlPtr); + p.Do(dlWritePtr); + p.Do(dlSize); + + p.Do(dataPtr); + p.Do(dataWritePtr); + p.Do(dataSize); + + p.Do(vertexStart); + p.Do(vertexCount); + + p.DoMarker("PPGeDraw"); +} + void __PPGeShutdown() { if (atlasPtr) @@ -279,8 +302,8 @@ void PPGeDraw4Patch(int atlasImage, float x, float y, float w, float h, u32 colo if (!dlPtr) return; const AtlasImage &img = ppge_images[atlasImage]; - float borderx = img.w / 2; - float bordery = img.h / 2; + float borderx = img.w / 20; + float bordery = img.h / 20; float u1 = img.u1, uhalf = (img.u1 + img.u2) / 2, u2 = img.u2; float v1 = img.v1, vhalf = (img.v1 + img.v2) / 2, v2 = img.v2; float xmid1 = x + borderx; diff --git a/Core/Util/PPGeDraw.h b/Core/Util/PPGeDraw.h index e77a53cd89..48f23bca4b 100644 --- a/Core/Util/PPGeDraw.h +++ b/Core/Util/PPGeDraw.h @@ -18,6 +18,7 @@ #pragma once #include "../../Globals.h" +#include "../../Common/ChunkFile.h" #include "ppge_atlas.h" ///////////////////////////////////////////////////////////////////////////////////////////// @@ -29,6 +30,9 @@ // space for the display list. The PSP must be inited. void __PPGeInit(); +// Saves to and restores from savestates (kernel RAM pointers, etc.) +void __PPGeDoState(PointerWrap &p); + // Just frees up the allocated kernel memory. void __PPGeShutdown(); diff --git a/Core/Util/ppge_atlas.cpp b/Core/Util/ppge_atlas.cpp index 7a8bf0fde9..b211096eb3 100644 --- a/Core/Util/ppge_atlas.cpp +++ b/Core/Util/ppge_atlas.cpp @@ -112,7 +112,7 @@ const AtlasFont *ppge_fonts[1] = { }; const AtlasImage ppge_images[6] = { {0.458984f, 0.001953f, 0.576172f, 0.119141f, 31, 31, "I_CROSS"}, - {0.193359f, 0.001953f, 0.314453f, 0.123047f, 32, 32, "I_CIRCLE"}, + {0.193359f, 0.001953f, 0.324453f, 0.133047f, 32, 32, "I_CIRCLE"}, {0.685547f, 0.001953f, 0.794922f, 0.111328f, 29, 29, "I_SQUARE"}, {0.322266f, 0.001953f, 0.451172f, 0.111328f, 34, 29, "I_TRIANGLE"}, {0.802734f, 0.001953f, 0.861328f, 0.193359f, 16, 50, "I_BUTTON"}, diff --git a/GPU/CMakeLists.txt b/GPU/CMakeLists.txt index 803200ae6d..ad56972231 100644 --- a/GPU/CMakeLists.txt +++ b/GPU/CMakeLists.txt @@ -4,6 +4,7 @@ set(SRCS GLES/DisplayListInterpreter.cpp GLES/FragmentShaderGenerator.cpp GLES/Framebuffer.cpp + GLES/IndexGenerator.cpp GLES/ShaderManager.cpp GLES/StateMapping.cpp GLES/TextureCache.cpp diff --git a/GPU/GLES/DisplayListInterpreter.cpp b/GPU/GLES/DisplayListInterpreter.cpp index 90931d75bc..af4966ee18 100644 --- a/GPU/GLES/DisplayListInterpreter.cpp +++ b/GPU/GLES/DisplayListInterpreter.cpp @@ -23,6 +23,7 @@ #include "../GPUState.h" #include "../ge_constants.h" +#include "../GeDisasm.h" #include "ShaderManager.h" #include "DisplayListInterpreter.h" @@ -33,48 +34,179 @@ #include "../../Core/HLE/sceKernelThread.h" #include "../../Core/HLE/sceKernelInterrupt.h" -inline void glEnDis(GLuint cmd, int value) -{ - (value ? glEnable : glDisable)(cmd); -} - -ShaderManager shaderManager; - extern u32 curTextureWidth; extern u32 curTextureHeight; +const int flushOnChangedBeforeCommandList[] = { + GE_CMD_VERTEXTYPE, + GE_CMD_BLENDMODE, + GE_CMD_BLENDFIXEDA, + GE_CMD_BLENDFIXEDB, +}; + +const int flushBeforeCommandList[] = { + GE_CMD_BEZIER, + GE_CMD_SPLINE, + GE_CMD_SIGNAL, + GE_CMD_FINISH, + GE_CMD_BJUMP, + GE_CMD_OFFSETADDR, + GE_CMD_REGION1, + GE_CMD_REGION2, + GE_CMD_CULLFACEENABLE, + GE_CMD_TEXTUREMAPENABLE, + GE_CMD_LIGHTINGENABLE, + GE_CMD_FOGENABLE, + GE_CMD_TEXSCALEU, + GE_CMD_TEXSCALEV, + GE_CMD_TEXOFFSETU, + GE_CMD_TEXOFFSETV, + GE_CMD_MINZ, + GE_CMD_MAXZ, + GE_CMD_FRAMEBUFPTR, + GE_CMD_FRAMEBUFWIDTH, + GE_CMD_FRAMEBUFPIXFORMAT, + GE_CMD_TEXADDR0, + GE_CMD_CLUTADDR, + GE_CMD_LOADCLUT, + GE_CMD_TEXMAPMODE, + GE_CMD_TEXSHADELS, + GE_CMD_CLUTFORMAT, + GE_CMD_TRANSFERSTART, + GE_CMD_TEXBUFWIDTH0, + GE_CMD_TEXSIZE0, + GE_CMD_TEXSIZE1, + GE_CMD_TEXSIZE2, + GE_CMD_TEXSIZE3, + GE_CMD_TEXSIZE4, + GE_CMD_TEXSIZE5, + GE_CMD_TEXSIZE6, + GE_CMD_TEXSIZE7, + GE_CMD_ZBUFPTR, + GE_CMD_ZBUFWIDTH, + GE_CMD_AMBIENTCOLOR, + GE_CMD_AMBIENTALPHA, + GE_CMD_MATERIALAMBIENT, + GE_CMD_MATERIALDIFFUSE, + GE_CMD_MATERIALEMISSIVE, + GE_CMD_MATERIALSPECULAR, + GE_CMD_MATERIALALPHA, + GE_CMD_MATERIALSPECULARCOEF, + GE_CMD_LIGHTTYPE0, + GE_CMD_LIGHTTYPE1, + GE_CMD_LIGHTTYPE2, + GE_CMD_LIGHTTYPE3, + GE_CMD_LX0, + GE_CMD_LX1, + GE_CMD_LX2, + GE_CMD_LX3, + GE_CMD_LDX0, + GE_CMD_LDX1, + GE_CMD_LDX2, + GE_CMD_LDX3, + GE_CMD_LKA0, + GE_CMD_LAC0, + GE_CMD_LDC0, + GE_CMD_LSC0, + GE_CMD_VIEWPORTX1, + GE_CMD_VIEWPORTY1, + GE_CMD_VIEWPORTX2, + GE_CMD_VIEWPORTY2, + GE_CMD_VIEWPORTZ1, + GE_CMD_VIEWPORTZ2, + GE_CMD_LIGHTENABLE0, + GE_CMD_LIGHTENABLE1, + GE_CMD_LIGHTENABLE2, + GE_CMD_LIGHTENABLE3, + GE_CMD_CULL, + GE_CMD_LMODE, + GE_CMD_REVERSENORMAL, + GE_CMD_PATCHDIVISION, + GE_CMD_MATERIALUPDATE, + GE_CMD_CLEARMODE, + GE_CMD_ALPHABLENDENABLE, + GE_CMD_ALPHATESTENABLE, + GE_CMD_ALPHATEST, + GE_CMD_TEXFUNC, + GE_CMD_TEXFILTER, + GE_CMD_TEXENVCOLOR, + GE_CMD_TEXMODE, + GE_CMD_TEXFORMAT, + GE_CMD_TEXFLUSH, + GE_CMD_TEXWRAP, + GE_CMD_ZTESTENABLE, + GE_CMD_STENCILTESTENABLE, + GE_CMD_STENCILOP, + GE_CMD_ZTEST, + GE_CMD_FOG1, + GE_CMD_FOG2, + GE_CMD_FOGCOLOR, + GE_CMD_MORPHWEIGHT0, + GE_CMD_MORPHWEIGHT1, + GE_CMD_MORPHWEIGHT2, + GE_CMD_MORPHWEIGHT3, + GE_CMD_MORPHWEIGHT4, + GE_CMD_MORPHWEIGHT5, + GE_CMD_MORPHWEIGHT6, + GE_CMD_MORPHWEIGHT7, + GE_CMD_WORLDMATRIXNUMBER, + GE_CMD_VIEWMATRIXNUMBER, + GE_CMD_PROJMATRIXNUMBER, + GE_CMD_PROJMATRIXDATA, + GE_CMD_TGENMATRIXNUMBER, + GE_CMD_BONEMATRIXNUMBER, +}; + GLES_GPU::GLES_GPU(int renderWidth, int renderHeight) - : interruptsEnabled_(true), +: interruptsEnabled_(true), + displayFramebufPtr_(0), renderWidth_(renderWidth), renderHeight_(renderHeight), dlIdGenerator(1), - displayFramebufPtr_(0) -{ + dumpThisFrame_(false), + dumpNextFrame_(false) { renderWidthFactor_ = (float)renderWidth / 480.0f; renderHeightFactor_ = (float)renderHeight / 272.0f; - shaderManager_ = &shaderManager; + shaderManager_ = new ShaderManager(); + transformDraw_.SetShaderManager(shaderManager_); TextureCache_Init(); // Sanity check gstate if ((int *)&gstate.transferstart - (int *)&gstate != 0xEA) { ERROR_LOG(G3D, "gstate has drifted out of sync!"); } + + flushBeforeCommand_ = new u8[256]; + memset(flushBeforeCommand_, 0, 256 * sizeof(bool)); + for (int i = 0; i < ARRAY_SIZE(flushOnChangedBeforeCommandList); i++) { + flushBeforeCommand_[flushOnChangedBeforeCommandList[i]] = 2; + } + for (int i = 0; i < ARRAY_SIZE(flushBeforeCommandList); i++) { + flushBeforeCommand_[flushBeforeCommandList[i]] = 1; + } + flushBeforeCommand_[1] = 0; } -GLES_GPU::~GLES_GPU() -{ +GLES_GPU::~GLES_GPU() { TextureCache_Shutdown(); - for (auto iter = vfbs_.begin(); iter != vfbs_.end(); ++iter) - { + for (auto iter = vfbs_.begin(); iter != vfbs_.end(); ++iter) { fbo_destroy((*iter)->fbo); delete (*iter); } vfbs_.clear(); + shaderManager_->ClearCache(true); + delete shaderManager_; + delete [] flushBeforeCommand_; } -void GLES_GPU::InitClear() -{ - if (!g_Config.bBufferedRendering) - { +void GLES_GPU::DeviceLost() { + // Simply drop all caches and textures. + // FBO:s appear to survive? Or no? + shaderManager_->ClearCache(false); + TextureCache_Clear(false); +} + +void GLES_GPU::InitClear() { + if (!g_Config.bBufferedRendering) { glClearColor(0,0,0,1); // glClearColor(1,0,1,1); glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); @@ -82,13 +214,27 @@ void GLES_GPU::InitClear() glViewport(0, 0, PSP_CoreParameter().pixelWidth, PSP_CoreParameter().pixelHeight); } -void GLES_GPU::BeginFrame() -{ +void GLES_GPU::DumpNextFrame() { + dumpNextFrame_ = true; +} + +void GLES_GPU::BeginFrame() { TextureCache_Decimate(); + if (dumpNextFrame_) { + NOTICE_LOG(G3D, "DUMPING THIS FRAME"); + dumpThisFrame_ = true; + dumpNextFrame_ = false; + } else if (dumpThisFrame_) { + dumpThisFrame_ = false; + } + shaderManager_->DirtyShader(); + + // Not sure if this is really needed. + shaderManager_->DirtyUniform(DIRTY_ALL); + // NOTE - this is all wrong. At the beginning of the frame is a TERRIBLE time to draw the fb. - if (g_Config.bDisplayFramebuffer && displayFramebufPtr_) - { + if (g_Config.bDisplayFramebuffer && displayFramebufPtr_) { INFO_LOG(HLE, "Drawing the framebuffer"); const u8 *pspframebuf = Memory::GetPointer((0x44000000) | (displayFramebufPtr_ & 0x1FFFFF)); // TODO - check glstate.cullFace.disable(); @@ -100,9 +246,9 @@ void GLES_GPU::BeginFrame() currentRenderVfb_ = 0; } -void GLES_GPU::SetDisplayFramebuffer(u32 framebuf, u32 stride, int format) -{ +void GLES_GPU::SetDisplayFramebuffer(u32 framebuf, u32 stride, int format) { if (framebuf & 0x04000000) { + //DEBUG_LOG(G3D, "Switch display framebuffer %08x", framebuf); displayFramebufPtr_ = framebuf; displayStride_ = stride; displayFormat_ = format; @@ -111,11 +257,13 @@ void GLES_GPU::SetDisplayFramebuffer(u32 framebuf, u32 stride, int format) } } -void GLES_GPU::CopyDisplayToOutput() -{ +void GLES_GPU::CopyDisplayToOutput() { + transformDraw_.Flush(); if (!g_Config.bBufferedRendering) return; + EndDebugDraw(); + VirtualFramebuffer *vfb = GetDisplayFBO(); fbo_unbind(); @@ -142,15 +290,15 @@ void GLES_GPU::CopyDisplayToOutput() // These are in the output display coordinates framebufferManager.DrawActiveTexture(480, 272, true); - shaderManager.DirtyShader(); - shaderManager.DirtyUniform(DIRTY_ALL); + shaderManager_->DirtyShader(); + shaderManager_->DirtyUniform(DIRTY_ALL); gstate_c.textureChanged = true; + + BeginDebugDraw(); } -GLES_GPU::VirtualFramebuffer *GLES_GPU::GetDisplayFBO() -{ - for (auto iter = vfbs_.begin(); iter != vfbs_.end(); ++iter) - { +GLES_GPU::VirtualFramebuffer *GLES_GPU::GetDisplayFBO() { + for (auto iter = vfbs_.begin(); iter != vfbs_.end(); ++iter) { if (((*iter)->fb_address & 0x3FFFFFF) == (displayFramebufPtr_ & 0x3FFFFFF)) { // Could check w to but whatever return *iter; @@ -160,8 +308,7 @@ GLES_GPU::VirtualFramebuffer *GLES_GPU::GetDisplayFBO() return 0; } -void GLES_GPU::SetRenderFrameBuffer() -{ +void GLES_GPU::SetRenderFrameBuffer() { if (!g_Config.bBufferedRendering) return; // Get parameters @@ -170,7 +317,7 @@ void GLES_GPU::SetRenderFrameBuffer() u32 z_address = (gstate.zbptr & 0xFFE000) | ((gstate.zbwidth & 0xFF0000) << 8); int z_stride = gstate.zbwidth & 0x3C0; - + // Yeah this is not completely right. but it'll do for now. int drawing_width = ((gstate.region2) & 0x3FF) + 1; int drawing_height = ((gstate.region2 >> 10) & 0x3FF) + 1; @@ -179,8 +326,7 @@ void GLES_GPU::SetRenderFrameBuffer() // Find a matching framebuffer VirtualFramebuffer *vfb = 0; - for (auto iter = vfbs_.begin(); iter != vfbs_.end(); ++iter) - { + for (auto iter = vfbs_.begin(); iter != vfbs_.end(); ++iter) { VirtualFramebuffer *v = *iter; if (v->fb_address == fb_address) { // Let's not be so picky for now. Let's say this is the one. @@ -193,6 +339,8 @@ void GLES_GPU::SetRenderFrameBuffer() // None found? Create one. if (!vfb) { + transformDraw_.Flush(); + gstate_c.textureChanged = true; vfb = new VirtualFramebuffer; vfb->fb_address = fb_address; vfb->fb_stride = fb_stride; @@ -211,36 +359,45 @@ void GLES_GPU::SetRenderFrameBuffer() return; } - if (vfb != currentRenderVfb_) - { + if (vfb != currentRenderVfb_) { + transformDraw_.Flush(); // Use it as a render target. DEBUG_LOG(HLE, "Switching render target to FBO for %08x", vfb->fb_address); + gstate_c.textureChanged = true; fbo_bind_as_render_target(vfb->fbo); glViewport(0, 0, renderWidth_, renderHeight_); currentRenderVfb_ = vfb; } } +void GLES_GPU::BeginDebugDraw() { + if (g_Config.bDrawWireframe) { +#ifndef USING_GLES2 + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); +#endif + // glClear(GL_COLOR_BUFFER_BIT); + } +} +void GLES_GPU::EndDebugDraw() { +#ifndef USING_GLES2 + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); +#endif +} // Render queue -bool GLES_GPU::ProcessDLQueue() -{ +bool GLES_GPU::ProcessDLQueue() { std::vector::iterator iter = dlQueue.begin(); - while (!(iter == dlQueue.end())) - { + while (!(iter == dlQueue.end())) { DisplayList &l = *iter; dcontext.pc = l.listpc; dcontext.stallAddr = l.stall; -// DEBUG_LOG(G3D,"Okay, starting DL execution at %08 - stall = %08x", context.pc, stallAddr); - if (!InterpretList()) - { +// //DEBUG_LOG(G3D,"Okay, starting DL execution at %08 - stall = %08x", context.pc, stallAddr); + if (!InterpretList()) { l.listpc = dcontext.pc; l.stall = dcontext.stallAddr; return false; - } - else - { + } else { //At the end, we can remove it from the queue and continue dlQueue.erase(iter); //this invalidated the iterator, let's fix it @@ -250,8 +407,7 @@ bool GLES_GPU::ProcessDLQueue() return true; //no more lists! } -u32 GLES_GPU::EnqueueList(u32 listpc, u32 stall) -{ +u32 GLES_GPU::EnqueueList(u32 listpc, u32 stall) { DisplayList dl; dl.id = dlIdGenerator++; dl.listpc = listpc & 0xFFFFFFF; @@ -263,8 +419,7 @@ u32 GLES_GPU::EnqueueList(u32 listpc, u32 stall) return 0; } -void GLES_GPU::UpdateStall(int listid, u32 newstall) -{ +void GLES_GPU::UpdateStall(int listid, u32 newstall) { // this needs improvement.... for (std::vector::iterator iter = dlQueue.begin(); iter != dlQueue.end(); iter++) { @@ -274,55 +429,22 @@ void GLES_GPU::UpdateStall(int listid, u32 newstall) l.stall = newstall & 0xFFFFFFF; } } - ProcessDLQueue(); } -void GLES_GPU::DrawSync(int mode) -{ - +void GLES_GPU::DrawSync(int mode) { + transformDraw_.Flush(); } -void GLES_GPU::Continue() -{ +void GLES_GPU::Continue() { } -void GLES_GPU::Break() -{ +void GLES_GPU::Break() { } -// Just to get something on the screen, we'll just not subdivide correctly. -void GLES_GPU::DrawBezier(int ucount, int vcount) -{ - u16 indices[3 * 3 * 6]; - float customUV[32]; - int c = 0; - for (int y = 0; y < 3; y++) { - for (int x = 0; x < 3; x++) { - indices[c++] = y * 4 + x; - indices[c++] = y * 4 + x + 1; - indices[c++] = (y + 1) * 4 + x + 1; - indices[c++] = (y + 1) * 4 + x + 1; - indices[c++] = (y + 1) * 4 + x; - indices[c++] = y * 4 + x; - } - } - - for (int y = 0; y < 4; y++) { - for (int x = 0; x < 4; x++) { - customUV[(y * 4 + x) * 2 + 0] = (float)x/3.0f; - customUV[(y * 4 + x) * 2 + 1] = (float)y/3.0f; - } - } - - TransformAndDrawPrim(Memory::GetPointer(gstate_c.vertexAddr), &indices[0], GE_PRIM_TRIANGLES, 3 * 3 * 6, customUV, GE_VTYPE_IDX_16BIT); -} - - -void EnterClearMode(u32 data) -{ +static void EnterClearMode(u32 data) { bool colMask = (data >> 8) & 1; bool alphaMask = (data >> 9) & 1; bool updateZ = (data >> 10) & 1; @@ -330,8 +452,7 @@ void EnterClearMode(u32 data) glstate.depthWrite.set(updateZ ? GL_TRUE : GL_FALSE); } -void LeaveClearMode() -{ +static void LeaveClearMode() { // We have to reset the following state as per the state of the command registers: // Back face culling // Texture map enable (meh) @@ -343,26 +464,24 @@ void LeaveClearMode() // dirtyshader? } -void GLES_GPU::ExecuteOp(u32 op, u32 diff) -{ +void GLES_GPU::ExecuteOp(u32 op, u32 diff) { u32 cmd = op >> 24; u32 data = op & 0xFFFFFF; // Handle control and drawing commands here directly. The others we delegate. - switch (cmd) - { + switch (cmd) { case GE_CMD_BASE: - DEBUG_LOG(G3D,"DL BASE: %06x", data & 0xFFFFFF); + //DEBUG_LOG(G3D,"DL BASE: %06x", data & 0xFFFFFF); break; case GE_CMD_VADDR: /// <<8???? gstate_c.vertexAddr = ((gstate.base & 0x00FF0000) << 8)|data; - DEBUG_LOG(G3D,"DL VADDR: %06x", gstate_c.vertexAddr); + //DEBUG_LOG(G3D,"DL VADDR: %06x", gstate_c.vertexAddr); break; case GE_CMD_IADDR: gstate_c.indexAddr = ((gstate.base & 0x00FF0000) << 8)|data; - DEBUG_LOG(G3D,"DL IADDR: %06x", gstate_c.indexAddr); + //DEBUG_LOG(G3D,"DL IADDR: %06x", gstate_c.indexAddr); break; case GE_CMD_PRIM: @@ -371,29 +490,37 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) u32 count = data & 0xFFFF; u32 type = data >> 16; - static const char* types[7] = { - "POINTS=0,", - "LINES=1,", - "LINE_STRIP=2,", - "TRIANGLES=3,", - "TRIANGLE_STRIP=4,", - "TRIANGLE_FAN=5,", - "RECTANGLES=6,", - }; - DEBUG_LOG(G3D, "DL DrawPrim type: %s count: %i vaddr= %08x, iaddr= %08x", type<7 ? types[type] : "INVALID", count, gstate_c.vertexAddr, gstate_c.indexAddr); + + if (!Memory::IsValidAddress(gstate_c.vertexAddr)) { + ERROR_LOG(G3D, "Bad vertex address %08x!", gstate_c.vertexAddr); + break; + } // TODO: Split this so that we can collect sequences of primitives, can greatly speed things up // on platforms where draw calls are expensive like mobile and D3D void *verts = Memory::GetPointer(gstate_c.vertexAddr); void *inds = 0; - if ((gstate.vertType & GE_VTYPE_IDX_MASK) != GE_VTYPE_IDX_NONE) + if ((gstate.vertType & GE_VTYPE_IDX_MASK) != GE_VTYPE_IDX_NONE) { + if (!Memory::IsValidAddress(gstate_c.indexAddr)) { + ERROR_LOG(G3D, "Bad index address %08x!", gstate_c.indexAddr); + break; + } inds = Memory::GetPointer(gstate_c.indexAddr); + } - // Seems we have to advance the vertex addr, at least in some cases. - // Question: Should we also advance the index addr? int bytesRead; - TransformAndDrawPrim(verts, inds, type, count, 0, -1, &bytesRead); - gstate_c.vertexAddr += bytesRead; + transformDraw_.SubmitPrim(verts, inds, type, count, gstate.vertType, 0, -1, &bytesRead); + // After drawing, we advance the vertexAddr (when non indexed) or indexAddr (when indexed). + // Some games rely on this, they don't bother reloading VADDR and IADDR. + // Q: Are these changed reflected in the real registers? Needs testing. + if (inds) { + int indexSize = 1; + if ((gstate.vertType & GE_VTYPE_IDX_MASK) == GE_VTYPE_IDX_16BIT) + indexSize = 2; + gstate_c.indexAddr += count * indexSize; + } else { + gstate_c.vertexAddr += bytesRead; + } } break; @@ -402,8 +529,7 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) { int bz_ucount = data & 0xFF; int bz_vcount = (data >> 8) & 0xFF; - DrawBezier(bz_ucount, bz_vcount); - DEBUG_LOG(G3D,"DL DRAW BEZIER: %i x %i", bz_ucount, bz_vcount); + transformDraw_.DrawBezier(bz_ucount, bz_vcount); } break; @@ -413,20 +539,22 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) int sp_vcount = (data >> 8) & 0xFF; int sp_utype = (data >> 16) & 0x3; int sp_vtype = (data >> 18) & 0x3; - //drawSpline(sp_ucount, sp_vcount, sp_utype, sp_vtype); - DEBUG_LOG(G3D,"DL DRAW SPLINE: %i x %i, %i x %i", sp_ucount, sp_vcount, sp_utype, sp_vtype); + transformDraw_.DrawSpline(sp_ucount, sp_vcount, sp_utype, sp_vtype); } break; - case GE_CMD_JUMP: + case GE_CMD_JUMP: { u32 target = (((gstate.base & 0x00FF0000) << 8) | (op & 0xFFFFFC)) & 0x0FFFFFFF; - DEBUG_LOG(G3D,"DL CMD JUMP - %08x to %08x", dcontext.pc, target); - dcontext.pc = target - 4; // pc will be increased after we return, counteract that + if (Memory::IsValidAddress(target)) { + dcontext.pc = target - 4; // pc will be increased after we return, counteract that + } else { + ERROR_LOG(G3D, "JUMP to illegal address %08x - ignoring??", target); + } } break; - case GE_CMD_CALL: + case GE_CMD_CALL: { u32 retval = dcontext.pc + 4; if (stackptr == ARRAY_SIZE(stack)) { @@ -434,40 +562,35 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) } else { stack[stackptr++] = retval; u32 target = (((gstate.base & 0x00FF0000) << 8) | (op & 0xFFFFFC)) & 0xFFFFFFF; - DEBUG_LOG(G3D,"DL CMD CALL - %08x to %08x, ret=%08x", dcontext.pc, target, retval); dcontext.pc = target - 4; // pc will be increased after we return, counteract that } } break; - case GE_CMD_RET: - //TODO : debug! + case GE_CMD_RET: { - u32 target = (dcontext.pc & 0xF0000000) | (stack[--stackptr] & 0x0FFFFFFF); - DEBUG_LOG(G3D,"DL CMD RET - from %08x to %08x", dcontext.pc, target); + u32 target = (dcontext.pc & 0xF0000000) | (stack[--stackptr] & 0x0FFFFFFF); dcontext.pc = target - 4; } break; case GE_CMD_SIGNAL: { - ERROR_LOG(G3D, "DL GE_CMD_SIGNAL %08x", data & 0xFFFFFF); - // Processed in GE_END. + // Processed in GE_END. Has data. } break; case GE_CMD_FINISH: - DEBUG_LOG(G3D,"DL CMD FINISH"); + // TODO: Should this run while interrupts are suspended? if (interruptsEnabled_) - __TriggerInterruptWithArg(PSP_GE_INTR, PSP_GE_SUBINTR_FINISH, 0); + __TriggerInterruptWithArg(PSP_INTR_HLE, PSP_GE_INTR, PSP_GE_SUBINTR_FINISH, 0); break; - case GE_CMD_END: - DEBUG_LOG(G3D,"DL CMD END"); - switch (prev >> 24) - { + case GE_CMD_END: + switch (prev >> 24) { case GE_CMD_SIGNAL: { + // TODO: see http://code.google.com/p/jpcsp/source/detail?r=2935# int behaviour = (prev >> 16) & 0xFF; int signal = prev & 0xFFFF; int enddata = data & 0xFFFF; @@ -477,7 +600,7 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) ERROR_LOG(G3D, "Signal with Wait UNIMPLEMENTED! signal/end: %04x %04x", signal, enddata); break; case 2: - DEBUG_LOG(G3D, "Signal without wait. signal/end: %04x %04x", signal, enddata); + ERROR_LOG(G3D, "Signal without wait. signal/end: %04x %04x", signal, enddata); break; case 3: ERROR_LOG(G3D, "Signal with Pause UNIMPLEMENTED! signal/end: %04x %04x", signal, enddata); @@ -495,8 +618,9 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) ERROR_LOG(G3D, "UNKNOWN Signal UNIMPLEMENTED %i ! signal/end: %04x %04x", behaviour, signal, enddata); break; } + // TODO: Should this run while interrupts are suspended? if (interruptsEnabled_) - __TriggerInterruptWithArg(PSP_GE_INTR, PSP_GE_SUBINTR_SIGNAL, signal); + __TriggerInterruptWithArg(PSP_INTR_HLE, PSP_GE_INTR, PSP_GE_SUBINTR_SIGNAL, signal); } break; case GE_CMD_FINISH: @@ -510,12 +634,10 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) case GE_CMD_BJUMP: // bounding box jump. Let's just not jump, for now. - ERROR_LOG(G3D,"DL BBOX JUMP - unimplemented"); break; case GE_CMD_BOUNDINGBOX: // bounding box test. Let's do nothing. - ERROR_LOG(G3D,"DL BBOX TEST - unimplemented"); break; case GE_CMD_ORIGIN: @@ -523,10 +645,9 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) break; case GE_CMD_VERTEXTYPE: - DEBUG_LOG(G3D,"DL SetVertexType: %06x", data); if (diff & GE_VTYPE_THROUGH) { // Throughmode changed, let's make the proj matrix dirty. - shaderManager.DirtyUniform(DIRTY_PROJMATRIX); + shaderManager_->DirtyUniform(DIRTY_PROJMATRIX); } // This sets through-mode or not, as well. break; @@ -540,7 +661,6 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) int x1 = data & 0x3ff; int y1 = data >> 10; //topleft - DEBUG_LOG(G3D,"DL Region TL: %d %d", x1, y1); } break; @@ -548,110 +668,77 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) { int x2 = data & 0x3ff; int y2 = data >> 10; - DEBUG_LOG(G3D,"DL Region BR: %d %d", x2, y2); } break; case GE_CMD_CLIPENABLE: - DEBUG_LOG(G3D, "DL Clip Enable: %i (ignoring)", data); //we always clip, this is opengl break; - case GE_CMD_CULLFACEENABLE: - DEBUG_LOG(G3D, "DL CullFace Enable: %i (ignoring)", data); + case GE_CMD_CULLFACEENABLE: break; - case GE_CMD_TEXTUREMAPENABLE: + case GE_CMD_TEXTUREMAPENABLE: gstate_c.textureChanged = true; - DEBUG_LOG(G3D, "DL Texture map enable: %i", data); break; case GE_CMD_LIGHTINGENABLE: - DEBUG_LOG(G3D, "DL Lighting enable: %i", data); - data += 1; - //We don't use OpenGL lighting break; - case GE_CMD_FOGENABLE: - DEBUG_LOG(G3D, "DL Fog Enable: %i", data); + case GE_CMD_FOGENABLE: break; case GE_CMD_DITHERENABLE: - DEBUG_LOG(G3D, "DL Dither Enable: %i", data); break; - case GE_CMD_OFFSETX: - DEBUG_LOG(G3D, "DL Offset X: %i", data); + case GE_CMD_OFFSETX: break; - case GE_CMD_OFFSETY: - DEBUG_LOG(G3D, "DL Offset Y: %i", data); + case GE_CMD_OFFSETY: break; case GE_CMD_TEXSCALEU: gstate_c.uScale = getFloat24(data); - DEBUG_LOG(G3D, "DL Texture U Scale: %f", gstate_c.uScale); + shaderManager_->DirtyUniform(DIRTY_UVSCALEOFFSET); break; case GE_CMD_TEXSCALEV: gstate_c.vScale = getFloat24(data); - DEBUG_LOG(G3D, "DL Texture V Scale: %f", gstate_c.vScale); + shaderManager_->DirtyUniform(DIRTY_UVSCALEOFFSET); break; case GE_CMD_TEXOFFSETU: gstate_c.uOff = getFloat24(data); - DEBUG_LOG(G3D, "DL Texture U Offset: %f", gstate_c.uOff); + shaderManager_->DirtyUniform(DIRTY_UVSCALEOFFSET); break; case GE_CMD_TEXOFFSETV: gstate_c.vOff = getFloat24(data); - DEBUG_LOG(G3D, "DL Texture V Offset: %f", gstate_c.vOff); + shaderManager_->DirtyUniform(DIRTY_UVSCALEOFFSET); break; case GE_CMD_SCISSOR1: - { - int x1 = data & 0x3ff; - int y1 = data >> 10; - DEBUG_LOG(G3D, "DL Scissor TL: %i, %i", x1,y1); - } - break; case GE_CMD_SCISSOR2: - { - int x2 = data & 0x3ff; - int y2 = data >> 10; - DEBUG_LOG(G3D, "DL Scissor BR: %i, %i", x2, y2); - } break; case GE_CMD_MINZ: gstate_c.zMin = getFloat24(data) / 65535.f; - DEBUG_LOG(G3D, "DL MinZ: %f", gstate_c.zMin); break; case GE_CMD_MAXZ: gstate_c.zMax = getFloat24(data) / 65535.f; - DEBUG_LOG(G3D, "DL MaxZ: %f", gstate_c.zMax); break; case GE_CMD_FRAMEBUFPTR: - { - u32 ptr = op & 0xFFE000; - DEBUG_LOG(G3D, "DL FramebufPtr: %08x", ptr); - } break; case GE_CMD_FRAMEBUFWIDTH: - { - u32 w = data & 0xFFFFFF; - DEBUG_LOG(G3D, "DL FramebufWidth: %i", w); - } break; case GE_CMD_FRAMEBUFPIXFORMAT: break; case GE_CMD_TEXADDR0: - gstate_c.textureChanged = true; case GE_CMD_TEXADDR1: case GE_CMD_TEXADDR2: case GE_CMD_TEXADDR3: @@ -659,11 +746,10 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) case GE_CMD_TEXADDR5: case GE_CMD_TEXADDR6: case GE_CMD_TEXADDR7: - DEBUG_LOG(G3D,"DL Texture address %i: %06x", cmd-GE_CMD_TEXADDR0, data); + gstate_c.textureChanged = true; break; case GE_CMD_TEXBUFWIDTH0: - gstate_c.textureChanged = true; case GE_CMD_TEXBUFWIDTH1: case GE_CMD_TEXBUFWIDTH2: case GE_CMD_TEXBUFWIDTH3: @@ -671,84 +757,42 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) case GE_CMD_TEXBUFWIDTH5: case GE_CMD_TEXBUFWIDTH6: case GE_CMD_TEXBUFWIDTH7: - DEBUG_LOG(G3D,"DL Texture BUFWIDTHess %i: %06x", cmd-GE_CMD_TEXBUFWIDTH0, data); + gstate_c.textureChanged = true; break; case GE_CMD_CLUTADDR: - //DEBUG_LOG(G3D,"CLUT base addr: %06x", data); + gstate_c.textureChanged = true; break; case GE_CMD_CLUTADDRUPPER: - DEBUG_LOG(G3D,"DL CLUT addr: %08x", ((gstate.clutaddrupper & 0xFF0000)<<8) | (gstate.clutaddr & 0xFFFFFF)); + gstate_c.textureChanged = true; break; case GE_CMD_LOADCLUT: + gstate_c.textureChanged = true; // This could be used to "dirty" textures with clut. - { - u32 clutAddr = ((gstate.clutaddrupper & 0xFF0000)<<8) | (gstate.clutaddr & 0xFFFFFF); - if (clutAddr) - { - DEBUG_LOG(G3D,"DL Clut load: %08x", clutAddr); - } - else - { - DEBUG_LOG(G3D,"DL Empty Clut load"); - } - // Should hash and invalidate all paletted textures on use - } + break; + + case GE_CMD_TEXMAPMODE: + break; + + case GE_CMD_TEXSHADELS: + break; + + case GE_CMD_CLUTFORMAT: + gstate_c.textureChanged = true; break; case GE_CMD_TRANSFERSRC: - { - // Nothing to do, the next one prints - } - break; - case GE_CMD_TRANSFERSRCW: - { - u32 xferSrc = gstate.transfersrc | ((data&0xFF0000)<<8); - u32 xferSrcW = gstate.transfersrcw & 1023; - DEBUG_LOG(G3D,"Block Transfer Src: %08x W: %i", xferSrc, xferSrcW); - break; - } - case GE_CMD_TRANSFERDST: - { - // Nothing to do, the next one prints - } - break; - case GE_CMD_TRANSFERDSTW: - { - u32 xferDst= gstate.transferdst | ((data&0xFF0000)<<8); - u32 xferDstW = gstate.transferdstw & 1023; - DEBUG_LOG(G3D,"Block Transfer Dest: %08x W: %i", xferDst, xferDstW); - break; - } - case GE_CMD_TRANSFERSRCPOS: - { - u32 x = (data & 1023)+1; - u32 y = ((data>>10) & 1023)+1; - DEBUG_LOG(G3D, "DL Block Transfer Src Rect TL: %i, %i", x, y); - break; - } - case GE_CMD_TRANSFERDSTPOS: - { - u32 x = (data & 1023)+1; - u32 y = ((data>>10) & 1023)+1; - DEBUG_LOG(G3D, "DL Block Transfer Dest Rect TL: %i, %i", x, y); - break; - } + break; case GE_CMD_TRANSFERSIZE: - { - u32 w = (data & 1023)+1; - u32 h = ((data>>10) & 1023)+1; - DEBUG_LOG(G3D, "DL Block Transfer Rect Size: %i x %i", w, h); - break; - } + break; case GE_CMD_TRANSFERSTART: // Orphis calls this TRXKICK { @@ -759,7 +803,6 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) } case GE_CMD_TEXSIZE0: - gstate_c.textureChanged = true; gstate_c.curTextureWidth = 1 << (gstate.texsize[0] & 0xf); gstate_c.curTextureHeight = 1 << ((gstate.texsize[0]>>8) & 0xf); //fall thru - ignoring the mipmap sizes for now @@ -770,60 +813,51 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) case GE_CMD_TEXSIZE5: case GE_CMD_TEXSIZE6: case GE_CMD_TEXSIZE7: - DEBUG_LOG(G3D,"DL Texture Size %i: %06x", cmd - GE_CMD_TEXSIZE0, data); + gstate_c.textureChanged = true; break; case GE_CMD_ZBUFPTR: - { - u32 ptr = op & 0xFFE000; - DEBUG_LOG(G3D,"Zbuf Ptr: %06x", ptr); - } - break; - case GE_CMD_ZBUFWIDTH: - { - u32 w = data & 0xFFFFFF; - DEBUG_LOG(G3D,"Zbuf Width: %i", w); - } break; case GE_CMD_AMBIENTCOLOR: - DEBUG_LOG(G3D,"DL Ambient Color: %06x", data); - break; - case GE_CMD_AMBIENTALPHA: - DEBUG_LOG(G3D,"DL Ambient Alpha: %06x", data); break; case GE_CMD_MATERIALAMBIENT: - DEBUG_LOG(G3D,"DL Material Ambient Color: %06x", data); + if (diff) + shaderManager_->DirtyUniform(DIRTY_MATAMBIENTALPHA); break; case GE_CMD_MATERIALDIFFUSE: - DEBUG_LOG(G3D,"DL Material Diffuse Color: %06x", data); + if (diff) + shaderManager_->DirtyUniform(DIRTY_MATDIFFUSE); break; case GE_CMD_MATERIALEMISSIVE: - DEBUG_LOG(G3D,"DL Material Emissive Color: %06x", data); + if (diff) + shaderManager_->DirtyUniform(DIRTY_MATEMISSIVE); break; case GE_CMD_MATERIALSPECULAR: - DEBUG_LOG(G3D,"DL Material Specular Color: %06x", data); + if (diff) + shaderManager_->DirtyUniform(DIRTY_MATSPECULAR); break; case GE_CMD_MATERIALALPHA: - DEBUG_LOG(G3D,"DL Material Alpha Color: %06x", data); + if (diff) + shaderManager_->DirtyUniform(DIRTY_MATAMBIENTALPHA); break; case GE_CMD_MATERIALSPECULARCOEF: - DEBUG_LOG(G3D,"DL Material specular coef: %f", getFloat24(data)); + if (diff) + shaderManager_->DirtyUniform(DIRTY_MATSPECULAR); break; case GE_CMD_LIGHTTYPE0: case GE_CMD_LIGHTTYPE1: case GE_CMD_LIGHTTYPE2: case GE_CMD_LIGHTTYPE3: - DEBUG_LOG(G3D,"DL Light %i type: %06x", cmd-GE_CMD_LIGHTTYPE0, data); break; case GE_CMD_LX0:case GE_CMD_LY0:case GE_CMD_LZ0: @@ -834,9 +868,9 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) int n = cmd - GE_CMD_LX0; int l = n / 3; int c = n % 3; - float val = getFloat24(data); - DEBUG_LOG(G3D,"DL Light %i %c pos: %f", l, c+'X', val); - gstate_c.lightpos[l][c] = val; + gstate_c.lightpos[l][c] = getFloat24(data); + if (diff) + shaderManager_->DirtyUniform(DIRTY_LIGHT0 << l); } break; @@ -848,9 +882,9 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) int n = cmd - GE_CMD_LDX0; int l = n / 3; int c = n % 3; - float val = getFloat24(data); - DEBUG_LOG(G3D,"DL Light %i %c dir: %f", l, c+'X', val); - gstate_c.lightdir[l][c] = val; + gstate_c.lightdir[l][c] = getFloat24(data); + if (diff) + shaderManager_->DirtyUniform(DIRTY_LIGHT0 << l); } break; @@ -862,9 +896,9 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) int n = cmd - GE_CMD_LKA0; int l = n / 3; int c = n % 3; - float val = getFloat24(data); - DEBUG_LOG(G3D,"DL Light %i %c att: %f", l, c+'X', val); - gstate_c.lightatt[l][c] = val; + gstate_c.lightatt[l][c] = getFloat24(data); + if (diff) + shaderManager_->DirtyUniform(DIRTY_LIGHT0 << l); } break; @@ -879,9 +913,11 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) int l = (cmd - GE_CMD_LAC0) / 3; int t = (cmd - GE_CMD_LAC0) % 3; - gstate_c.lightColor[t][l].r = r; - gstate_c.lightColor[t][l].g = g; - gstate_c.lightColor[t][l].b = b; + gstate_c.lightColor[t][l][0] = r; + gstate_c.lightColor[t][l][1] = g; + gstate_c.lightColor[t][l][2] = b; + if (diff) + shaderManager_->DirtyUniform(DIRTY_LIGHT0 << l); } break; @@ -889,41 +925,37 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) case GE_CMD_VIEWPORTY1: case GE_CMD_VIEWPORTX2: case GE_CMD_VIEWPORTY2: - DEBUG_LOG(G3D,"DL Viewport param %i: %f", cmd-GE_CMD_VIEWPORTX1, getFloat24(data)); break; + case GE_CMD_VIEWPORTZ1: gstate_c.zScale = getFloat24(data) / 65535.f; - DEBUG_LOG(G3D,"DL Z scale: %f", gstate_c.zScale); break; + case GE_CMD_VIEWPORTZ2: gstate_c.zOff = getFloat24(data) / 65535.f; - DEBUG_LOG(G3D,"DL Z pos: %f", gstate_c.zOff); break; + case GE_CMD_LIGHTENABLE0: case GE_CMD_LIGHTENABLE1: case GE_CMD_LIGHTENABLE2: case GE_CMD_LIGHTENABLE3: - DEBUG_LOG(G3D,"DL Light %i enable: %d", cmd-GE_CMD_LIGHTENABLE0, data); break; + case GE_CMD_CULL: - DEBUG_LOG(G3D,"DL cull: %06x", data); break; case GE_CMD_LMODE: - DEBUG_LOG(G3D,"DL Shade mode: %06x", data); break; case GE_CMD_PATCHDIVISION: - gstate_c.patch_div_s = data & 0xFF; - gstate_c.patch_div_t = (data >> 8) & 0xFF; - DEBUG_LOG(G3D, "DL Patch subdivision: %i x %i", gstate_c.patch_div_s, gstate_c.patch_div_t); + case GE_CMD_PATCHPRIMITIVE: + case GE_CMD_PATCHFACING: break; + case GE_CMD_MATERIALUPDATE: - DEBUG_LOG(G3D,"DL Material Update: %d", data); break; - ////////////////////////////////////////////////////////////////// // CLEARING ////////////////////////////////////////////////////////////////// @@ -933,7 +965,6 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) EnterClearMode(data); else LeaveClearMode(); - DEBUG_LOG(G3D,"DL Clear mode: %06x", data); break; @@ -941,96 +972,39 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) // ALPHA BLENDING ////////////////////////////////////////////////////////////////// case GE_CMD_ALPHABLENDENABLE: - DEBUG_LOG(G3D,"DL Alpha blend enable: %d", data); - break; - case GE_CMD_BLENDMODE: - DEBUG_LOG(G3D,"DL Blend mode: %06x", data); - break; - case GE_CMD_BLENDFIXEDA: - DEBUG_LOG(G3D,"DL Blend fix A: %06x", data); - break; - case GE_CMD_BLENDFIXEDB: - DEBUG_LOG(G3D,"DL Blend fix B: %06x", data); break; case GE_CMD_ALPHATESTENABLE: - DEBUG_LOG(G3D,"DL Alpha test enable: %d", data); // This is done in the shader. break; case GE_CMD_ALPHATEST: - DEBUG_LOG(G3D,"DL Alpha test settings"); - shaderManager.DirtyUniform(DIRTY_ALPHAREF); + shaderManager_->DirtyUniform(DIRTY_ALPHACOLORREF); + break; + + case GE_CMD_TEXENVCOLOR: + if (diff) + shaderManager_->DirtyUniform(DIRTY_TEXENV); break; case GE_CMD_TEXFUNC: - { - DEBUG_LOG(G3D,"DL TexFunc %i", data&7); - /* - int m=GL_MODULATE; - switch (data & 7) - { - case 0: m=GL_MODULATE; break; - case 1: m=GL_DECAL; break; - case 2: m=GL_BLEND; break; - case 3: m=GL_REPLACE; break; - case 4: m=GL_ADD; break; - }*/ - - /* - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); - glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_CONSTANT); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_TEXTURE); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); - glTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE, 1); - - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, m); - glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE);*/ - break; - } case GE_CMD_TEXFILTER: - { - int min = data & 7; - int mag = (data >> 8) & 1; - DEBUG_LOG(G3D,"DL TexFilter min: %i mag: %i", min, mag); - } - break; - case GE_CMD_TEXENVCOLOR: - DEBUG_LOG(G3D,"DL TexEnvColor %06x", data); - break; case GE_CMD_TEXMODE: - DEBUG_LOG(G3D,"DL TexMode %08x", data); - break; case GE_CMD_TEXFORMAT: - DEBUG_LOG(G3D,"DL TexFormat %08x", data); - break; case GE_CMD_TEXFLUSH: - DEBUG_LOG(G3D,"DL TexFlush"); - break; case GE_CMD_TEXWRAP: - DEBUG_LOG(G3D,"DL TexWrap %08x", data); break; + ////////////////////////////////////////////////////////////////// // Z/STENCIL TESTING ////////////////////////////////////////////////////////////////// - case GE_CMD_ZTESTENABLE: - DEBUG_LOG(G3D,"DL Z test enable: %d", data & 1); - break; - case GE_CMD_STENCILTESTENABLE: - DEBUG_LOG(G3D,"DL Stencil test enable: %d", data); - break; - + case GE_CMD_ZTESTENABLE: case GE_CMD_ZTEST: - { - DEBUG_LOG(G3D,"DL Z test mode: %i", data); - } break; case GE_CMD_MORPHWEIGHT0: @@ -1041,90 +1015,78 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) case GE_CMD_MORPHWEIGHT5: case GE_CMD_MORPHWEIGHT6: case GE_CMD_MORPHWEIGHT7: - { - int index = cmd - GE_CMD_MORPHWEIGHT0; - float weight = getFloat24(data); - DEBUG_LOG(G3D,"DL MorphWeight %i = %f", index, weight); - gstate_c.morphWeights[index] = weight; - } + gstate_c.morphWeights[cmd - GE_CMD_MORPHWEIGHT0] = getFloat24(data); break; case GE_CMD_DITH0: case GE_CMD_DITH1: case GE_CMD_DITH2: case GE_CMD_DITH3: - DEBUG_LOG(G3D,"DL DitherMatrix %i = %06x",cmd-GE_CMD_DITH0,data); break; case GE_CMD_WORLDMATRIXNUMBER: - DEBUG_LOG(G3D,"DL World # %i", data & 0xF); gstate.worldmtxnum &= 0xFF00000F; break; case GE_CMD_WORLDMATRIXDATA: - DEBUG_LOG(G3D,"DL World data # %f", getFloat24(data)); { int num = gstate.worldmtxnum & 0xF; if (num < 12) gstate.worldMatrix[num++] = getFloat24(data); gstate.worldmtxnum = (gstate.worldmtxnum & 0xFF000000) | (num & 0xF); + shaderManager_->DirtyUniform(DIRTY_WORLDMATRIX); } break; case GE_CMD_VIEWMATRIXNUMBER: - DEBUG_LOG(G3D,"DL VIEW # %i", data & 0xF); gstate.viewmtxnum &= 0xFF00000F; break; case GE_CMD_VIEWMATRIXDATA: - DEBUG_LOG(G3D,"DL VIEW data # %f", getFloat24(data)); { int num = gstate.viewmtxnum & 0xF; if (num < 12) gstate.viewMatrix[num++] = getFloat24(data); gstate.viewmtxnum = (gstate.viewmtxnum & 0xFF000000) | (num & 0xF); + shaderManager_->DirtyUniform(DIRTY_VIEWMATRIX); } break; case GE_CMD_PROJMATRIXNUMBER: - DEBUG_LOG(G3D,"DL PROJECTION # %i", data & 0xF); gstate.projmtxnum &= 0xFF00000F; break; case GE_CMD_PROJMATRIXDATA: - DEBUG_LOG(G3D,"DL PROJECTION matrix data # %f", getFloat24(data)); { int num = gstate.projmtxnum & 0xF; gstate.projMatrix[num++] = getFloat24(data); gstate.projmtxnum = (gstate.projmtxnum & 0xFF000000) | (num & 0xF); } - shaderManager.DirtyUniform(DIRTY_PROJMATRIX); + shaderManager_->DirtyUniform(DIRTY_PROJMATRIX); break; case GE_CMD_TGENMATRIXNUMBER: - DEBUG_LOG(G3D,"DL TGEN # %i", data & 0xF); gstate.texmtxnum &= 0xFF00000F; break; case GE_CMD_TGENMATRIXDATA: - DEBUG_LOG(G3D,"DL TGEN data # %f", getFloat24(data)); { int num = gstate.texmtxnum & 0xF; if (num < 12) gstate.tgenMatrix[num++] = getFloat24(data); gstate.texmtxnum = (gstate.texmtxnum & 0xFF000000) | (num & 0xF); } + shaderManager_->DirtyUniform(DIRTY_TEXMATRIX); break; case GE_CMD_BONEMATRIXNUMBER: - DEBUG_LOG(G3D,"DL BONE #%i", data); gstate.boneMatrixNumber &= 0xFF00007F; break; case GE_CMD_BONEMATRIXDATA: - DEBUG_LOG(G3D,"DL BONE data #%i %f", gstate.boneMatrixNumber & 0x7f, getFloat24(data)); { int num = gstate.boneMatrixNumber & 0x7F; + shaderManager_->DirtyUniform(DIRTY_BONEMATRIX0 << (num / 12)); if (num < 96) { gstate.boneMatrix[num++] = getFloat24(data); } @@ -1135,8 +1097,6 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) default: DEBUG_LOG(G3D,"DL Unknown: %08x @ %08x", op, dcontext.pc); break; - - //ETC... } } @@ -1159,7 +1119,16 @@ bool GLES_GPU::InterpretList() op = Memory::ReadUnchecked_U32(dcontext.pc); //read from memory u32 cmd = op >> 24; u32 diff = op ^ gstate.cmdmem[cmd]; - gstate.cmdmem[cmd] = op; // crashes if I try to put the whole op there?? + if (flushBeforeCommand_[cmd] == 1 || (diff && flushBeforeCommand_[cmd] == 2)) + transformDraw_.Flush(); + // TODO: Add a compiler flag to remove stuff like this at very-final build time. + if (dumpThisFrame_) { + char temp[256]; + GeDisassembleOp(dcontext.pc, op, prev, temp); + NOTICE_LOG(G3D, "%08x: %s", dcontext.pc, temp); + } + + gstate.cmdmem[cmd] = op; ExecuteOp(op, diff); @@ -1169,17 +1138,23 @@ bool GLES_GPU::InterpretList() return true; } -void GLES_GPU::UpdateStats() -{ - gpuStats.numVertexShaders = shaderManager.NumVertexShaders(); - gpuStats.numFragmentShaders = shaderManager.NumFragmentShaders(); - gpuStats.numShaders = shaderManager.NumPrograms(); +void GLES_GPU::UpdateStats() { + gpuStats.numVertexShaders = shaderManager_->NumVertexShaders(); + gpuStats.numFragmentShaders = shaderManager_->NumFragmentShaders(); + gpuStats.numShaders = shaderManager_->NumPrograms(); gpuStats.numTextures = TextureCache_NumLoadedTextures(); } +void GLES_GPU::DoBlockTransfer() { + // TODO: This is used a lot to copy data around between render targets and textures, + // and also to quickly load textures from RAM to VRAM. So we should do checks like the following: + // * Does dstBasePtr point to an existing texture? If so maybe reload it immediately. + // + // * Does srcBasePtr point to a render target, and dstBasePtr to a texture? If so + // either copy between rt and texture or reassign the texture to point to the render target + // + // etc.... -void GLES_GPU::DoBlockTransfer() -{ u32 srcBasePtr = (gstate.transfersrc & 0xFFFFFF) | ((gstate.transfersrcw & 0xFF0000) << 8); u32 srcStride = gstate.transfersrcw & 0x3FF; @@ -1194,10 +1169,10 @@ void GLES_GPU::DoBlockTransfer() int width = (gstate.transfersize & 0x3FF) + 1; int height = ((gstate.transfersize >> 10) & 0x3FF) + 1; - + int bpp = (gstate.transferstart & 1) ? 4 : 2; - NOTICE_LOG(HLE, "Block transfer: %08x to %08x, %i x %i , ...", srcBasePtr, dstBasePtr, width, height); + DEBUG_LOG(G3D, "Block transfer: %08x to %08x, %i x %i , ...", srcBasePtr, dstBasePtr, width, height); // Do the copy! for (int y = 0; y < height; y++) { @@ -1206,5 +1181,18 @@ void GLES_GPU::DoBlockTransfer() memcpy(dst, src, width * bpp); } - // TODO: Notify all overlapping textures that it's time to die/reload. + // TODO: Notify all overlapping FBOs that they need to reload. + + TextureCache_Invalidate(dstBasePtr + dstY * dstStride + dstX, height * dstStride + width * bpp); +} + +void GLES_GPU::InvalidateCache(u32 addr, int size) { + if (size > 0) + TextureCache_Invalidate(addr, size); + else + TextureCache_Clear(true); +} + +void GLES_GPU::Flush() { + transformDraw_.Flush(); } diff --git a/GPU/GLES/DisplayListInterpreter.h b/GPU/GLES/DisplayListInterpreter.h index a105ca7818..ae22e56188 100644 --- a/GPU/GLES/DisplayListInterpreter.h +++ b/GPU/GLES/DisplayListInterpreter.h @@ -22,9 +22,12 @@ #include "../GPUInterface.h" #include "Framebuffer.h" +#include "VertexDecoder.h" +#include "TransformPipeline.h" #include "gfx_es2/fbo.h" class ShaderManager; +class LinkedShader; class GLES_GPU : public GPUInterface { @@ -47,18 +50,24 @@ public: virtual void CopyDisplayToOutput(); virtual void BeginFrame(); virtual void UpdateStats(); + virtual void InvalidateCache(u32 addr, int size); + virtual void DeviceLost(); // Only happens on Android. Drop all textures and shaders. + + virtual void DumpNextFrame(); + virtual void Flush(); private: - // TransformPipeline.cpp - void TransformAndDrawPrim(void *verts, void *inds, int prim, int vertexCount, float *customUV, int forceIndexType, int *bytesRead = 0); - void UpdateViewportAndProjection(); - void DrawBezier(int ucount, int vcount); void DoBlockTransfer(); bool ProcessDLQueue(); - FramebufferManager framebufferManager; + // Applies states for debugging if enabled. + void BeginDebugDraw(); + void EndDebugDraw(); + FramebufferManager framebufferManager; + TransformDrawEngine transformDraw_; ShaderManager *shaderManager_; + u8 *flushBeforeCommand_; bool interruptsEnabled_; u32 displayFramebufPtr_; @@ -71,8 +80,10 @@ private: float renderWidthFactor_; float renderHeightFactor_; - struct CmdProcessorState - { + bool dumpNextFrame_; + bool dumpThisFrame_; + + struct CmdProcessorState { u32 pc; u32 stallAddr; }; @@ -81,8 +92,7 @@ private: int dlIdGenerator; - struct DisplayList - { + struct DisplayList { int id; u32 listpc; u32 stall; diff --git a/GPU/GLES/FragmentShaderGenerator.cpp b/GPU/GLES/FragmentShaderGenerator.cpp index e42e96e2b6..7cd7e2302b 100644 --- a/GPU/GLES/FragmentShaderGenerator.cpp +++ b/GPU/GLES/FragmentShaderGenerator.cpp @@ -79,16 +79,16 @@ char *GenerateFragmentShader() if (doTexture) WRITE(p, "uniform sampler2D tex;\n"); - if (gstate.alphaTestEnable & 1) - WRITE(p, "uniform vec4 u_alpharef;\n"); + if ((gstate.alphaTestEnable & 1) || (gstate.colorTestEnable & 1)) + WRITE(p, "uniform vec4 u_alphacolorref;\n"); if (gstate.fogEnable & 1) { WRITE(p, "uniform vec3 u_fogcolor;\n"); WRITE(p, "uniform vec2 u_fogcoef;\n"); } - WRITE(p, "uniform vec4 u_texenv;\n"); + WRITE(p, "uniform vec3 u_texenv;\n"); WRITE(p, "varying vec4 v_color0;\n"); if (lmode) - WRITE(p, "varying vec4 v_color1;\n"); + WRITE(p, "varying vec3 v_color1;\n"); if (doTexture) WRITE(p, "varying vec2 v_texcoord;\n"); if (gstate.isFogEnabled()) @@ -107,7 +107,7 @@ char *GenerateFragmentShader() const char *secondary = ""; // Secondary color for specular on top of texture if (lmode) { - WRITE(p, " vec4 s = vec4(v_color1.xyz, 0.0);"); + WRITE(p, " vec4 s = vec4(v_color1, 0.0);"); secondary = " + s"; } else { WRITE(p, " vec4 s = vec4(0.0, 0.0, 0.0, 0.0);\n"); @@ -163,9 +163,20 @@ char *GenerateFragmentShader() int alphaTestFunc = gstate.alphatest & 7; const char *alphaTestFuncs[] = { "#", "#", " == ", " != ", " < ", " <= ", " > ", " >= " }; // never/always don't make sense if (alphaTestFuncs[alphaTestFunc][0] != '#') - WRITE(p, "if (!(v.a %s u_alpharef.x)) discard;", alphaTestFuncs[alphaTestFunc]); + WRITE(p, "if (!(v.a %s u_alphacolorref.a)) discard;", alphaTestFuncs[alphaTestFunc]); } + // Disabled for now until we actually find a need for it. + /* + if (gstate.colorTestEnable & 1) { + // TODO: There are some colortestmasks we could handle. + int colorTestFunc = gstate.colortest & 3; + const char *colorTestFuncs[] = { "#", "#", " == ", " != " }; // never/always don't make sense} + int colorTestMask = gstate.colormask; + if (colorTestFuncs[colorTestFunc][0] != '#') + WRITE(p, "if (!(v.rgb %s u_alphacolorref.rgb)) discard;", colorTestFuncs[colorTestFunc]); + }*/ + if (gstate.isFogEnabled()) { // Haven't figured out how to adjust the depth range yet. // WRITE(p, " v = mix(v, u_fogcolor, u_fogcoef.x + u_fogcoef.y * v_depth;\n"); diff --git a/GPU/GLES/Framebuffer.cpp b/GPU/GLES/Framebuffer.cpp index f458916fb4..f3a6519fa0 100644 --- a/GPU/GLES/Framebuffer.cpp +++ b/GPU/GLES/Framebuffer.cpp @@ -37,14 +37,17 @@ const char tex_fs[] = "}\n"; const char basic_vs[] = +#ifndef USING_GLES2 + "#version 120\n" +#endif "attribute vec4 a_position;\n" "attribute vec2 a_texcoord0;\n" "uniform mat4 u_viewproj;\n" "varying vec4 v_color;\n" "varying vec2 v_texcoord0;\n" "void main() {\n" - " v_texcoord0 = a_texcoord0;\n" - " gl_Position = u_viewproj * a_position;\n" + " v_texcoord0 = a_texcoord0;\n" + " gl_Position = u_viewproj * a_position;\n" "}\n"; FramebufferManager::FramebufferManager() { diff --git a/GPU/GLES/IndexGenerator.cpp b/GPU/GLES/IndexGenerator.cpp new file mode 100644 index 0000000000..0193c88203 --- /dev/null +++ b/GPU/GLES/IndexGenerator.cpp @@ -0,0 +1,363 @@ +// Copyright (c) 2012- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "IndexGenerator.h" + +// Points don't need indexing... +const u8 indexedPrimitiveType[7] = { + GE_PRIM_POINTS, + GE_PRIM_LINES, + GE_PRIM_LINES, + GE_PRIM_TRIANGLES, + GE_PRIM_TRIANGLES, + GE_PRIM_TRIANGLES, + GE_PRIM_RECTANGLES, +}; + +enum { + SEEN_INDEX8 = 1 << 29, + SEEN_INDEX16 = 1 << 30 +}; + +void IndexGenerator::Reset() { + prim_ = -1; + count_ = 0; + index_ = 0; + seenPrims_ = 0; + this->inds_ = indsBase_; +} + +bool IndexGenerator::PrimCompatible(int prim) { + if (prim_ == -1) + return true; + return indexedPrimitiveType[prim] == prim_; +} + +void IndexGenerator::Setup(u16 *inds) { + this->indsBase_ = inds; + Reset(); +} + +void IndexGenerator::AddPoints(int numVerts) { + //if we have no vertices return + for (int i = 0; i < numVerts; i++) + { + *inds_++ = index_ + i; + } + // ignore overflow verts + index_ += numVerts; + count_ += numVerts; + prim_ = GE_PRIM_POINTS; + seenPrims_ |= 1 << GE_PRIM_POINTS; +} + +void IndexGenerator::AddList(int numVerts) +{ + //if we have no vertices return + int numTris = numVerts / 3; + for (int i = 0; i < numTris; i++) + { + *inds_++ = index_ + i*3; + *inds_++ = index_ + i*3 + 1; + *inds_++ = index_ + i*3 + 2; + } + + // ignore overflow verts + index_ += numVerts; + count_ += numTris * 3; + prim_ = GE_PRIM_TRIANGLES; + seenPrims_ |= 1 << GE_PRIM_TRIANGLES; +} + +void IndexGenerator::AddStrip(int numVerts) +{ + bool wind = false; + int numTris = numVerts - 2; + for (int i = 0; i < numTris; i++) + { + *inds_++ = index_ + i; + *inds_++ = index_ + i+(wind?2:1); + *inds_++ = index_ + i+(wind?1:2); + wind = !wind; + } + index_ += numVerts; + count_ += numTris * 3; + prim_ = GE_PRIM_TRIANGLES; + seenPrims_ |= 1 << GE_PRIM_TRIANGLE_STRIP; +} + +void IndexGenerator::AddFan(int numVerts) +{ + int numTris = numVerts - 2; + for (int i = 0; i < numTris; i++) + { + *inds_++ = index_; + *inds_++ = index_ + i + 1; + *inds_++ = index_ + i + 2; + } + index_ += numVerts; + count_ += numTris * 3; + prim_ = GE_PRIM_TRIANGLES; + seenPrims_ |= 1 << GE_PRIM_TRIANGLE_FAN; +} + +void IndexGenerator::TranslatePoints(int numVerts, const u8 *inds, int offset) +{ + for (int i = 0; i < numVerts; i++) + { + *inds_++ = index_ + offset + inds[i]; + } + index_ += numVerts; + count_ += numVerts; + prim_ = GE_PRIM_POINTS; + seenPrims_ |= (1 << GE_PRIM_POINTS) | SEEN_INDEX8; +} + +void IndexGenerator::TranslatePoints(int numVerts, const u16 *inds, int offset) +{ + for (int i = 0; i < numVerts; i++) + { + *inds_++ = index_ + offset + inds[i]; + } + index_ += numVerts; + count_ += numVerts; + prim_ = GE_PRIM_POINTS; + seenPrims_ |= (1 << GE_PRIM_POINTS) | SEEN_INDEX16; +} + +void IndexGenerator::TranslateList(int numVerts, const u8 *inds, int offset) +{ + int numTris = numVerts / 3; + for (int i = 0; i < numTris; i++) + { + *inds_++ = index_ + offset + inds[i*3]; + *inds_++ = index_ + offset + inds[i*3 + 1]; + *inds_++ = index_ + offset + inds[i*3 + 2]; + } + index_ += numVerts; + count_ += numTris * 3; + prim_ = GE_PRIM_TRIANGLES; + seenPrims_ |= (1 << GE_PRIM_TRIANGLES) | SEEN_INDEX8; +} + +void IndexGenerator::TranslateStrip(int numVerts, const u8 *inds, int offset) +{ + bool wind = false; + int numTris = numVerts - 2; + for (int i = 0; i < numTris; i++) + { + *inds_++ = index_ + offset + inds[i]; + *inds_++ = index_ + offset + inds[i + (wind?2:1)]; + *inds_++ = index_ + offset + inds[i + (wind?1:2)]; + wind = !wind; + } + index_ += numVerts; + count_ += numTris * 3; + prim_ = GE_PRIM_TRIANGLES; + seenPrims_ |= (1 << GE_PRIM_TRIANGLE_STRIP) | SEEN_INDEX8; +} + +void IndexGenerator::TranslateFan(int numVerts, const u8 *inds, int offset) +{ + if (numVerts <= 0) return; + int numTris = numVerts - 2; + for (int i = 0; i < numTris; i++) + { + *inds_++ = index_ + offset + inds[0]; + *inds_++ = index_ + offset + inds[i + 1]; + *inds_++ = index_ + offset + inds[i + 2]; + } + index_ += numVerts; + count_ += numTris * 3; + prim_ = GE_PRIM_TRIANGLES; + seenPrims_ |= (1 << GE_PRIM_TRIANGLE_STRIP) | SEEN_INDEX8;; +} + +void IndexGenerator::TranslateList(int numVerts, const u16 *inds, int offset) +{ + int numTris = numVerts / 3; + for (int i = 0; i < numTris; i++) + { + *inds_++ = index_ + offset + inds[i*3]; + *inds_++ = index_ + offset + inds[i*3 + 1]; + *inds_++ = index_ + offset + inds[i*3 + 2]; + } + index_ += numVerts; + count_ += numTris * 3; + prim_ = GE_PRIM_TRIANGLES; + seenPrims_ |= (1 << GE_PRIM_TRIANGLES) | SEEN_INDEX16; +} + +void IndexGenerator::TranslateStrip(int numVerts, const u16 *inds, int offset) +{ + bool wind = false; + int numTris = numVerts - 2; + for (int i = 0; i < numTris; i++) + { + *inds_++ = index_ + offset + inds[i]; + *inds_++ = index_ + offset + inds[i + (wind?2:1)]; + *inds_++ = index_ + offset + inds[i + (wind?1:2)]; + wind = !wind; + } + index_ += numVerts; + count_ += numTris * 3; + prim_ = GE_PRIM_TRIANGLES; + seenPrims_ |= (1 << GE_PRIM_TRIANGLE_STRIP) | SEEN_INDEX16; +} + +void IndexGenerator::TranslateFan(int numVerts, const u16 *inds, int offset) +{ + if (numVerts <= 0) return; + int numTris = numVerts - 2; + for (int i = 0; i < numTris; i++) + { + *inds_++ = index_ + offset + inds[0]; + *inds_++ = index_ + offset + inds[i + 1]; + *inds_++ = index_ + offset + inds[i + 2]; + } + index_ += numVerts; + count_ += numTris * 3; + prim_ = GE_PRIM_TRIANGLES; + seenPrims_ |= (1 << GE_PRIM_TRIANGLE_FAN) | SEEN_INDEX16; +} + +//Lines +void IndexGenerator::AddLineList(int numVerts) +{ + int numLines = numVerts / 2; + for (int i = 0; i < numLines; i++) + { + *inds_++ = index_ + i*2; + *inds_++ = index_ + i*2+1; + } + index_ += numVerts; + count_ += numLines * 2; + prim_ = GE_PRIM_LINES; + seenPrims_ |= 1 << prim_; +} + +void IndexGenerator::AddLineStrip(int numVerts) +{ + int numLines = numVerts - 1; + for (int i = 0; i < numLines; i++) + { + *inds_++ = index_ + i; + *inds_++ = index_ + i + 1; + } + index_ += numVerts; + count_ += numLines * 2; + prim_ = GE_PRIM_LINES; + seenPrims_ |= 1 << GE_PRIM_LINE_STRIP; +} + +void IndexGenerator::AddRectangles(int numVerts) +{ + int numRects = numVerts / 2; + for (int i = 0; i < numRects; i++) + { + *inds_++ = index_ + i*2; + *inds_++ = index_ + i*2+1; + } + index_ += numVerts; + count_ += numRects * 2; + prim_ = GE_PRIM_RECTANGLES; + seenPrims_ |= 1 << GE_PRIM_RECTANGLES; +} + +void IndexGenerator::TranslateLineList(int numVerts, const u8 *inds, int offset) +{ + int numLines = numVerts / 2; + for (int i = 0; i < numLines; i++) + { + *inds_++ = index_ + offset + inds[i*2]; + *inds_++ = index_ + offset + inds[i*2+1]; + } + index_ += numVerts; + count_ += numLines * 2; + prim_ = GE_PRIM_LINES; + seenPrims_ |= (1 << GE_PRIM_LINES) | SEEN_INDEX8; +} + +void IndexGenerator::TranslateLineStrip(int numVerts, const u8 *inds, int offset) +{ + int numLines = numVerts - 1; + for (int i = 0; i < numLines; i++) + { + *inds_++ = index_ + offset + inds[i]; + *inds_++ = index_ + offset + inds[i + 1]; + } + index_ += numVerts; + count_ += numLines * 2; + prim_ = GE_PRIM_LINES; + seenPrims_ |= (1 << GE_PRIM_LINE_STRIP) | SEEN_INDEX8; +} + +void IndexGenerator::TranslateLineList(int numVerts, const u16 *inds, int offset) +{ + int numLines = numVerts / 2; + for (int i = 0; i < numLines; i++) + { + *inds_++ = index_ + offset + inds[i*2]; + *inds_++ = index_ + offset + inds[i*2+1]; + } + index_ += numVerts; + count_ += numLines * 2; + prim_ = GE_PRIM_LINES; + seenPrims_ |= 1 << GE_PRIM_LINES; +} + +void IndexGenerator::TranslateLineStrip(int numVerts, const u16 *inds, int offset) +{ + int numLines = numVerts - 1; + for (int i = 0; i < numLines; i++) + { + *inds_++ = index_ + offset + inds[i]; + *inds_++ = index_ + offset + inds[i + 1]; + } + index_ += numVerts; + count_ += numLines * 2; + prim_ = GE_PRIM_LINES; + seenPrims_ |= 1 << GE_PRIM_LINE_STRIP; +} + +void IndexGenerator::TranslateRectangles(int numVerts, const u8 *inds, int offset) +{ + int numRects = numVerts / 2; + for (int i = 0; i < numRects; i++) + { + *inds_++ = index_ + offset + inds[i*2]; + *inds_++ = index_ + offset + inds[i*2+1]; + } + index_ += numVerts; + count_ += numRects * 2; + prim_ = GE_PRIM_RECTANGLES; + seenPrims_ |= 1 << GE_PRIM_RECTANGLES; +} + +void IndexGenerator::TranslateRectangles(int numVerts, const u16 *inds, int offset) +{ + int numRects = numVerts / 2; + for (int i = 0; i < numRects; i++) + { + *inds_++ = index_ + offset + inds[i*2]; + *inds_++ = index_ + offset + inds[i*2+1]; + } + index_ += numVerts; + count_ += numRects * 2; + prim_ = GE_PRIM_RECTANGLES; + seenPrims_ |= 1 << GE_PRIM_RECTANGLES; +} diff --git a/GPU/GLES/IndexGenerator.h b/GPU/GLES/IndexGenerator.h new file mode 100644 index 0000000000..68cc4d8a74 --- /dev/null +++ b/GPU/GLES/IndexGenerator.h @@ -0,0 +1,78 @@ +// Copyright (c) 2012- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + + +#pragma once + +#include "CommonTypes.h" +#include "../ge_constants.h" + +class IndexGenerator +{ +public: + void Setup(u16 *indexptr); + void Reset(); + bool PrimCompatible(int prim); + int Prim() const { return prim_; } + + // Points (why index these? code simplicity) + void AddPoints(int numVerts); + // Triangles + void AddList(int numVerts); + void AddStrip(int numVerts); + void AddFan(int numVerts); + // Lines + void AddLineList(int numVerts); + void AddLineStrip(int numVerts); + // Rectangles + void AddRectangles(int numVerts); + + void TranslatePoints(int numVerts, const u8 *inds, int offset); + void TranslatePoints(int numVerts, const u16 *inds, int offset); + // Translates already indexed lists + void TranslateLineList(int numVerts, const u8 *inds, int offset); + void TranslateLineList(int numVerts, const u16 *inds, int offset); + void TranslateLineStrip(int numVerts, const u8 *inds, int offset); + void TranslateLineStrip(int numVerts, const u16 *inds, int offset); + + void TranslateRectangles(int numVerts, const u8 *inds, int offset); + void TranslateRectangles(int numVerts, const u16 *inds, int offset); + + void TranslateList(int numVerts, const u8 *inds, int offset); + void TranslateStrip(int numVerts, const u8 *inds, int offset); + void TranslateFan(int numVerts, const u8 *inds, int offset); + void TranslateList(int numVerts, const u16 *inds, int offset); + void TranslateStrip(int numVerts, const u16 *inds, int offset); + void TranslateFan(int numVerts, const u16 *inds, int offset); + + int MaxIndex() { return index_; } + int VertexCount() { return count_; } + + bool Empty() { return index_ == 0; } + + void SetIndex(int ind) { index_ = ind; } + int SeenPrims() const { return seenPrims_; } + +private: + u16 *indsBase_; + u16 *inds_; + int index_; + int count_; + int prim_; + int seenPrims_; +}; + diff --git a/GPU/GLES/ShaderManager.cpp b/GPU/GLES/ShaderManager.cpp index 857968560b..ec5b60e290 100644 --- a/GPU/GLES/ShaderManager.cpp +++ b/GPU/GLES/ShaderManager.cpp @@ -79,27 +79,110 @@ LinkedShader::LinkedShader(Shader *vs, Shader *fs) u_texenv = glGetUniformLocation(program, "u_texenv"); u_fogcolor = glGetUniformLocation(program, "u_fogcolor"); u_fogcoef = glGetUniformLocation(program, "u_fogcoef"); - u_alpharef = glGetUniformLocation(program, "u_alpharef"); + u_alphacolorref = glGetUniformLocation(program, "u_alphacolorref"); + + // Transform + u_view = glGetUniformLocation(program, "u_view"); + u_world = glGetUniformLocation(program, "u_world"); + u_texmtx = glGetUniformLocation(program, "u_texmtx"); + for (int i = 0; i < 8; i++) { + char name[64]; + sprintf(name, "u_bone%i", i); + u_bone[i] = glGetUniformLocation(program, name); + } + + // Lighting, texturing + u_ambient = glGetUniformLocation(program, "u_ambient"); + u_matambientalpha = glGetUniformLocation(program, "u_matambientalpha"); + u_matdiffuse = glGetUniformLocation(program, "u_matdiffuse"); + u_matspecular = glGetUniformLocation(program, "u_matspecular"); + u_matemissive = glGetUniformLocation(program, "u_matemissive"); + u_uvscaleoffset = glGetUniformLocation(program, "u_uvscaleoffset"); + + for (int i = 0; i < 4; i++) { + char temp[64]; + sprintf(temp, "u_lightpos%i", i); + u_lightpos[i] = glGetUniformLocation(program, temp); + sprintf(temp, "u_lightdir%i", i); + u_lightdir[i] = glGetUniformLocation(program, temp); + sprintf(temp, "u_lightatt%i", i); + u_lightatt[i] = glGetUniformLocation(program, temp); + sprintf(temp, "u_lightambient%i", i); + u_lightambient[i] = glGetUniformLocation(program, temp); + sprintf(temp, "u_lightdiffuse%i", i); + u_lightdiffuse[i] = glGetUniformLocation(program, temp); + sprintf(temp, "u_lightspecular%i", i); + u_lightspecular[i] = glGetUniformLocation(program, temp); + } a_position = glGetAttribLocation(program, "a_position"); a_color0 = glGetAttribLocation(program, "a_color0"); a_color1 = glGetAttribLocation(program, "a_color1"); a_texcoord = glGetAttribLocation(program, "a_texcoord"); + a_normal = glGetAttribLocation(program, "a_normal"); + a_weight0123 = glGetAttribLocation(program, "a_weight0123"); + a_weight4567 = glGetAttribLocation(program, "a_weight4567"); glUseProgram(program); // Default uniform values glUniform1i(u_tex, 0); // The rest, use the "dirty" mechanism. - dirtyUniforms = DIRTY_PROJMATRIX | DIRTY_PROJTHROUGHMATRIX | DIRTY_TEXENV | DIRTY_ALPHAREF; + dirtyUniforms = DIRTY_ALL; } LinkedShader::~LinkedShader() { glDeleteProgram(program); } +// Utility +static void SetColorUniform3(int uniform, u32 color) +{ + const float col[3] = { ((color & 0xFF0000) >> 16) / 255.0f, ((color & 0xFF00) >> 8) / 255.0f, ((color & 0xFF)) / 255.0f}; + glUniform3fv(uniform, 1, col); +} + +static void SetColorUniform3Alpha(int uniform, u32 color, u8 alpha) +{ + const float col[4] = { ((color & 0xFF0000) >> 16) / 255.0f, ((color & 0xFF00) >> 8) / 255.0f, ((color & 0xFF)) / 255.0f, alpha/255.0f}; + glUniform4fv(uniform, 1, col); +} + +static void SetColorUniform3ExtraFloat(int uniform, u32 color, float extra) +{ + const float col[4] = { ((color & 0xFF0000) >> 16) / 255.0f, ((color & 0xFF00) >> 8) / 255.0f, ((color & 0xFF)) / 255.0f, extra}; + glUniform4fv(uniform, 1, col); +} + +static void SetMatrix4x3(int uniform, const float *m4x3) { + float m4x4[16]; + m4x4[0] = m4x3[0]; + m4x4[1] = m4x3[1]; + m4x4[2] = m4x3[2]; + m4x4[3] = 0.0f; + m4x4[4] = m4x3[3]; + m4x4[5] = m4x3[4]; + m4x4[6] = m4x3[5]; + m4x4[7] = 0.0f; + m4x4[8] = m4x3[6]; + m4x4[9] = m4x3[7]; + m4x4[10] = m4x3[8]; + m4x4[11] = 0.0f; + m4x4[12] = m4x3[9]; + m4x4[13] = m4x3[10]; + m4x4[14] = m4x3[11]; + m4x4[15] = 1.0f; + glUniformMatrix4fv(uniform, 1, GL_FALSE, m4x4); +} + void LinkedShader::use() { glUseProgram(program); - glUniform1i(u_tex, 0); + updateUniforms(); +} + +void LinkedShader::updateUniforms() { + if (!dirtyUniforms) + return; + // Update any dirty uniforms before we draw if (u_proj != -1 && (dirtyUniforms & DIRTY_PROJMATRIX)) { glUniformMatrix4fv(u_proj, 1, GL_FALSE, gstate.projMatrix); @@ -122,20 +205,69 @@ void LinkedShader::use() { glUniformMatrix4fv(u_proj_through, 1, GL_FALSE, proj_through.getReadPtr()); } if (u_texenv != -1 && (dirtyUniforms & DIRTY_TEXENV)) { - glUniform4f(u_texenv, 1.0, 1.0, 1.0, 1.0); // TODO + SetColorUniform3(u_texenv, gstate.texenvcolor); } - if (u_alpharef != -1 && (dirtyUniforms & DIRTY_ALPHAREF)) { - glUniform4f(u_alpharef, ((float)((gstate.alphatest >> 8) & 0xFF)) / 255.0f, 0.0f, 0.0f, 0.0f); + if (u_alphacolorref != -1 && (dirtyUniforms & DIRTY_ALPHACOLORREF)) { + glUniform4f(u_alphacolorref, 0.0f, 0.0f, 0.0f, ((float)((gstate.alphatest >> 8) & 0xFF)) / 255.0f); } if (u_fogcolor != -1 && (dirtyUniforms & DIRTY_FOGCOLOR)) { - const float fogc[3] = { ((gstate.fogcolor & 0xFF0000) >> 16) / 255.0f, ((gstate.fogcolor & 0xFF00) >> 8) / 255.0f, ((gstate.fogcolor & 0xFF)) / 255.0f}; - glUniform3fv(u_fogcolor, 1, fogc); + SetColorUniform3(u_fogcolor, gstate.fogcolor); } if (u_fogcoef != -1 && (dirtyUniforms & DIRTY_FOGCOEF)) { const float fogcoef[2] = { getFloat24(gstate.fog1), getFloat24(gstate.fog2) }; glUniform2fv(u_fogcoef, 1, fogcoef); } + // Texturing + if (u_uvscaleoffset != -1 && (dirtyUniforms & DIRTY_UVSCALEOFFSET)) { + const float uvscaleoff[4] = { gstate_c.uScale, gstate_c.vScale, gstate_c.uOff, gstate_c.vOff}; + glUniform4fv(u_uvscaleoffset, 1, uvscaleoff); + } + + // Transform + if (u_world != -1 && (dirtyUniforms & DIRTY_WORLDMATRIX)) { + SetMatrix4x3(u_world, gstate.worldMatrix); + } + if (u_view != -1 && (dirtyUniforms & DIRTY_VIEWMATRIX)) { + SetMatrix4x3(u_view, gstate.viewMatrix); + } + if (u_texmtx != -1 && (dirtyUniforms & DIRTY_TEXMATRIX)) { + SetMatrix4x3(u_texmtx, gstate.tgenMatrix); + } + for (int i = 0; i < 8; i++) { + if (u_bone[i] != -1 && (dirtyUniforms & (DIRTY_BONEMATRIX0 << i))) { + SetMatrix4x3(u_bone[i], gstate.boneMatrix + 12 * i); + } + } + + // Lighting + if (u_ambient != -1 && (dirtyUniforms & DIRTY_AMBIENT)) { + SetColorUniform3Alpha(u_ambient, gstate.ambientcolor, gstate.ambientalpha & 0xFF); + } + if (u_matambientalpha != -1 && (dirtyUniforms & DIRTY_MATAMBIENTALPHA)) { + SetColorUniform3Alpha(u_matambientalpha, gstate.materialambient, gstate.materialalpha & 0xFF); + } + if (u_matdiffuse != -1 && (dirtyUniforms & DIRTY_MATDIFFUSE)) { + SetColorUniform3(u_matdiffuse, gstate.materialdiffuse); + } + if (u_matemissive != -1 && (dirtyUniforms & DIRTY_MATEMISSIVE)) { + SetColorUniform3(u_matemissive, gstate.materialemissive); + } + if (u_matspecular != -1 && (dirtyUniforms & DIRTY_MATSPECULAR)) { + SetColorUniform3ExtraFloat(u_matspecular, gstate.materialspecular, getFloat24(gstate.materialspecularcoef)); + } + + for (int i = 0; i < 4; i++) { + if (u_lightdiffuse[i] != -1 && (dirtyUniforms & (DIRTY_LIGHT0 << i))) { + glUniform3fv(u_lightpos[i], 1, gstate_c.lightpos[i]); + glUniform3fv(u_lightdir[i], 1, gstate_c.lightdir[i]); + glUniform3fv(u_lightatt[i], 1, gstate_c.lightatt[i]); + glUniform3fv(u_lightambient[i], 1, gstate_c.lightColor[0][i]); + glUniform3fv(u_lightdiffuse[i], 1, gstate_c.lightColor[1][i]); + glUniform3fv(u_lightspecular[i], 1, gstate_c.lightColor[2][i]); + } + } + dirtyUniforms = 0; } @@ -170,6 +302,7 @@ void ShaderManager::DirtyShader() // Forget the last shader ID lastFSID.clear(); lastVSID.clear(); + lastShader = 0; } @@ -188,8 +321,11 @@ LinkedShader *ShaderManager::ApplyShader(int prim) ComputeVertexShaderID(&VSID, prim); ComputeFragmentShaderID(&FSID); - // Bail quickly in the no-op case. TODO: why does it cause trouble? - // if (VSID == lastVSID && FSID == lastFSID) return lastShader; // Already all set. + // Just update uniforms if this is the same shader as last time. + if (lastShader != 0 && VSID == lastVSID && FSID == lastFSID) { + lastShader->updateUniforms(); + return lastShader; // Already all set. + } lastVSID = VSID; lastFSID = FSID; @@ -198,7 +334,7 @@ LinkedShader *ShaderManager::ApplyShader(int prim) Shader *vs; if (vsIter == vsCache.end()) { // Vertex shader not in cache. Let's compile it. - char *shaderCode = GenerateVertexShader(); + char *shaderCode = GenerateVertexShader(prim); vs = new Shader(shaderCode, GL_VERTEX_SHADER); vsCache[VSID] = vs; } else { @@ -225,10 +361,9 @@ LinkedShader *ShaderManager::ApplyShader(int prim) linkedShaderCache[linkedID] = ls; } else { ls = iter->second; + ls->use(); } - ls->use(); - lastShader = ls; return ls; } diff --git a/GPU/GLES/ShaderManager.h b/GPU/GLES/ShaderManager.h index 33c5b308dc..275b479cdf 100644 --- a/GPU/GLES/ShaderManager.h +++ b/GPU/GLES/ShaderManager.h @@ -23,14 +23,16 @@ #include "VertexShaderGenerator.h" #include "FragmentShaderGenerator.h" -struct Shader; +class Shader; -struct LinkedShader +class LinkedShader { +public: LinkedShader(Shader *vs, Shader *fs); ~LinkedShader(); void use(); + void updateUniforms(); uint32_t program; u32 dirtyUniforms; @@ -40,22 +42,39 @@ struct LinkedShader int a_color0; int a_color1; int a_texcoord; - // int a_blendWeight0123; - // int a_blendWeight4567; + int a_normal; + int a_weight0123; + int a_weight4567; int u_tex; int u_proj; int u_proj_through; int u_texenv; - + int u_view; + int u_texmtx; + int u_world; + int u_bone[8]; + // Fragment processing inputs - int u_alpharef; + int u_alphacolorref; int u_fogcolor; int u_fogcoef; + // Texturing + int u_uvscaleoffset; + // Lighting - int u_ambientcolor; - int u_light[4]; // each light consist of vec4[3] + int u_ambient; + int u_matambientalpha; + int u_matdiffuse; + int u_matspecular; + int u_matemissive; + int u_lightpos[4]; + int u_lightdir[4]; + int u_lightatt[4]; // attenuation + int u_lightdiffuse[4]; // each light consist of vec4[3] + int u_lightspecular[4]; // attenuation + int u_lightambient[4]; // attenuation }; // Will reach 32 bits soon :P @@ -66,18 +85,23 @@ enum DIRTY_FOGCOLOR = (1 << 2), DIRTY_FOGCOEF = (1 << 3), DIRTY_TEXENV = (1 << 4), - DIRTY_ALPHAREF = (1 << 5), + DIRTY_ALPHACOLORREF = (1 << 5), DIRTY_COLORREF = (1 << 6), - DIRTY_LIGHT0 = (1 << 12), - DIRTY_LIGHT1 = (1 << 13), - DIRTY_LIGHT2 = (1 << 14), - DIRTY_LIGHT3 = (1 << 15), + DIRTY_LIGHT0 = (1 << 8), + DIRTY_LIGHT1 = (1 << 9), + DIRTY_LIGHT2 = (1 << 10), + DIRTY_LIGHT3 = (1 << 11), - DIRTY_GLOBALAMBIENT = (1 << 16), + DIRTY_MATDIFFUSE = (1 << 12), + DIRTY_MATSPECULAR = (1 << 13), + DIRTY_MATEMISSIVE = (1 << 14), + DIRTY_AMBIENT = (1 << 15), + DIRTY_MATAMBIENTALPHA = (1 << 16), DIRTY_MATERIAL = (1 << 17), // let's set all 4 together (emissive ambient diffuse specular). We hide specular coef in specular.a DIRTY_UVSCALEOFFSET = (1 << 18), // this will be dirtied ALL THE TIME... maybe we'll need to do "last value with this shader compares" + DIRTY_WORLDMATRIX = (1 << 21), DIRTY_VIEWMATRIX = (1 << 22), // Maybe we'll fold this into projmatrix eventually DIRTY_TEXMATRIX = (1 << 23), DIRTY_BONEMATRIX0 = (1 << 24), @@ -94,11 +118,12 @@ enum // Real public interface -struct Shader -{ +class Shader { +public: Shader(const char *code, uint32_t shaderType); uint32_t shader; const std::string &source() const { return source_; } + private: std::string source_; }; diff --git a/GPU/GLES/StateMapping.cpp b/GPU/GLES/StateMapping.cpp index dd4b58b6f2..1ea3b7cc26 100644 --- a/GPU/GLES/StateMapping.cpp +++ b/GPU/GLES/StateMapping.cpp @@ -1,4 +1,12 @@ #include "StateMapping.h" +#include "../../native/gfx_es2/gl_state.h" + +#include "../Math3D.h" +#include "../GPUState.h" +#include "../../Core/System.h" +#include "../ge_constants.h" +#include "DisplayListInterpreter.h" +#include "ShaderManager.h" const GLint aLookup[] = { GL_DST_COLOR, @@ -51,3 +59,168 @@ const GLuint ztests[] = GL_NEVER, GL_ALWAYS, GL_EQUAL, GL_NOTEQUAL, GL_LESS, GL_LEQUAL, GL_GREATER, GL_GEQUAL, }; + +void ApplyDrawState() +{ + + // TODO: All this setup is soon so expensive that we'll need dirty flags, or simply do it in the command writes where we detect dirty by xoring. Silly to do all this work on every drawcall. + + // TODO: The top bit of the alpha channel should be written to the stencil bit somehow. This appears to require very expensive multipass rendering :( Alternatively, one could do a + // single fullscreen pass that converts alpha to stencil (or 2 passes, to set both the 0 and 1 values) very easily. + + // Set cull + bool wantCull = !gstate.isModeClear() && !gstate.isModeThrough() && gstate.isCullEnabled(); + glstate.cullFace.set(wantCull); + + if (wantCull) { + u8 cullMode = gstate.getCullMode(); + glstate.cullFaceMode.set(cullingMode[cullMode]); + } + + // Set blend + bool wantBlend = !gstate.isModeClear() && (gstate.alphaBlendEnable & 1); + glstate.blend.set(wantBlend); + if(wantBlend) { + // This can't be done exactly as there are several PSP blend modes that are impossible to do on OpenGL ES 2.0, and some even on regular OpenGL for desktop. + // HOWEVER - we should be able to approximate the 2x modes in the shader, although they will clip wrongly. + + // Examples of seen unimplementable blend states: + // Mortal Kombat Unchained: FixA=0000ff FixB=000080 FuncA=10 FuncB=10 + + int blendFuncA = gstate.getBlendFuncA(); + int blendFuncB = gstate.getBlendFuncB(); + int blendFuncEq = gstate.getBlendEq(); + + glstate.blendEquation.set(eqLookup[blendFuncEq]); + + if (blendFuncA != GE_SRCBLEND_FIXA && blendFuncB != GE_DSTBLEND_FIXB) { + // All is valid, no blendcolor needed + glstate.blendFunc.set(aLookup[blendFuncA], bLookup[blendFuncB]); + } else { + GLuint glBlendFuncA = blendFuncA == GE_SRCBLEND_FIXA ? GL_INVALID_ENUM : aLookup[blendFuncA]; + GLuint glBlendFuncB = blendFuncB == GE_DSTBLEND_FIXB ? GL_INVALID_ENUM : bLookup[blendFuncB]; + u32 fixA = gstate.getFixA(); + u32 fixB = gstate.getFixB(); + // Shortcut by using GL_ONE where possible, no need to set blendcolor + if (glBlendFuncA == GL_INVALID_ENUM && blendFuncA == GE_SRCBLEND_FIXA) { + if (fixA == 0xFFFFFF) + glBlendFuncA = GL_ONE; + else if (fixA == 0) + glBlendFuncA = GL_ZERO; + } + if (glBlendFuncB == GL_INVALID_ENUM && blendFuncB == GE_DSTBLEND_FIXB) { + if (fixB == 0xFFFFFF) + glBlendFuncB = GL_ONE; + else if (fixB == 0) + glBlendFuncB = GL_ZERO; + } + if (glBlendFuncA == GL_INVALID_ENUM && glBlendFuncB != GL_INVALID_ENUM) { + // Can use blendcolor trivially. + const float blendColor[4] = {(fixA & 0xFF)/255.0f, ((fixA >> 8) & 0xFF)/255.0f, ((fixA >> 16) & 0xFF)/255.0f, 1.0f}; + glstate.blendColor.set(blendColor); + glBlendFuncA = GL_CONSTANT_COLOR; + } else if (glBlendFuncA != GL_INVALID_ENUM && glBlendFuncB == GL_INVALID_ENUM) { + // Can use blendcolor trivially. + const float blendColor[4] = {(fixB & 0xFF)/255.0f, ((fixB >> 8) & 0xFF)/255.0f, ((fixB >> 16) & 0xFF)/255.0f, 1.0f}; + glstate.blendColor.set(blendColor); + glBlendFuncB = GL_CONSTANT_COLOR; + } else if (glBlendFuncA == GL_INVALID_ENUM && glBlendFuncB == GL_INVALID_ENUM) { // Should also check for approximate equality + if (fixA == (fixB ^ 0xFFFFFF)) { + glBlendFuncA = GL_CONSTANT_COLOR; + glBlendFuncB = GL_ONE_MINUS_CONSTANT_COLOR; + const float blendColor[4] = {(fixA & 0xFF)/255.0f, ((fixA >> 8) & 0xFF)/255.0f, ((fixA >> 16) & 0xFF)/255.0f, 1.0f}; + glstate.blendColor.set(blendColor); + } else if (fixA == fixB) { + glBlendFuncA = GL_CONSTANT_COLOR; + glBlendFuncB = GL_CONSTANT_COLOR; + const float blendColor[4] = {(fixA & 0xFF)/255.0f, ((fixA >> 8) & 0xFF)/255.0f, ((fixA >> 16) & 0xFF)/255.0f, 1.0f}; + glstate.blendColor.set(blendColor); + } else { + DEBUG_LOG(HLE, "ERROR INVALID blendcolorstate: FixA=%06x FixB=%06x FuncA=%i FuncB=%i", gstate.getFixA(), gstate.getFixB(), gstate.getBlendFuncA(), gstate.getBlendFuncB()); + glBlendFuncA = GL_ONE; + glBlendFuncB = GL_ONE; + } + } + // At this point, through all paths above, glBlendFuncA and glBlendFuncB will be set somehow. + + glstate.blendFunc.set(glBlendFuncA, glBlendFuncB); + } + } + + bool wantDepthTest = gstate.isModeClear() || gstate.isDepthTestEnabled(); + glstate.depthTest.set(wantDepthTest); + if(wantDepthTest) { + // Force GL_ALWAYS if mode clear + int depthTestFunc = gstate.isModeClear() ? 1 : gstate.getDepthTestFunc(); + glstate.depthFunc.set(ztests[depthTestFunc]); + } + + bool wantDepthWrite = gstate.isModeClear() || gstate.isDepthWriteEnabled(); + glstate.depthWrite.set(wantDepthWrite ? GL_TRUE : GL_FALSE); + + float depthRangeMin = gstate_c.zOff - gstate_c.zScale; + float depthRangeMax = gstate_c.zOff + gstate_c.zScale; + glstate.depthRange.set(depthRangeMin, depthRangeMax); +} + +void UpdateViewportAndProjection() { + int renderWidth = PSP_CoreParameter().renderWidth; + int renderHeight = PSP_CoreParameter().renderHeight; + float renderWidthFactor = (float)renderWidth / 480.0f; + float renderHeightFactor = (float)renderHeight / 272.0f; + bool throughmode = (gstate.vertType & GE_VTYPE_THROUGH_MASK) != 0; + + // We can probably use these to simply set scissors? Maybe we need to offset by regionX1/Y1 + int regionX1 = gstate.region1 & 0x3FF; + int regionY1 = (gstate.region1 >> 10) & 0x3FF; + int regionX2 = (gstate.region2 & 0x3FF) + 1; + int regionY2 = ((gstate.region2 >> 10) & 0x3FF) + 1; + + float offsetX = (float)(gstate.offsetx & 0xFFFF) / 16.0f; + float offsetY = (float)(gstate.offsety & 0xFFFF) / 16.0f; + + if (throughmode) { + // No viewport transform here. Let's experiment with using region. + return; + glViewport((0 + regionX1) * renderWidthFactor, (0 - regionY1) * renderHeightFactor, (regionX2 - regionX1) * renderWidthFactor, (regionY2 - regionY1) * renderHeightFactor); + } else { + // These we can turn into a glViewport call, offset by offsetX and offsetY. Math after. + float vpXa = getFloat24(gstate.viewportx1); + float vpXb = getFloat24(gstate.viewportx2); + float vpYa = getFloat24(gstate.viewporty1); + float vpYb = getFloat24(gstate.viewporty2); + float vpZa = getFloat24(gstate.viewportz1); // / 65536.0f should map it to OpenGL's 0.0-1.0 Z range + float vpZb = getFloat24(gstate.viewportz2); // / 65536.0f + + // The viewport transform appears to go like this: + // Xscreen = -offsetX + vpXb + vpXa * Xview + // Yscreen = -offsetY + vpYb + vpYa * Yview + // Zscreen = vpZb + vpZa * Zview + + // This means that to get the analogue glViewport we must: + float vpX0 = vpXb - offsetX - vpXa; + float vpY0 = vpYb - offsetY + vpYa; // Need to account for sign of Y + gstate_c.vpWidth = vpXa * 2; + gstate_c.vpHeight = -vpYa * 2; + + return; + + float vpWidth = fabsf(gstate_c.vpWidth); + float vpHeight = fabsf(gstate_c.vpHeight); + + // TODO: These two should feed into glDepthRange somehow. + float vpZ0 = (vpZb - vpZa) / 65536.0f; + float vpZ1 = (vpZa * 2) / 65536.0f; + + vpX0 *= renderWidthFactor; + vpY0 *= renderHeightFactor; + vpWidth *= renderWidthFactor; + vpHeight *= renderHeightFactor; + + // Flip vpY0 to match the OpenGL coordinate system. + vpY0 = renderHeight - (vpY0 + vpHeight); + glViewport(vpX0, vpY0, vpWidth, vpHeight); + // Sadly, as glViewport takes integers, we will not be able to support sub pixel offsets this way. But meh. + // shaderManager_->DirtyUniform(DIRTY_PROJMATRIX); + } +} diff --git a/GPU/GLES/StateMapping.h b/GPU/GLES/StateMapping.h index f6251f5b53..7532a3f3d4 100644 --- a/GPU/GLES/StateMapping.h +++ b/GPU/GLES/StateMapping.h @@ -5,3 +5,8 @@ extern const GLint bLookup[]; extern const GLint eqLookup[]; extern const GLint cullingMode[]; extern const GLuint ztests[]; + + +void ApplyDrawState(); +void UpdateViewportAndProjection(); + diff --git a/GPU/GLES/TextureCache.cpp b/GPU/GLES/TextureCache.cpp index 9ad2685657..3e13c466e3 100644 --- a/GPU/GLES/TextureCache.cpp +++ b/GPU/GLES/TextureCache.cpp @@ -21,7 +21,7 @@ #include "../ge_constants.h" #include "../GPUState.h" #include "TextureCache.h" - +#include "../Core/Config.h" // If a texture hasn't been seen for 200 frames, get rid of it. #define TEXTURE_KILL_AGE 200 @@ -106,6 +106,26 @@ void TextureCache_Decimate() } } +void TextureCache_Invalidate(u32 addr, int size) +{ + u32 addr_end = addr + size; + + for (TexCache::iterator iter = cache.begin(); iter != cache.end(); ) + { + // Clear if either the addr or clutaddr is in the range. + bool invalidate = iter->second.addr >= addr && iter->second.addr < addr_end; + invalidate |= iter->second.clutaddr >= addr && iter->second.clutaddr < addr_end; + + if (invalidate) + { + glDeleteTextures(1, &iter->second.texture); + cache.erase(iter++); + } + else + ++iter; + } +} + int TextureCache_NumLoadedTextures() { return cache.size(); @@ -408,8 +428,17 @@ void UpdateSamplingParams() int tClamp = (gstate.texwrap>>8) & 1; glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, sClamp ? GL_CLAMP_TO_EDGE : GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, tClamp ? GL_CLAMP_TO_EDGE : GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilt ? GL_LINEAR : GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilt ? GL_LINEAR : GL_NEAREST); + // Tested mag/minFilt only work in either one case that can allow GL_LINEAR to be enable + //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilt ? GL_LINEAR : GL_NEAREST); + //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilt ? GL_LINEAR : GL_NEAREST); + // User define linear filtering + if ( g_Config.bLinearFiltering ) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + } else { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + } } @@ -597,17 +626,16 @@ void convertColors(u8 *finalBuf, GLuint dstFmt, int numPixels) void PSPSetTexture() { + static int lastBoundTexture = -1; + u32 texaddr = (gstate.texaddr[0] & 0xFFFFF0) | ((gstate.texbufwidth[0]<<8) & 0xFF000000); texaddr &= 0xFFFFFFF; - if (!texaddr) return; - u8 level = 0; u32 format = gstate.texformat & 0xF; u32 clutformat = gstate.clutformat & 3; u32 clutaddr = GetClutAddr(clutformat == GE_CMODE_32BIT_ABGR8888 ? 4 : 2); - DEBUG_LOG(G3D,"Texture at %08x",texaddr); u8 *texptr = Memory::GetPointer(texaddr); u32 texhash = texptr ? *(u32*)texptr : 0; @@ -636,8 +664,11 @@ void PSPSetTexture() if (match) { //got one! entry.frameCounter = gpuStats.numFrames; - glBindTexture(GL_TEXTURE_2D, entry.texture); - UpdateSamplingParams(); + if (true || entry.texture != lastBoundTexture) { + glBindTexture(GL_TEXTURE_2D, entry.texture); + UpdateSamplingParams(); + lastBoundTexture = entry.texture; + } DEBUG_LOG(G3D, "Texture at %08x Found in Cache, applying", texaddr); return; //Done! } else { @@ -653,7 +684,7 @@ void PSPSetTexture() //we have to decode it - TexCacheEntry entry; + TexCacheEntry entry = {0}; entry.addr = texaddr; entry.hash = texhash; @@ -671,9 +702,6 @@ void PSPSetTexture() entry.clutaddr = 0; } - glGenTextures(1, &entry.texture); - glBindTexture(GL_TEXTURE_2D, entry.texture); - int bufw = gstate.texbufwidth[0] & 0x3ff; entry.dim = gstate.texsize[0] & 0xF0F; @@ -681,8 +709,6 @@ void PSPSetTexture() int w = 1 << (gstate.texsize[0] & 0xf); int h = 1 << ((gstate.texsize[0]>>8) & 0xf); - INFO_LOG(G3D, "Creating texture %i from %08x: %i x %i (stride: %i). fmt: %i", entry.texture, entry.addr, w, h, bufw, entry.format); - gstate_c.curTextureWidth=w; gstate_c.curTextureHeight=h; GLenum dstFmt = 0; @@ -932,19 +958,20 @@ void PSPSetTexture() } } + gpuStats.numTexturesDecoded++; // Can restore these and remove the above fixup on some platforms. //glPixelStorei(GL_UNPACK_ROW_LENGTH, bufw); glPixelStorei(GL_UNPACK_ALIGNMENT, texByteAlign); //glPixelStorei(GL_PACK_ROW_LENGTH, bufw); glPixelStorei(GL_PACK_ALIGNMENT, texByteAlign); + INFO_LOG(G3D, "Creating texture %i from %08x: %i x %i (stride: %i). fmt: %i", entry.texture, entry.addr, w, h, bufw, entry.format); + + glGenTextures(1, &entry.texture); + glBindTexture(GL_TEXTURE_2D, entry.texture); + lastBoundTexture = entry.texture; GLuint components = dstFmt == GL_UNSIGNED_SHORT_5_6_5 ? GL_RGB : GL_RGBA; glTexImage2D(GL_TEXTURE_2D, 0, components, w, h, 0, components, dstFmt, finalBuf); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - // glGenerateMipmap(GL_TEXTURE_2D); UpdateSamplingParams(); diff --git a/GPU/GLES/TextureCache.h b/GPU/GLES/TextureCache.h index 2579aa677e..c895c569c7 100644 --- a/GPU/GLES/TextureCache.h +++ b/GPU/GLES/TextureCache.h @@ -25,4 +25,5 @@ void TextureCache_Init(); void TextureCache_Shutdown(); void TextureCache_Clear(bool delete_them); void TextureCache_Decimate(); // Run this once per frame to get rid of old textures. +void TextureCache_Invalidate(u32 addr, int size); int TextureCache_NumLoadedTextures(); diff --git a/GPU/GLES/TransformPipeline.cpp b/GPU/GLES/TransformPipeline.cpp index 58ce633357..03c3c1901f 100644 --- a/GPU/GLES/TransformPipeline.cpp +++ b/GPU/GLES/TransformPipeline.cpp @@ -31,8 +31,7 @@ #include "ShaderManager.h" #include "DisplayListInterpreter.h" -GLuint glprim[8] = -{ +const GLuint glprim[8] = { GL_POINTS, GL_LINES, GL_LINE_STRIP, @@ -42,12 +41,57 @@ GLuint glprim[8] = GL_TRIANGLES, // With OpenGL ES we have to expand sprites into triangles, tripling the data instead of doubling. sigh. OpenGL ES, Y U NO SUPPORT GL_QUADS? }; -DecodedVertex decoded[65536]; -TransformedVertex transformed[65536]; -TransformedVertex transformedExpanded[65536]; -uint16_t indexBuffer[65536]; // Unused +TransformDrawEngine::TransformDrawEngine() + : numVerts(0), + lastVType(-1), + shaderManager_(0) { + decoded = new u8[65536 * 48]; + decIndex = new u16[65536]; + transformed = new TransformedVertex[65536]; + transformedExpanded = new TransformedVertex[65536 * 3]; -// TODO: This should really return 2 colors, one for specular and one for diffuse. + indexGen.Setup(decIndex); +} + +TransformDrawEngine::~TransformDrawEngine() { + delete [] decoded; + delete [] decIndex; + delete [] transformed; + delete [] transformedExpanded; +} + +// Just to get something on the screen, we'll just not subdivide correctly. +void TransformDrawEngine::DrawBezier(int ucount, int vcount) { + u16 indices[3 * 3 * 6]; + + u32 customVertType = gstate.vertType; //(gstate.vertType & ~GE_VTYPE_TC_MASK) | GE_VTYPE_TC_FLOAT; + float customUV[32]; + int c = 0; + for (int y = 0; y < 3; y++) { + for (int x = 0; x < 3; x++) { + indices[c++] = y * 4 + x; + indices[c++] = y * 4 + x + 1; + indices[c++] = (y + 1) * 4 + x + 1; + indices[c++] = (y + 1) * 4 + x + 1; + indices[c++] = (y + 1) * 4 + x; + indices[c++] = y * 4 + x; + } + } + + for (int y = 0; y < 4; y++) { + for (int x = 0; x < 4; x++) { + customUV[(y * 4 + x) * 2 + 0] = (float)x/3.0f; + customUV[(y * 4 + x) * 2 + 1] = (float)y/3.0f; + } + } + + int vertexCount = 3 * 3 * 6; + SubmitPrim(Memory::GetPointer(gstate_c.vertexAddr), &indices[0], GE_PRIM_TRIANGLES, vertexCount, customVertType, customUV, GE_VTYPE_IDX_16BIT, 0); +} + +void TransformDrawEngine::DrawSpline(int ucount, int vcount, int utype, int vtype) { + // TODO +} // Convenient way to do precomputation to save the parts of the lighting calculation // that's common between the many vertices of a draw call. @@ -64,7 +108,7 @@ private: Color4 materialDiffuse; Color4 materialSpecular; float specCoef_; - Vec3 viewer_; + // Vec3 viewer_; bool doShadeMapping_; int materialUpdate_; }; @@ -87,7 +131,7 @@ Lighter::Lighter() { materialSpecular.GetFromRGB(gstate.materialspecular); materialSpecular.a = 1.0f; specCoef_ = getFloat24(gstate.materialspecularcoef); - viewer_ = Vec3(-gstate.viewMatrix[9], -gstate.viewMatrix[10], -gstate.viewMatrix[11]); + // viewer_ = Vec3(-gstate.viewMatrix[9], -gstate.viewMatrix[10], -gstate.viewMatrix[11]); materialUpdate_ = gstate.materialupdate & 7; } @@ -143,14 +187,6 @@ void Lighter::Light(float colorOut0[4], float colorOut1[4], const float colorIn[ bool doSpecular = (comp != GE_LIGHTCOMP_ONLYDIFFUSE); bool poweredDiffuse = comp == GE_LIGHTCOMP_BOTHWITHPOWDIFFUSE; - float lightScale = 1.0f; - if (type != GE_LIGHTTYPE_DIRECTIONAL) - { - float distance = toLight.Normalize(); - lightScale = 1.0f / (gstate_c.lightatt[l][0] + gstate_c.lightatt[l][1]*distance + gstate_c.lightatt[l][2]*distance*distance); - if (lightScale > 1.0f) lightScale = 1.0f; - } - float dot = toLight * norm; // Clamp dot to zero. @@ -159,7 +195,16 @@ void Lighter::Light(float colorOut0[4], float colorOut1[4], const float colorIn[ if (poweredDiffuse) dot = powf(dot, specCoef_); - Color4 diff = (gstate_c.lightColor[1][l] * *diffuse) * (dot * lightScale); + float lightScale = 1.0f; + float distance = toLight.Normalize(); + if (type != GE_LIGHTTYPE_DIRECTIONAL) + { + lightScale = 1.0f / (gstate_c.lightatt[l][0] + gstate_c.lightatt[l][1]*distance + gstate_c.lightatt[l][2]*distance*distance); + if (lightScale > 1.0f) lightScale = 1.0f; + } + + Color4 lightDiff(gstate_c.lightColor[1][l], 0.0f); + Color4 diff = (lightDiff * *diffuse) * (dot * lightScale); // Real PSP specular Vec3 toViewer(0,0,1); @@ -175,13 +220,15 @@ void Lighter::Light(float colorOut0[4], float colorOut1[4], const float colorIn[ dot = halfVec * norm; if (dot >= 0) { - lightSum1 += (gstate_c.lightColor[2][l] * *specular * (powf(dot, specCoef_)*lightScale)); + Color4 lightSpec(gstate_c.lightColor[2][l], 0.0f); + lightSum1 += (lightSpec * *specular * (powf(dot, specCoef_)*lightScale)); } } dots[l] = dot; if (gstate.lightEnable[l] & 1) { - lightSum0 += gstate_c.lightColor[0][l] * *ambient + diff; + Color4 lightAmbient(gstate_c.lightColor[2][l], 1.0f); + lightSum0 += lightAmbient * *ambient + diff; } } @@ -192,58 +239,111 @@ void Lighter::Light(float colorOut0[4], float colorOut1[4], const float colorIn[ } } +struct GlTypeInfo { + GLuint type; + int count; + GLboolean normalized; +}; + +const GlTypeInfo GLComp[8] = { + {0}, // DEC_NONE, + {GL_FLOAT, 1, GL_FALSE}, // DEC_FLOAT_1, + {GL_FLOAT, 2, GL_FALSE}, // DEC_FLOAT_2, + {GL_FLOAT, 3, GL_FALSE}, // DEC_FLOAT_3, + {GL_FLOAT, 4, GL_FALSE}, // DEC_FLOAT_4, + {GL_BYTE, 3, GL_TRUE}, // DEC_S8_3, + {GL_SHORT, 3, GL_TRUE},// DEC_S16_3, + {GL_UNSIGNED_BYTE, 4, GL_TRUE},// DEC_U8_4, +}; + +static inline void VertexAttribSetup(int attrib, int fmt, int stride, u8 *ptr) { + if (attrib != -1 && fmt) { + const GlTypeInfo &type = GLComp[fmt]; + glEnableVertexAttribArray(attrib); + glVertexAttribPointer(attrib, type.count, type.type, type.normalized, stride, ptr); + } +} +static inline void VertexAttribDisable(int attrib, int fmt) { + if (attrib != -1 && fmt) { + glDisableVertexAttribArray(attrib); + } +} + +// TODO: Use VBO and get rid of the vertexData pointers - with that, we will supply only offsets +static void SetupDecFmtForDraw(LinkedShader *program, const DecVtxFormat &decFmt, u8 *vertexData) { + VertexAttribSetup(program->a_weight0123, decFmt.w0fmt, decFmt.stride, vertexData + decFmt.w0off); + VertexAttribSetup(program->a_weight4567, decFmt.w1fmt, decFmt.stride, vertexData + decFmt.w1off); + VertexAttribSetup(program->a_texcoord, decFmt.uvfmt, decFmt.stride, vertexData + decFmt.uvoff); + VertexAttribSetup(program->a_color0, decFmt.c0fmt, decFmt.stride, vertexData + decFmt.c0off); + VertexAttribSetup(program->a_color1, decFmt.c1fmt, decFmt.stride, vertexData + decFmt.c1off); + VertexAttribSetup(program->a_normal, decFmt.nrmfmt, decFmt.stride, vertexData + decFmt.nrmoff); + VertexAttribSetup(program->a_position, decFmt.posfmt, decFmt.stride, vertexData + decFmt.posoff); +} + +static void DesetupDecFmtForDraw(LinkedShader *program, const DecVtxFormat &decFmt) { + VertexAttribDisable(program->a_weight0123, decFmt.w0fmt); + VertexAttribDisable(program->a_weight4567, decFmt.w1fmt); + VertexAttribDisable(program->a_texcoord, decFmt.uvfmt); + VertexAttribDisable(program->a_color0, decFmt.c0fmt); + VertexAttribDisable(program->a_color1, decFmt.c1fmt); + VertexAttribDisable(program->a_normal, decFmt.nrmfmt); + VertexAttribDisable(program->a_position, decFmt.posfmt); +} + +// The verts are in the order: BR BL TL TR +static void SwapUVs(TransformedVertex &a, TransformedVertex &b) { + float tempu = a.u; + float tempv = a.v; + a.u = b.u; + a.v = b.v; + b.u = tempu; + b.v = tempv; +} +// 2 3 3 2 0 3 2 1 +// to to or +// 1 0 0 1 1 2 3 0 + +static void RotateUVs(TransformedVertex v[4]) { + if (v[0].y < v[2].y && v[0].x > v[2].x) { + // This appears to be wrong. + // SwapUVs(v[0], v[2]); + } else if (v[0].y > v[2].y && v[0].x < v[2].x) { + // This works fine in Star Soldier. + SwapUVs(v[1], v[3]); + } +} + // This is the software transform pipeline, which is necessary for supporting RECT -// primitives correctly. Other primitives are possible to transform and light in hardware -// using vertex shader, which will be way, way faster, especially on mobile. This has -// not yet been implemented though. -void GLES_GPU::TransformAndDrawPrim(void *verts, void *inds, int prim, int vertexCount, float *customUV, int forceIndexType, int *bytesRead) -{ - int indexLowerBound, indexUpperBound; - // First, decode the verts and apply morphing - VertexDecoder dec; - dec.SetVertexType(gstate.vertType); - dec.DecodeVerts(decoded, verts, inds, prim, vertexCount, &indexLowerBound, &indexUpperBound); -#if 0 - for (int i = indexLowerBound; i <= indexUpperBound; i++) { - PrintDecodedVertex(decoded[i], gstate.vertType); - } -#endif - bool useTexCoord = false; +// primitives correctly, and may be easier to use for debugging than the hardware +// transform pipeline. - // Check if anything needs updating - if (gstate_c.textureChanged) - { - if ((gstate.textureMapEnable & 1) && !gstate.isModeClear()) - { - PSPSetTexture(); - useTexCoord = true; - } - } - gpuStats.numDrawCalls++; - gpuStats.numVertsTransformed += vertexCount; +// There's code here that simply expands transformed RECTANGLES into plain triangles. - if (bytesRead) - *bytesRead = vertexCount * dec.VertexSize(); +// We're gonna have to keep software transforming RECTANGLES, unless we use a geom shader which we can't on OpenGL ES 2.0. +// Usually, though, these primitives don't use lighting etc so it's no biggie performance wise, but it would be nice to get rid of +// this code. - bool throughmode = (gstate.vertType & GE_VTYPE_THROUGH_MASK) != 0; - // Then, transform and draw in one big swoop (urgh!) - // need to move this to the shader. +// Actually, if we find the camera-relative right and down vectors, it might even be possible to add the extra points in pre-transformed +// space and thus make decent use of hardware transform. - // We're gonna have to keep software transforming RECTANGLES, unless we use a geom shader which we can't on OpenGL ES 2.0. - // Usually, though, these primitives don't use lighting etc so it's no biggie performance wise, but it would be nice to get rid of - // this code. +// Actually again, single quads could be drawn more efficiently using GL_TRIANGLE_STRIP, no need to duplicate verts as for +// GL_TRIANGLES. Still need to sw transform to compute the extra two corners though. +void TransformDrawEngine::SoftwareTransformAndDraw( + int prim, u8 *decoded, LinkedShader *program, int vertexCount, u32 vertType, void *inds, int indexType, const DecVtxFormat &decVtxFormat, int maxIndex) { + /* + DEBUG_LOG(G3D, "View matrix:"); + const float *m = &gstate.viewMatrix[0]; + DEBUG_LOG(G3D, "%f %f %f", m[0], m[1], m[2]); + DEBUG_LOG(G3D, "%f %f %f", m[3], m[4], m[5]); + DEBUG_LOG(G3D, "%f %f %f", m[6], m[7], m[8]); + DEBUG_LOG(G3D, "%f %f %f", m[9], m[10], m[11]); + */ - // Actually, if we find the camera-relative right and down vectors, it might even be possible to add the extra points in pre-transformed - // space and thus make decent use of hardware transform. - - // Actually again, single quads could be drawn more efficiently using GL_TRIANGLE_STRIP, no need to duplicate verts as for - // GL_TRIANGLES. Still need to sw transform to compute the extra two corners though. - // Temporary storage for RECTANGLES emulation float v2[3] = {0}; float uv2[2] = {0}; - // TODO: Could use glDrawElements in some cases, see below. + bool throughmode = (vertType & GE_VTYPE_THROUGH_MASK) != 0; // TODO: Split up into multiple draw calls for GLES 2.0 where you can't guarantee support for more than 0x10000 verts. @@ -254,82 +354,94 @@ void GLES_GPU::TransformAndDrawPrim(void *verts, void *inds, int prim, int verte Lighter lighter; - for (int index = indexLowerBound; index <= indexUpperBound; index++) - { + VertexReader reader(decoded, decVtxFormat); + for (int index = 0; index < maxIndex; index++) { + reader.Goto(index); + float v[3] = {0, 0, 0}; float c0[4] = {1, 1, 1, 1}; float c1[4] = {0, 0, 0, 0}; float uv[2] = {0, 0}; - if (throughmode) - { + if (throughmode) { // Do not touch the coordinates or the colors. No lighting. - for (int j=0; j<3; j++) - v[j] = decoded[index].pos[j]; - if(dec.hasColor()) { - for (int j=0; j<4; j++) { - c0[j] = decoded[index].color[j] / 255.0f; + reader.ReadPos(v); + if (reader.hasColor0()) { + reader.ReadColor0(c0); + for (int j = 0; j < 4; j++) { c1[j] = 0.0f; } - } - else - { + } else { c0[0] = (gstate.materialambient & 0xFF) / 255.f; c0[1] = ((gstate.materialambient >> 8) & 0xFF) / 255.f; c0[2] = ((gstate.materialambient >> 16) & 0xFF) / 255.f; c0[3] = (gstate.materialalpha & 0xFF) / 255.f; } - // TODO : check if has uv - for (int j=0; j<2; j++) - uv[j] = decoded[index].uv[j]; - // Rescale UV? - } - else - { + if (reader.hasUV()) { + reader.ReadUV(uv); + } + // Scale UV? + } else { // We do software T&L for now float out[3], norm[3]; - if ((gstate.vertType & GE_VTYPE_WEIGHT_MASK) == GE_VTYPE_WEIGHT_NONE) - { - Vec3ByMatrix43(out, decoded[index].pos, gstate.worldMatrix); - Norm3ByMatrix43(norm, decoded[index].normal, gstate.worldMatrix); - } - else - { + float pos[3], nrm[3] = {0}; + reader.ReadPos(pos); + if (reader.hasNormal()) + reader.ReadNrm(nrm); + + if ((vertType & GE_VTYPE_WEIGHT_MASK) == GE_VTYPE_WEIGHT_NONE) { + Vec3ByMatrix43(out, pos, gstate.worldMatrix); + if (reader.hasNormal()) { + Norm3ByMatrix43(norm, nrm, gstate.worldMatrix); + } else { + memset(norm, 0, 12); + } + } else { + float weights[8]; + reader.ReadWeights(weights); // Skinning Vec3 psum(0,0,0); Vec3 nsum(0,0,0); - int nweights = ((gstate.vertType & GE_VTYPE_WEIGHTCOUNT_MASK) >> GE_VTYPE_WEIGHTCOUNT_SHIFT) + 1; + int nweights = ((vertType & GE_VTYPE_WEIGHTCOUNT_MASK) >> GE_VTYPE_WEIGHTCOUNT_SHIFT) + 1; for (int i = 0; i < nweights; i++) { - if (decoded[index].weights[i] != 0.0f) { - Vec3ByMatrix43(out, decoded[index].pos, gstate.boneMatrix+i*12); - Norm3ByMatrix43(norm, decoded[index].normal, gstate.boneMatrix+i*12); - Vec3 tpos(out), tnorm(norm); - psum += tpos*decoded[index].weights[i]; - nsum += tnorm*decoded[index].weights[i]; + if (weights[i] != 0.0f) { + Vec3ByMatrix43(out, pos, gstate.boneMatrix+i*12); + Vec3 tpos(out); + psum += tpos * weights[i]; + if (reader.hasNormal()) { + Norm3ByMatrix43(norm, nrm, gstate.boneMatrix+i*12); + Vec3 tnorm(norm); + nsum += tnorm * weights[i]; + } } } - nsum.Normalize(); - + // Yes, we really must multiply by the world matrix too. Vec3ByMatrix43(out, psum.v, gstate.worldMatrix); - Norm3ByMatrix43(norm, nsum.v, gstate.worldMatrix); + if (reader.hasNormal()) { + Norm3ByMatrix43(norm, nsum.v, gstate.worldMatrix); + } } // Perform lighting here if enabled. don't need to check through, it's checked above. float dots[4] = {0,0,0,0}; - float unlitColor[4]; - for (int j = 0; j < 4; j++) { - unlitColor[j] = decoded[index].color[j] / 255.0f; + float unlitColor[4] = {1, 1, 1, 1}; + if (reader.hasColor0()) { + reader.ReadColor0(unlitColor); + } else { + unlitColor[0] = (gstate.materialambient & 0xFF) / 255.f; + unlitColor[1] = ((gstate.materialambient >> 8) & 0xFF) / 255.f; + unlitColor[2] = ((gstate.materialambient >> 16) & 0xFF) / 255.f; + unlitColor[3] = (gstate.materialalpha & 0xFF) / 255.f; } float litColor0[4]; float litColor1[4]; lighter.Light(litColor0, litColor1, unlitColor, out, norm, dots); - if (gstate.lightingEnable & 1) - { - // TODO: don't ignore gstate.lmode - we should send two colors in that case + if (gstate.lightingEnable & 1) { + // Don't ignore gstate.lmode - we should send two colors in that case if (gstate.lmode & 1) { // Separate colors for (int j = 0; j < 4; j++) { @@ -343,10 +455,8 @@ void GLES_GPU::TransformAndDrawPrim(void *verts, void *inds, int prim, int verte c1[j] = 0.0f; } } - } - else - { - if(dec.hasColor()) { + } else { + if (reader.hasColor0()) { for (int j = 0; j < 4; j++) { c0[j] = unlitColor[j]; c1[j] = 0.0f; @@ -359,29 +469,28 @@ void GLES_GPU::TransformAndDrawPrim(void *verts, void *inds, int prim, int verte } } - if (customUV) { - uv[0] = customUV[index * 2 + 0]*gstate_c.uScale + gstate_c.uOff; - uv[1] = customUV[index * 2 + 1]*gstate_c.vScale + gstate_c.vOff; - } else { + if (reader.hasUV()) { + float ruv[2]; + reader.ReadUV(ruv); // Perform texture coordinate generation after the transform and lighting - one style of UV depends on lights. - switch (gstate.texmapmode & 0x3) + switch (gstate.getUVGenMode()) { case 0: // UV mapping // Texture scale/offset is only performed in this mode. - uv[0] = decoded[index].uv[0]*gstate_c.uScale + gstate_c.uOff; - uv[1] = decoded[index].uv[1]*gstate_c.vScale + gstate_c.vOff; + uv[0] = ruv[0]*gstate_c.uScale + gstate_c.uOff; + uv[1] = ruv[1]*gstate_c.vScale + gstate_c.vOff; break; case 1: { // Projection mapping Vec3 source; - switch ((gstate.texmapmode >> 8) & 0x3) + switch (gstate.getUVProjMode()) { case 0: // Use model space XYZ as source - source = decoded[index].pos; + source = pos; break; case 1: // Use unscaled UV as source - source = Vec3(decoded[index].uv[0], decoded[index].uv[1], 0.0f); + source = Vec3(ruv[0], ruv[1], 0.0f); break; case 2: // Use normalized normal as source source = Vec3(norm).Normalized(); @@ -390,6 +499,7 @@ void GLES_GPU::TransformAndDrawPrim(void *verts, void *inds, int prim, int verte source = Vec3(norm); break; } + float uvw[3]; Vec3ByMatrix43(uvw, &source.x, gstate.tgenMatrix); uv[0] = uvw[0]; @@ -397,12 +507,10 @@ void GLES_GPU::TransformAndDrawPrim(void *verts, void *inds, int prim, int verte } break; case 2: - // Shade mapping + // Shade mapping - use dot products from light sources to generate U and V. { - int lightsource1 = gstate.texshade & 0x3; - int lightsource2 = (gstate.texshade >> 8) & 0x3; - uv[0] = dots[lightsource1]; - uv[1] = dots[lightsource2]; + uv[0] = dots[gstate.getUVLS0()]; + uv[1] = dots[gstate.getUVLS1()]; } break; case 3: @@ -412,66 +520,33 @@ void GLES_GPU::TransformAndDrawPrim(void *verts, void *inds, int prim, int verte } // Transform the coord by the view matrix. - // We only really need to do it here for RECTANGLES drawing. However, - // there's no point in optimizing it out because all other primitives - // will be moved to hardware transform anyway. Vec3ByMatrix43(v, out, gstate.viewMatrix); } + + // TODO: Write to a flexible buffer, we don't always need all four components. memcpy(&transformed[index].x, v, 3 * sizeof(float)); - memcpy(&transformed[index].uv, uv, 2 * sizeof(float)); + memcpy(&transformed[index].u, uv, 2 * sizeof(float)); memcpy(&transformed[index].color0, c0, 4 * sizeof(float)); - memcpy(&transformed[index].color1, c1, 4 * sizeof(float)); + memcpy(&transformed[index].color1, c1, 3 * sizeof(float)); } - - // Step 2: Expand using the index buffer, and expand rectangles. - + // Step 2: expand rectangles. const TransformedVertex *drawBuffer = transformed; int numTrans = 0; - int indexType = (gstate.vertType & GE_VTYPE_IDX_MASK); - if (forceIndexType != -1) { - indexType = forceIndexType; - } - bool drawIndexed = false; - GLuint glIndexType = 0; if (prim != GE_PRIM_RECTANGLES) { // We can simply draw the unexpanded buffer. numTrans = vertexCount; - switch (indexType) { - case GE_VTYPE_IDX_8BIT: - drawIndexed = true; - glIndexType = GL_UNSIGNED_BYTE; - break; - case GE_VTYPE_IDX_16BIT: - drawIndexed = true; - glIndexType = GL_UNSIGNED_SHORT; - break; - default: - drawIndexed = false; - break; - } + drawIndexed = true; } else { numTrans = 0; drawBuffer = transformedExpanded; TransformedVertex *trans = &transformedExpanded[0]; TransformedVertex saved; for (int i = 0; i < vertexCount; i++) { - int index; - if (indexType == GE_VTYPE_IDX_8BIT) - { - index = ((u8*)inds)[i]; - } - else if (indexType == GE_VTYPE_IDX_16BIT) - { - index = ((u16*)inds)[i]; - } - else - { - index = i; - } + int index = ((u16*)inds)[i]; TransformedVertex &transVtx = transformed[index]; if ((i & 1) == 0) @@ -483,224 +558,178 @@ void GLES_GPU::TransformAndDrawPrim(void *verts, void *inds, int prim, int verte { // We have to turn the rectangle into two triangles, so 6 points. Sigh. - // TODO: there's supposed to be extra magic here to rotate the UV coordinates depending on if upside down etc. - // bottom right - *trans = transVtx; - trans++; - - // top left - *trans = transVtx; - trans->x = saved.x; - trans->uv[0] = saved.uv[0]; - trans->y = saved.y; - trans->uv[1] = saved.uv[1]; - trans++; - - // top right - *trans = transVtx; - trans->x = saved.x; - trans->uv[0] = saved.uv[0]; - trans++; + trans[0] = transVtx; // bottom left - *trans = transVtx; - trans->y = saved.y; - trans->uv[1] = saved.uv[1]; - trans++; - - // bottom right - *trans = transVtx; - trans->x = saved.x; - trans->uv[0] = saved.uv[0]; - trans->y = saved.y; - trans->uv[1] = saved.uv[1]; - trans++; + trans[1] = transVtx; + trans[1].y = saved.y; + trans[1].v = saved.v; // top left - *trans = transVtx; - trans++; + trans[2] = transVtx; + trans[2].x = saved.x; + trans[2].y = saved.y; + trans[2].u = saved.u; + trans[2].v = saved.v; + + // top right + trans[3] = transVtx; + trans[3].x = saved.x; + trans[3].u = saved.u; + + // That's the four corners. Now process UV rotation. + RotateUVs(trans); + + // bottom right + trans[4] = trans[0]; + + // top left + trans[5] = trans[2]; + trans += 6; numTrans += 6; } } } - // TODO: All this setup is soon so expensive that we'll need dirty flags, or simply do it in the command writes where we detect dirty by xoring. Silly to do all this work on every drawcall. - - // TODO: The top bit of the alpha channel should be written to the stencil bit somehow. This appears to require very expensive multipass rendering :( Alternatively, one could do a - // single fullscreen pass that converts alpha to stencil (or 2 passes, to set both the 0 and 1 values) very easily. - - // Set cull - bool wantCull = !gstate.isModeClear() && !gstate.isModeThrough() && gstate.isCullEnabled(); - glstate.cullFace.set(wantCull); - - if(wantCull) { - u8 cullMode = gstate.getCullMode(); - glstate.cullFaceMode.set(cullingMode[cullMode]); - } - - // Set blend - bool wantBlend = !gstate.isModeClear() && (gstate.alphaBlendEnable & 1); - glstate.blend.set(wantBlend); - if(wantBlend) { - // This can't be done exactly as there are several PSP blend modes that are impossible to do on OpenGL ES 2.0, and some even on regular OpenGL for desktop. - // HOWEVER - we should be able to approximate the 2x modes in the shader, although they will clip wrongly. - int blendFuncA = gstate.getBlendFuncA(); - int blendFuncB = gstate.getBlendFuncB(); - int blendFuncEq = gstate.getBlendEq(); - - glstate.blendEquation.set(eqLookup[blendFuncEq]); - - if (blendFuncA != GE_SRCBLEND_FIXA && blendFuncB != GE_DSTBLEND_FIXB) { - // All is valid, no blendcolor needed - glstate.blendFunc.set(aLookup[blendFuncA], bLookup[blendFuncB]); - } else { - GLuint glBlendFuncA = blendFuncA == GE_SRCBLEND_FIXA ? GL_INVALID_ENUM : aLookup[blendFuncA]; - GLuint glBlendFuncB = blendFuncB == GE_DSTBLEND_FIXB ? GL_INVALID_ENUM : bLookup[blendFuncB]; - u32 fixA = gstate.getFixA(); - u32 fixB = gstate.getFixB(); - // Shortcut by using GL_ONE where possible, no need to set blendcolor - if (glBlendFuncA == GL_INVALID_ENUM && blendFuncA == GE_SRCBLEND_FIXA) { - if (fixA == 0xFFFFFF) - glBlendFuncA = GL_ONE; - else if (fixA == 0) - glBlendFuncA = GL_ZERO; - } - if (glBlendFuncB == GL_INVALID_ENUM && blendFuncB == GE_DSTBLEND_FIXB) { - if (fixB == 0xFFFFFF) - glBlendFuncB = GL_ONE; - else if (fixB == 0) - glBlendFuncB = GL_ZERO; - } - if (glBlendFuncA == GL_INVALID_ENUM && glBlendFuncB != GL_INVALID_ENUM) { - // Can use blendcolor trivially. - const float blendColor[4] = {(fixA & 0xFF)/255.0f, ((fixA >> 8) & 0xFF)/255.0f, ((fixA >> 16) & 0xFF)/255.0f, 1.0f}; - glstate.blendColor.set(blendColor); - glBlendFuncA = GL_CONSTANT_COLOR; - } else if (glBlendFuncA != GL_INVALID_ENUM && glBlendFuncB == GL_INVALID_ENUM) { - // Can use blendcolor trivially. - const float blendColor[4] = {(fixB & 0xFF)/255.0f, ((fixB >> 8) & 0xFF)/255.0f, ((fixB >> 16) & 0xFF)/255.0f, 1.0f}; - glstate.blendColor.set(blendColor); - glBlendFuncB = GL_CONSTANT_COLOR; - } else if (glBlendFuncA == GL_INVALID_ENUM && glBlendFuncB == GL_INVALID_ENUM) { // Should also check for approximate equality - if (fixA == (fixB ^ 0xFFFFFF)) { - glBlendFuncA = GL_CONSTANT_COLOR; - glBlendFuncB = GL_ONE_MINUS_CONSTANT_COLOR; - const float blendColor[4] = {(fixA & 0xFF)/255.0f, ((fixA >> 8) & 0xFF)/255.0f, ((fixA >> 16) & 0xFF)/255.0f, 1.0f}; - glstate.blendColor.set(blendColor); - } else if (fixA == fixB) { - glBlendFuncA = GL_CONSTANT_COLOR; - glBlendFuncB = GL_CONSTANT_COLOR; - const float blendColor[4] = {(fixA & 0xFF)/255.0f, ((fixA >> 8) & 0xFF)/255.0f, ((fixA >> 16) & 0xFF)/255.0f, 1.0f}; - glstate.blendColor.set(blendColor); - } else { - NOTICE_LOG(HLE, "ERROR INVALID blendcolorstate: FixA=%06x FixB=%06x FuncA=%i FuncB=%i", gstate.getFixA(), gstate.getFixB(), gstate.getBlendFuncA(), gstate.getBlendFuncB()); - glBlendFuncA = GL_ONE; - glBlendFuncB = GL_ONE; - } - } - // At this point, through all paths above, glBlendFuncA and glBlendFuncB will be set somehow. - - glstate.blendFunc.set(glBlendFuncA, glBlendFuncB); - } - } - - bool wantDepthTest = gstate.isModeClear() || gstate.isDepthTestEnabled(); - glstate.depthTest.set(wantDepthTest); - if(wantDepthTest) { - // Force GL_ALWAYS if mode clear - int depthTestFunc = gstate.isModeClear() ? 1 : gstate.getDepthTestFunc(); - glstate.depthFunc.set(ztests[depthTestFunc]); - } - - bool wantDepthWrite = gstate.isModeClear() || gstate.isDepthWriteEnabled(); - glstate.depthWrite.set(wantDepthWrite ? GL_TRUE : GL_FALSE); - - float depthRangeMin = gstate_c.zOff - gstate_c.zScale; - float depthRangeMax = gstate_c.zOff + gstate_c.zScale; - glstate.depthRange.set(depthRangeMin, depthRangeMax); - - UpdateViewportAndProjection(); - LinkedShader *program = shaderManager_->ApplyShader(prim); - - // TODO: Make a cache for glEnableVertexAttribArray and glVertexAttribPtr states, these spam the gDebugger log. + // TODO: Make a cache for glEnableVertexAttribArray and glVertexAttribPtr states, + // these spam the gDebugger log. glEnableVertexAttribArray(program->a_position); - if (useTexCoord && program->a_texcoord != -1) glEnableVertexAttribArray(program->a_texcoord); + if (program->a_texcoord != -1) glEnableVertexAttribArray(program->a_texcoord); if (program->a_color0 != -1) glEnableVertexAttribArray(program->a_color0); if (program->a_color1 != -1) glEnableVertexAttribArray(program->a_color1); const int vertexSize = sizeof(transformed[0]); glVertexAttribPointer(program->a_position, 3, GL_FLOAT, GL_FALSE, vertexSize, drawBuffer); - if (useTexCoord && program->a_texcoord != -1) glVertexAttribPointer(program->a_texcoord, 2, GL_FLOAT, GL_FALSE, vertexSize, ((uint8_t*)drawBuffer) + 3 * 4); + if (program->a_texcoord != -1) glVertexAttribPointer(program->a_texcoord, 2, GL_FLOAT, GL_FALSE, vertexSize, ((uint8_t*)drawBuffer) + 3 * 4); if (program->a_color0 != -1) glVertexAttribPointer(program->a_color0, 4, GL_FLOAT, GL_FALSE, vertexSize, ((uint8_t*)drawBuffer) + 5 * 4); - if (program->a_color1 != -1) glVertexAttribPointer(program->a_color1, 4, GL_FLOAT, GL_FALSE, vertexSize, ((uint8_t*)drawBuffer) + 9 * 4); - // NOTICE_LOG(G3D,"DrawPrimitive: %i", numTrans); + if (program->a_color1 != -1) glVertexAttribPointer(program->a_color1, 3, GL_FLOAT, GL_FALSE, vertexSize, ((uint8_t*)drawBuffer) + 9 * 4); if (drawIndexed) { - glDrawElements(glprim[prim], numTrans, glIndexType, (GLvoid *)inds); + glDrawElements(glprim[prim], numTrans, GL_UNSIGNED_SHORT, (GLvoid *)inds); } else { glDrawArrays(glprim[prim], 0, numTrans); } glDisableVertexAttribArray(program->a_position); - if (useTexCoord && program->a_texcoord != -1) glDisableVertexAttribArray(program->a_texcoord); + if (program->a_texcoord != -1) glDisableVertexAttribArray(program->a_texcoord); if (program->a_color0 != -1) glDisableVertexAttribArray(program->a_color0); if (program->a_color1 != -1) glDisableVertexAttribArray(program->a_color1); } -void GLES_GPU::UpdateViewportAndProjection() -{ - bool throughmode = (gstate.vertType & GE_VTYPE_THROUGH_MASK) != 0; +void TransformDrawEngine::SubmitPrim(void *verts, void *inds, int prim, int vertexCount, u32 vertType, float *customUV, int forceIndexType, int *bytesRead) { + // For the future + if (!indexGen.PrimCompatible(prim)) + Flush(); - // We can probably use these to simply set scissors? Maybe we need to offset by regionX1/Y1 - int regionX1 = gstate.region1 & 0x3FF; - int regionY1 = (gstate.region1 >> 10) & 0x3FF; - int regionX2 = (gstate.region2 & 0x3FF) + 1; - int regionY2 = ((gstate.region2 >> 10) & 0x3FF) + 1; + if (!indexGen.Empty()) { + gpuStats.numJoins++; + } + gpuStats.numDrawCalls++; + gpuStats.numVertsTransformed += vertexCount; - float offsetX = (float)(gstate.offsetx & 0xFFFF) / 16.0f; - float offsetY = (float)(gstate.offsety & 0xFFFF) / 16.0f; + indexGen.SetIndex(numVerts); + int indexLowerBound, indexUpperBound; + // If vtype has changed, setup the vertex decoder. + // TODO: Simply cache the setup decoders instead. + if (vertType != lastVType) { + dec.SetVertexType(vertType); + lastVType = vertType; + } - if (throughmode) { - // No viewport transform here. Let's experiment with using region. - return; - glViewport((0 + regionX1) * renderWidthFactor_, (0 - regionY1) * renderHeightFactor_, (regionX2 - regionX1) * renderWidthFactor_, (regionY2 - regionY1) * renderHeightFactor_); - } else { - // These we can turn into a glViewport call, offset by offsetX and offsetY. Math after. - float vpXa = getFloat24(gstate.viewportx1); - float vpXb = getFloat24(gstate.viewportx2); - float vpYa = getFloat24(gstate.viewporty1); - float vpYb = getFloat24(gstate.viewporty2); - float vpZa = getFloat24(gstate.viewportz1); // / 65536.0f should map it to OpenGL's 0.0-1.0 Z range - float vpZb = getFloat24(gstate.viewportz2); // / 65536.0f + // Decode the verts and apply morphing + dec.DecodeVerts(decoded + numVerts * (int)dec.GetDecVtxFmt().stride, verts, inds, prim, vertexCount, &indexLowerBound, &indexUpperBound); + numVerts += indexUpperBound - indexLowerBound + 1; + if (bytesRead) + *bytesRead = vertexCount * dec.VertexSize(); - // The viewport transform appears to go like this: - // Xscreen = -offsetX + vpXb + vpXa * Xview - // Yscreen = -offsetY + vpYb + vpYa * Yview - // Zscreen = vpZb + vpZa * Zview + int indexType = vertType & GE_VTYPE_IDX_MASK; + if (forceIndexType != -1) indexType = forceIndexType; + switch (indexType) { + case GE_VTYPE_IDX_NONE: + switch (prim) { + case GE_PRIM_POINTS: indexGen.AddPoints(vertexCount); break; + case GE_PRIM_LINES: indexGen.AddLineList(vertexCount); break; + case GE_PRIM_LINE_STRIP: indexGen.AddLineStrip(vertexCount); break; + case GE_PRIM_TRIANGLES: indexGen.AddList(vertexCount); break; + case GE_PRIM_TRIANGLE_STRIP: indexGen.AddStrip(vertexCount); break; + case GE_PRIM_TRIANGLE_FAN: indexGen.AddFan(vertexCount); break; + case GE_PRIM_RECTANGLES: indexGen.AddRectangles(vertexCount); break; // Same + } + break; - // This means that to get the analogue glViewport we must: - float vpX0 = vpXb - offsetX - vpXa; - float vpY0 = vpYb - offsetY + vpYa; // Need to account for sign of Y - gstate_c.vpWidth = vpXa * 2; - gstate_c.vpHeight = -vpYa * 2; + case GE_VTYPE_IDX_8BIT: + switch (prim) { + case GE_PRIM_POINTS: indexGen.TranslatePoints(vertexCount, (const u8 *)inds, -indexLowerBound); break; + case GE_PRIM_LINES: indexGen.TranslateLineList(vertexCount, (const u8 *)inds, -indexLowerBound); break; + case GE_PRIM_LINE_STRIP: indexGen.TranslateLineStrip(vertexCount, (const u8 *)inds, -indexLowerBound); break; + case GE_PRIM_TRIANGLES: indexGen.TranslateList(vertexCount, (const u8 *)inds, -indexLowerBound); break; + case GE_PRIM_TRIANGLE_STRIP: indexGen.TranslateStrip(vertexCount, (const u8 *)inds, -indexLowerBound); break; + case GE_PRIM_TRIANGLE_FAN: indexGen.TranslateFan(vertexCount, (const u8 *)inds, -indexLowerBound); break; + case GE_PRIM_RECTANGLES: indexGen.TranslateRectangles(vertexCount, (const u8 *)inds, -indexLowerBound); break; // Same + } + break; - return; - - float vpWidth = fabsf(gstate_c.vpWidth); - float vpHeight = fabsf(gstate_c.vpHeight); - - // TODO: These two should feed into glDepthRange somehow. - float vpZ0 = (vpZb - vpZa) / 65536.0f; - float vpZ1 = (vpZa * 2) / 65536.0f; - - vpX0 *= renderWidthFactor_; - vpY0 *= renderHeightFactor_; - vpWidth *= renderWidthFactor_; - vpHeight *= renderHeightFactor_; - - // Flip vpY0 to match the OpenGL coordinate system. - vpY0 = renderHeight_ - (vpY0 + vpHeight); - glViewport(vpX0, vpY0, vpWidth, vpHeight); - // Sadly, as glViewport takes integers, we will not be able to support sub pixel offsets this way. But meh. - shaderManager_->DirtyUniform(DIRTY_PROJMATRIX); + case GE_VTYPE_IDX_16BIT: + switch (prim) { + case GE_PRIM_POINTS: indexGen.TranslatePoints(vertexCount, (const u16 *)inds, -indexLowerBound); break; + case GE_PRIM_LINES: indexGen.TranslateLineList(vertexCount, (const u16 *)inds, -indexLowerBound); break; + case GE_PRIM_LINE_STRIP: indexGen.TranslateLineStrip(vertexCount, (const u16 *)inds, -indexLowerBound); break; + case GE_PRIM_TRIANGLES: indexGen.TranslateList(vertexCount, (const u16 *)inds, -indexLowerBound); break; + case GE_PRIM_TRIANGLE_STRIP: indexGen.TranslateStrip(vertexCount, (const u16 *)inds, -indexLowerBound); break; + case GE_PRIM_TRIANGLE_FAN: indexGen.TranslateFan(vertexCount, (const u16 *)inds, -indexLowerBound); break; + case GE_PRIM_RECTANGLES: indexGen.TranslateRectangles(vertexCount, (const u16 *)inds, -indexLowerBound); break; // Same + } + break; } } + +void TransformDrawEngine::Flush() { + if (indexGen.Empty()) + return; + +#if 0 + for (int i = indexLowerBound; i <= indexUpperBound; i++) { + PrintDecodedVertex(decoded[i], vertType); + } +#endif + + // Check if anything needs updating + if (gstate_c.textureChanged) { + if ((gstate.textureMapEnable & 1) && !gstate.isModeClear()) { + PSPSetTexture(); + } + gstate_c.textureChanged = false; + } + gpuStats.numFlushes++; + + // TODO: This should not be done on every drawcall, we should collect vertex data + // until critical state changes. That's when we draw (flush). + + int prim = indexGen.Prim(); + + ApplyDrawState(); + UpdateViewportAndProjection(); + + LinkedShader *program = shaderManager_->ApplyShader(prim); + + DEBUG_LOG(G3D, "Flush prim %i! %i verts in one go", prim, numVerts); + + if (CanUseHardwareTransform(prim)) { + SetupDecFmtForDraw(program, dec.GetDecVtxFmt(), decoded); + // If there's only been one primitive type, and it's either TRIANGLES, LINES or POINTS, + // there is no need for the index buffer we built. We can then use glDrawArrays instead + // for a very minor speed boost. + int seen = indexGen.SeenPrims() | 0x83204820; + if (seen == (1 << GE_PRIM_TRIANGLES) || seen == (1 << GE_PRIM_LINES) || seen == (1 << GE_PRIM_POINTS)) { + glDrawArrays(glprim[prim], 0, indexGen.VertexCount()); + } else { + glDrawElements(glprim[prim], indexGen.VertexCount(), GL_UNSIGNED_SHORT, (GLvoid *)decIndex); + } + DesetupDecFmtForDraw(program, dec.GetDecVtxFmt()); + } else { + SoftwareTransformAndDraw(prim, decoded, program, indexGen.VertexCount(), dec.VertexType(), (void *)decIndex, GE_VTYPE_IDX_16BIT, dec.GetDecVtxFmt(), + indexGen.MaxIndex()); + } + + indexGen.Reset(); + numVerts = 0; +} diff --git a/GPU/GLES/TransformPipeline.h b/GPU/GLES/TransformPipeline.h index 1abecce3ef..3be12aaa1c 100644 --- a/GPU/GLES/TransformPipeline.h +++ b/GPU/GLES/TransformPipeline.h @@ -17,4 +17,80 @@ #pragma once -struct LinkedShader; +#include "IndexGenerator.h" +#include "VertexDecoder.h" + +class LinkedShader; +class ShaderManager; +struct DecVtxFormat; + +// Handles transform, lighting and drawing. +class TransformDrawEngine { +public: + TransformDrawEngine(); + ~TransformDrawEngine(); + void SubmitPrim(void *verts, void *inds, int prim, int vertexCount, u32 vertexType, float *customUV, int forceIndexType, int *bytesRead); + void DrawBezier(int ucount, int vcount); + void DrawSpline(int ucount, int vcount, int utype, int vtype); + void Flush(); + void SetShaderManager(ShaderManager *shaderManager) { + shaderManager_ = shaderManager; + } + +private: + void SoftwareTransformAndDraw(int prim, u8 *decoded, LinkedShader *program, int vertexCount, u32 vertexType, void *inds, int indexType, const DecVtxFormat &decVtxFormat, int maxIndex); + + // Vertex collector state + IndexGenerator indexGen; + int numVerts; + + // Vertex collector buffers + VertexDecoder dec; + u32 lastVType; + u8 *decoded; + u16 *decIndex; + + TransformedVertex *transformed; + TransformedVertex *transformedExpanded; + + // Other + ShaderManager *shaderManager_; +}; + +// Only used by SW transform +struct Color4 { + float r, g, b, a; + + Color4() : r(0), g(0), b(0), a(0) { } + Color4(float _r, float _g, float _b, float _a=1.0f) + : r(_r), g(_g), b(_b), a(_a) { + } + Color4(const float in[4]) {r=in[0];g=in[1];b=in[2];a=in[3];} + Color4(const float in[3], float alpha) {r=in[0];g=in[1];b=in[2];a=alpha;} + + const float &operator [](int i) const {return *(&r + i);} + + Color4 operator *(float f) const { + return Color4(f*r,f*g,f*b,f*a); + } + Color4 operator *(const Color4 &c) const { + return Color4(r*c.r,g*c.g,b*c.b,a*c.a); + } + Color4 operator +(const Color4 &c) const { + return Color4(r+c.r,g+c.g,b+c.b,a+c.a); + } + void operator +=(const Color4 &c) { + r+=c.r; + g+=c.g; + b+=c.b; + a+=c.a; + } + void GetFromRGB(u32 col) { + r = ((col>>16) & 0xff)/255.0f; + g = ((col>>8) & 0xff)/255.0f; + b = ((col>>0) & 0xff)/255.0f; + } + void GetFromA(u32 col) { + a = (col&0xff)/255.0f; + } +}; diff --git a/GPU/GLES/VertexDecoder.cpp b/GPU/GLES/VertexDecoder.cpp index 33f3281865..3a3ffe650c 100644 --- a/GPU/GLES/VertexDecoder.cpp +++ b/GPU/GLES/VertexDecoder.cpp @@ -22,13 +22,32 @@ #include "VertexDecoder.h" -void PrintDecodedVertex(const DecodedVertex &vtx, u32 vtype) -{ - if (vtype & GE_VTYPE_NRM_MASK) printf("N: %f %f %f\n", vtx.normal[0], vtx.normal[1], vtx.normal[2]); - if (vtype & GE_VTYPE_TC_MASK) printf("TC: %f %f\n", vtx.uv[0], vtx.uv[1]); - if (vtype & GE_VTYPE_COL_MASK) printf("C: %02x %02x %02x %02x\n", vtx.color[0], vtx.color[1], vtx.color[2], vtx.color[3]); - if (vtype & GE_VTYPE_WEIGHT_MASK) printf("W: TODO\n"); - printf("P: %f %f %f\n", vtx.pos[0], vtx.pos[1], vtx.pos[2]); +void PrintDecodedVertex(VertexReader &vtx) { + if (vtx.hasNormal()) + { + float nrm[3]; + vtx.ReadNrm(nrm); + printf("N: %f %f %f\n", nrm[0], nrm[1], nrm[2]); + } + if (vtx.hasUV()) { + float uv[2]; + vtx.ReadUV(uv); + printf("TC: %f %f\n", uv[0], uv[1]); + } + if (vtx.hasColor0()) { + float col0[4]; + vtx.ReadColor0(col0); + printf("C0: %f %f %f %f\n", col0[0], col0[1], col0[2], col0[3]); + } + if (vtx.hasColor0()) { + float col1[3]; + vtx.ReadColor1(col1); + printf("C1: %f %f %f\n", col1[0], col1[1], col1[2]); + } + // Etc.. + float pos[3]; + vtx.ReadPos(pos); + printf("P: %f %f %f\n", pos[0], pos[1], pos[2]); } const int tcsize[4] = {0,2,4,8}, tcalign[4] = {0,1,2,4}; @@ -37,67 +56,559 @@ const int nrmsize[4] = {0,3,6,12}, nrmalign[4] = {0,1,2,4}; const int possize[4] = {0,3,6,12}, posalign[4] = {0,1,2,4}; const int wtsize[4] = {0,1,2,4}, wtalign[4] = {0,1,2,4}; -inline int align(int n, int align) -{ +inline int align(int n, int align) { return (n + (align - 1)) & ~(align - 1); } -void VertexDecoder::SetVertexType(u32 fmt) +int DecFmtSize(u8 fmt) { + switch (fmt) { + case DEC_NONE: return 0; + case DEC_FLOAT_1: return 4; + case DEC_FLOAT_2: return 8; + case DEC_FLOAT_3: return 12; + case DEC_FLOAT_4: return 16; + case DEC_S8_3: return 4; + case DEC_S16_3: return 8; + case DEC_U8_4: return 4; + default: + return 0; + } +} +#if 0 +// This is what the software transform spits out, and thus w +DecVtxFormat GetTransformedVtxFormat(const DecVtxFormat &fmt) { + DecVtxFormat tfm = {0}; + int size = 0; + int offset = 0; + // Weights disappear during transform. + if (fmt.uvfmt) { + // UV always becomes float2. + tfm.uvfmt = DEC_FLOAT_2; + tfm.uvoff = offset; + offset += DecFmtSize(tfm.uvfmt); + } + // We always (?) get two colors out, they're floats (although we'd probably be fine with less precision). + tfm.c0fmt = DEC_FLOAT_4; + tfm.c0off = offset; + offset += DecFmtSize(tfm.c0fmt); + tfm.c1fmt = DEC_FLOAT_3; // color1 (specular) doesn't have alpha. + tfm.c1off = offset; + offset += DecFmtSize(tfm.c1fmt); + // We never get a normal, it's gone. + // But we do get a position, and it's always float3. + tfm.posfmt = DEC_FLOAT_3; + tfm.posoff = offset; + offset += DecFmtSize(tfm.posfmt); + // Update stride. + tfm.stride = offset; + return tfm; +} +#endif + +void VertexDecoder::Step_WeightsU8() const { - fmt = fmt; + float *wt = (float *)(decoded_ + decFmt.w0off); + const u8 *wdata = (const u8*)(ptr_); + for (int j = 0; j < nweights; j++) + wt[j] = (float)wdata[j] / 128.0f; +} + +void VertexDecoder::Step_WeightsU16() const +{ + float *wt = (float *)(decoded_ + decFmt.w0off); + const u16 *wdata = (const u16*)(ptr_); + for (int j = 0; j < nweights; j++) + wt[j] = (float)wdata[j] / 32768.0f; +} + +void VertexDecoder::Step_WeightsFloat() const +{ + float *wt = (float *)(decoded_ + decFmt.w0off); + const float *wdata = (const float*)(ptr_); + for (int j = 0; j < nweights; j++) + wt[j] = wdata[j]; +} + +void VertexDecoder::Step_TcU8() const +{ + float *uv = (float *)(decoded_ + decFmt.uvoff); + const u8 *uvdata = (const u8*)(ptr_ + tcoff); + for (int j = 0; j < 2; j++) + uv[j] = (float)uvdata[j] / 128.0f; +} + +void VertexDecoder::Step_TcU16() const +{ + float *uv = (float *)(decoded_ + decFmt.uvoff); + const u16 *uvdata = (const u16*)(ptr_ + tcoff); + uv[0] = (float)uvdata[0] / 32768.0f; + uv[1] = (float)uvdata[1] / 32768.0f; +} + +void VertexDecoder::Step_TcU16Through() const +{ + float *uv = (float *)(decoded_ + decFmt.uvoff); + const u16 *uvdata = (const u16*)(ptr_ + tcoff); + uv[0] = (float)uvdata[0] / (float)(gstate_c.curTextureWidth); + uv[1] = (float)uvdata[1] / (float)(gstate_c.curTextureHeight); +} + +void VertexDecoder::Step_TcFloat() const +{ + float *uv = (float *)(decoded_ + decFmt.uvoff); + const float *uvdata = (const float*)(ptr_ + tcoff); + uv[0] = uvdata[0]; + uv[1] = uvdata[1]; +} + +void VertexDecoder::Step_TcFloatThrough() const +{ + float *uv = (float *)(decoded_ + decFmt.uvoff); + const float *uvdata = (const float*)(ptr_ + tcoff); + uv[0] = uvdata[0] / (float)(gstate_c.curTextureWidth); + uv[1] = uvdata[1] / (float)(gstate_c.curTextureHeight); +} + +void VertexDecoder::Step_Color565() const +{ + u8 *c = decoded_ + decFmt.c0off; + u16 cdata = *(u16*)(ptr_ + coloff); + c[0] = Convert5To8(cdata & 0x1f); + c[1] = Convert6To8((cdata>>5) & 0x3f); + c[2] = Convert5To8((cdata>>11) & 0x1f); + c[3] = 1.0f; +} + +void VertexDecoder::Step_Color5551() const +{ + u8 *c = decoded_ + decFmt.c0off; + u16 cdata = *(u16*)(ptr_ + coloff); + c[0] = Convert5To8(cdata & 0x1f); + c[1] = Convert5To8((cdata>>5) & 0x1f); + c[2] = Convert5To8((cdata>>10) & 0x1f); + c[3] = (cdata>>15) ? 255 : 0; +} + +void VertexDecoder::Step_Color4444() const +{ + u8 *c = decoded_ + decFmt.c0off; + u16 cdata = *(u16*)(ptr_ + coloff); + for (int j = 0; j < 4; j++) + c[j] = Convert4To8((cdata >> (j * 4)) & 0xF); +} + +void VertexDecoder::Step_Color8888() const +{ + u8 *c = decoded_ + decFmt.c0off; + // TODO: speedup + const u8 *cdata = (const u8*)(ptr_ + coloff); + for (int j = 0; j < 4; j++) + c[j] = cdata[j]; +} + +void VertexDecoder::Step_Color565Morph() const +{ + float col[3] = {0}; + for (int n = 0; n < morphcount; n++) + { + float w = gstate_c.morphWeights[n]; + u16 cdata = *(u16*)(ptr_ + onesize_*n + coloff); + col[0] += w * (cdata & 0x1f) / 31.f; + col[1] += w * ((cdata>>5) & 0x3f) / 63.f; + col[2] += w * ((cdata>>11) & 0x1f) / 31.f; + } + u8 *c = decoded_ + decFmt.c0off; + for (int i = 0; i < 3; i++) { + c[i] = (u8)(col[i] * 255.0f); + } + c[3] = 255; +} + +void VertexDecoder::Step_Color5551Morph() const +{ + float col[4] = {0}; + for (int n = 0; n < morphcount; n++) + { + float w = gstate_c.morphWeights[n]; + u16 cdata = *(u16*)(ptr_ + onesize_*n + coloff); + col[0] += w * (cdata & 0x1f) / 31.f; + col[1] += w * ((cdata>>5) & 0x1f) / 31.f; + col[2] += w * ((cdata>>10) & 0x1f) / 31.f; + col[3] += w * ((cdata>>15) ? 1.0f : 0.0f); + } + u8 *c = decoded_ + decFmt.c0off; + for (int i = 0; i < 4; i++) { + c[i] = (u8)(col[i] * 255.0f); + } +} + +void VertexDecoder::Step_Color4444Morph() const +{ + float col[4] = {0}; + for (int n = 0; n < morphcount; n++) + { + float w = gstate_c.morphWeights[n]; + u16 cdata = *(u16*)(ptr_ + onesize_*n + coloff); + for (int j = 0; j < 4; j++) + col[j] += w * ((cdata >> (j * 4)) & 0xF) / 15.f; + } + u8 *c = decoded_ + decFmt.c0off; + for (int i = 0; i < 4; i++) { + c[i] = (u8)(col[i] * 255.0f); + } +} + +void VertexDecoder::Step_Color8888Morph() const +{ + float col[4] = {0}; + for (int n = 0; n < morphcount; n++) + { + float w = gstate_c.morphWeights[n]; + const u8 *cdata = (const u8*)(ptr_ + onesize_*n + coloff); + for (int j = 0; j < 4; j++) + col[j] += w * cdata[j]; + } + u8 *c = decoded_ + decFmt.c0off; + for (int i = 0; i < 4; i++) { + c[i] = (u8)(col[i]); + } +} + +void VertexDecoder::Step_NormalS8() const +{ + float *normal = (float *)(decoded_ + decFmt.nrmoff); + float multiplier = 1.0f; + if (gstate.reversenormals & 0xFFFFFF) + multiplier = -multiplier; + const s8 *sv = (const s8*)(ptr_ + nrmoff); + for (int j = 0; j < 3; j++) + normal[j] = (sv[j] / 127.0f) * multiplier; +} + +void VertexDecoder::Step_NormalS16() const +{ + float *normal = (float *)(decoded_ + decFmt.nrmoff); + float multiplier = 1.0f; + if (gstate.reversenormals & 0xFFFFFF) + multiplier = -multiplier; + const short *sv = (const short*)(ptr_ + nrmoff); + for (int j = 0; j < 3; j++) + normal[j] = (sv[j] / 32767.0f) * multiplier; +} + +void VertexDecoder::Step_NormalFloat() const +{ + float *normal = (float *)(decoded_ + decFmt.nrmoff); + float multiplier = 1.0f; + if (gstate.reversenormals & 0xFFFFFF) + multiplier = -multiplier; + const float *fv = (const float*)(ptr_ + nrmoff); + for (int j = 0; j < 3; j++) + normal[j] = fv[j] * multiplier; +} + +void VertexDecoder::Step_NormalS8Morph() const +{ + float *normal = (float *)(decoded_ + decFmt.nrmoff); + memset(normal, 0, sizeof(float)*3); + for (int n = 0; n < morphcount; n++) + { + float multiplier = gstate_c.morphWeights[n]; + if (gstate.reversenormals & 0xFFFFFF) { + multiplier = -multiplier; + } + const s8 *sv = (const s8*)(ptr_ + onesize_*n + nrmoff); + for (int j = 0; j < 3; j++) + normal[j] += (sv[j]/32767.0f) * multiplier; + } +} + +void VertexDecoder::Step_NormalS16Morph() const +{ + float *normal = (float *)(decoded_ + decFmt.nrmoff); + memset(normal, 0, sizeof(float)*3); + for (int n = 0; n < morphcount; n++) + { + float multiplier = gstate_c.morphWeights[n]; + if (gstate.reversenormals & 0xFFFFFF) { + multiplier = -multiplier; + } + const float *fv = (const float*)(ptr_ + onesize_*n + nrmoff); + for (int j = 0; j < 3; j++) + normal[j] += fv[j] * multiplier; + } +} + +void VertexDecoder::Step_NormalFloatMorph() const +{ + float *normal = (float *)(decoded_ + decFmt.nrmoff); + memset(normal, 0, sizeof(float)*3); + for (int n = 0; n < morphcount; n++) + { + float multiplier = gstate_c.morphWeights[n]; + if (gstate.reversenormals & 0xFFFFFF) { + multiplier = -multiplier; + } + const float *fv = (const float*)(ptr_ + onesize_*n + nrmoff); + for (int j = 0; j < 3; j++) + normal[j] += fv[j] * multiplier; + } +} + +void VertexDecoder::Step_PosS8() const +{ + float *v = (float *)(decoded_ + decFmt.posoff); + float multiplier = 1.0f / 127.0f; + const s8 *sv = (const s8*)(ptr_ + posoff); + for (int j = 0; j < 3; j++) + v[j] = sv[j] * multiplier; +} + +void VertexDecoder::Step_PosS16() const +{ + float *v = (float *)(decoded_ + decFmt.posoff); + float multiplier = 1.0f / 32767.0f; + const short *sv = (const short*)(ptr_ + posoff); + for (int j = 0; j < 3; j++) + v[j] = sv[j] * multiplier; +} + +void VertexDecoder::Step_PosFloat() const +{ + float *v = (float *)(decoded_ + decFmt.posoff); + const float *fv = (const float*)(ptr_ + posoff); + for (int j = 0; j < 3; j++) + v[j] = fv[j]; +} + +void VertexDecoder::Step_PosS8Through() const +{ + float *v = (float *)(decoded_ + decFmt.posoff); + const s8 *sv = (const s8*)(ptr_ + posoff); + for (int j = 0; j < 3; j++) + v[j] = sv[j]; +} + +void VertexDecoder::Step_PosS16Through() const +{ + float *v = (float *)(decoded_ + decFmt.posoff); + const short *sv = (const short*)(ptr_ + posoff); + for (int j = 0; j < 3; j++) + v[j] = sv[j]; +} + +void VertexDecoder::Step_PosFloatThrough() const +{ + float *v = (float *)(decoded_ + decFmt.posoff); + const float *fv = (const float*)(ptr_ + posoff); + for (int j = 0; j < 3; j++) + v[j] = fv[j]; +} + +void VertexDecoder::Step_PosS8Morph() const +{ + float *v = (float *)(decoded_ + decFmt.posoff); + memset(v, 0, sizeof(float) * 3); + for (int n = 0; n < morphcount; n++) { + const s8 *sv = (const s8*)(ptr_ + onesize_*n + posoff); + for (int j = 0; j < 3; j++) + v[j] += (sv[j] / 127.f) * gstate_c.morphWeights[n]; + } +} + +void VertexDecoder::Step_PosS16Morph() const +{ + float *v = (float *)(decoded_ + decFmt.posoff); + memset(v, 0, sizeof(float) * 3); + for (int n = 0; n < morphcount; n++) { + float multiplier = 1.0f / 32767.0f; + const short *sv = (const short*)(ptr_ + onesize_*n + posoff); + for (int j = 0; j < 3; j++) + v[j] += (sv[j] * multiplier) * gstate_c.morphWeights[n]; + } +} + +void VertexDecoder::Step_PosFloatMorph() const +{ + float *v = (float *)(decoded_ + decFmt.posoff); + memset(v, 0, sizeof(float) * 3); + for (int n = 0; n < morphcount; n++) { + const float *fv = (const float*)(ptr_ + onesize_*n + posoff); + for (int j = 0; j < 3; j++) + v[j] += fv[j] * gstate_c.morphWeights[n]; + } +} + +const StepFunction wtstep[4] = { + 0, + &VertexDecoder::Step_WeightsU8, + &VertexDecoder::Step_WeightsU16, + &VertexDecoder::Step_WeightsFloat, +}; + +const StepFunction tcstep[4] = { + 0, + &VertexDecoder::Step_TcU8, + &VertexDecoder::Step_TcU16, + &VertexDecoder::Step_TcFloat, +}; + +const StepFunction tcstep_through[4] = { + 0, + &VertexDecoder::Step_TcU8, + &VertexDecoder::Step_TcU16Through, + &VertexDecoder::Step_TcFloatThrough, +}; + +// TODO: Tc Morph + +const StepFunction colstep[8] = { + 0, 0, 0, 0, + &VertexDecoder::Step_Color565, + &VertexDecoder::Step_Color5551, + &VertexDecoder::Step_Color4444, + &VertexDecoder::Step_Color8888, +}; + +const StepFunction colstep_morph[8] = { + 0, 0, 0, 0, + &VertexDecoder::Step_Color565Morph, + &VertexDecoder::Step_Color5551Morph, + &VertexDecoder::Step_Color4444Morph, + &VertexDecoder::Step_Color8888Morph, +}; + +const StepFunction nrmstep[4] = { + 0, + &VertexDecoder::Step_NormalS8, + &VertexDecoder::Step_NormalS16, + &VertexDecoder::Step_NormalFloat, +}; + +const StepFunction nrmstep_morph[4] = { + 0, + &VertexDecoder::Step_NormalS8Morph, + &VertexDecoder::Step_NormalS16Morph, + &VertexDecoder::Step_NormalFloatMorph, +}; + +const StepFunction posstep[4] = { + 0, + &VertexDecoder::Step_PosS8, + &VertexDecoder::Step_PosS16, + &VertexDecoder::Step_PosFloat, +}; + +const StepFunction posstep_morph[4] = { + 0, + &VertexDecoder::Step_PosS8Morph, + &VertexDecoder::Step_PosS16Morph, + &VertexDecoder::Step_PosFloatMorph, +}; + +const StepFunction posstep_through[4] = { + 0, + &VertexDecoder::Step_PosS8Through, + &VertexDecoder::Step_PosS16Through, + &VertexDecoder::Step_PosFloatThrough, +}; + + +void VertexDecoder::SetVertexType(u32 fmt) { + fmt_ = fmt; throughmode = (fmt & GE_VTYPE_THROUGH) != 0; + numSteps_ = 0; int biggest = 0; size = 0; - tc = fmt & 0x3; - col = (fmt >> 2) & 0x7; - nrm = (fmt >> 5) & 0x3; - pos = (fmt >> 7) & 0x3; + tc = fmt & 0x3; + col = (fmt >> 2) & 0x7; + nrm = (fmt >> 5) & 0x3; + pos = (fmt >> 7) & 0x3; weighttype = (fmt >> 9) & 0x3; - idx = (fmt >> 11) & 0x3; + idx = (fmt >> 11) & 0x3; morphcount = ((fmt >> 18) & 0x7)+1; - nweights = ((fmt >> 14) & 0x7)+1; + nweights = ((fmt >> 14) & 0x7)+1; + + int decOff = 0; + memset(&decFmt, 0, sizeof(decFmt)); DEBUG_LOG(G3D,"VTYPE: THRU=%i TC=%i COL=%i POS=%i NRM=%i WT=%i NW=%i IDX=%i MC=%i", (int)throughmode, tc,col,pos,nrm,weighttype,nweights,idx,morphcount); - if (weighttype) - { + if (weighttype) { // && nweights? //size = align(size, wtalign[weighttype]); unnecessary size += wtsize[weighttype] * nweights; if (wtalign[weighttype] > biggest) biggest = wtalign[weighttype]; + + steps_[numSteps_++] = wtstep[weighttype]; + + if (nweights < 5) { + decFmt.w0off = decOff; + decFmt.w0fmt = DEC_FLOAT_1 + nweights - 1; + } else { + decFmt.w0off = decOff; + decFmt.w0fmt = DEC_FLOAT_4; + decFmt.w1off = decOff + 4 * 4; + decFmt.w1fmt = DEC_FLOAT_1 + nweights - 5; + } + decOff += nweights * 4; } - if (tc) - { + if (tc) { size = align(size, tcalign[tc]); tcoff = size; size += tcsize[tc]; if (tcalign[tc] > biggest) biggest = tcalign[tc]; + + steps_[numSteps_++] = throughmode ? tcstep_through[tc] : tcstep[tc]; + + // All UV decode to DEC_FLOAT2 currently. + decFmt.uvfmt = DEC_FLOAT_2; + decFmt.uvoff = decOff; + decOff += DecFmtSize(decFmt.uvfmt); } - if (col) - { + if (col) { size = align(size, colalign[col]); coloff = size; size += colsize[col]; if (colalign[col] > biggest) biggest = colalign[col]; - } - else - { + + steps_[numSteps_++] = morphcount == 1 ? colstep[col] : colstep_morph[col]; + + // All color formats decode to DEC_U8_4 currently. + // They can become floats later during transform though. + decFmt.c0fmt = DEC_U8_4; + decFmt.c0off = decOff; + decOff += DecFmtSize(decFmt.c0fmt); + } else { coloff = 0; } - if (nrm) - { + if (nrm) { size = align(size, nrmalign[nrm]); nrmoff = size; size += nrmsize[nrm]; if (nrmalign[nrm] > biggest) biggest = nrmalign[nrm]; + + steps_[numSteps_++] = morphcount == 1 ? nrmstep[nrm] : nrmstep_morph[nrm]; + + // The normal formats match the gl formats perfectly, let's use 'em. + switch (nrm) { + case GE_VTYPE_NRM_8BIT >> GE_VTYPE_NRM_SHIFT: decFmt.nrmfmt = DEC_S8_3; break; + case GE_VTYPE_NRM_16BIT >> GE_VTYPE_NRM_SHIFT: decFmt.nrmfmt = DEC_S16_3; break; + case GE_VTYPE_NRM_FLOAT >> GE_VTYPE_NRM_SHIFT: decFmt.nrmfmt = DEC_FLOAT_3; break; + } + + // Actually, temporarily let's not. + decFmt.nrmfmt = DEC_FLOAT_3; + decFmt.nrmoff = decOff; + decOff += DecFmtSize(decFmt.nrmfmt); } //if (pos) - there's always a position @@ -107,7 +618,26 @@ void VertexDecoder::SetVertexType(u32 fmt) size += possize[pos]; if (posalign[pos] > biggest) biggest = posalign[pos]; + + if (throughmode) { + steps_[numSteps_++] = posstep_through[pos]; + decFmt.posfmt = DEC_FLOAT_3; + } else { + steps_[numSteps_++] = morphcount == 1 ? posstep[pos] : posstep_morph[pos]; + + // The non-through-mode position formats match the gl formats perfectly, let's use 'em. + switch (pos) { + case GE_VTYPE_POS_8BIT >> GE_VTYPE_POS_SHIFT: decFmt.posfmt = DEC_S8_3; break; + case GE_VTYPE_POS_16BIT >> GE_VTYPE_POS_SHIFT: decFmt.posfmt = DEC_S16_3; break; + case GE_VTYPE_POS_FLOAT >> GE_VTYPE_POS_SHIFT: decFmt.posfmt = DEC_FLOAT_3; break; + } + // Actually, temporarily let's not. + decFmt.posfmt = DEC_FLOAT_3; + } + decFmt.posoff = decOff; + decOff += DecFmtSize(decFmt.posfmt); } + decFmt.stride = decOff; size = align(size, biggest); onesize_ = size; @@ -115,15 +645,10 @@ void VertexDecoder::SetVertexType(u32 fmt) DEBUG_LOG(G3D,"SVT : size = %i, aligned to biggest %i", size, biggest); } -void VertexDecoder::DecodeVerts(DecodedVertex *decoded, const void *verts, const void *inds, int prim, int count, int *indexLowerBound, int *indexUpperBound) const +void VertexDecoder::DecodeVerts(u8 *decodedptr, const void *verts, const void *inds, int prim, int count, int *indexLowerBound, int *indexUpperBound) const { - // TODO: Remove - if (morphcount == 1) - gstate_c.morphWeights[0] = 1.0f; - - char *ptr = (char *)verts; - // Find index bounds. Could cache this in display lists. + // Also, this could be greatly sped up with SSE2, although rarely a bottleneck. int lowerBound = 0x7FFFFFFF; int upperBound = 0; if (idx == (GE_VTYPE_IDX_8BIT >> GE_VTYPE_IDX_SHIFT)) { @@ -149,248 +674,15 @@ void VertexDecoder::DecodeVerts(DecodedVertex *decoded, const void *verts, const *indexLowerBound = lowerBound; *indexUpperBound = upperBound; - // Decode the vertices within the found bounds, once each (unlike the previous way..) + // Decode the vertices within the found bounds, once each + decoded_ = decodedptr; // + lowerBound * decFmt.stride; + ptr_ = (const u8*)verts + lowerBound * size; for (int index = lowerBound; index <= upperBound; index++) { - ptr = (char*)verts + (index * size); - - // TODO: Should weights be morphed? - float *wt = decoded[index].weights; - switch (weighttype) - { - case GE_VTYPE_WEIGHT_NONE >> 9: - break; - - case GE_VTYPE_WEIGHT_8BIT >> 9: - { - const u8 *wdata = (const u8*)(ptr); - for (int j = 0; j < nweights; j++) - wt[j] = (float)wdata[j] / 128.0f; - } - break; - - case GE_VTYPE_WEIGHT_16BIT >> 9: - { - const u16 *wdata = (const u16*)(ptr); - for (int j = 0; j < nweights; j++) - wt[j] = (float)wdata[j] / 32768.0f; - } - break; - - case GE_VTYPE_WEIGHT_FLOAT >> 9: - { - const float *wdata = (const float*)(ptr+0); - for (int j = 0; j < nweights; j++) - wt[j] = wdata[j]; - } - break; - } - - // TODO: Not morphing UV yet - float *uv = decoded[index].uv; - switch (tc) - { - case GE_VTYPE_TC_NONE: - uv[0] = 0.0f; - uv[1] = 0.0f; - break; - - case GE_VTYPE_TC_8BIT: - { - const u8 *uvdata = (const u8*)(ptr + tcoff); - for (int j = 0; j < 2; j++) - uv[j] = (float)uvdata[j] / 128.0f; - break; - } - - case GE_VTYPE_TC_16BIT: - { - const u16 *uvdata = (const u16*)(ptr + tcoff); - if (throughmode) - { - uv[0] = (float)uvdata[0] / (float)(gstate_c.curTextureWidth); - uv[1] = (float)uvdata[1] / (float)(gstate_c.curTextureHeight); - } - else - { - uv[0] = (float)uvdata[0] / 32768.0f; - uv[1] = (float)uvdata[1] / 32768.0f; - } - } - break; - - case GE_VTYPE_TC_FLOAT: - { - const float *uvdata = (const float*)(ptr + tcoff); - if (throughmode) { - uv[0] = uvdata[0] / (float)(gstate_c.curTextureWidth); - uv[1] = uvdata[1] / (float)(gstate_c.curTextureHeight); - } else { - uv[0] = uvdata[0]; - uv[1] = uvdata[1]; - } - } - break; - } - - // TODO: Not morphing color yet - u8 *c = decoded[index].color; - switch (col) - { - case GE_VTYPE_COL_4444 >> 2: - { - u16 cdata = *(u16*)(ptr + coloff); - for (int j = 0; j < 4; j++) - c[j] = Convert4To8((cdata >> (j * 4)) & 0xF); - } - break; - - case GE_VTYPE_COL_565 >> 2: - { - u16 cdata = *(u16*)(ptr + coloff); - c[0] = Convert5To8(cdata & 0x1f); - c[1] = Convert6To8((cdata>>5) & 0x3f); - c[2] = Convert5To8((cdata>>11) & 0x1f); - c[3] = 1.0f; - } - break; - - case GE_VTYPE_COL_5551 >> 2: - { - u16 cdata = *(u16*)(ptr + coloff); - c[0] = Convert5To8(cdata & 0x1f); - c[1] = Convert5To8((cdata>>5) & 0x1f); - c[2] = Convert5To8((cdata>>10) & 0x1f); - c[3] = (cdata>>15) ? 255 : 0; - } - break; - - case GE_VTYPE_COL_8888 >> 2: - { - // TODO: speedup - u8 *cdata = (u8*)(ptr + coloff); - for (int j = 0; j < 4; j++) - c[j] = cdata[j]; - } - break; - - default: - c[0] = 255; - c[1] = 255; - c[2] = 255; - c[3] = 255; - break; - } - - float *normal = decoded[index].normal; - memset(normal, 0, sizeof(float)*3); - for (int n = 0; n < morphcount; n++) - { - float multiplier = gstate_c.morphWeights[n]; - if (gstate.reversenormals & 0xFFFFFF) { - multiplier = -multiplier; - } - switch (nrm) - { - case GE_VTYPE_NRM_8BIT: - { - const s8 *sv = (const s8*)(ptr + onesize_*n + nrmoff); - for (int j = 0; j < 3; j++) - normal[j] += (sv[j]/127.0f) * multiplier; - } - break; - - case GE_VTYPE_NRM_FLOAT >> 5: - { - const float *fv = (const float*)(ptr + onesize_*n + nrmoff); - for (int j = 0; j < 3; j++) - normal[j] += fv[j] * multiplier; - } - break; - - case GE_VTYPE_NRM_16BIT >> 5: - { - const short *sv = (const short*)(ptr + onesize_*n + nrmoff); - for (int j = 0; j < 3; j++) - normal[j] += (sv[j]/32767.0f) * multiplier; - } - break; - } - } - - float *v = decoded[index].pos; - - if (morphcount == 1) { - switch (pos) - { - case GE_VTYPE_POS_FLOAT >> 7: - { - const float *fv = (const float*)(ptr + posoff); - for (int j = 0; j < 3; j++) - v[j] = fv[j]; - } - break; - - case GE_VTYPE_POS_16BIT >> 7: - { - float multiplier = 1.0f / 32767.0f; - if (throughmode) multiplier = 1.0f; - const short *sv = (const short*)(ptr + posoff); - for (int j = 0; j < 3; j++) - v[j] = sv[j] * multiplier; - } - break; - - case GE_VTYPE_POS_8BIT >> 7: - { - const s8 *sv = (const s8*)(ptr + posoff); - for (int j = 0; j < 3; j++) - v[j] = sv[j] / 127.f; - } - break; - - default: - ERROR_LOG(G3D,"Unknown position format %i",pos); - break; - } - } else { - memset(v, 0, sizeof(float) * 3); - for (int n = 0; n < morphcount; n++) - { - switch (pos) - { - case GE_VTYPE_POS_FLOAT >> 7: - { - const float *fv = (const float*)(ptr + onesize_*n + posoff); - for (int j = 0; j < 3; j++) - v[j] += fv[j] * gstate_c.morphWeights[n]; - } - break; - - case GE_VTYPE_POS_16BIT >> 7: - { - float multiplier = 1.0f / 32767.0f; - if (throughmode) multiplier = 1.0f; - const short *sv = (const short*)(ptr + onesize_*n + posoff); - for (int j = 0; j < 3; j++) - v[j] += (sv[j] * multiplier) * gstate_c.morphWeights[n]; - } - break; - - case GE_VTYPE_POS_8BIT >> 7: - { - const s8 *sv = (const s8*)(ptr + onesize_*n + posoff); - for (int j = 0; j < 3; j++) - v[j] += (sv[j] / 127.f) * gstate_c.morphWeights[n]; - } - break; - - default: - ERROR_LOG(G3D,"Unknown position format %i",pos); - break; - } - } + for (int i = 0; i < numSteps_; i++) { + ((*this).*steps_[i])(); } + ptr_ += size; + decoded_ += decFmt.stride; } } - diff --git a/GPU/GLES/VertexDecoder.h b/GPU/GLES/VertexDecoder.h index 464ebf72a1..406b668428 100644 --- a/GPU/GLES/VertexDecoder.h +++ b/GPU/GLES/VertexDecoder.h @@ -21,48 +21,132 @@ #include "../Globals.h" #include "base/basictypes.h" -struct DecodedVertex -{ - float pos[3]; // in case of morph, preblend during decode - float normal[3]; // in case of morph, preblend during decode - float uv[2]; // scaled by uscale, vscale, if there - u8 color[4]; // unlit - float weights[8]; // ugh, expensive +// DecVtxFormat - vertex formats for PC +// Kind of like a D3D VertexDeclaration. +// Can write code to easily bind these using OpenGL, or read these manually. +// No morph support, that is taken care of by the VertexDecoder. + +enum { + DEC_NONE, + DEC_FLOAT_1, + DEC_FLOAT_2, + DEC_FLOAT_3, + DEC_FLOAT_4, + DEC_S8_3, + DEC_S16_3, + DEC_U8_4, }; +int DecFmtSize(u8 fmt); + +struct DecVtxFormat { + u8 w0fmt; u8 w0off; // first 4 weights + u8 w1fmt; u8 w1off; // second 4 weights + u8 uvfmt; u8 uvoff; + u8 c0fmt; u8 c0off; // First color + u8 c1fmt; u8 c1off; + u8 nrmfmt; u8 nrmoff; + u8 posfmt; u8 posoff; + short stride; +}; + +// This struct too. struct TransformedVertex { float x, y, z; // in case of morph, preblend during decode - float uv[2]; // scaled by uscale, vscale, if there + float u; float v; // scaled by uscale, vscale, if there float color0[4]; // prelit - float color1[4]; // prelit + float color1[3]; // prelit }; +DecVtxFormat GetTransformedVtxFormat(const DecVtxFormat &fmt); + +class VertexDecoder; + +typedef void (VertexDecoder::*StepFunction)() const; // Right now // - only contains computed information // - does decoding in nasty branchfilled loops // Future TODO +// - should be cached, not recreated every time +// - will compile into list of called functions // - will compile into lighting fast specialized x86 and ARM // - will not bother translating components that can be read directly -// by OpenGL ES. Will still have to translate 565 colors, and things +// by OpenGL ES. Will still have to translate 565 colors and things // like that. DecodedVertex will not be a fixed struct. Will have to // do morphing here. -// -// We want 100% perf on 1Ghz even in vertex complex games! class VertexDecoder { public: VertexDecoder() : coloff(0), nrmoff(0), posoff(0) {} ~VertexDecoder() {} + void SetVertexType(u32 vtype); - void DecodeVerts(DecodedVertex *decoded, const void *verts, const void *inds, int prim, int count, int *indexLowerBound, int *indexUpperBound) const; + u32 VertexType() const { return fmt_; } + const DecVtxFormat &GetDecVtxFmt() { return decFmt; } + + void DecodeVerts(u8 *decoded, const void *verts, const void *inds, int prim, int count, int *indexLowerBound, int *indexUpperBound) const; bool hasColor() const { return col != 0; } int VertexSize() const { return size; } -private: - u32 fmt; + void Step_WeightsU8() const; + void Step_WeightsU16() const; + void Step_WeightsFloat() const; + + void Step_TcU8() const; + void Step_TcU16() const; + void Step_TcFloat() const; + void Step_TcU16Through() const; + void Step_TcFloatThrough() const; + + // TODO: tcmorph + + void Step_Color4444() const; + void Step_Color565() const; + void Step_Color5551() const; + void Step_Color8888() const; + + void Step_Color4444Morph() const; + void Step_Color565Morph() const; + void Step_Color5551Morph() const; + void Step_Color8888Morph() const; + + void Step_NormalS8() const; + void Step_NormalS16() const; + void Step_NormalFloat() const; + + void Step_NormalS8Morph() const; + void Step_NormalS16Morph() const; + void Step_NormalFloatMorph() const; + + void Step_PosS8() const; + void Step_PosS16() const; + void Step_PosFloat() const; + + void Step_PosS8Morph() const; + void Step_PosS16Morph() const; + void Step_PosFloatMorph() const; + + void Step_PosS8Through() const; + void Step_PosS16Through() const; + void Step_PosFloatThrough() const; + + + // Mutable decoder state + mutable u8 *decoded_; + mutable const u8 *ptr_; + + // "Immutable" state, set at startup + + // The decoding steps + StepFunction steps_[5]; + int numSteps_; + + u32 fmt_; + DecVtxFormat decFmt; + bool throughmode; int biggest; int size; @@ -84,6 +168,146 @@ private: int nweights; }; +// Reads decoded vertex formats in a convenient way. For software transform and debugging. +class VertexReader +{ +public: + VertexReader(u8 *base, const DecVtxFormat &decFmt) : base_(base), data_(base), decFmt_(decFmt) {} + + void ReadPos(float pos[3]) { + switch (decFmt_.posfmt) { + case DEC_FLOAT_3: + memcpy(pos, data_ + decFmt_.posoff, 12); + break; + case DEC_S16_3: + { + s16 *p = (s16 *)(data_ + decFmt_.posoff); + for (int i = 0; i < 3; i++) + pos[i] = p[i] / 32767.0f; + } + break; + case DEC_S8_3: + { + s8 *p = (s8 *)(data_ + decFmt_.posoff); + for (int i = 0; i < 3; i++) + pos[i] = p[i] / 127.0f; + } + break; + default: + ERROR_LOG(G3D, "Reader: Unsupported Pos Format"); + break; + } + } + + void ReadNrm(float nrm[3]) { + switch (decFmt_.nrmfmt) { + case DEC_FLOAT_3: + memcpy(nrm, data_ + decFmt_.nrmoff, 12); + break; + case DEC_S16_3: + { + s16 *p = (s16 *)(data_ + decFmt_.nrmoff); + for (int i = 0; i < 3; i++) + nrm[i] = p[i] / 32767.0f; + } + break; + case DEC_S8_3: + { + s8 *p = (s8 *)(data_ + decFmt_.nrmoff); + for (int i = 0; i < 3; i++) + nrm[i] = p[i] / 127.0f; + } + break; + default: + ERROR_LOG(G3D, "Reader: Unsupported Nrm Format"); + break; + } + } + + void ReadUV(float uv[2]) { + switch (decFmt_.uvfmt) { + case DEC_FLOAT_2: + memcpy(uv, data_ + decFmt_.uvoff, 8); break; + default: + ERROR_LOG(G3D, "Reader: Unsupported UV Format"); + break; + } + } + + void ReadColor0(float color[4]) { + switch (decFmt_.c0fmt) { + case DEC_U8_4: + { + u8 *p = (u8 *)(data_ + decFmt_.c0off); + for (int i = 0; i < 4; i++) + color[i] = p[i] / 255.0f; + } + break; + case DEC_FLOAT_4: + memcpy(color, data_ + decFmt_.c0off, 16); break; + default: + ERROR_LOG(G3D, "Reader: Unsupported C0 Format"); + break; + } + } + + void ReadColor1(float color[3]) { + switch (decFmt_.c1fmt) { + case DEC_U8_4: + { + u8 *p = (u8 *)(data_ + decFmt_.c1off); + for (int i = 0; i < 3; i++) + color[i] = p[i] / 255.0f; + } + break; + case DEC_FLOAT_4: + memcpy(color, data_ + decFmt_.c1off, 12); break; + default: + ERROR_LOG(G3D, "Reader: Unsupported C1 Format"); + break; + } + } + + void ReadWeights(float weights[8]) { + switch (decFmt_.w0fmt) { + case DEC_FLOAT_1: memcpy(weights, data_ + decFmt_.w0off, 4); break; + case DEC_FLOAT_2: memcpy(weights, data_ + decFmt_.w0off, 8); break; + case DEC_FLOAT_3: memcpy(weights, data_ + decFmt_.w0off, 12); break; + case DEC_FLOAT_4: memcpy(weights, data_ + decFmt_.w0off, 16); break; + default: + ERROR_LOG(G3D, "Reader: Unsupported W0 Format"); + break; + } + switch (decFmt_.w1fmt) { + case 0: + // It's fine for there to be w0 weights but not w1. + break; + case DEC_FLOAT_1: memcpy(weights + 4, data_ + decFmt_.w1off, 4); break; + case DEC_FLOAT_2: memcpy(weights + 4, data_ + decFmt_.w1off, 8); break; + case DEC_FLOAT_3: memcpy(weights + 4, data_ + decFmt_.w1off, 12); break; + case DEC_FLOAT_4: memcpy(weights + 4, data_ + decFmt_.w1off, 16); break; + default: + ERROR_LOG(G3D, "Reader: Unsupported W1 Format"); + break; + } + } + + bool hasColor0() const { return decFmt_.c0fmt != 0; } + bool hasNormal() const { return decFmt_.nrmfmt != 0; } + bool hasUV() const { return decFmt_.uvfmt != 0; } + + void Goto(int index) { + data_ = base_ + index * decFmt_.stride; + } + +private: + u8 *base_; + u8 *data_; + DecVtxFormat decFmt_; + int vtype_; +}; + // Debugging utilities -void PrintDecodedVertex(const DecodedVertex &vtx, u32 vtype); +void PrintDecodedVertex(VertexReader &vtx); + diff --git a/GPU/GLES/VertexShaderGenerator.cpp b/GPU/GLES/VertexShaderGenerator.cpp index d573942aa4..319802a9e6 100644 --- a/GPU/GLES/VertexShaderGenerator.cpp +++ b/GPU/GLES/VertexShaderGenerator.cpp @@ -15,14 +15,14 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. -// TODO: We should transition from doing the transform in software, as seen in TransformPipeline.cpp, -// into doing the transform in the vertex shader - except for Rectangles, there we really need to do -// the transforms ourselves. - #include +#if defined(_WIN32) && defined(_DEBUG) +#include +#endif #include "../ge_constants.h" #include "../GPUState.h" +#include "../../Core/Config.h" #include "VertexShaderGenerator.h" @@ -36,81 +36,358 @@ static char buffer[16384]; -#define WRITE(x, ...) p+=sprintf(p, x "\n" __VA_ARGS__) +#define WRITE p+=sprintf + +bool CanUseHardwareTransform(int prim) +{ + if (!g_Config.bHardwareTransform) + return false; + return !gstate.isModeThrough() && prim != GE_PRIM_RECTANGLES; +} // prim so we can special case for RECTANGLES :( void ComputeVertexShaderID(VertexShaderID *id, int prim) { int doTexture = (gstate.textureMapEnable & 1) && !(gstate.clearmode & 1); + bool hasColor = (gstate.vertType & GE_VTYPE_COL_MASK) != 0; + bool hasNormal = (gstate.vertType & GE_VTYPE_NRM_MASK) != 0; + bool hasBones = (gstate.vertType & GE_VTYPE_WEIGHT_MASK) != 0; + + int shadeLight0 = gstate.getUVGenMode() == 2 ? gstate.getUVLS0() : -1; + int shadeLight1 = gstate.getUVGenMode() == 2 ? gstate.getUVLS1() : -1; + memset(id->d, 0, sizeof(id->d)); id->d[0] = gstate.lmode & 1; id->d[0] |= ((int)gstate.isModeThrough()) << 1; id->d[0] |= ((int)gstate.isFogEnabled()) << 2; id->d[0] |= doTexture << 3; + id->d[0] |= (hasColor & 1) << 4; + if (CanUseHardwareTransform(prim)) { + id->d[0] |= 1 << 8; + id->d[0] |= (hasNormal & 1) << 9; + id->d[0] |= (hasBones & 1) << 10; - // Bits that we will need: - // lightenable * 4 - // lighttype * 4 - // lightcomp * 4 - // uv gen: - // mapping type - // texshade light choices (ONLY IF uv mapping type is shade) + // UV generation mode + id->d[0] |= gstate.getUVGenMode() << 16; + + // The next bits are used differently depending on UVgen mode + if (gstate.getUVGenMode() == 1) { + id->d[0] |= gstate.getUVProjMode() << 18; + } else if (gstate.getUVGenMode() == 2) { + id->d[0] |= gstate.getUVLS0() << 18; + id->d[0] |= gstate.getUVLS1() << 20; + } + + // Bones + id->d[0] |= (gstate.getNumBoneWeights() - 1) << 22; + + // Light bits + for (int i = 0; i < 4; i++) { + id->d[1] |= (gstate.ltype[i] & 3) << (i * 4); + id->d[1] |= ((gstate.ltype[i] >> 8) & 3) << (i * 4 + 2); + } + id->d[1] |= (gstate.materialupdate & 7) << 16; + id->d[1] |= (gstate.lightingEnable & 1) << 19; + for (int i = 0; i < 4; i++) { + id->d[1] |= (gstate.lightEnable[i] & 1) << (20 + i); + } + } } -void WriteLight(char *p, int l) { - // TODO -} +const char *boneWeightAttrDecl[8] = { + "attribute float a_weight0123;\n", + "attribute vec2 a_weight0123;\n", + "attribute vec3 a_weight0123;\n", + "attribute vec4 a_weight0123;\n", + "attribute vec4 a_weight0123;\nattribute float a_weight4567;\n", + "attribute vec4 a_weight0123;\nattribute vec2 a_weight4567;\n", + "attribute vec4 a_weight0123;\nattribute vec3 a_weight4567;\n", + "attribute vec4 a_weight0123;\nattribute vec4 a_weight4567;\n", +}; -char *GenerateVertexShader() +const char *boneWeightAttr[8] = { + "a_weight0123.x", + "a_weight0123.y", + "a_weight0123.z", + "a_weight0123.w", + "a_weight4567.x", + "a_weight4567.y", + "a_weight4567.z", + "a_weight4567.w", +}; + +enum DoLightComputation { + LIGHT_OFF, + LIGHT_DOTONLY, + LIGHT_FULL, +}; + +char *GenerateVertexShader(int prim) { char *p = buffer; #if defined(USING_GLES2) - WRITE("precision highp float;"); + WRITE(p, "precision highp float;\n"); #elif !defined(FORCE_OPENGL_2_0) - WRITE("#version 130"); + WRITE(p, "#version 130\n"); #endif int lmode = gstate.lmode & 1; - int doTexture = (gstate.textureMapEnable & 1) && !(gstate.clearmode & 1); - WRITE("attribute vec3 a_position;"); - if (doTexture) - WRITE("attribute vec2 a_texcoord;"); - WRITE("attribute vec4 a_color0;"); - if (lmode) - WRITE("attribute vec4 a_color1;"); + bool hwXForm = CanUseHardwareTransform(prim); + bool hasColor = (gstate.vertType & GE_VTYPE_COL_MASK) != 0 || !hwXForm; + bool hasNormal = (gstate.vertType & GE_VTYPE_NRM_MASK) != 0 && hwXForm; + + DoLightComputation doLight[4] = {LIGHT_OFF, LIGHT_OFF, LIGHT_OFF, LIGHT_OFF}; + if (hwXForm) { + int shadeLight0 = gstate.getUVGenMode() == 2 ? gstate.getUVLS0() : -1; + int shadeLight1 = gstate.getUVGenMode() == 2 ? gstate.getUVLS1() : -1; + for (int i = 0; i < 4; i++) { + if (!hasNormal) + continue; + if (i == shadeLight0 || i == shadeLight1) + doLight[i] = LIGHT_DOTONLY; + if ((gstate.lightingEnable & 1) && (gstate.lightEnable[i] & 1)) + doLight[i] = LIGHT_FULL; + } + } + + if ((gstate.vertType & GE_VTYPE_WEIGHT_MASK) != GE_VTYPE_WEIGHT_NONE) { + WRITE(p, "%s", boneWeightAttrDecl[gstate.getNumBoneWeights() - 1]); + } + + WRITE(p, "attribute vec3 a_position;\n"); + if (doTexture) WRITE(p, "attribute vec2 a_texcoord;\n"); + if (hasColor) { + WRITE(p, "attribute vec4 a_color0;\n"); + if (lmode && !hwXForm) // only software transform supplies color1 as vertex data + WRITE(p, "attribute vec3 a_color1;\n"); + } + + if (hwXForm && hasNormal) + WRITE(p, "attribute vec3 a_normal;\n"); if (gstate.isModeThrough()) { - WRITE("uniform mat4 u_proj_through;"); + WRITE(p, "uniform mat4 u_proj_through;\n"); } else { - WRITE("uniform mat4 u_proj;"); + WRITE(p, "uniform mat4 u_proj;\n"); // Add all the uniforms we'll need to transform properly. } - WRITE("varying vec4 v_color0;"); - if (lmode) - WRITE("varying vec4 v_color1;"); - if (doTexture) - WRITE("varying vec2 v_texcoord;"); - if (gstate.isFogEnabled()) - WRITE("varying float v_depth;"); - WRITE("void main() {"); - WRITE(" v_color0 = a_color0;"); - if (lmode) - WRITE(" v_color1 = a_color1;"); - if (doTexture) - WRITE(" v_texcoord = a_texcoord;"); - if (gstate.isModeThrough()) { - WRITE(" gl_Position = u_proj_through * vec4(a_position, 1.0);"); + if (hwXForm || !hasColor) + WRITE(p, "uniform vec4 u_matambientalpha;\n"); // matambient + matalpha + + if (hwXForm) { + // When transforming by hardware, we need a great deal more uniforms... + WRITE(p, "uniform mat4 u_world;\n"); + WRITE(p, "uniform mat4 u_view;\n"); + if (gstate.getUVGenMode() == 0) + WRITE(p, "uniform vec4 u_uvscaleoffset;\n"); + else if (gstate.getUVGenMode() == 1) + WRITE(p, "uniform mat4 u_texmtx;\n"); + if ((gstate.vertType & GE_VTYPE_WEIGHT_MASK) != GE_VTYPE_WEIGHT_NONE) { + int numBones = 1 + ((gstate.vertType & GE_VTYPE_WEIGHTCOUNT_MASK) >> GE_VTYPE_WEIGHTCOUNT_SHIFT); + for (int i = 0; i < numBones; i++) { + WRITE(p, "uniform mat4 u_bone%i;\n", i); + } + } + if (gstate.lightingEnable & 1) { + WRITE(p, "uniform vec4 u_ambient;\n"); + if ((gstate.materialupdate & 2) == 0) + WRITE(p, "uniform vec3 u_matdiffuse;\n"); + // if ((gstate.materialupdate & 4) == 0) + WRITE(p, "uniform vec4 u_matspecular;\n"); // Specular coef is contained in alpha + WRITE(p, "uniform vec3 u_matemissive;\n"); + } + for (int i = 0; i < 4; i++) { + if (doLight[i] != LIGHT_OFF) { + // These are needed for dot product only (for shade mapping) + WRITE(p, "uniform vec3 u_lightpos%i;\n", i); + WRITE(p, "uniform vec3 u_lightdir%i;\n", i); + WRITE(p, "uniform vec3 u_lightatt%i;\n", i); + } + if (doLight[i] == LIGHT_FULL) { + // These are needed for the full thing + WRITE(p, "uniform vec3 u_lightambient%i;\n", i); + WRITE(p, "uniform vec3 u_lightdiffuse%i;\n", i); + WRITE(p, "uniform vec3 u_lightspecular%i;\n", i); + } + } + } + + WRITE(p, "varying vec4 v_color0;\n"); + if (lmode) WRITE(p, "varying vec3 v_color1;\n"); + if (doTexture) WRITE(p, "varying vec2 v_texcoord;\n"); + if (gstate.isFogEnabled()) WRITE(p, "varying float v_depth;\n"); + WRITE(p, "void main() {\n"); + + if (!hwXForm) { + // Simple pass-through of vertex data to fragment shader + if (doTexture) + WRITE(p, " v_texcoord = a_texcoord;\n"); + if (hasColor) { + WRITE(p, " v_color0 = a_color0;\n"); + if (lmode) + WRITE(p, " v_color1 = a_color1;\n"); + } else { + WRITE(p, " v_color0 = u_matambientalpha;\n"); + if (lmode) + WRITE(p, " v_color1 = vec3(0.0, 0.0, 0.0);\n"); + } + if (gstate.isModeThrough()) { + WRITE(p, " gl_Position = u_proj_through * vec4(a_position, 1.0);\n"); + } else { + WRITE(p, " gl_Position = u_proj * vec4(a_position, 1.0);\n"); + } } else { - WRITE(" gl_Position = u_proj * vec4(a_position, 1.0);"); + // Step 1: World Transform / Skinning + if ((gstate.vertType & GE_VTYPE_WEIGHT_MASK) == GE_VTYPE_WEIGHT_NONE) { + // No skinning, just standard T&L. + WRITE(p, " vec3 worldpos = (u_world * vec4(a_position, 1.0)).xyz;\n"); + if (hasNormal) + WRITE(p, " vec3 worldnormal = (u_world * vec4(a_normal, 0.0)).xyz;\n"); + } else { + WRITE(p, " vec3 worldpos = vec3(0.0, 0.0, 0.0);\n"); + if (hasNormal) + WRITE(p, " vec3 worldnormal = vec3(0.0, 0.0, 0.0);\n"); + int numWeights = 1 + ((gstate.vertType & GE_VTYPE_WEIGHTCOUNT_MASK) >> GE_VTYPE_WEIGHTCOUNT_SHIFT); + for (int i = 0; i < numWeights; i++) { + const char *weightAttr = boneWeightAttr[i]; + // workaround for "cant do .x of scalar" issue + if (numWeights == 1 && i == 0) weightAttr = "a_weight0123"; + if (numWeights == 5 && i == 4) weightAttr = "a_weight4567"; + WRITE(p, " worldpos += %s * (u_bone%i * vec4(a_position, 1.0)).xyz;\n", weightAttr, i); + if (hasNormal) + WRITE(p, " worldnormal += %s * (u_bone%i * vec4(a_normal, 0.0)).xyz;\n", weightAttr, i); + } + // Finally, multiply by world matrix (yes, we have to). + WRITE(p, " worldpos = (u_world * vec4(worldpos, 1.0)).xyz;\n"); + if (hasNormal) + WRITE(p, " worldnormal = (u_world * vec4(worldnormal, 0.0)).xyz;\n"); + } + if (hasNormal) + WRITE(p, " worldnormal = normalize(worldnormal);\n"); + + // Step 2: Color/Lighting + if (hasColor) { + WRITE(p, " vec3 unlitColor = a_color0.rgb;\n"); + } else { + WRITE(p, " vec3 unlitColor = vec3(1.0, 1.0, 1.0);\n"); + } + // TODO: Declare variables for dots for shade mapping if needed. + + const char *ambient = (gstate.materialupdate & 1) ? "unlitColor" : "u_matambientalpha.rgb"; + const char *diffuse = (gstate.materialupdate & 2) ? "unlitColor" : "u_matdiffuse"; + const char *specular = (gstate.materialupdate & 4) ? "unlitColor" : "u_matspecular.rgb"; + + if (gstate.lightingEnable & 1) { + WRITE(p, " vec4 lightSum0 = vec4(0.0);\n"); + WRITE(p, " vec3 lightSum1 = vec3(0.0);\n"); + } + + // Calculate lights if needed. If shade mapping is enabled, lights may need to be + // at least partially calculated. + for (int i = 0; i < 4; i++) { + if (doLight[i] == LIGHT_OFF) + continue; + + GELightComputation comp = (GELightComputation)(gstate.ltype[i] & 3); + GELightType type = (GELightType)((gstate.ltype[i] >> 8) & 3); + + if (type == GE_LIGHTTYPE_DIRECTIONAL) + WRITE(p, " vec3 toLight%i = u_lightpos%i;\n", i, i); + else + WRITE(p, " vec3 toLight%i = u_lightpos%i - worldpos;\n", i, i); + + bool doSpecular = (comp != GE_LIGHTCOMP_ONLYDIFFUSE); + bool poweredDiffuse = comp == GE_LIGHTCOMP_BOTHWITHPOWDIFFUSE; + + WRITE(p, " float dot%i = dot(normalize(toLight%i), worldnormal);\n", i, i); + if (poweredDiffuse) { + WRITE(p, " dot%i = pow(dot%i, u_matspecular.a);\n", i, i); + } + + if (doLight[i] == LIGHT_DOTONLY) + continue; // TODO: Actually, might want specular dot.... TODO + + WRITE(p, " float lightScale%i = 1.0;\n", i); + if (type != GE_LIGHTTYPE_DIRECTIONAL) { + // Attenuation + WRITE(p, " float distance%i = length(toLight%i);\n", i, i); + WRITE(p, " lightScale%i = 1.0 / dot(u_lightatt%i, vec3(1.0, distance%i, distance%i*distance%i));\n", i, i, i, i, i); + WRITE(p, " if (lightScale%i > 1.0) lightScale%i = 1.0;\n", i, i); + } + WRITE(p, " vec3 diffuse%i = (u_lightdiffuse%i * %s) * (max(dot%i, 0.0) * lightScale%i);\n", i, i, diffuse, i, i); + if (doSpecular) { + WRITE(p, " vec3 halfVec%i = normalize(normalize(toLight%i) + vec3(0, 0, 1));\n", i, i); + WRITE(p, " dot%i = dot(halfVec%i, worldnormal);\n", i, i); + WRITE(p, " if (dot%i > 0.0)\n", i); + WRITE(p, " lightSum1 += u_lightspecular%i * %s * (pow(dot%i, u_matspecular.a) * (dot%i * lightScale%i));\n", i, specular, i, i, i); + } + WRITE(p, " lightSum0 += vec4(u_lightambient%i + diffuse%i, 0.0);\n", i, i); + } + + if (gstate.lightingEnable & 1) { + // Sum up ambient, emissive here. + WRITE(p, " v_color0 = clamp(lightSum0 + u_ambient * vec4(%s, 1.0) + vec4(u_matemissive, 0.0), 0.0, 1.0);\n", ambient); + if (lmode) { + WRITE(p, " v_color1 = clamp(lightSum1, 0.0, 1.0);\n"); + } else { + WRITE(p, " v_color0 += vec4(lightSum1, 0.0);\n"); + } + } else { + // Lighting doesn't affect color. + if (hasColor) { + WRITE(p, " v_color0 = a_color0;\n"); + } else { + WRITE(p, " v_color0 = u_matambientalpha;\n"); + } + if (lmode) + WRITE(p, " v_color1 = vec3(0.0, 0.0, 0.0);\n"); + } + + // Step 3: UV generation + if (doTexture) { + switch (gstate.getUVGenMode()) { + case 0: // Scale-offset. Easy. + WRITE(p, " v_texcoord = a_texcoord * u_uvscaleoffset.xy + u_uvscaleoffset.zw;\n"); + break; + + case 1: // Projection mapping. + switch (gstate.getUVProjMode()) { + case 0: // Use model space XYZ as source + WRITE(p, " vec3 temp_tc = a_position;\n"); + break; + case 1: // Use unscaled UV as source + WRITE(p, " vec3 temp_tc = vec3(a_texcoord.xy, 0.0);\n"); + break; + case 2: // Use normalized transformed normal as source + WRITE(p, " vec3 temp_tc = normalize(worldnormal);\n"); + break; + case 3: // Use non-normalized transformed normal as source + WRITE(p, " vec3 temp_tc = worldnormal;\n"); + break; + } + // Transform by texture matrix + WRITE(p, " v_texcoord = (u_texmtx * vec4(temp_tc, 1.0)).xy;\n"); + break; + + case 2: // Shade mapping - use dots from light sources. + WRITE(p, " v_texcoord = vec2(dot%i, dot%i);\n", gstate.getUVLS0(), gstate.getUVLS1()); + break; + + case 3: + // ILLEGAL + break; + } + } + // Step 4: Final view and projection transforms. + WRITE(p, " gl_Position = u_proj * (u_view * vec4(worldpos, 1.0));\n"); } - if (gstate.isFogEnabled()) { - WRITE(" v_depth = gl_Position.z;"); - } - WRITE("}"); + if (gstate.isFogEnabled()) + WRITE(p, " v_depth = gl_Position.z;\n"); + WRITE(p, "}\n"); return buffer; } diff --git a/GPU/GLES/VertexShaderGenerator.h b/GPU/GLES/VertexShaderGenerator.h index 4ce2bdb87b..92d7efec45 100644 --- a/GPU/GLES/VertexShaderGenerator.h +++ b/GPU/GLES/VertexShaderGenerator.h @@ -46,7 +46,9 @@ struct VertexShaderID } }; +bool CanUseHardwareTransform(int prim); + void ComputeVertexShaderID(VertexShaderID *id, int prim); // The return value is only valid until the function is called again. -char *GenerateVertexShader(); +char *GenerateVertexShader(int prim); diff --git a/GPU/GPU.vcxproj b/GPU/GPU.vcxproj index 0f58cd8f1c..a88d235a12 100644 --- a/GPU/GPU.vcxproj +++ b/GPU/GPU.vcxproj @@ -91,6 +91,9 @@ true true ../common;..;../native;../native/ext/glew; + false + StreamingSIMDExtensions2 + Fast true @@ -117,12 +120,14 @@ + + @@ -132,12 +137,16 @@ + - + + AssemblyAndSourceCode + + @@ -153,4 +162,4 @@ - \ No newline at end of file + diff --git a/GPU/GPU.vcxproj.filters b/GPU/GPU.vcxproj.filters index aad3ad15c2..e5a783590f 100644 --- a/GPU/GPU.vcxproj.filters +++ b/GPU/GPU.vcxproj.filters @@ -57,6 +57,9 @@ GLES + + GLES + @@ -95,6 +98,9 @@ GLES + + GLES + diff --git a/GPU/GPUInterface.h b/GPU/GPUInterface.h index f3fb0dad5e..81d71d9a52 100644 --- a/GPU/GPUInterface.h +++ b/GPU/GPUInterface.h @@ -33,7 +33,7 @@ public: virtual void UpdateStall(int listid, u32 newstall) = 0; virtual void DrawSync(int mode) = 0; virtual void Continue() = 0; - + virtual void ExecuteOp(u32 op, u32 diff) = 0; virtual bool InterpretList() = 0; @@ -45,6 +45,16 @@ public: // Tells the GPU to update the gpuStats structure. virtual void UpdateStats() = 0; + // Invalidate any cached content sourced from the specified range. + // If size = -1, invalidate everything. + virtual void InvalidateCache(u32 addr, int size) = 0; + // Internal hack to avoid interrupts from "PPGe" drawing (utility UI, etc) virtual void EnableInterrupts(bool enable) = 0; + + virtual void DeviceLost() = 0; + virtual void Flush() = 0; + + // Debugging + virtual void DumpNextFrame() = 0; }; diff --git a/GPU/GPUState.cpp b/GPU/GPUState.cpp index ff4e868b27..c45d65ae88 100644 --- a/GPU/GPUState.cpp +++ b/GPU/GPUState.cpp @@ -37,7 +37,7 @@ void InitGfxState() gstate.lightingEnable = 0x17000001; - static const float identity4x3[12] = + static const float identity4x3[12] = {1,0,0, 0,1,0, 0,0,1, @@ -95,21 +95,21 @@ void ReapplyGfxState() for (int i = GE_CMD_VERTEXTYPE; i < GE_CMD_BONEMATRIXNUMBER; i++) { - gpu->ExecuteOp(gstate.cmdmem[i], 0xFFFFFFFF); + gpu->ExecuteOp(gstate.cmdmem[i], 0xFFFFFFFF); } - + // Can't write to bonematrixnumber here for (int i = GE_CMD_MORPHWEIGHT0; i < GE_CMD_PATCHFACING; i++) { - gpu->ExecuteOp(gstate.cmdmem[i], 0xFFFFFFFF); + gpu->ExecuteOp(gstate.cmdmem[i], 0xFFFFFFFF); } // There are a few here in the middle that we shouldn't execute... for (int i = GE_CMD_VIEWPORTX1; i < GE_CMD_TRANSFERSTART; i++) { - gpu->ExecuteOp(gstate.cmdmem[i], 0xFFFFFFFF); + gpu->ExecuteOp(gstate.cmdmem[i], 0xFFFFFFFF); } // TODO: there's more... diff --git a/GPU/GPUState.h b/GPU/GPUState.h index a111b09418..843f531006 100644 --- a/GPU/GPUState.h +++ b/GPU/GPUState.h @@ -22,58 +22,10 @@ #include "ge_constants.h" #include -// TODO: this doesn't belong here -struct Color4 -{ - float r,g,b,a; - Color4() : r(0), g(0), b(0), a(0) { } - Color4(float _r, float _g, float _b, float _a=1.0f) - { - r=_r; g=_g; b=_b; a=_a; - } - Color4(const float in[4]) {r=in[0];g=in[1];b=in[2];a=in[3];} - - float &operator [](int i) {return *(&r + i);} - const float &operator [](int i) const {return *(&r + i);} - - Color4 operator *(float f) const - { - return Color4(f*r,f*g,f*b,f*a); - } - Color4 operator *(const Color4 &c) const - { - return Color4(r*c.r,g*c.g,b*c.b,a*c.a); - } - void operator *=(const Color4 &c) - { - r*=c.r,g*=c.g,b*=c.b,a*=c.a; - } - Color4 operator +(const Color4 &c) const - { - return Color4(r+c.r,g+c.g,b+c.b,a+c.a); - } - void operator +=(const Color4 &c) - { - r+=c.r; - g+=c.g; - b+=c.b; - a+=c.a; - } - void GetFromRGB(u32 col) - { - r = ((col>>16)&0xff)/255.0f; - g = ((col>>8)&0xff)/255.0f; - b = ((col>>0)&0xff)/255.0f; - } - void GetFromA(u32 col) - { - a = (col&0xff)/255.0f; - } -}; - - struct GPUgstate { + // Getting rid of this ugly union in favor of the accessor functions + // might be a good idea.... union { u32 cmdmem[256]; @@ -121,10 +73,14 @@ struct GPUgstate boneMatrixNumber, boneMatrixData, morphwgt[8], //dont use - pad04[0x39-0x33], + pad04[2], + patchdivision, + patchprimitive, + patchfacing, + pad04_a, - worldmtxnum,//0x3A - worldmtxdata, //0x3B + worldmtxnum, //0x3A + worldmtxdata, //0x3B viewmtxnum, //0x3C viewmtxdata, projmtxnum, @@ -239,22 +195,34 @@ struct GPUgstate float tgenMatrix[12]; float boneMatrix[12 * 8]; // Eight bone matrices. - bool isModeThrough() const { return (vertType & GE_VTYPE_THROUGH) != 0; } + // Pixel Pipeline bool isModeClear() const { return clearmode & 1; } bool isCullEnabled() const { return cullfaceEnable & 1; } - int getCullMode() const { return cullmode & 1; } - int getBlendFuncA() const { return blend & 0xF; } + int getCullMode() const { return cullmode & 1; } + int getBlendFuncA() const { return blend & 0xF; } u32 getFixA() const { return blendfixa & 0xFFFFFF; } u32 getFixB() const { return blendfixb & 0xFFFFFF; } - int getBlendFuncB() const { return (blend >> 4) & 0xF; } - int getBlendEq() const { return (blend >> 8) & 0x7; } + int getBlendFuncB() const { return (blend >> 4) & 0xF; } + int getBlendEq() const { return (blend >> 8) & 0x7; } bool isDepthTestEnabled() const { return zTestEnable & 1; } bool isDepthWriteEnabled() const { return !(zmsk & 1); } - int getDepthTestFunc() const { return ztestfunc & 0x7; } + int getDepthTestFunc() const { return ztestfunc & 0x7; } bool isFogEnabled() const { return fogEnable & 1; } -}; -// Real data in the context ends here + // UV gen + int getUVGenMode() const { return texmapmode & 3;} // 2 bits + int getUVProjMode() const { return (texmapmode >> 8) & 3;} // 2 bits + int getUVLS0() const { return texshade & 0x3; } // 2 bits + int getUVLS1() const { return (texshade >> 8) & 0x3; } // 2 bits + + // Vertex type + bool isModeThrough() const { return (vertType & GE_VTYPE_THROUGH) != 0; } + int getNumBoneWeights() const { + return 1 + ((vertType & GE_VTYPE_WEIGHTCOUNT_MASK) >> GE_VTYPE_WEIGHTCOUNT_SHIFT); + } +// Real data in the context ends here +}; + // The rest is cached simplified/converted data for fast access. // Does not need to be saved when saving/restoring context. struct GPUStateCache @@ -270,13 +238,9 @@ struct GPUStateCache float lightpos[4][3]; float lightdir[4][3]; float lightatt[4][3]; - Color4 lightColor[3][4]; //Amtient Diffuse Specular + float lightColor[3][4][3]; //Amtient Diffuse Specular float morphWeights[8]; - // bezier patch subdivision - int patch_div_s; - int patch_div_t; - u32 curTextureWidth; u32 curTextureHeight; @@ -291,17 +255,23 @@ struct GPUStatistics memset(this, 0, sizeof(*this)); } void resetFrame() { + numJoins = 0; numDrawCalls = 0; numVertsTransformed = 0; numTextureSwitches = 0; numShaderSwitches = 0; + numFlushes = 0; + numTexturesDecoded = 0; } // Per frame statistics + int numJoins; int numDrawCalls; + int numFlushes; int numVertsTransformed; int numTextureSwitches; int numShaderSwitches; + int numTexturesDecoded; // Total statistics, updated by the GPU core in UpdateStats int numFrames; diff --git a/GPU/GeDisasm.cpp b/GPU/GeDisasm.cpp new file mode 100644 index 0000000000..ab13ac1c51 --- /dev/null +++ b/GPU/GeDisasm.cpp @@ -0,0 +1,773 @@ +// Copyright (c) 2012- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "../Core/MemMap.h" + +#include "GPUState.h" +#include "ge_constants.h" + +void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer) { + u32 cmd = op >> 24; + u32 data = op & 0xFFFFFF; + + // Handle control and drawing commands here directly. The others we delegate. + switch (cmd) + { + case GE_CMD_BASE: + sprintf(buffer, "BASE: %06x", data & 0xFFFFFF); + break; + + case GE_CMD_VADDR: /// <<8???? + sprintf(buffer, "VADDR: %06x", gstate_c.vertexAddr); + break; + + case GE_CMD_IADDR: + sprintf(buffer, "IADDR: %06x", gstate_c.indexAddr); + break; + + case GE_CMD_PRIM: + { + u32 count = data & 0xFFFF; + u32 type = data >> 16; + static const char* types[7] = { + "POINTS", + "LINES", + "LINE_STRIP", + "TRIANGLES", + "TRIANGLE_STRIP", + "TRIANGLE_FAN", + "RECTANGLES", + }; + sprintf(buffer, "DrawPrim type: %s count: %i vaddr= %08x, iaddr= %08x", type < 7 ? types[type] : "INVALID", count, gstate_c.vertexAddr, gstate_c.indexAddr); + } + break; + + // The arrow and other rotary items in Puzbob are bezier patches, strangely enough. + case GE_CMD_BEZIER: + { + int bz_ucount = data & 0xFF; + int bz_vcount = (data >> 8) & 0xFF; + sprintf(buffer, "DRAW BEZIER: %i x %i", bz_ucount, bz_vcount); + } + break; + + case GE_CMD_SPLINE: + { + int sp_ucount = data & 0xFF; + int sp_vcount = (data >> 8) & 0xFF; + int sp_utype = (data >> 16) & 0x3; + int sp_vtype = (data >> 18) & 0x3; + sprintf(buffer, "DRAW SPLINE: %i x %i, %i x %i", sp_ucount, sp_vcount, sp_utype, sp_vtype); + } + break; + + case GE_CMD_JUMP: + { + u32 target = (((gstate.base & 0x00FF0000) << 8) | (op & 0xFFFFFC)) & 0x0FFFFFFF; + sprintf(buffer, "CMD JUMP - %08x to %08x", pc, target); + } + break; + + case GE_CMD_CALL: + { + u32 retval = pc + 4; + u32 target = (((gstate.base & 0x00FF0000) << 8) | (op & 0xFFFFFC)) & 0xFFFFFFF; + sprintf(buffer, "CMD CALL - %08x to %08x, ret=%08x", pc, target, retval); + } + break; + + case GE_CMD_RET: + sprintf(buffer, "CMD RET"); + break; + + case GE_CMD_SIGNAL: + sprintf(buffer, "GE_CMD_SIGNAL %06x", data); + break; + + case GE_CMD_FINISH: + sprintf(buffer, "CMD FINISH %06x", data); + break; + + case GE_CMD_END: + sprintf(buffer, "CMD END"); + switch (prev >> 24) + { + case GE_CMD_SIGNAL: + { + // TODO: see http://code.google.com/p/jpcsp/source/detail?r=2935# + int behaviour = (prev >> 16) & 0xFF; + int signal = prev & 0xFFFF; + int enddata = data & 0xFFFF; + // We should probably defer to sceGe here, no sense in implementing this stuff in every GPU + switch (behaviour) { + case 1: // Signal with Wait + sprintf(buffer, "Signal with Wait UNIMPLEMENTED! signal/end: %04x %04x", signal, enddata); + break; + case 2: + sprintf(buffer, "Signal without wait. signal/end: %04x %04x", signal, enddata); + break; + case 3: + sprintf(buffer, "Signal with Pause UNIMPLEMENTED! signal/end: %04x %04x", signal, enddata); + break; + case 0x10: + sprintf(buffer, "Signal with Jump UNIMPLEMENTED! signal/end: %04x %04x", signal, enddata); + break; + case 0x11: + sprintf(buffer, "Signal with Call UNIMPLEMENTED! signal/end: %04x %04x", signal, enddata); + break; + case 0x12: + sprintf(buffer, "Signal with Return UNIMPLEMENTED! signal/end: %04x %04x", signal, enddata); + break; + default: + sprintf(buffer, "UNKNOWN Signal UNIMPLEMENTED %i ! signal/end: %04x %04x", behaviour, signal, enddata); + break; + } + } + break; + case GE_CMD_FINISH: + break; + default: + sprintf(buffer, "Ah, not finished: %06x", prev & 0xFFFFFF); + break; + } + break; + + case GE_CMD_BJUMP: + // bounding box jump. Let's just not jump, for now. + sprintf(buffer, "BBOX JUMP - unimplemented"); + break; + + case GE_CMD_BOUNDINGBOX: + // bounding box test. Let's do nothing. + sprintf(buffer, "BBOX TEST - unimplemented"); + break; + + case GE_CMD_ORIGIN: + sprintf(buffer, "Origin: %06x", data); + break; + + case GE_CMD_VERTEXTYPE: + sprintf(buffer, "SetVertexType: %06x", data); + break; + + case GE_CMD_OFFSETADDR: + sprintf(buffer, "OffsetAddr: %06x", data); + break; + + case GE_CMD_REGION1: + { + int x1 = data & 0x3ff; + int y1 = data >> 10; + //topleft + sprintf(buffer, "Region TL: %d %d", x1, y1); + } + break; + + case GE_CMD_REGION2: + { + int x2 = data & 0x3ff; + int y2 = data >> 10; + sprintf(buffer, "Region BR: %d %d", x2, y2); + } + break; + + case GE_CMD_CLIPENABLE: + sprintf(buffer, "Clip Enable: %i", data); + break; + + case GE_CMD_CULLFACEENABLE: + sprintf(buffer, "CullFace Enable: %i", data); + break; + + case GE_CMD_TEXTUREMAPENABLE: + sprintf(buffer, "Texture map enable: %i", data); + break; + + case GE_CMD_LIGHTINGENABLE: + sprintf(buffer, "Lighting enable: %i", data); + break; + + case GE_CMD_FOGENABLE: + sprintf(buffer, "Fog Enable: %i", data); + break; + + case GE_CMD_DITHERENABLE: + sprintf(buffer, "Dither Enable: %i", data); + break; + + case GE_CMD_OFFSETX: + sprintf(buffer, "Offset X: %i", data); + break; + + case GE_CMD_OFFSETY: + sprintf(buffer, "Offset Y: %i", data); + break; + + case GE_CMD_TEXSCALEU: + sprintf(buffer, "Texture U Scale: %f", gstate_c.uScale); + break; + + case GE_CMD_TEXSCALEV: + sprintf(buffer, "Texture V Scale: %f", gstate_c.vScale); + break; + + case GE_CMD_TEXOFFSETU: + sprintf(buffer, "Texture U Offset: %f", gstate_c.uOff); + break; + + case GE_CMD_TEXOFFSETV: + sprintf(buffer, "Texture V Offset: %f", gstate_c.vOff); + break; + + case GE_CMD_SCISSOR1: + { + int x1 = data & 0x3ff; + int y1 = data >> 10; + sprintf(buffer, "Scissor TL: %i, %i", x1,y1); + } + break; + case GE_CMD_SCISSOR2: + { + int x2 = data & 0x3ff; + int y2 = data >> 10; + sprintf(buffer, "Scissor BR: %i, %i", x2, y2); + } + break; + + case GE_CMD_MINZ: + { + float zMin = getFloat24(data) / 65535.f; + sprintf(buffer, "MinZ: %f", zMin); + } + break; + + case GE_CMD_MAXZ: + { + float zMax = getFloat24(data) / 65535.f; + sprintf(buffer, "MaxZ: %f", zMax); + } + break; + + case GE_CMD_FRAMEBUFPTR: + { + u32 ptr = op & 0xFFE000; + sprintf(buffer, "FramebufPtr: %08x", ptr); + } + break; + + case GE_CMD_FRAMEBUFWIDTH: + { + sprintf(buffer, "FramebufWidth: %i", data); + } + break; + + case GE_CMD_FRAMEBUFPIXFORMAT: + sprintf(buffer, "FramebufPixeFormat: %i", data); + break; + + case GE_CMD_TEXADDR0: + case GE_CMD_TEXADDR1: + case GE_CMD_TEXADDR2: + case GE_CMD_TEXADDR3: + case GE_CMD_TEXADDR4: + case GE_CMD_TEXADDR5: + case GE_CMD_TEXADDR6: + case GE_CMD_TEXADDR7: + sprintf(buffer, "Texture address %i: %06x", cmd-GE_CMD_TEXADDR0, data); + break; + + case GE_CMD_TEXBUFWIDTH0: + case GE_CMD_TEXBUFWIDTH1: + case GE_CMD_TEXBUFWIDTH2: + case GE_CMD_TEXBUFWIDTH3: + case GE_CMD_TEXBUFWIDTH4: + case GE_CMD_TEXBUFWIDTH5: + case GE_CMD_TEXBUFWIDTH6: + case GE_CMD_TEXBUFWIDTH7: + sprintf(buffer, "Texture BUFWIDTHess %i: %06x", cmd-GE_CMD_TEXBUFWIDTH0, data); + break; + + case GE_CMD_CLUTADDR: + sprintf(buffer, "CLUT base addr: %06x", data); + break; + + case GE_CMD_CLUTADDRUPPER: + sprintf(buffer, "CLUT addr upper %08x", data); + break; + + case GE_CMD_LOADCLUT: + // This could be used to "dirty" textures with clut. + sprintf(buffer, "Clut load"); + break; + + case GE_CMD_TEXMAPMODE: + sprintf(buffer, "Tex map mode: %06x", data); + break; + + case GE_CMD_TEXSHADELS: + sprintf(buffer, "Tex shade light sources: %06x", data); + break; + + case GE_CMD_CLUTFORMAT: + { + sprintf(buffer, "Clut format: %06x", data); + } + break; + + case GE_CMD_TRANSFERSRC: + { + sprintf(buffer, "Block Transfer Src: %06x", data); + // Nothing to do, the next one prints + } + break; + + case GE_CMD_TRANSFERSRCW: + { + u32 xferSrc = gstate.transfersrc | ((data&0xFF0000)<<8); + u32 xferSrcW = gstate.transfersrcw & 1023; + sprintf(buffer, "Block Transfer Src: %08x W: %i", xferSrc, xferSrcW); + break; + } + + case GE_CMD_TRANSFERDST: + { + // Nothing to do, the next one prints + sprintf(buffer, "Block Transfer Dst: %06x", data); + } + break; + + case GE_CMD_TRANSFERDSTW: + { + u32 xferDst= gstate.transferdst | ((data&0xFF0000)<<8); + u32 xferDstW = gstate.transferdstw & 1023; + sprintf(buffer, "Block Transfer Dest: %08x W: %i", xferDst, xferDstW); + break; + } + + case GE_CMD_TRANSFERSRCPOS: + { + u32 x = (data & 1023)+1; + u32 y = ((data>>10) & 1023)+1; + sprintf(buffer, "Block Transfer Src Rect TL: %i, %i", x, y); + break; + } + + case GE_CMD_TRANSFERDSTPOS: + { + u32 x = (data & 1023)+1; + u32 y = ((data>>10) & 1023)+1; + sprintf(buffer, "Block Transfer Dest Rect TL: %i, %i", x, y); + break; + } + + case GE_CMD_TRANSFERSIZE: + { + u32 w = (data & 1023)+1; + u32 h = ((data>>10) & 1023)+1; + sprintf(buffer, "Block Transfer Rect Size: %i x %i", w, h); + break; + } + + case GE_CMD_TRANSFERSTART: // Orphis calls this TRXKICK + { + sprintf(buffer, "Block Transfer Start"); + break; + } + + case GE_CMD_TEXSIZE0: + case GE_CMD_TEXSIZE1: + case GE_CMD_TEXSIZE2: + case GE_CMD_TEXSIZE3: + case GE_CMD_TEXSIZE4: + case GE_CMD_TEXSIZE5: + case GE_CMD_TEXSIZE6: + case GE_CMD_TEXSIZE7: + sprintf(buffer, "Texture Size %i: %06x", cmd - GE_CMD_TEXSIZE0, data); + break; + + case GE_CMD_ZBUFPTR: + { + u32 ptr = op & 0xFFE000; + sprintf(buffer, "Zbuf Ptr: %06x", ptr); + } + break; + + case GE_CMD_ZBUFWIDTH: + { + sprintf(buffer, "Zbuf Width: %06x", data); + } + break; + + case GE_CMD_AMBIENTCOLOR: + sprintf(buffer, "Ambient Color: %06x", data); + break; + + case GE_CMD_AMBIENTALPHA: + sprintf(buffer, "Ambient Alpha: %06x", data); + break; + + case GE_CMD_MATERIALAMBIENT: + sprintf(buffer, "Material Ambient Color: %06x", data); + break; + + case GE_CMD_MATERIALDIFFUSE: + sprintf(buffer, "Material Diffuse Color: %06x", data); + break; + + case GE_CMD_MATERIALEMISSIVE: + sprintf(buffer, "Material Emissive Color: %06x", data); + break; + + case GE_CMD_MATERIALSPECULAR: + sprintf(buffer, "Material Specular Color: %06x", data); + break; + + case GE_CMD_MATERIALALPHA: + sprintf(buffer, "Material Alpha Color: %06x", data); + break; + + case GE_CMD_MATERIALSPECULARCOEF: + sprintf(buffer, "Material specular coef: %f", getFloat24(data)); + break; + + case GE_CMD_LIGHTTYPE0: + case GE_CMD_LIGHTTYPE1: + case GE_CMD_LIGHTTYPE2: + case GE_CMD_LIGHTTYPE3: + sprintf(buffer, "Light %i type: %06x", cmd-GE_CMD_LIGHTTYPE0, data); + break; + + case GE_CMD_LX0:case GE_CMD_LY0:case GE_CMD_LZ0: + case GE_CMD_LX1:case GE_CMD_LY1:case GE_CMD_LZ1: + case GE_CMD_LX2:case GE_CMD_LY2:case GE_CMD_LZ2: + case GE_CMD_LX3:case GE_CMD_LY3:case GE_CMD_LZ3: + { + int n = cmd - GE_CMD_LX0; + int l = n / 3; + int c = n % 3; + float val = getFloat24(data); + sprintf(buffer, "Light %i %c pos: %f", l, c+'X', val); + } + break; + + case GE_CMD_LDX0:case GE_CMD_LDY0:case GE_CMD_LDZ0: + case GE_CMD_LDX1:case GE_CMD_LDY1:case GE_CMD_LDZ1: + case GE_CMD_LDX2:case GE_CMD_LDY2:case GE_CMD_LDZ2: + case GE_CMD_LDX3:case GE_CMD_LDY3:case GE_CMD_LDZ3: + { + int n = cmd - GE_CMD_LDX0; + int l = n / 3; + int c = n % 3; + float val = getFloat24(data); + sprintf(buffer, "Light %i %c dir: %f", l, c+'X', val); + } + break; + + case GE_CMD_LKA0:case GE_CMD_LKB0:case GE_CMD_LKC0: + case GE_CMD_LKA1:case GE_CMD_LKB1:case GE_CMD_LKC1: + case GE_CMD_LKA2:case GE_CMD_LKB2:case GE_CMD_LKC2: + case GE_CMD_LKA3:case GE_CMD_LKB3:case GE_CMD_LKC3: + { + int n = cmd - GE_CMD_LKA0; + int l = n / 3; + int c = n % 3; + float val = getFloat24(data); + sprintf(buffer, "Light %i %c att: %f", l, c+'X', val); + } + break; + + case GE_CMD_LAC0:case GE_CMD_LAC1:case GE_CMD_LAC2:case GE_CMD_LAC3: + case GE_CMD_LDC0:case GE_CMD_LDC1:case GE_CMD_LDC2:case GE_CMD_LDC3: + case GE_CMD_LSC0:case GE_CMD_LSC1:case GE_CMD_LSC2:case GE_CMD_LSC3: + { + float r = (float)(data & 0xff)/255.0f; + float g = (float)((data>>8) & 0xff)/255.0f; + float b = (float)(data>>16)/255.0f; + + int l = (cmd - GE_CMD_LAC0) / 3; + int t = (cmd - GE_CMD_LAC0) % 3; + sprintf(buffer, "Light %i color %i: %f %f %f", l, t, r, g, b); + } + break; + + case GE_CMD_VIEWPORTX1: + case GE_CMD_VIEWPORTY1: + case GE_CMD_VIEWPORTX2: + case GE_CMD_VIEWPORTY2: + sprintf(buffer, "Viewport param %i: %f", cmd-GE_CMD_VIEWPORTX1, getFloat24(data)); + break; + case GE_CMD_VIEWPORTZ1: + { + float zScale = getFloat24(data) / 65535.f; + sprintf(buffer, "Z scale: %f", zScale); + } + break; + case GE_CMD_VIEWPORTZ2: + { + float zOff = getFloat24(data) / 65535.f; + sprintf(buffer, "Z pos: %f", zOff); + } + break; + + case GE_CMD_LIGHTENABLE0: + case GE_CMD_LIGHTENABLE1: + case GE_CMD_LIGHTENABLE2: + case GE_CMD_LIGHTENABLE3: + sprintf(buffer, "Light %i enable: %d", cmd-GE_CMD_LIGHTENABLE0, data); + break; + + case GE_CMD_CULL: + sprintf(buffer, "cull: %06x", data); + break; + + case GE_CMD_LMODE: + sprintf(buffer, "Shade mode: %06x", data); + break; + + case GE_CMD_PATCHDIVISION: + { + int patch_div_s = data & 0xFF; + int patch_div_t = (data >> 8) & 0xFF; + sprintf(buffer, "Patch subdivision: %i x %i", patch_div_s, patch_div_t); + } + break; + + case GE_CMD_PATCHPRIMITIVE: + sprintf(buffer, "Patch Primitive: %d", data); + break; + + case GE_CMD_PATCHFACING: + sprintf(buffer, "Patch Facing: %d", data); + break; + + case GE_CMD_MATERIALUPDATE: + sprintf(buffer, "Material Update: %d", data); + break; + + + ////////////////////////////////////////////////////////////////// + // CLEARING + ////////////////////////////////////////////////////////////////// + case GE_CMD_CLEARMODE: + // If it becomes a performance problem, check diff&1 + sprintf(buffer, "Clear mode: %06x", data); + break; + + + ////////////////////////////////////////////////////////////////// + // ALPHA BLENDING + ////////////////////////////////////////////////////////////////// + case GE_CMD_ALPHABLENDENABLE: + sprintf(buffer, "Alpha blend enable: %d", data); + break; + + case GE_CMD_BLENDMODE: + sprintf(buffer, "Blend mode: %06x", data); + break; + + case GE_CMD_BLENDFIXEDA: + sprintf(buffer, "Blend fix A: %06x", data); + break; + + case GE_CMD_BLENDFIXEDB: + sprintf(buffer, "Blend fix B: %06x", data); + break; + + case GE_CMD_ALPHATESTENABLE: + sprintf(buffer, "Alpha test enable: %d", data); + break; + + case GE_CMD_ALPHATEST: + sprintf(buffer, "Alpha test settings"); + break; + + case GE_CMD_ANTIALIASENABLE: + sprintf(buffer, "Antialias enable: %d", data); + break; + + case GE_CMD_PATCHCULLENABLE: + sprintf(buffer, "Antialias enable: %d", data); + break; + + case GE_CMD_COLORTESTENABLE: + sprintf(buffer, "Color Test enable: %d", data); + break; + + case GE_CMD_LOGICOPENABLE: + sprintf(buffer, "Logic op enable: %d", data); + break; + + case GE_CMD_TEXFUNC: + sprintf(buffer, "TexFunc %i", data&7); + break; + + case GE_CMD_TEXFILTER: + { + int min = data & 7; + int mag = (data >> 8) & 1; + sprintf(buffer, "TexFilter min: %i mag: %i", min, mag); + } + break; + + case GE_CMD_TEXENVCOLOR: + sprintf(buffer, "TexEnvColor %06x", data); + break; + + case GE_CMD_TEXMODE: + sprintf(buffer, "TexMode %08x", data); + break; + + case GE_CMD_TEXFORMAT: + sprintf(buffer, "TexFormat %08x", data); + break; + + case GE_CMD_TEXFLUSH: + sprintf(buffer, "TexFlush"); + break; + + case GE_CMD_TEXSYNC: + sprintf(buffer, "TexSync"); + break; + + case GE_CMD_TEXWRAP: + sprintf(buffer, "TexWrap %08x", data); + break; + + case GE_CMD_FOG1: + sprintf(buffer, "Fog1 %f", getFloat24(data)); + break; + + case GE_CMD_FOG2: + sprintf(buffer, "Fog2 %f", getFloat24(data)); + break; + + case GE_CMD_FOGCOLOR: + sprintf(buffer, "FogColor %06x", data); + break; + + case GE_CMD_TEXLODSLOPE: + sprintf(buffer, "TexLodSlope %06x", data); + break; + + ////////////////////////////////////////////////////////////////// + // Z/STENCIL TESTING + ////////////////////////////////////////////////////////////////// + + case GE_CMD_ZTESTENABLE: + sprintf(buffer, "Z test enable: %d", data & 1); + break; + + case GE_CMD_STENCILOP: + sprintf(buffer, "Stencil op: %06x", data); + break; + + case GE_CMD_STENCILTEST: + sprintf(buffer, "Stencil test: %06x", data); + break; + + case GE_CMD_STENCILTESTENABLE: + sprintf(buffer, "Stencil test enable: %d", data); + break; + + case GE_CMD_ZTEST: + sprintf(buffer, "Z test mode: %i", data); + break; + + case GE_CMD_MORPHWEIGHT0: + case GE_CMD_MORPHWEIGHT1: + case GE_CMD_MORPHWEIGHT2: + case GE_CMD_MORPHWEIGHT3: + case GE_CMD_MORPHWEIGHT4: + case GE_CMD_MORPHWEIGHT5: + case GE_CMD_MORPHWEIGHT6: + case GE_CMD_MORPHWEIGHT7: + { + int index = cmd - GE_CMD_MORPHWEIGHT0; + float weight = getFloat24(data); + sprintf(buffer, "MorphWeight %i = %f", index, weight); + } + break; + + case GE_CMD_DITH0: + case GE_CMD_DITH1: + case GE_CMD_DITH2: + case GE_CMD_DITH3: + sprintf(buffer, "DitherMatrix %i = %06x",cmd-GE_CMD_DITH0,data); + break; + + case GE_CMD_LOGICOP: + sprintf(buffer, "LogicOp: %06x", data); + break; + + case GE_CMD_ZWRITEDISABLE: + sprintf(buffer, "ZMask: %06x", data); + break; + + case GE_CMD_MASKRGB: + sprintf(buffer, "MaskRGB: %06x", data); + break; + + case GE_CMD_MASKALPHA: + sprintf(buffer, "MaskAlpha: %06x", data); + break; + + case GE_CMD_WORLDMATRIXNUMBER: + sprintf(buffer, "World # %i", data & 0xF); + break; + + case GE_CMD_WORLDMATRIXDATA: + sprintf(buffer, "World data # %f", getFloat24(data)); + break; + + case GE_CMD_VIEWMATRIXNUMBER: + sprintf(buffer, "VIEW # %i", data & 0xF); + break; + + case GE_CMD_VIEWMATRIXDATA: + sprintf(buffer, "VIEW data # %f", getFloat24(data)); + break; + + case GE_CMD_PROJMATRIXNUMBER: + sprintf(buffer, "PROJECTION # %i", data & 0xF); + break; + + case GE_CMD_PROJMATRIXDATA: + sprintf(buffer, "PROJECTION matrix data # %f", getFloat24(data)); + break; + + case GE_CMD_TGENMATRIXNUMBER: + sprintf(buffer, "TGEN # %i", data & 0xF); + break; + + case GE_CMD_TGENMATRIXDATA: + sprintf(buffer, "TGEN data # %f", getFloat24(data)); + break; + + case GE_CMD_BONEMATRIXNUMBER: + sprintf(buffer, "BONE #%i", data); + break; + + case GE_CMD_BONEMATRIXDATA: + sprintf(buffer, "BONE data #%i %f", gstate.boneMatrixNumber & 0x7f, getFloat24(data)); + break; + + default: + sprintf(buffer, "Unknown: %08x", op); + break; + } +} + diff --git a/GPU/Null/NullDisplayListInterpreter.h b/GPU/GeDisasm.h similarity index 74% rename from GPU/Null/NullDisplayListInterpreter.h rename to GPU/GeDisasm.h index dc83dacb59..6badfc49a3 100644 --- a/GPU/Null/NullDisplayListInterpreter.h +++ b/GPU/GeDisasm.h @@ -15,17 +15,4 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. -#pragma once - -#include "../../Globals.h" - -class ShaderManager; - -class GPU -{ -public: - static u32 EnqueueList(u32 listpc, u32 stall); - static void UpdateStall(int listid, u32 newstall); - static void ExecuteOp(u32 op, u32 diff); - static bool InterpretList(); -}; +void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer); diff --git a/GPU/Null/NullDisplayListInterpreter.cpp b/GPU/Null/NullDisplayListInterpreter.cpp deleted file mode 100644 index ef00c7a373..0000000000 --- a/GPU/Null/NullDisplayListInterpreter.cpp +++ /dev/null @@ -1,997 +0,0 @@ -// Copyright (c) 2012- PPSSPP Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0 or later versions. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official git repository and contact information can be found at -// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. - -#include "../../Core/MemMap.h" -#include "../../Core/Host.h" - -#include "../GPUState.h" -#include "../ge_constants.h" - -#include "ShaderManager.h" -#include "DisplayListInterpreter.h" -#include "TransformPipeline.h" - -#include "../../Core/HLE/sceKernelThread.h" -#include "../../Core/HLE/sceKernelInterrupt.h" - -inline void glEnDis(GLuint cmd, int value) -{ - (value ? glEnable : glDisable)(cmd); -} - -struct DisplayState -{ - u32 pc; - u32 stallAddr; -}; - -static DisplayState dcontext; -ShaderManager shaderManager; - -extern u32 curTextureWidth; -extern u32 curTextureHeight; - -int dlIdGenerator = 1; - -struct DisplayList -{ - int id; - u32 listpc; - u32 stall; -}; - -std::vector dlQueue; - -static u32 prev; -u32 stack[2]; -u32 stackptr = 0; -bool finished; - -u8 bezierBuf[16000]; - -bool ProcessDLQueue() -{ - std::vector::iterator iter = dlQueue.begin(); - while (!(iter == dlQueue.end())) - { - DisplayList &l = *iter; - dcontext.pc = l.listpc; - dcontext.stallAddr = l.stall; -// DEBUG_LOG(G3D,"Okay, starting DL execution at %08 - stall = %08x", context.pc, stallAddr); - if (!GPU::InterpretList()) - { - l.listpc = dcontext.pc; - l.stall = dcontext.stallAddr; - return false; - } - else - { - //At the end, we can remove it from the queue and continue - dlQueue.erase(iter); - //this invalidated the iterator, let's fix it - iter = dlQueue.begin(); - } - } - return true; //no more lists! -} - -u32 GPU::EnqueueList(u32 listpc, u32 stall) -{ - DisplayList dl; - dl.id = dlIdGenerator++; - dl.listpc = listpc&0xFFFFFFF; - dl.stall = stall&0xFFFFFFF; - dlQueue.push_back(dl); - if (!ProcessDLQueue()) - return dl.id; - else - return 0; -} - -void GPU::UpdateStall(int listid, u32 newstall) -{ - // this needs improvement.... - for (std::vector::iterator iter = dlQueue.begin(); iter != dlQueue.end(); iter++) - { - DisplayList &l = *iter; - if (l.id == listid) - { - l.stall = newstall & 0xFFFFFFF; - } - } - - ProcessDLQueue(); -} - -// Just to get something on the screen, we'll just not subdivide correctly. -void drawBezier(int ucount, int vcount) -{ - u16 indices[3 * 3 * 6]; - float customUV[32]; - int c = 0; - for (int y = 0; y < 3; y++) { - for (int x = 0; x < 3; x++) { - indices[c++] = y * 4 + x; - indices[c++] = y * 4 + x + 1; - indices[c++] = (y + 1) * 4 + x + 1; - indices[c++] = (y + 1) * 4 + x + 1; - indices[c++] = (y + 1) * 4 + x; - indices[c++] = y * 4 + x; - } - } - - for (int y = 0; y < 4; y++) { - for (int x = 0; x < 4; x++) { - customUV[(y * 4 + x) * 2 + 0] = (float)x/3.0f; - customUV[(y * 4 + x) * 2 + 1] = (float)y/3.0f; - } - } - - LinkedShader *linkedShader = shaderManager.ApplyShader(); - TransformAndDrawPrim(Memory::GetPointer(gstate.vertexAddr), &indices[0], GE_PRIM_TRIANGLES, 3 * 3 * 6, linkedShader, customUV, GE_VTYPE_IDX_16BIT); -} - - -void EnterClearMode(u32 data) -{ - bool colMask = (data >> 8) & 1; - bool alphaMask = (data >> 9) & 1; - bool updateZ = (data >> 10) & 1; - glColorMask(colMask, colMask, colMask, alphaMask); - glDepthMask(updateZ); // Update Z or not - // Note that depth test must be enabled for depth writes to go through! So we use GL_ALWAYS - glEnable(GL_DEPTH_TEST); - glDepthFunc(GL_ALWAYS); - glDisable(GL_CULL_FACE); // ?? -} - -void LeaveClearMode() -{ - // We have to reset the following state as per the state of the command registers: - // Back face culling - // Texture map enable (meh) - // Fogging - // Antialiasing - // Alpha test - glDepthMask(1); - glColorMask(1,1,1,1); - glEnDis(GL_DEPTH_TEST, gstate.zTestEnable & 1); - glDepthFunc(GL_LEQUAL); // TODO - - // dirtyshader? -} - -void SetBlendModePSP(u32 data) -{ - // This can't be done exactly as there are several PSP blend modes that are impossible to do on OpenGL ES 2.0, and some even on regular OpenGL for desktop. - - // HOWEVER - we should be able to approximate the 2x modes in the shader, although they will clip wrongly. - - const GLint aLookup[] = { - GL_DST_COLOR, - GL_ONE_MINUS_DST_COLOR, - GL_SRC_ALPHA, - GL_ONE_MINUS_SRC_ALPHA, - GL_DST_ALPHA, - GL_ONE_MINUS_DST_ALPHA, - GL_SRC_ALPHA, // should be 2x - GL_ONE_MINUS_SRC_ALPHA, // should be 2x - GL_DST_ALPHA, // should be 2x - GL_ONE_MINUS_DST_ALPHA, // should be 2x - and COLOR? - GL_SRC_ALPHA, // should be FIXA - }; - const GLint bLookup[] = { - GL_SRC_COLOR, - GL_ONE_MINUS_SRC_COLOR, - GL_SRC_ALPHA, - GL_ONE_MINUS_SRC_ALPHA, - GL_DST_ALPHA, - GL_ONE_MINUS_DST_ALPHA, - GL_SRC_ALPHA, // should be 2x - GL_ONE_MINUS_SRC_ALPHA, // should be 2x - GL_DST_ALPHA, // should be 2x - GL_ONE_MINUS_DST_ALPHA, // should be 2x - GL_SRC_ALPHA, // should be FIXB - }; - const GLint eqLookup[] = { - GL_FUNC_ADD, - GL_FUNC_SUBTRACT, - GL_FUNC_REVERSE_SUBTRACT, -#if defined(USING_GLES2) - GL_FUNC_ADD, - GL_FUNC_ADD, -#else - GL_MIN, - GL_MAX, -#endif - GL_FUNC_ADD, // should be abs(diff) - }; - int a = data & 0xF; - int b = (data >> 4) & 0xF; - int eq = (data >> 8) & 0x7; - glBlendFunc(aLookup[a], bLookup[b]); - glBlendEquation(eqLookup[eq]); -} - - -void GPU::ExecuteOp(u32 op, u32 diff) -{ - u32 cmd = op >> 24; - u32 data = op & 0xFFFFFF; - - // Handle control and drawing commands here directly. The others we delegate. - switch (cmd) - { - case GE_CMD_BASE: - DEBUG_LOG(G3D,"DL BASE: %06x", data); - break; - - case GE_CMD_VADDR: /// <<8???? - gstate.vertexAddr = (gstate.base<<8)|data; - DEBUG_LOG(G3D,"DL VADDR: %06x", gstate.vertexAddr); - break; - - case GE_CMD_IADDR: - gstate.indexAddr = (gstate.base<<8)|data; - DEBUG_LOG(G3D,"DL IADDR: %06x", gstate.indexAddr); - break; - - case GE_CMD_PRIM: - { - u32 count = data & 0xFFFF; - u32 type = data >> 16; - static const char* types[7] = { - "POINTS=0,", - "LINES=1,", - "LINE_STRIP=2,", - "TRIANGLES=3,", - "TRIANGLE_STRIP=4,", - "TRIANGLE_FAN=5,", - "RECTANGLES=6,", - }; - DEBUG_LOG(G3D, "DL DrawPrim type: %s count: %i vaddr= %08x, iaddr= %08x", type<7 ? types[type] : "INVALID", count, gstate.vertexAddr, gstate.indexAddr); - - LinkedShader *linkedShader = shaderManager.ApplyShader(); - // TODO: Split this so that we can collect sequences of primitives, can greatly speed things up - // on platforms where draw calls are expensive like mobile and D3D - void *verts = Memory::GetPointer(gstate.vertexAddr); - void *inds = 0; - if ((gstate.vertType & GE_VTYPE_IDX_MASK) != GE_VTYPE_IDX_NONE) - inds = Memory::GetPointer(gstate.indexAddr); - TransformAndDrawPrim(verts, inds, type, count, linkedShader); - } - break; - - // The arrow and other rotary items in Puzbob are bezier patches, strangely enough. - case GE_CMD_BEZIER: - { - int bz_ucount = data & 0xFF; - int bz_vcount = (data >> 8) & 0xFF; - drawBezier(bz_ucount, bz_vcount); - DEBUG_LOG(G3D,"DL DRAW BEZIER: %i x %i", bz_ucount, bz_vcount); - } - break; - - case GE_CMD_SPLINE: - { - int sp_ucount = data & 0xFF; - int sp_vcount = (data >> 8) & 0xFF; - int sp_utype = (data >> 16) & 0x3; - int sp_vtype = (data >> 18) & 0x3; - //drawSpline(sp_ucount, sp_vcount, sp_utype, sp_vtype); - DEBUG_LOG(G3D,"DL DRAW SPLINE: %i x %i, %i x %i", sp_ucount, sp_vcount, sp_utype, sp_vtype); - } - break; - - case GE_CMD_JUMP: - { - u32 target = ((gstate.base << 8) | (op & 0xFFFFFC)) & 0x0FFFFFFF; - DEBUG_LOG(G3D,"DL CMD JUMP - %08x to %08x", dcontext.pc, target); - dcontext.pc = target - 4; // pc will be increased after we return, counteract that - } - break; - - case GE_CMD_CALL: - { - u32 retval = dcontext.pc + 4; - stack[stackptr++] = retval; - u32 target = ((gstate.base << 8) | (op & 0xFFFFFC)) & 0xFFFFFFF; - DEBUG_LOG(G3D,"DL CMD CALL - %08x to %08x, ret=%08x", dcontext.pc, target, retval); - dcontext.pc = target - 4; // pc will be increased after we return, counteract that - } - break; - - case GE_CMD_RET: - //TODO : debug! - { - u32 target = stack[--stackptr] & 0xFFFFFFF; - DEBUG_LOG(G3D,"DL CMD RET - from %08x to %08x", dcontext.pc, target); - dcontext.pc = target - 4; - } - break; - - case GE_CMD_SIGNAL: - ERROR_LOG(G3D, "DL GE_CMD_SIGNAL %08x", data & 0xFFFFFF); - { - // int behaviour = (data >> 16) & 0xFF; - // int signal = data & 0xFFFF; - } - - // This should generate a GE Interrupt - // __TriggerInterrupt(PSP_GE_INTR); - - // Apparently, these callbacks should be done in a special interrupt way. - //for (size_t i = 0; i < signalCallbacks.size(); i++) - //{ - // __KernelNotifyCallback(-1, signalCallbacks[i].first, signal); - //} - - break; - - case GE_CMD_BJUMP: - // bounding box jump. Let's just not jump, for now. - DEBUG_LOG(G3D,"DL BBOX JUMP - unimplemented"); - break; - - case GE_CMD_BOUNDINGBOX: - // bounding box test. Let's do nothing. - DEBUG_LOG(G3D,"DL BBOX TEST - unimplemented"); - break; - - case GE_CMD_ORIGIN: - gstate.offsetAddr = dcontext.pc & 0xFFFFFF; - break; - - case GE_CMD_VERTEXTYPE: - DEBUG_LOG(G3D,"DL SetVertexType: %06x", data); - if (diff & GE_VTYPE_THROUGH) { - // Throughmode changed, let's make the proj matrix dirty. - shaderManager.DirtyUniform(DIRTY_PROJMATRIX); - } - if (data & GE_VTYPE_THROUGH) { - glDisable(GL_CULL_FACE); - } - // This sets through-mode or not, as well. - break; - - case GE_CMD_OFFSETADDR: - // offsetAddr = data<<8; - break; - - - case GE_CMD_FINISH: - DEBUG_LOG(G3D,"DL CMD FINISH"); - // Trigger the finish callbacks - { - // Apparently, these callbacks should be done in a special interrupt way. - - //for (size_t i = 0; i < finishCallbacks.size(); i++) - //{ - // __KernelNotifyCallback(-1, finishCallbacks[i].first, 0); - //} - } - break; - - case GE_CMD_END: - DEBUG_LOG(G3D,"DL CMD END"); - { - switch (prev >> 24) - { - case GE_CMD_FINISH: - finished = true; - break; - default: - DEBUG_LOG(G3D,"Ah, not finished: %06x", prev & 0xFFFFFF); - break; - } - } - - // This should generate a Reading Ended interrupt - // __TriggerInterrupt(PSP_GE_INTR); - - break; - - case GE_CMD_REGION1: - { - int x1 = data & 0x3ff; - int y1 = data >> 10; - //topleft - DEBUG_LOG(G3D,"DL Region TL: %d %d", x1, y1); - } - break; - - case GE_CMD_REGION2: - { - int x2 = data & 0x3ff; - int y2 = data >> 10; - DEBUG_LOG(G3D,"DL Region BR: %d %d", x2, y2); - } - break; - - case GE_CMD_CLIPENABLE: - DEBUG_LOG(G3D, "DL Clip Enable: %i (ignoring)", data); - //we always clip, this is opengl - break; - - case GE_CMD_CULLFACEENABLE: - DEBUG_LOG(G3D, "DL CullFace Enable: %i (ignoring)", data); - glEnDis(GL_CULL_FACE, data&1); - break; - - case GE_CMD_TEXTUREMAPENABLE: - DEBUG_LOG(G3D, "DL Texture map enable: %i", data); - glEnDis(GL_TEXTURE_2D, data&1); - break; - - case GE_CMD_LIGHTINGENABLE: - DEBUG_LOG(G3D, "DL Lighting enable: %i", data); - data += 1; - //We don't use OpenGL lighting - break; - - case GE_CMD_FOGENABLE: - DEBUG_LOG(G3D, "DL Fog Enable: %i", gstate.fogEnable); - break; - - case GE_CMD_DITHERENABLE: - DEBUG_LOG(G3D, "DL Dither Enable: %i", gstate.ditherEnable); - break; - - case GE_CMD_OFFSETX: - DEBUG_LOG(G3D, "DL Offset X: %i", gstate.offsetx); - break; - - case GE_CMD_OFFSETY: - DEBUG_LOG(G3D, "DL Offset Y: %i", gstate.offsety); - break; - - case GE_CMD_TEXSCALEU: - gstate.uScale = getFloat24(data); - DEBUG_LOG(G3D, "DL Texture U Scale: %f", gstate.uScale); - break; - - case GE_CMD_TEXSCALEV: - gstate.vScale = getFloat24(data); - DEBUG_LOG(G3D, "DL Texture V Scale: %f", gstate.vScale); - break; - - case GE_CMD_TEXOFFSETU: - gstate.uOff = getFloat24(data); - DEBUG_LOG(G3D, "DL Texture U Offset: %f", gstate.uOff); - break; - - case GE_CMD_TEXOFFSETV: - gstate.vOff = getFloat24(data); - DEBUG_LOG(G3D, "DL Texture V Offset: %f", gstate.vOff); - break; - - case GE_CMD_SCISSOR1: - { - int x1 = data & 0x3ff; - int y1 = data >> 10; - DEBUG_LOG(G3D, "DL Scissor TL: %i, %i", x1,y1); - } - break; - case GE_CMD_SCISSOR2: - { - int x2 = data & 0x3ff; - int y2 = data >> 10; - DEBUG_LOG(G3D, "DL Scissor BR: %i, %i", x2, y2); - } - break; - - case GE_CMD_MINZ: - DEBUG_LOG(G3D, "DL MinZ: %i", data); - break; - - case GE_CMD_MAXZ: - DEBUG_LOG(G3D, "DL MaxZ: %i", data); - break; - - case GE_CMD_FRAMEBUFPTR: - { - u32 ptr = op & 0xFFE000; - DEBUG_LOG(G3D, "DL FramebufPtr: %08x", ptr); - } - break; - - case GE_CMD_FRAMEBUFWIDTH: - { - u32 w = data & 0xFFFFFF; - DEBUG_LOG(G3D, "DL FramebufWidth: %i", w); - } - break; - - case GE_CMD_FRAMEBUFPIXFORMAT: - break; - - case GE_CMD_TEXADDR0: - gstate.textureChanged=true; - case GE_CMD_TEXADDR1: - case GE_CMD_TEXADDR2: - case GE_CMD_TEXADDR3: - case GE_CMD_TEXADDR4: - case GE_CMD_TEXADDR5: - case GE_CMD_TEXADDR6: - case GE_CMD_TEXADDR7: - DEBUG_LOG(G3D,"DL Texture address %i: %06x", cmd-GE_CMD_TEXADDR0, data); - break; - - case GE_CMD_TEXBUFWIDTH0: - gstate.textureChanged=true; - case GE_CMD_TEXBUFWIDTH1: - case GE_CMD_TEXBUFWIDTH2: - case GE_CMD_TEXBUFWIDTH3: - case GE_CMD_TEXBUFWIDTH4: - case GE_CMD_TEXBUFWIDTH5: - case GE_CMD_TEXBUFWIDTH6: - case GE_CMD_TEXBUFWIDTH7: - DEBUG_LOG(G3D,"DL Texture BUFWIDTHess %i: %06x", cmd-GE_CMD_TEXBUFWIDTH0, data); - break; - - case GE_CMD_CLUTADDR: - //DEBUG_LOG(G3D,"CLUT base addr: %06x", data); - break; - - case GE_CMD_CLUTADDRUPPER: - DEBUG_LOG(G3D,"DL CLUT addr: %08x", ((gstate.clutaddrupper & 0xFF0000)<<8) | (gstate.clutaddr & 0xFFFFFF)); - break; - - case GE_CMD_LOADCLUT: - { - u32 clutAttr = ((gstate.clutaddrupper & 0xFF0000)<<8) | (gstate.clutaddr & 0xFFFFFF); - if (clutAttr) - { - u16 *clut = (u16*)Memory::GetPointer(clutAttr); - if (clut) { - int numColors = 16 * (data&0x3F); - memcpy(&gstate.paletteMem[0], clut, numColors * 2); - } - DEBUG_LOG(G3D,"DL Clut load: %i palettes", data); - } - else - { - DEBUG_LOG(G3D,"DL Empty Clut load"); - } - // Should hash and invalidate all paletted textures on use - } - break; - -// case GE_CMD_TRANSFERSRC: - - case GE_CMD_TRANSFERSRCW: - { - u32 xferSrc = gstate.transfersrc | ((data&0xFF0000)<<8); - u32 xferSrcW = gstate.transfersrcw & 1023; - DEBUG_LOG(G3D,"Block Transfer Src: %08x W: %i", xferSrc, xferSrcW); - break; - } -// case GE_CMD_TRANSFERDST: - - case GE_CMD_TRANSFERDSTW: - { - u32 xferDst= gstate.transferdst | ((data&0xFF0000)<<8); - u32 xferDstW = gstate.transferdstw & 1023; - DEBUG_LOG(G3D,"Block Transfer Dest: %08x W: %i", xferDst, xferDstW); - break; - } - - case GE_CMD_TRANSFERSRCPOS: - { - u32 x = (data & 1023)+1; - u32 y = ((data>>10) & 1023)+1; - DEBUG_LOG(G3D, "DL Block Transfer Src Rect TL: %i, %i", x, y); - break; - } - - case GE_CMD_TRANSFERDSTPOS: - { - u32 x = (data & 1023)+1; - u32 y = ((data>>10) & 1023)+1; - DEBUG_LOG(G3D, "DL Block Transfer Dest Rect TL: %i, %i", x, y); - break; - } - - case GE_CMD_TRANSFERSIZE: - { - u32 w = (data & 1023)+1; - u32 h = ((data>>10) & 1023)+1; - DEBUG_LOG(G3D, "DL Block Transfer Rect Size: %i x %i", w, h); - break; - } - - case GE_CMD_TRANSFERSTART: - { - DEBUG_LOG(G3D, "DL Texture Transfer Start: PixFormat %i", data); - // TODO: Here we should check if the transfer overlaps a framebuffer or any textures, - // and take appropriate action. If not, this should just be a block transfer within - // GPU memory which could be implemented by a copy loop. - break; - } - - case GE_CMD_TEXSIZE0: - gstate.textureChanged=true; - gstate.curTextureWidth = 1 << (gstate.texsize[0] & 0xf); - gstate.curTextureHeight = 1 << ((gstate.texsize[0]>>8) & 0xf); - //fall thru - ignoring the mipmap sizes for now - case GE_CMD_TEXSIZE1: - case GE_CMD_TEXSIZE2: - case GE_CMD_TEXSIZE3: - case GE_CMD_TEXSIZE4: - case GE_CMD_TEXSIZE5: - case GE_CMD_TEXSIZE6: - case GE_CMD_TEXSIZE7: - DEBUG_LOG(G3D,"DL Texture Size: %06x", data); - break; - - case GE_CMD_ZBUFPTR: - { - u32 ptr = op & 0xFFE000; - DEBUG_LOG(G3D,"Zbuf Ptr: %06x", ptr); - } - break; - - case GE_CMD_ZBUFWIDTH: - { - u32 w = data & 0xFFFFFF; - DEBUG_LOG(G3D,"Zbuf Width: %i", w); - } - break; - - case GE_CMD_AMBIENTCOLOR: - DEBUG_LOG(G3D,"DL Ambient Color: %06x", data); - break; - - case GE_CMD_AMBIENTALPHA: - DEBUG_LOG(G3D,"DL Ambient Alpha: %06x", data); - break; - - case GE_CMD_MATERIALAMBIENT: - DEBUG_LOG(G3D,"DL Material Ambient Color: %06x", data); - break; - - case GE_CMD_MATERIALDIFFUSE: - DEBUG_LOG(G3D,"DL Material Diffuse Color: %06x", data); - break; - - case GE_CMD_MATERIALEMISSIVE: - DEBUG_LOG(G3D,"DL Material Emissive Color: %06x", data); - break; - - case GE_CMD_MATERIALSPECULAR: - DEBUG_LOG(G3D,"DL Material Specular Color: %06x", data); - break; - - case GE_CMD_MATERIALALPHA: - DEBUG_LOG(G3D,"DL Material Alpha Color: %06x", data); - break; - - case GE_CMD_MATERIALSPECULARCOEF: - DEBUG_LOG(G3D,"DL Material specular coef: %f", getFloat24(data)); - break; - - case GE_CMD_LIGHTTYPE0: - case GE_CMD_LIGHTTYPE1: - case GE_CMD_LIGHTTYPE2: - case GE_CMD_LIGHTTYPE3: - DEBUG_LOG(G3D,"DL Light %i type: %06x", cmd-GE_CMD_LIGHTTYPE0, data); - break; - - case GE_CMD_LX0:case GE_CMD_LY0:case GE_CMD_LZ0: - case GE_CMD_LX1:case GE_CMD_LY1:case GE_CMD_LZ1: - case GE_CMD_LX2:case GE_CMD_LY2:case GE_CMD_LZ2: - case GE_CMD_LX3:case GE_CMD_LY3:case GE_CMD_LZ3: - { - int n = cmd - GE_CMD_LX0; - int l = n / 3; - int c = n % 3; - float val = getFloat24(data); - DEBUG_LOG(G3D,"DL Light %i %c pos: %f", l, c+'X', val); - gstate.lightpos[l][c] = val; - } - break; - - case GE_CMD_LDX0:case GE_CMD_LDY0:case GE_CMD_LDZ0: - case GE_CMD_LDX1:case GE_CMD_LDY1:case GE_CMD_LDZ1: - case GE_CMD_LDX2:case GE_CMD_LDY2:case GE_CMD_LDZ2: - case GE_CMD_LDX3:case GE_CMD_LDY3:case GE_CMD_LDZ3: - { - int n = cmd - GE_CMD_LDX0; - int l = n / 3; - int c = n % 3; - float val = getFloat24(data); - DEBUG_LOG(G3D,"DL Light %i %c dir: %f", l, c+'X', val); - gstate.lightdir[l][c] = val; - } - break; - - case GE_CMD_LKA0:case GE_CMD_LKB0:case GE_CMD_LKC0: - case GE_CMD_LKA1:case GE_CMD_LKB1:case GE_CMD_LKC1: - case GE_CMD_LKA2:case GE_CMD_LKB2:case GE_CMD_LKC2: - case GE_CMD_LKA3:case GE_CMD_LKB3:case GE_CMD_LKC3: - { - int n = cmd - GE_CMD_LKA0; - int l = n / 3; - int c = n % 3; - float val = getFloat24(data); - DEBUG_LOG(G3D,"DL Light %i %c att: %f", l, c+'X', val); - gstate.lightatt[l][c] = val; - } - break; - - - case GE_CMD_LAC0:case GE_CMD_LAC1:case GE_CMD_LAC2:case GE_CMD_LAC3: - case GE_CMD_LDC0:case GE_CMD_LDC1:case GE_CMD_LDC2:case GE_CMD_LDC3: - case GE_CMD_LSC0:case GE_CMD_LSC1:case GE_CMD_LSC2:case GE_CMD_LSC3: - { - float r = (float)(data>>16)/255.0f; - float g = (float)((data>>8) & 0xff)/255.0f; - float b = (float)(data & 0xff)/255.0f; - - int l = (cmd - GE_CMD_LAC0) / 3; - int t = (cmd - GE_CMD_LAC0) % 3; - gstate.lightColor[t][l].r = r; - gstate.lightColor[t][l].g = g; - gstate.lightColor[t][l].b = b; - } - break; - - case GE_CMD_VIEWPORTX1: - case GE_CMD_VIEWPORTY1: - case GE_CMD_VIEWPORTZ1: - case GE_CMD_VIEWPORTX2: - case GE_CMD_VIEWPORTY2: - case GE_CMD_VIEWPORTZ2: - DEBUG_LOG(G3D,"DL Viewport param %i: %f", cmd-GE_CMD_VIEWPORTX1, getFloat24(data)); - break; - case GE_CMD_LIGHTENABLE0: - case GE_CMD_LIGHTENABLE1: - case GE_CMD_LIGHTENABLE2: - case GE_CMD_LIGHTENABLE3: - DEBUG_LOG(G3D,"DL Light %i enable: %d", cmd-GE_CMD_LIGHTENABLE0, data); - break; - case GE_CMD_CULL: - DEBUG_LOG(G3D,"DL cull: %06x", data); - glCullFace(data ? GL_BACK : GL_FRONT); - break; - - case GE_CMD_LMODE: - DEBUG_LOG(G3D,"DL Shade mode: %06x", data); - break; - - case GE_CMD_PATCHDIVISION: - gstate.patch_div_s = data & 0xFF; - gstate.patch_div_t = (data >> 8) & 0xFF; - DEBUG_LOG(G3D, "DL Patch subdivision: %i x %i", gstate.patch_div_s, gstate.patch_div_t); - break; - - case GE_CMD_MATERIALUPDATE: - DEBUG_LOG(G3D,"DL Material Update: %d", data); - break; - - - ////////////////////////////////////////////////////////////////// - // CLEARING - ////////////////////////////////////////////////////////////////// - case GE_CMD_CLEARMODE: - // If it becomes a performance problem, check diff&1 - if (data & 1) - EnterClearMode(data); - else - LeaveClearMode(); - DEBUG_LOG(G3D,"DL Clear mode: %06x", data); - break; - - - ////////////////////////////////////////////////////////////////// - // ALPHA BLENDING - ////////////////////////////////////////////////////////////////// - case GE_CMD_ALPHABLENDENABLE: - DEBUG_LOG(G3D,"DL Alpha blend enable: %d", data); - glEnDis(GL_BLEND, data); - break; - - case GE_CMD_BLENDMODE: - DEBUG_LOG(G3D,"DL Blend mode: %06x", data); - SetBlendModePSP(data); - break; - - case GE_CMD_BLENDFIXEDA: - DEBUG_LOG(G3D,"DL Blend fix A: %06x", data); - break; - - case GE_CMD_BLENDFIXEDB: - DEBUG_LOG(G3D,"DL Blend fix B: %06x", data); - break; - - case GE_CMD_ALPHATESTENABLE: - DEBUG_LOG(G3D,"DL Alpha test enable: %d", data); - // This is done in the shader. - break; - - case GE_CMD_ALPHATEST: - DEBUG_LOG(G3D,"DL Alpha test settings"); - shaderManager.DirtyUniform(DIRTY_ALPHAREF); - break; - - case GE_CMD_TEXFUNC: - { - DEBUG_LOG(G3D,"DL TexFunc %i", data&7); - /* - int m=GL_MODULATE; - switch (data & 7) - { - case 0: m=GL_MODULATE; break; - case 1: m=GL_DECAL; break; - case 2: m=GL_BLEND; break; - case 3: m=GL_REPLACE; break; - case 4: m=GL_ADD; break; - }*/ - - /* - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); - glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_CONSTANT); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_TEXTURE); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); - glTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE, 1); - - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, m); - glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE);*/ - break; - } - case GE_CMD_TEXFILTER: - { - int min = data & 7; - int mag = (data >> 8) & 1; - DEBUG_LOG(G3D,"DL TexFilter min: %i mag: %i", min, mag); - } - - break; - ////////////////////////////////////////////////////////////////// - // Z/STENCIL TESTING - ////////////////////////////////////////////////////////////////// - - case GE_CMD_ZTESTENABLE: - glEnDis(GL_DEPTH_TEST, data & 1); - DEBUG_LOG(G3D,"DL Z test enable: %d", data & 1); - break; - - case GE_CMD_STENCILTESTENABLE: - DEBUG_LOG(G3D,"DL Stencil test enable: %d", data); - break; - - case GE_CMD_ZTEST: - { - static const GLuint ztests[8] = - { - GL_NEVER, GL_ALWAYS, GL_EQUAL, GL_NOTEQUAL, - GL_LESS, GL_LEQUAL, GL_GREATER, GL_GEQUAL - }; - //glDepthFunc(ztests[data&7]); - glDepthFunc(GL_LEQUAL); - DEBUG_LOG(G3D,"DL Z test mode: %i", data); - } - break; - - case GE_CMD_MORPHWEIGHT0: - case GE_CMD_MORPHWEIGHT1: - case GE_CMD_MORPHWEIGHT2: - case GE_CMD_MORPHWEIGHT3: - case GE_CMD_MORPHWEIGHT4: - case GE_CMD_MORPHWEIGHT5: - case GE_CMD_MORPHWEIGHT6: - case GE_CMD_MORPHWEIGHT7: - { - int index = cmd - GE_CMD_MORPHWEIGHT0; - float weight = getFloat24(data); - DEBUG_LOG(G3D,"DL MorphWeight %i = %f", index, weight); - gstate.morphWeights[index] = weight; - } - break; - - case GE_CMD_DITH0: - case GE_CMD_DITH1: - case GE_CMD_DITH2: - case GE_CMD_DITH3: - DEBUG_LOG(G3D,"DL DitherMatrix %i = %06x",cmd-GE_CMD_DITH0,data); - break; - - case GE_CMD_WORLDMATRIXNUMBER: - DEBUG_LOG(G3D,"DL World matrix # %i", data); - gstate.worldmtxnum = data&0xF; - break; - - case GE_CMD_WORLDMATRIXDATA: - DEBUG_LOG(G3D,"DL World matrix data # %f", getFloat24(data)); - gstate.worldMatrix[gstate.worldmtxnum++] = getFloat24(data); - break; - - case GE_CMD_VIEWMATRIXNUMBER: - DEBUG_LOG(G3D,"DL VIEW matrix # %i", data); - gstate.viewmtxnum = data&0xF; - break; - - case GE_CMD_VIEWMATRIXDATA: - DEBUG_LOG(G3D,"DL VIEW matrix data # %f", getFloat24(data)); - gstate.viewMatrix[gstate.viewmtxnum++] = getFloat24(data); - break; - - case GE_CMD_PROJMATRIXNUMBER: - DEBUG_LOG(G3D,"DL PROJECTION matrix # %i", data); - gstate.projmtxnum = data&0xF; - break; - - case GE_CMD_PROJMATRIXDATA: - DEBUG_LOG(G3D,"DL PROJECTION matrix data # %f", getFloat24(data)); - gstate.projMatrix[gstate.projmtxnum++] = getFloat24(data); - shaderManager.DirtyUniform(DIRTY_PROJMATRIX); - break; - - case GE_CMD_TGENMATRIXNUMBER: - DEBUG_LOG(G3D,"DL TGEN matrix # %i", data); - gstate.texmtxnum = data&0xF; - break; - - case GE_CMD_TGENMATRIXDATA: - DEBUG_LOG(G3D,"DL TGEN matrix data # %f", getFloat24(data)); - gstate.tgenMatrix[gstate.texmtxnum++] = getFloat24(data); - break; - - case GE_CMD_BONEMATRIXNUMBER: - DEBUG_LOG(G3D,"DL BONE matrix #%i", data); - gstate.boneMatrixNumber = data; - break; - - case GE_CMD_BONEMATRIXDATA: - DEBUG_LOG(G3D,"DL BONE matrix data #%i %f", gstate.boneMatrixNumber, getFloat24(data)); - gstate.boneMatrix[gstate.boneMatrixNumber++] = getFloat24(data); - break; - - default: - DEBUG_LOG(G3D,"DL Unknown: %08x @ %08x", op, dcontext.pc); - break; - - //ETC... - } -} - -bool GPU::InterpretList() -{ - // Reset stackptr for safety - stackptr = 0; - u32 op = 0; - prev = 0; - finished = false; - while (!finished) - { - if (dcontext.pc == dcontext.stallAddr) - return false; - - op = Memory::ReadUnchecked_U32(dcontext.pc); //read from memory - u32 cmd = op >> 24; - u32 diff = op ^ gstate.cmdmem[cmd]; - gstate.cmdmem[cmd] = op; // crashes if I try to put the whole op there?? - - ExecuteOp(op, diff); - - dcontext.pc += 4; - prev = op; - } - return true; -} diff --git a/GPU/Null/NullGpu.cpp b/GPU/Null/NullGpu.cpp index 7174166e66..80d7fc9652 100644 --- a/GPU/Null/NullGpu.cpp +++ b/GPU/Null/NullGpu.cpp @@ -46,6 +46,17 @@ static bool finished; static int dlIdGenerator = 1; +NullGPU::NullGPU() +{ + interruptsEnabled_ = true; + dlIdGenerator = 1; +} + +NullGPU::~NullGPU() +{ + dlQueue.clear(); +} + bool NullGPU::ProcessDLQueue() { std::vector::iterator iter = dlQueue.begin(); @@ -206,8 +217,9 @@ void NullGPU::ExecuteOp(u32 op, u32 diff) int behaviour = (data >> 16) & 0xFF; int signal = data & 0xFFFF; + // TODO: Should this run while interrupts are suspended? if (interruptsEnabled_) - __TriggerInterruptWithArg(PSP_GE_INTR, PSP_GE_SUBINTR_SIGNAL, signal); + __TriggerInterruptWithArg(PSP_INTR_HLE, PSP_GE_INTR, PSP_GE_SUBINTR_SIGNAL, signal); } break; @@ -237,8 +249,9 @@ void NullGPU::ExecuteOp(u32 op, u32 diff) case GE_CMD_FINISH: DEBUG_LOG(G3D,"DL CMD FINISH"); + // TODO: Should this run while interrupts are suspended? if (interruptsEnabled_) - __TriggerInterruptWithArg(PSP_GE_INTR, PSP_GE_SUBINTR_FINISH, 0); + __TriggerInterruptWithArg(PSP_INTR_HLE, PSP_GE_INTR, PSP_GE_SUBINTR_FINISH, 0); break; case GE_CMD_END: @@ -594,9 +607,9 @@ void NullGPU::ExecuteOp(u32 op, u32 diff) int l = (cmd - GE_CMD_LAC0) / 3; int t = (cmd - GE_CMD_LAC0) % 3; - gstate_c.lightColor[t][l].r = r; - gstate_c.lightColor[t][l].g = g; - gstate_c.lightColor[t][l].b = b; + gstate_c.lightColor[t][l][0] = r; + gstate_c.lightColor[t][l][1] = g; + gstate_c.lightColor[t][l][2] = b; } break; @@ -623,9 +636,6 @@ void NullGPU::ExecuteOp(u32 op, u32 diff) break; case GE_CMD_PATCHDIVISION: - gstate_c.patch_div_s = data & 0xFF; - gstate_c.patch_div_t = (data >> 8) & 0xFF; - DEBUG_LOG(G3D, "DL Patch subdivision: %i x %i", gstate_c.patch_div_s, gstate_c.patch_div_t); break; case GE_CMD_MATERIALUPDATE: @@ -836,3 +846,8 @@ void NullGPU::UpdateStats() gpuStats.numShaders = 0; gpuStats.numTextures = 0; } + +void NullGPU::InvalidateCache(u32 addr, int size) +{ + // Nothing to invalidate. +} diff --git a/GPU/Null/NullGpu.h b/GPU/Null/NullGpu.h index 4acbf6895c..5e9f163713 100644 --- a/GPU/Null/NullGpu.h +++ b/GPU/Null/NullGpu.h @@ -24,7 +24,8 @@ class ShaderManager; class NullGPU : public GPUInterface { public: - NullGPU() : interruptsEnabled_(true) {} + NullGPU(); + ~NullGPU(); virtual void InitClear() {} virtual u32 EnqueueList(u32 listpc, u32 stall); virtual void UpdateStall(int listid, u32 newstall); @@ -40,6 +41,11 @@ public: virtual void SetDisplayFramebuffer(u32 framebuf, u32 stride, int format) {} virtual void CopyDisplayToOutput() {} virtual void UpdateStats(); + virtual void InvalidateCache(u32 addr, int size); + virtual void Flush() {} + + virtual void DeviceLost() {} + virtual void DumpNextFrame() {} private: bool ProcessDLQueue(); diff --git a/GPU/ge_constants.h b/GPU/ge_constants.h index d23449f88c..6ae1d554e3 100644 --- a/GPU/ge_constants.h +++ b/GPU/ge_constants.h @@ -52,11 +52,11 @@ enum GECommand GE_CMD_DITHERENABLE, GE_CMD_ALPHABLENDENABLE=0x21, GE_CMD_ALPHATESTENABLE=0x22, - GE_CMD_ZTESTENABLE, + GE_CMD_ZTESTENABLE=0x23, GE_CMD_STENCILTESTENABLE=0x24, - GE_CMD_ANTIALIASENABLE, + GE_CMD_ANTIALIASENABLE=0x25, GE_CMD_PATCHCULLENABLE=0x26, - GE_CMD_COLORTESTENABLE, + GE_CMD_COLORTESTENABLE=0x27, GE_CMD_LOGICOPENABLE=0x28, GE_CMD_BONEMATRIXNUMBER=0x2A, GE_CMD_BONEMATRIXDATA=0x2B, @@ -69,7 +69,7 @@ enum GECommand GE_CMD_MORPHWEIGHT6=0x32, GE_CMD_MORPHWEIGHT7=0x33, GE_CMD_PATCHDIVISION=0x36, - GE_CMD_PATCHPRIMIIVE=0x37, + GE_CMD_PATCHPRIMITIVE=0x37, GE_CMD_PATCHFACING=0x38, GE_CMD_WORLDMATRIXNUMBER=0x3A, GE_CMD_WORLDMATRIXDATA, diff --git a/Qt/Common.pro b/Qt/Common.pro index 350da2f1ab..7817c8ab3e 100755 --- a/Qt/Common.pro +++ b/Qt/Common.pro @@ -4,65 +4,75 @@ TARGET = Common TEMPLATE = lib CONFIG += staticlib -blackberry: { - QMAKE_CC = ntoarmv7-gcc - QMAKE_CXX = ntoarmv7-g++ - DEFINES += "_QNX_SOURCE=1" "_C99=1" +include(Settings.pri) + +arm { + SOURCES += ../Common/ArmABI.cpp \ + ../Common/ArmEmitter.cpp \ + ../Common/ThunkARM.cpp + HEADERS += ../Common/ArmABI.h \ + ../Common/ArmEmitter.h +} +x86 { + SOURCES += ../Common/ABI.cpp \ + ../Common/CPUDetect.cpp \ + ../Common/MathUtil.cpp \ + ../Common/Thunk.cpp \ + ../Common/x64Analyzer.cpp \ + ../Common/x64Emitter.cpp + HEADERS += ../Common/ABI.h \ + ../Common/CPUDetect.h \ + ../Common/MathUtil.h \ + ../Common/Thunk.h \ + ../Common/x64Analyzer.h \ + ../Common/x64Emitter.h +} +win32 { + SOURCES += ../Common/stdafx.cpp + HEADERS += ../Common/stdafx.h } -SOURCES += ../Common/ArmABI.cpp \ - ../Common/ArmEmitter.cpp \ - ../Common/ThunkARM.cpp \ - ../Common/Action.cpp \ - ../Common/ColorUtil.cpp \ - ../Common/ConsoleListener.cpp \ - ../Common/Crypto/aes_cbc.cpp \ - ../Common/Crypto/aes_core.cpp \ - ../Common/Crypto/bn.cpp \ - ../Common/Crypto/ec.cpp \ - ../Common/Crypto/md5.cpp \ - ../Common/Crypto/sha1.cpp \ - ../Common/ExtendedTrace.cpp \ - ../Common/FPURoundModeGeneric.cpp \ - ../Common/FileSearch.cpp \ - ../Common/FileUtil.cpp \ - ../Common/Hash.cpp \ - ../Common/IniFile.cpp \ - ../Common/LogManager.cpp \ - ../Common/MemArena.cpp \ - ../Common/MemoryUtil.cpp \ - ../Common/Misc.cpp \ - ../Common/MathUtil.cpp \ - ../Common/MsgHandler.cpp \ - ../Common/StringUtil.cpp \ - ../Common/Thread.cpp \ - ../Common/Timer.cpp \ - ../Common/Version.cpp -HEADERS += ../Common/ArmABI.h \ - ../Common/ArmEmitter.h \ - ../Common/Action.h \ - ../Common/ColorUtil.h \ - ../Common/ConsoleListener.h \ - ../Common/Crypto/md5.h \ - ../Common/Crypto/sha1.h \ - ../Common/ExtendedTrace.h \ - ../Common/FileSearch.h \ - ../Common/FileUtil.h \ - ../Common/Hash.h \ - ../Common/IniFile.h \ - ../Common/LogManager.h \ - ../Common/MemArena.h \ - ../Common/MemoryUtil.h \ - ../Common/MathUtil.h \ - ../Common/MsgHandler.h \ - ../Common/StringUtil.h \ - ../Common/Thread.h \ - ../Common/Timer.h +SOURCES += ../Common/Action.cpp \ + ../Common/ColorUtil.cpp \ + ../Common/ConsoleListener.cpp \ + ../Common/Crypto/aes_cbc.cpp \ + ../Common/Crypto/aes_core.cpp \ + ../Common/Crypto/bn.cpp \ + ../Common/Crypto/ec.cpp \ + ../Common/Crypto/md5.cpp \ + ../Common/Crypto/sha1.cpp \ + ../Common/ExtendedTrace.cpp \ + ../Common/FPURoundModeGeneric.cpp \ + ../Common/FileSearch.cpp \ + ../Common/FileUtil.cpp \ + ../Common/Hash.cpp \ + ../Common/IniFile.cpp \ + ../Common/LogManager.cpp \ + ../Common/MemArena.cpp \ + ../Common/MemoryUtil.cpp \ + ../Common/Misc.cpp \ + ../Common/MathUtil.cpp \ + ../Common/MsgHandler.cpp \ + ../Common/StringUtil.cpp \ + ../Common/Thread.cpp \ + ../Common/Timer.cpp \ + ../Common/Version.cpp +HEADERS += ../Common/Action.h \ + ../Common/ColorUtil.h \ + ../Common/ConsoleListener.h \ + ../Common/Crypto/md5.h \ + ../Common/Crypto/sha1.h \ + ../Common/ExtendedTrace.h \ + ../Common/FileSearch.h \ + ../Common/FileUtil.h \ + ../Common/Hash.h \ + ../Common/IniFile.h \ + ../Common/LogManager.h \ + ../Common/MemArena.h \ + ../Common/MemoryUtil.h \ + ../Common/MathUtil.h \ + ../Common/MsgHandler.h \ + ../Common/StringUtil.h \ + ../Common/Thread.h \ + ../Common/Timer.h -QMAKE_CXXFLAGS += -std=c++0x -Wno-unused-function -Wno-unused-variable -Wno-multichar -Wno-uninitialized -Wno-ignored-qualifiers -Wno-missing-field-initializers -Wno-unused-parameter -DEFINES += ARM USING_GLES2 -blackberry: DEFINES += BLACKBERRY BLACKBERRY10 -symbian: { - QMAKE_CXXFLAGS += -march=armv6 -mfpu=vfp -mfloat-abi=softfp -marm -Wno-parentheses -Wno-comment - DEFINES += SYMBIAN -} diff --git a/Qt/Core.pro b/Qt/Core.pro index 2b351629f3..6c2cada922 100755 --- a/Qt/Core.pro +++ b/Qt/Core.pro @@ -4,236 +4,252 @@ TARGET = Core TEMPLATE = lib CONFIG += staticlib -blackberry: { - QMAKE_CC = ntoarmv7-gcc - QMAKE_CXX = ntoarmv7-g++ - DEFINES += "_QNX_SOURCE=1" "_C99=1" -} +include(Settings.pri) INCLUDEPATH += ../native ../Common ../ -SOURCES += ../Core/MIPS/ARM/Asm.cpp \ #CoreARM - ../Core/MIPS/ARM/CompALU.cpp \ - ../Core/MIPS/ARM/CompBranch.cpp \ - ../Core/MIPS/ARM/CompFPU.cpp \ - ../Core/MIPS/ARM/CompLoadStore.cpp \ - ../Core/MIPS/ARM/CompVFPU.cpp \ - ../Core/MIPS/ARM/Jit.cpp \ - ../Core/MIPS/ARM/JitCache.cpp \ - ../Core/MIPS/ARM/RegCache.cpp \ - ../Core/CPU.cpp \ # Core - ../Core/Config.cpp \ - ../Core/Core.cpp \ - ../Core/CoreTiming.cpp \ - ../Core/Debugger/Breakpoints.cpp \ - ../Core/Debugger/SymbolMap.cpp \ - ../Core/Dialog/PSPDialog.cpp \ - ../Core/Dialog/PSPMsgDialog.cpp \ - ../Core/Dialog/PSPOskDialog.cpp \ - ../Core/Dialog/PSPPlaceholderDialog.cpp \ - ../Core/Dialog/PSPSaveDialog.cpp \ - ../Core/Dialog/SavedataParam.cpp \ - ../Core/ELF/ElfReader.cpp \ - ../Core/ELF/PrxDecrypter.cpp \ - ../Core/ELF/ParamSFO.cpp \ - ../Core/FileSystems/BlockDevices.cpp \ - ../Core/FileSystems/DirectoryFileSystem.cpp \ - ../Core/FileSystems/ISOFileSystem.cpp \ - ../Core/FileSystems/MetaFileSystem.cpp \ - ../Core/HLE/HLE.cpp \ - ../Core/HLE/HLETables.cpp \ - ../Core/HLE/__sceAudio.cpp \ - ../Core/HLE/sceAtrac.cpp \ - ../Core/HLE/sceAudio.cpp \ - ../Core/HLE/sceCtrl.cpp \ - ../Core/HLE/sceDisplay.cpp \ - ../Core/HLE/sceDmac.cpp \ - ../Core/HLE/sceGe.cpp \ - ../Core/HLE/sceFont.cpp \ - ../Core/HLE/sceHprm.cpp \ - ../Core/HLE/sceHttp.cpp \ - ../Core/HLE/sceImpose.cpp \ - ../Core/HLE/sceIo.cpp \ - ../Core/HLE/sceKernel.cpp \ - ../Core/HLE/sceKernelAlarm.cpp \ - ../Core/HLE/sceKernelEventFlag.cpp \ - ../Core/HLE/sceKernelInterrupt.cpp \ - ../Core/HLE/sceKernelMbx.cpp \ - ../Core/HLE/sceKernelMemory.cpp \ - ../Core/HLE/sceKernelModule.cpp \ - ../Core/HLE/sceKernelMsgPipe.cpp \ - ../Core/HLE/sceKernelMutex.cpp \ - ../Core/HLE/sceKernelSemaphore.cpp \ - ../Core/HLE/sceKernelThread.cpp \ - ../Core/HLE/sceKernelThread.h \ - ../Core/HLE/sceKernelTime.cpp \ - ../Core/HLE/sceKernelVTimer.cpp \ - ../Core/HLE/sceMpeg.cpp \ - ../Core/HLE/sceNet.cpp \ - ../Core/HLE/sceOpenPSID.cpp \ - ../Core/HLE/sceParseHttp.cpp \ - ../Core/HLE/sceParseUri.cpp \ - ../Core/HLE/scePower.cpp \ - ../Core/HLE/scePsmf.cpp \ - ../Core/HLE/sceRtc.cpp \ - ../Core/HLE/sceSas.cpp \ - ../Core/HLE/sceSsl.cpp \ - ../Core/HLE/scesupPreAcc.cpp \ - ../Core/HLE/sceUmd.cpp \ - ../Core/HLE/sceUtility.cpp \ - ../Core/HLE/sceVaudio.cpp \ - ../Core/HW/MemoryStick.cpp \ - ../Core/Host.cpp \ - ../Core/Loaders.cpp \ - ../Core/MIPS/JitCommon/JitCommon.cpp \ - ../Core/MIPS/MIPS.cpp \ - ../Core/MIPS/MIPSAnalyst.cpp \ - ../Core/MIPS/MIPSCodeUtils.cpp \ - ../Core/MIPS/MIPSDebugInterface.cpp \ - ../Core/MIPS/MIPSDis.cpp \ - ../Core/MIPS/MIPSDisVFPU.cpp \ - ../Core/MIPS/MIPSInt.cpp \ - ../Core/MIPS/MIPSIntVFPU.cpp \ - ../Core/MIPS/MIPSTables.cpp \ - ../Core/MIPS/MIPSVFPUUtils.cpp \ - ../Core/MemMap.cpp \ - ../Core/MemMapFunctions.cpp \ - ../Core/PSPLoaders.cpp \ - ../Core/PSPMixer.cpp \ - ../Core/System.cpp \ - ../Core/Util/BlockAllocator.cpp \ - ../Core/Util/PPGeDraw.cpp \ - ../Core/Util/ppge_atlas.cpp \ # GPU - ../GPU/GLES/DisplayListInterpreter.cpp \ - ../GPU/GLES/FragmentShaderGenerator.cpp \ - ../GPU/GLES/Framebuffer.cpp \ - ../GPU/GLES/ShaderManager.cpp \ - ../GPU/GLES/StateMapping.cpp \ - ../GPU/GLES/TextureCache.cpp \ - ../GPU/GLES/TransformPipeline.cpp \ - ../GPU/GLES/VertexDecoder.cpp \ - ../GPU/GLES/VertexShaderGenerator.cpp \ - ../GPU/GPUState.cpp \ - ../GPU/Math3D.cpp \ - ../GPU/Null/NullGpu.cpp \ # Kirk - ../ext/libkirk/AES.c \ - ../ext/libkirk/SHA1.c \ - ../ext/libkirk/bn.c \ - ../ext/libkirk/ec.c \ - ../ext/libkirk/kirk_engine.c +arm { + SOURCES += ../Core/MIPS/ARM/Asm.cpp \ #CoreARM + ../Core/MIPS/ARM/CompALU.cpp \ + ../Core/MIPS/ARM/CompBranch.cpp \ + ../Core/MIPS/ARM/CompFPU.cpp \ + ../Core/MIPS/ARM/CompLoadStore.cpp \ + ../Core/MIPS/ARM/CompVFPU.cpp \ + ../Core/MIPS/ARM/Jit.cpp \ + ../Core/MIPS/ARM/JitCache.cpp \ + ../Core/MIPS/ARM/RegCache.cpp -HEADERS += ../Core/MIPS/ARM/Asm.h \ - ../Core/MIPS/ARM/Jit.h \ - ../Core/MIPS/ARM/JitCache.h \ - ../Core/MIPS/ARM/RegCache.h \ - ../Core/CPU.h \ - ../Core/Config.h \ - ../Core/Core.h \ - ../Core/CoreParameter.h \ - ../Core/CoreTiming.h \ - ../Core/Debugger/Breakpoints.h \ - ../Core/Debugger/DebugInterface.h \ - ../Core/Debugger/SymbolMap.h \ - ../Core/Dialog/PSPDialog.h \ - ../Core/Dialog/PSPMsgDialog.h \ - ../Core/Dialog/PSPOskDialog.h \ - ../Core/Dialog/PSPPlaceholderDialog.h \ - ../Core/Dialog/PSPSaveDialog.h \ - ../Core/Dialog/SavedataParam.h \ - ../Core/ELF/ElfReader.h \ - ../Core/ELF/ElfTypes.h \ - ../Core/ELF/PrxDecrypter.h \ - ../Core/ELF/ParamSFO.h \ - ../Core/FileSystems/BlockDevices.h \ - ../Core/FileSystems/DirectoryFileSystem.h \ - ../Core/FileSystems/FileSystem.h \ - ../Core/FileSystems/ISOFileSystem.h \ - ../Core/FileSystems/MetaFileSystem.h \ - ../Core/HLE/FunctionWrappers.h \ - ../Core/HLE/HLE.h \ - ../Core/HLE/HLETables.h \ - ../Core/HLE/__sceAudio.h \ - ../Core/HLE/sceAtrac.h \ - ../Core/HLE/sceAudio.h \ - ../Core/HLE/sceCtrl.h \ - ../Core/HLE/sceDisplay.h \ - ../Core/HLE/sceDmac.h \ - ../Core/HLE/sceGe.h \ - ../Core/HLE/sceFont.h \ - ../Core/HLE/sceHprm.h \ - ../Core/HLE/sceHttp.h \ - ../Core/HLE/sceImpose.h \ - ../Core/HLE/sceIo.h \ - ../Core/HLE/sceKernel.h \ - ../Core/HLE/sceKernelAlarm.h \ - ../Core/HLE/sceKernelEventFlag.h \ - ../Core/HLE/sceKernelInterrupt.h \ - ../Core/HLE/sceKernelMbx.h \ - ../Core/HLE/sceKernelMemory.h \ - ../Core/HLE/sceKernelModule.h \ - ../Core/HLE/sceKernelMsgPipe.h \ - ../Core/HLE/sceKernelMutex.h \ - ../Core/HLE/sceKernelSemaphore.h \ - ../Core/HLE/sceMpeg.h \ - ../Core/HLE/sceNet.h \ - ../Core/HLE/sceOpenPSID.h \ - ../Core/HLE/sceParseHttp.h \ - ../Core/HLE/sceParseUri.h \ - ../Core/HLE/scePower.h \ - ../Core/HLE/scePsmf.h \ - ../Core/HLE/sceRtc.h \ - ../Core/HLE/sceSas.h \ - ../Core/HLE/sceSsl.h \ - ../Core/HLE/scesupPreAcc.h \ - ../Core/HLE/sceUmd.h \ - ../Core/HLE/sceUtility.h \ - ../Core/HLE/sceVaudio.h \ - ../Core/HW/MemoryStick.h \ - ../Core/Host.h \ - ../Core/Loaders.h \ - ../Core/MIPS/JitCommon/JitCommon.h \ - ../Core/MIPS/MIPS.h \ - ../Core/MIPS/MIPSAnalyst.h \ - ../Core/HLE/sceKernelTime.h \ - ../Core/HLE/sceKernelVTimer.h \ - ../Core/MIPS/MIPSCodeUtils.h \ - ../Core/MIPS/MIPSDebugInterface.h \ - ../Core/MIPS/MIPSDis.h \ - ../Core/MIPS/MIPSDisVFPU.h \ - ../Core/MIPS/MIPSInt.h \ - ../Core/MIPS/MIPSIntVFPU.h \ - ../Core/MIPS/MIPSTables.h \ - ../Core/MIPS/MIPSVFPUUtils.h \ - ../Core/MemMap.h \ - ../Core/PSPLoaders.h \ - ../Core/PSPMixer.h \ - ../Core/System.h \ - ../Core/Util/BlockAllocator.h \ - ../Core/Util/PPGeDraw.h \ - ../Core/Util/Pool.h \ - ../Core/Util/ppge_atlas.h \ - ../GPU/GLES/DisplayListInterpreter.h \ - ../GPU/GLES/FragmentShaderGenerator.h \ - ../GPU/GLES/Framebuffer.h \ - ../GPU/GLES/ShaderManager.h \ - ../GPU/GLES/StateMapping.h \ - ../GPU/GLES/TextureCache.h \ - ../GPU/GLES/TransformPipeline.h \ - ../GPU/GLES/VertexDecoder.h \ - ../GPU/GLES/VertexShaderGenerator.h \ - ../GPU/GPUInterface.h \ - ../GPU/GPUState.h \ - ../GPU/Math3D.h \ - ../GPU/Null/NullGpu.h \ - ../GPU/ge_constants.h \ - ../ext/libkirk/AES.h \ - ../ext/libkirk/SHA1.h \ - ../ext/libkirk/kirk_engine.h - -QMAKE_CXXFLAGS += -std=c++0x -Wno-unused-function -Wno-unused-variable -Wno-multichar -Wno-uninitialized -Wno-ignored-qualifiers -Wno-missing-field-initializers -Wno-unused-parameter -DEFINES += ARM USING_GLES2 -blackberry: DEFINES += BLACKBERRY BLACKBERRY10 -symbian: { - QMAKE_CXXFLAGS += -march=armv6 -mfpu=vfp -mfloat-abi=softfp -marm -Wno-parentheses -Wno-comment - DEFINES += SYMBIAN + HEADERS += ../Core/MIPS/ARM/Asm.h \ + ../Core/MIPS/ARM/Jit.h \ + ../Core/MIPS/ARM/JitCache.h \ + ../Core/MIPS/ARM/RegCache.h } +x86 { + SOURCES += ../Core/MIPS/x86/Asm.cpp \ + ../Core/MIPS/x86/CompALU.cpp \ + ../Core/MIPS/x86/CompBranch.cpp \ + ../Core/MIPS/x86/CompFPU.cpp \ + ../Core/MIPS/x86/CompLoadStore.cpp \ + ../Core/MIPS/x86/CompVFPU.cpp \ + ../Core/MIPS/x86/Jit.cpp \ + ../Core/MIPS/x86/JitCache.cpp \ + ../Core/MIPS/x86/RegCache.cpp + HEADERS += ../Core/MIPS/x86/Asm.h \ + ../Core/MIPS/x86/Jit.h \ + ../Core/MIPS/x86/JitCache.h \ + ../Core/MIPS/x86/RegCache.h +} + +SOURCES += ../Core/CPU.cpp \ # Core + ../Core/Config.cpp \ + ../Core/Core.cpp \ + ../Core/CoreTiming.cpp \ + ../Core/Debugger/Breakpoints.cpp \ + ../Core/Debugger/SymbolMap.cpp \ + ../Core/Dialog/PSPDialog.cpp \ + ../Core/Dialog/PSPMsgDialog.cpp \ + ../Core/Dialog/PSPOskDialog.cpp \ + ../Core/Dialog/PSPPlaceholderDialog.cpp \ + ../Core/Dialog/PSPSaveDialog.cpp \ + ../Core/Dialog/SavedataParam.cpp \ + ../Core/ELF/ElfReader.cpp \ + ../Core/ELF/PrxDecrypter.cpp \ + ../Core/ELF/ParamSFO.cpp \ + ../Core/FileSystems/BlockDevices.cpp \ + ../Core/FileSystems/DirectoryFileSystem.cpp \ + ../Core/FileSystems/ISOFileSystem.cpp \ + ../Core/FileSystems/MetaFileSystem.cpp \ + ../Core/HLE/HLE.cpp \ + ../Core/HLE/HLETables.cpp \ + ../Core/HLE/__sceAudio.cpp \ + ../Core/HLE/sceAtrac.cpp \ + ../Core/HLE/sceAudio.cpp \ + ../Core/HLE/sceCtrl.cpp \ + ../Core/HLE/sceDisplay.cpp \ + ../Core/HLE/sceDmac.cpp \ + ../Core/HLE/sceGe.cpp \ + ../Core/HLE/sceFont.cpp \ + ../Core/HLE/sceHprm.cpp \ + ../Core/HLE/sceHttp.cpp \ + ../Core/HLE/sceImpose.cpp \ + ../Core/HLE/sceIo.cpp \ + ../Core/HLE/sceKernel.cpp \ + ../Core/HLE/sceKernelAlarm.cpp \ + ../Core/HLE/sceKernelEventFlag.cpp \ + ../Core/HLE/sceKernelInterrupt.cpp \ + ../Core/HLE/sceKernelMbx.cpp \ + ../Core/HLE/sceKernelMemory.cpp \ + ../Core/HLE/sceKernelModule.cpp \ + ../Core/HLE/sceKernelMsgPipe.cpp \ + ../Core/HLE/sceKernelMutex.cpp \ + ../Core/HLE/sceKernelSemaphore.cpp \ + ../Core/HLE/sceKernelThread.cpp \ + ../Core/HLE/sceKernelThread.h \ + ../Core/HLE/sceKernelTime.cpp \ + ../Core/HLE/sceKernelVTimer.cpp \ + ../Core/HLE/sceMpeg.cpp \ + ../Core/HLE/sceNet.cpp \ + ../Core/HLE/sceOpenPSID.cpp \ + ../Core/HLE/sceParseHttp.cpp \ + ../Core/HLE/sceParseUri.cpp \ + ../Core/HLE/scePower.cpp \ + ../Core/HLE/scePsmf.cpp \ + ../Core/HLE/sceRtc.cpp \ + ../Core/HLE/sceSas.cpp \ + ../Core/HLE/sceSsl.cpp \ + ../Core/HLE/scesupPreAcc.cpp \ + ../Core/HLE/sceUmd.cpp \ + ../Core/HLE/sceUsb.cpp \ + ../Core/HLE/sceUtility.cpp \ + ../Core/HLE/sceVaudio.cpp \ + ../Core/HW/MemoryStick.cpp \ + ../Core/HW/SasAudio.cpp \ + ../Core/Host.cpp \ + ../Core/Loaders.cpp \ + ../Core/MIPS/JitCommon/JitCommon.cpp \ + ../Core/MIPS/MIPS.cpp \ + ../Core/MIPS/MIPSAnalyst.cpp \ + ../Core/MIPS/MIPSCodeUtils.cpp \ + ../Core/MIPS/MIPSDebugInterface.cpp \ + ../Core/MIPS/MIPSDis.cpp \ + ../Core/MIPS/MIPSDisVFPU.cpp \ + ../Core/MIPS/MIPSInt.cpp \ + ../Core/MIPS/MIPSIntVFPU.cpp \ + ../Core/MIPS/MIPSTables.cpp \ + ../Core/MIPS/MIPSVFPUUtils.cpp \ + ../Core/MemMap.cpp \ + ../Core/MemMapFunctions.cpp \ + ../Core/PSPLoaders.cpp \ + ../Core/PSPMixer.cpp \ + ../Core/SaveState.cpp \ + ../Core/System.cpp \ + ../Core/Util/BlockAllocator.cpp \ + ../Core/Util/PPGeDraw.cpp \ + ../Core/Util/ppge_atlas.cpp \ # GPU + ../GPU/GLES/DisplayListInterpreter.cpp \ + ../GPU/GLES/FragmentShaderGenerator.cpp \ + ../GPU/GLES/Framebuffer.cpp \ + ../GPU/GLES/IndexGenerator.cpp \ + ../GPU/GLES/ShaderManager.cpp \ + ../GPU/GLES/StateMapping.cpp \ + ../GPU/GLES/TextureCache.cpp \ + ../GPU/GLES/TransformPipeline.cpp \ + ../GPU/GLES/VertexDecoder.cpp \ + ../GPU/GLES/VertexShaderGenerator.cpp \ + ../GPU/GPUState.cpp \ + ../GPU/Math3D.cpp \ + ../GPU/Null/NullGpu.cpp \ # Kirk + ../ext/libkirk/AES.c \ + ../ext/libkirk/SHA1.c \ + ../ext/libkirk/bn.c \ + ../ext/libkirk/ec.c \ + ../ext/libkirk/kirk_engine.c + +HEADERS += ../Core/CPU.h \ + ../Core/Config.h \ + ../Core/Core.h \ + ../Core/CoreParameter.h \ + ../Core/CoreTiming.h \ + ../Core/Debugger/Breakpoints.h \ + ../Core/Debugger/DebugInterface.h \ + ../Core/Debugger/SymbolMap.h \ + ../Core/Dialog/PSPDialog.h \ + ../Core/Dialog/PSPMsgDialog.h \ + ../Core/Dialog/PSPOskDialog.h \ + ../Core/Dialog/PSPPlaceholderDialog.h \ + ../Core/Dialog/PSPSaveDialog.h \ + ../Core/Dialog/SavedataParam.h \ + ../Core/ELF/ElfReader.h \ + ../Core/ELF/ElfTypes.h \ + ../Core/ELF/PrxDecrypter.h \ + ../Core/ELF/ParamSFO.h \ + ../Core/FileSystems/BlockDevices.h \ + ../Core/FileSystems/DirectoryFileSystem.h \ + ../Core/FileSystems/FileSystem.h \ + ../Core/FileSystems/ISOFileSystem.h \ + ../Core/FileSystems/MetaFileSystem.h \ + ../Core/HLE/FunctionWrappers.h \ + ../Core/HLE/HLE.h \ + ../Core/HLE/HLETables.h \ + ../Core/HLE/__sceAudio.h \ + ../Core/HLE/sceAtrac.h \ + ../Core/HLE/sceAudio.h \ + ../Core/HLE/sceCtrl.h \ + ../Core/HLE/sceDisplay.h \ + ../Core/HLE/sceDmac.h \ + ../Core/HLE/sceGe.h \ + ../Core/HLE/sceFont.h \ + ../Core/HLE/sceHprm.h \ + ../Core/HLE/sceHttp.h \ + ../Core/HLE/sceImpose.h \ + ../Core/HLE/sceIo.h \ + ../Core/HLE/sceKernel.h \ + ../Core/HLE/sceKernelAlarm.h \ + ../Core/HLE/sceKernelEventFlag.h \ + ../Core/HLE/sceKernelInterrupt.h \ + ../Core/HLE/sceKernelMbx.h \ + ../Core/HLE/sceKernelMemory.h \ + ../Core/HLE/sceKernelModule.h \ + ../Core/HLE/sceKernelMsgPipe.h \ + ../Core/HLE/sceKernelMutex.h \ + ../Core/HLE/sceKernelSemaphore.h \ + ../Core/HLE/sceMpeg.h \ + ../Core/HLE/sceNet.h \ + ../Core/HLE/sceOpenPSID.h \ + ../Core/HLE/sceParseHttp.h \ + ../Core/HLE/sceParseUri.h \ + ../Core/HLE/scePower.h \ + ../Core/HLE/scePsmf.h \ + ../Core/HLE/sceRtc.h \ + ../Core/HLE/sceSas.h \ + ../Core/HLE/sceSsl.h \ + ../Core/HLE/scesupPreAcc.h \ + ../Core/HLE/sceUmd.h \ + ../Core/HLE/sceUsb.h \ + ../Core/HLE/sceUtility.h \ + ../Core/HLE/sceVaudio.h \ + ../Core/HW/MemoryStick.h \ + ../Core/HW/SasAudio.h \ + ../Core/Host.h \ + ../Core/Loaders.h \ + ../Core/MIPS/JitCommon/JitCommon.h \ + ../Core/MIPS/MIPS.h \ + ../Core/MIPS/MIPSAnalyst.h \ + ../Core/HLE/sceKernelTime.h \ + ../Core/HLE/sceKernelVTimer.h \ + ../Core/MIPS/MIPSCodeUtils.h \ + ../Core/MIPS/MIPSDebugInterface.h \ + ../Core/MIPS/MIPSDis.h \ + ../Core/MIPS/MIPSDisVFPU.h \ + ../Core/MIPS/MIPSInt.h \ + ../Core/MIPS/MIPSIntVFPU.h \ + ../Core/MIPS/MIPSTables.h \ + ../Core/MIPS/MIPSVFPUUtils.h \ + ../Core/MemMap.h \ + ../Core/PSPLoaders.h \ + ../Core/PSPMixer.h \ + ../Core/SaveState.h \ + ../Core/System.h \ + ../Core/Util/BlockAllocator.h \ + ../Core/Util/PPGeDraw.h \ + ../Core/Util/Pool.h \ + ../Core/Util/ppge_atlas.h \ + ../GPU/GLES/DisplayListInterpreter.h \ + ../GPU/GLES/FragmentShaderGenerator.h \ + ../GPU/GLES/Framebuffer.h \ + ../GPU/GLES/IndexGenerator.h \ + ../GPU/GLES/ShaderManager.h \ + ../GPU/GLES/StateMapping.h \ + ../GPU/GLES/TextureCache.h \ + ../GPU/GLES/TransformPipeline.h \ + ../GPU/GLES/VertexDecoder.h \ + ../GPU/GLES/VertexShaderGenerator.h \ + ../GPU/GPUInterface.h \ + ../GPU/GPUState.h \ + ../GPU/Math3D.h \ + ../GPU/Null/NullGpu.h \ + ../GPU/ge_constants.h \ + ../ext/libkirk/AES.h \ + ../ext/libkirk/SHA1.h \ + ../ext/libkirk/kirk_engine.h + diff --git a/Qt/Native.pro b/Qt/Native.pro index e0d9242e29..a5916b80cb 100755 --- a/Qt/Native.pro +++ b/Qt/Native.pro @@ -4,20 +4,30 @@ TARGET = Native TEMPLATE = lib CONFIG += staticlib -blackberry: { - QMAKE_CC = ntoarmv7-gcc - QMAKE_CXX = ntoarmv7-g++ - DEFINES += "_QNX_SOURCE=1" "_C99=1" +include(Settings.pri) + +!mobile_platform: { + SOURCES += ../native/ext/glew/glew.c + HEADERS += ../native/ext/glew/GL/glew.h \ + ../native/ext/glew/GL/glxew.h \ + ../native/ext/glew/GL/wglew.h + INCLUDEPATH += ../native/ext/glew +} + +# Backtrace +x86:!mobile_platform: { + SOURCES += ../native/base/backtrace.cpp + HEADERS += ../native/base/backtrace.h } # EtcPack SOURCES += ../native/ext/etcpack/etcdec.cpp \ - ../native/ext/etcpack/etcpack.cpp \ - ../native/ext/etcpack/image.cpp + ../native/ext/etcpack/etcpack.cpp \ + ../native/ext/etcpack/image.cpp HEADERS += ../native/ext/etcpack/etcdec.h \ - ../native/ext/etcpack/etcpack.h \ - ../native/ext/etcpack/image.h + ../native/ext/etcpack/etcpack.h \ + ../native/ext/etcpack/image.h INCLUDEPATH += ../native/ext/etcpack # Stb_image @@ -32,163 +42,155 @@ SOURCES += ../native/ext/stb_vorbis/stb_vorbis.c HEADERS += ../native/ext/stb_vorbis/stb_vorbis.h INCLUDEPATH += ../native/ext/stb_vorbis -!symbian: { # Zlib - -SOURCES += ../ext/zlib/adler32.c \ - ../ext/zlib/compress.c \ - ../ext/zlib/crc32.c \ - ../ext/zlib/deflate.c \ - ../ext/zlib/gzclose.c \ - ../ext/zlib/gzlib.c \ - ../ext/zlib/gzread.c \ - ../ext/zlib/gzwrite.c \ - ../ext/zlib/infback.c \ - ../ext/zlib/inffast.c \ - ../ext/zlib/inflate.c \ - ../ext/zlib/inflate.h \ - ../ext/zlib/inftrees.c \ - ../ext/zlib/trees.c \ - ../ext/zlib/uncompr.c \ - ../ext/zlib/zutil.c -HEADERS += ../ext/zlib/crc32.h \ - ../ext/zlib/deflate.h \ - ../ext/zlib/gzguts.h \ - ../ext/zlib/inffast.h \ - ../ext/zlib/inffixed.h \ - ../ext/zlib/inftrees.h \ - ../ext/zlib/trees.h \ - ../ext/zlib/zconf.h \ - ../ext/zlib/zlib.h \ - ../ext/zlib/zutil.h -INCLUDEPATH += ../ext/zlib +!symbian: { + SOURCES += ../ext/zlib/adler32.c \ + ../ext/zlib/compress.c \ + ../ext/zlib/crc32.c \ + ../ext/zlib/deflate.c \ + ../ext/zlib/gzclose.c \ + ../ext/zlib/gzlib.c \ + ../ext/zlib/gzread.c \ + ../ext/zlib/gzwrite.c \ + ../ext/zlib/infback.c \ + ../ext/zlib/inffast.c \ + ../ext/zlib/inflate.c \ + ../ext/zlib/inflate.h \ + ../ext/zlib/inftrees.c \ + ../ext/zlib/trees.c \ + ../ext/zlib/uncompr.c \ + ../ext/zlib/zutil.c + HEADERS += ../ext/zlib/crc32.h \ + ../ext/zlib/deflate.h \ + ../ext/zlib/gzguts.h \ + ../ext/zlib/inffast.h \ + ../ext/zlib/inffixed.h \ + ../ext/zlib/inftrees.h \ + ../ext/zlib/trees.h \ + ../ext/zlib/zconf.h \ + ../ext/zlib/zlib.h \ + ../ext/zlib/zutil.h + INCLUDEPATH += ../ext/zlib } # Native SOURCES += ../native/audio/mixer.cpp \ - ../native/audio/wav_read.cpp \ - ../native/base/buffer.cpp \ - ../native/base/colorutil.cpp \ - ../native/base/display.cpp \ - ../native/base/error_context.cpp \ - ../native/base/fastlist_test.cpp \ - ../native/base/stringutil.cpp \ - ../native/base/threadutil.cpp \ - ../native/base/timeutil.cpp \ - ../native/file/chunk_file.cpp \ - ../native/file/dialog.cpp \ - ../native/file/easy_file.cpp \ - ../native/file/fd_util.cpp \ - ../native/file/file_util.cpp \ - ../native/file/zip_read.cpp \ - ../native/gfx/gl_debug_log.cpp \ - ../native/gfx/gl_lost_manager.cpp \ - ../native/gfx/texture.cpp \ - ../native/gfx/texture_atlas.cpp \ - ../native/gfx/texture_gen.cpp \ - ../native/gfx_es2/draw_buffer.cpp \ - ../native/gfx_es2/fbo.cpp \ - ../native/gfx_es2/gl_state.cpp \ - ../native/gfx_es2/glsl_program.cpp \ - ../native/gfx_es2/vertex_format.cpp \ - ../native/image/png_load.cpp \ - ../native/image/zim_load.cpp \ - ../native/image/zim_save.cpp \ - ../native/input/gesture_detector.cpp \ - ../native/json/json_writer.cpp \ - ../native/math/curves.cpp \ - ../native/math/lin/aabb.cpp \ - ../native/math/lin/matrix4x4.cpp \ - ../native/math/lin/plane.cpp \ - ../native/math/lin/quat.cpp \ - ../native/math/lin/vec3.cpp \ - ../native/math/math_util.cpp \ - ../native/midi/midi_input.cpp \ - ../native/net/http_client.cpp \ - ../native/net/resolve.cpp \ - ../native/profiler/profiler.cpp \ - ../native/ui/screen.cpp \ - ../native/ui/ui.cpp \ - ../native/ui/virtual_input.cpp \ - ../native/util/bits/bits.cpp \ - ../native/util/bits/varint.cpp \ - ../native/util/hash/hash.cpp \ - ../native/util/random/perlin.cpp + ../native/audio/wav_read.cpp \ + ../native/base/buffer.cpp \ + ../native/base/colorutil.cpp \ + ../native/base/display.cpp \ + ../native/base/error_context.cpp \ + ../native/base/fastlist_test.cpp \ + ../native/base/stringutil.cpp \ + ../native/base/threadutil.cpp \ + ../native/base/timeutil.cpp \ + ../native/file/chunk_file.cpp \ + ../native/file/dialog.cpp \ + ../native/file/easy_file.cpp \ + ../native/file/fd_util.cpp \ + ../native/file/file_util.cpp \ + ../native/file/zip_read.cpp \ + ../native/gfx/gl_debug_log.cpp \ + ../native/gfx/gl_lost_manager.cpp \ + ../native/gfx/texture.cpp \ + ../native/gfx/texture_atlas.cpp \ + ../native/gfx/texture_gen.cpp \ + ../native/gfx_es2/draw_buffer.cpp \ + ../native/gfx_es2/fbo.cpp \ + ../native/gfx_es2/gl_state.cpp \ + ../native/gfx_es2/glsl_program.cpp \ + ../native/gfx_es2/vertex_format.cpp \ + ../native/image/png_load.cpp \ + ../native/image/zim_load.cpp \ + ../native/image/zim_save.cpp \ + ../native/input/gesture_detector.cpp \ + ../native/json/json_writer.cpp \ + ../native/math/curves.cpp \ + ../native/math/lin/aabb.cpp \ + ../native/math/lin/matrix4x4.cpp \ + ../native/math/lin/plane.cpp \ + ../native/math/lin/quat.cpp \ + ../native/math/lin/vec3.cpp \ + ../native/math/math_util.cpp \ + ../native/midi/midi_input.cpp \ + ../native/net/http_client.cpp \ + ../native/net/resolve.cpp \ + ../native/profiler/profiler.cpp \ + ../native/ui/screen.cpp \ + ../native/ui/ui.cpp \ + ../native/ui/virtual_input.cpp \ + ../native/util/bits/bits.cpp \ + ../native/util/bits/varint.cpp \ + ../native/util/hash/hash.cpp \ + ../native/util/random/perlin.cpp HEADERS += ../native/audio/mixer.h \ - ../native/audio/wav_read.h \ - ../native/base/basictypes.h \ - ../native/base/buffer.h \ - ../native/base/color.h \ - ../native/base/colorutil.h \ - ../native/base/display.h \ - ../native/base/error_context.h \ - ../native/base/fastlist.h \ - ../native/base/linked_ptr.h \ - ../native/base/logging.h \ - ../native/base/mutex.h \ - ../native/base/scoped_ptr.h \ - ../native/base/stats.h \ - ../native/base/stringutil.h \ - ../native/base/threadutil.h \ - ../native/base/timeutil.h \ - ../native/file/chunk_file.h \ - ../native/file/dialog.h \ - ../native/file/easy_file.h \ - ../native/file/fd_util.h \ - ../native/file/file_util.h \ - ../native/file/vfs.h \ - ../native/file/zip_read.h \ - ../native/gfx/gl_debug_log.h \ - ../native/gfx/gl_lost_manager.h \ - ../native/gfx/texture.h \ - ../native/gfx/texture_atlas.h \ - ../native/gfx/texture_gen.h \ - ../native/gfx_es2/fbo.h \ - ../native/gfx_es2/gl_state.h \ - ../native/gfx_es2/glsl_program.h \ - ../native/gfx_es2/vertex_format.h \ - ../native/gfx_es2/draw_buffer.h \ - ../native/image/png_load.h \ - ../native/image/zim_load.h \ - ../native/image/zim_save.h \ - ../native/input/gesture_detector.h \ - ../native/input/input_state.h \ - ../native/json/json_writer.h \ - ../native/math/compression.h \ - ../native/math/curves.h \ - ../native/math/lin/aabb.h \ - ../native/math/lin/matrix4x4.h \ - ../native/math/lin/plane.h \ - ../native/math/lin/quat.h \ - ../native/math/lin/ray.h \ - ../native/math/lin/vec3.h \ - ../native/math/math_util.h \ - ../native/midi/midi_input.h \ - ../native/net/http_client.h \ - ../native/net/resolve.h \ - ../native/ui/ui.h \ - ../native/profiler/profiler.h \ - ../native/ui/screen.h \ - ../native/ui/virtual_input.h \ - ../native/util/bits/bits.h \ - ../native/util/bits/hamming.h \ - ../native/util/bits/varint.h \ - ../native/util/hash/hash.h \ - ../native/util/random/perlin.h \ - ../native/util/random/rng.h \ - ../native/ext/rapidxml/rapidxml.hpp \ - ../native/ext/rapidxml/rapidxml_iterators.hpp \ - ../native/ext/rapidxml/rapidxml_print.hpp \ - ../native/ext/rapidxml/rapidxml_utils.hpp -INCLUDEPATH+= ../native + ../native/audio/wav_read.h \ + ../native/base/basictypes.h \ + ../native/base/buffer.h \ + ../native/base/color.h \ + ../native/base/colorutil.h \ + ../native/base/display.h \ + ../native/base/error_context.h \ + ../native/base/fastlist.h \ + ../native/base/linked_ptr.h \ + ../native/base/logging.h \ + ../native/base/mutex.h \ + ../native/base/scoped_ptr.h \ + ../native/base/stats.h \ + ../native/base/stringutil.h \ + ../native/base/threadutil.h \ + ../native/base/timeutil.h \ + ../native/file/chunk_file.h \ + ../native/file/dialog.h \ + ../native/file/easy_file.h \ + ../native/file/fd_util.h \ + ../native/file/file_util.h \ + ../native/file/vfs.h \ + ../native/file/zip_read.h \ + ../native/gfx/gl_debug_log.h \ + ../native/gfx/gl_lost_manager.h \ + ../native/gfx/texture.h \ + ../native/gfx/texture_atlas.h \ + ../native/gfx/texture_gen.h \ + ../native/gfx_es2/fbo.h \ + ../native/gfx_es2/gl_state.h \ + ../native/gfx_es2/glsl_program.h \ + ../native/gfx_es2/vertex_format.h \ + ../native/gfx_es2/draw_buffer.h \ + ../native/image/png_load.h \ + ../native/image/zim_load.h \ + ../native/image/zim_save.h \ + ../native/input/gesture_detector.h \ + ../native/input/input_state.h \ + ../native/json/json_writer.h \ + ../native/math/compression.h \ + ../native/math/curves.h \ + ../native/math/lin/aabb.h \ + ../native/math/lin/matrix4x4.h \ + ../native/math/lin/plane.h \ + ../native/math/lin/quat.h \ + ../native/math/lin/ray.h \ + ../native/math/lin/vec3.h \ + ../native/math/math_util.h \ + ../native/midi/midi_input.h \ + ../native/net/http_client.h \ + ../native/net/resolve.h \ + ../native/ui/ui.h \ + ../native/profiler/profiler.h \ + ../native/ui/screen.h \ + ../native/ui/virtual_input.h \ + ../native/util/bits/bits.h \ + ../native/util/bits/hamming.h \ + ../native/util/bits/varint.h \ + ../native/util/hash/hash.h \ + ../native/util/random/perlin.h \ + ../native/util/random/rng.h \ + ../native/ext/rapidxml/rapidxml.hpp \ + ../native/ext/rapidxml/rapidxml_iterators.hpp \ + ../native/ext/rapidxml/rapidxml_print.hpp \ + ../native/ext/rapidxml/rapidxml_utils.hpp +INCLUDEPATH += ../native -QMAKE_CXXFLAGS += -std=c++0x -Wno-unused-function -Wno-unused-variable -Wno-multichar -Wno-uninitialized -Wno-ignored-qualifiers -Wno-missing-field-initializers -Wno-unused-parameter -DEFINES += ARM USING_GLES2 -blackberry: DEFINES += BLACKBERRY BLACKBERRY10 -symbian: { - QMAKE_CXXFLAGS += -march=armv6 -mfpu=vfp -mfloat-abi=softfp -marm -Wno-parentheses -Wno-comment - DEFINES += SYMBIAN -} diff --git a/Qt/PPSSPP.pro b/Qt/PPSSPP.pro index a795dabd99..3050a025f7 100755 --- a/Qt/PPSSPP.pro +++ b/Qt/PPSSPP.pro @@ -1,48 +1,47 @@ TARGET = PPSSPPQt -QT += core gui opengl multimedia +QT += core gui opengl -symbian: { - LIBS += -lCore.lib -lCommon.lib -lNative.lib -lcone -leikcore -lavkon -lezlib - CONFIG += 4.6.3 +include(Settings.pri) +linux { + CONFIG += mobility + MOBILITY += multimedia } -# They try to force QCC with all mkspecs -# QCC is 4.4.1, we need 4.6.3 -blackberry: { - QMAKE_CC = ntoarmv7-gcc - QMAKE_CXX = ntoarmv7-g++ - DEFINES += "_QNX_SOURCE=1" "_C99=1" - LIBS += -L. -lCore -lCommon -lNative -lscreen -lsocket -lstdc++ +else { + QT += multimedia } +# Libs +symbian: LIBS += -lCore.lib -lCommon.lib -lNative.lib -lcone -leikcore -lavkon -lezlib + +blackberry: LIBS += -L. -lCore -lCommon -lNative -lscreen -lsocket -lstdc++ + +win32: LIBS += -L. -lCore -lCommon -lNative -lwinmm -lws2_32 -lkernel32 -luser32 -lgdi32 -lshell32 -lcomctl32 -ldsound -lxinput + +linux: LIBS += -L. -lCore -lCommon -lNative + # Main SOURCES += ../native/base/QtMain.cpp HEADERS += ../native/base/QtMain.h # Native - SOURCES += ../android/jni/NativeApp.cpp \ - ../android/jni/EmuScreen.cpp \ - ../android/jni/MenuScreens.cpp \ - ../android/jni/GamepadEmu.cpp \ - ../android/jni/UIShader.cpp \ - ../android/jni/ui_atlas.cpp + ../android/jni/EmuScreen.cpp \ + ../android/jni/MenuScreens.cpp \ + ../android/jni/GamepadEmu.cpp \ + ../android/jni/UIShader.cpp \ + ../android/jni/ui_atlas.cpp INCLUDEPATH += .. ../Common ../native -QMAKE_CXXFLAGS += -std=c++0x -Wno-unused-function -Wno-unused-variable -Wno-multichar -Wno-uninitialized -Wno-ignored-qualifiers -Wno-missing-field-initializers -Wno-unused-parameter -DEFINES += ARM USING_GLES2 -blackberry: DEFINES += BLACKBERRY BLACKBERRY10 -symbian: { - QMAKE_CXXFLAGS += -march=armv6 -mfpu=vfp -mfloat-abi=softfp -marm -Wno-parentheses -Wno-comment - DEFINES += SYMBIAN - +# Packaging +symbian { vendorinfo = "%{\"Qtness\"}" ":\"Qtness\"" packageheader = "$${LITERAL_HASH}{\"PPSSPP\"}, (0xE0095B1D), 0, 0, 4, TYPE=SA" my_deployment.pkg_prerules = packageheader vendorinfo assets.sources = ../android/assets/ui_atlas.zim ../android/assets/ppge_atlas.zim assets.path = E:/PPSSPP DEPLOYMENT += my_deployment assets - ICON = ../assets/icon.svg + ICON = ../assets/icon.svg # 268MB maximum TARGET.EPOCHEAPSIZE = 0x40000 0x10000000 TARGET.EPOCSTACKSIZE = 0x10000 diff --git a/Qt/Settings.pri b/Qt/Settings.pri new file mode 100644 index 0000000000..87d3a408e3 --- /dev/null +++ b/Qt/Settings.pri @@ -0,0 +1,31 @@ +blackberry|symbian: CONFIG += mobile_platform +unix:!blackberry:!symbian:!macx: CONFIG += linux + +# Global specific +QMAKE_CXXFLAGS += -std=c++0x -Wno-unused-function -Wno-unused-variable -Wno-multichar -Wno-uninitialized -Wno-ignored-qualifiers -Wno-missing-field-initializers -Wno-unused-parameter + +# Arch specific +contains(QT_ARCH, i686)|contains(QT_ARCH, x86)|contains(QT_ARCH, x86_64): { + QMAKE_CXXFLAGS += -msse2 + CONFIG += x86 +} +else { # Assume ARM + DEFINES += ARM + CONFIG += arm +} +mobile_platform: DEFINES += USING_GLES2 + + +# Platform specific +blackberry: { +# They try to force QCC with all mkspecs +# QCC is 4.4.1, we need 4.6.3 + QMAKE_CC = ntoarmv7-gcc + QMAKE_CXX = ntoarmv7-g++ + DEFINES += BLACKBERRY BLACKBERRY10 "_QNX_SOURCE=1" "_C99=1" +} +symbian: { + QMAKE_CXXFLAGS += -march=armv6 -mfpu=vfp -mfloat-abi=softfp -marm -Wno-parentheses -Wno-comment + DEFINES += SYMBIAN + CONFIG += 4.6.3 +} diff --git a/README.md b/README.md index 40b4544486..1d94abdab2 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,9 @@ Of course in-tree builds are supported, but that makes cleanup harder to do; with out-of-tree builds you can just remove the `build` directory. +Note: There is also a Qt frontend available. Simply compile +PPSSPPQt.pro from [QtCreator][qt-creator]. + Building for Linux/BSD/etc -------------------------- @@ -51,7 +54,9 @@ Install the libsdl1.2 (SDL 1.2) development headers. This is called `sdl12` on BSD ports. Currently the user interface is identical to Android's, operated -with the mouse. A Qt-based interface is planned. +with the mouse. + +Alternatively, a Qt-based frontend is available in the Qt/ dir. Building for OSX ---------------- @@ -92,10 +97,7 @@ other than Microsoft's, but `NMake Makefiles` works fine. Building for Blackberry ----------------------- -To build for Blackberry, you must first have the latest Native SDK installed -from developer.blackberry.com/native and have compiled the SDL port available -from github.com/blackberry/SDL to your NDK workspace. Then checkout the latest -version of PPSSPP to your NDK workspace. +To build for Blackberry, you must first have the [latest Native SDK][blackberry-ndk] installed. To set up your environment for cross-compiling you must then use: source ~/bbndk/bbndk-env.sh @@ -105,20 +107,28 @@ Finally, you are ready to compile. Change directory to ppsspp/SDL and run: If you are on Windows, you will need GNU and CMake to run the bash script. -Alternatively, you can use the Qt UI by compiling the PPSSPPQt.pro in the Qt/ -directory with qmake from the NDK or QtCreator 2.6+. +Alternatively, you can use the Qt frontend by compiling the PPSSPPQt.pro in +the Qt/ directory with `qmake` from the NDK or [QtCreator 2.6+][qt-creator]. Building for Symbian -------------------- To build for Symbian, you require: -1) GCC 4.6.3 from Mentor Graphics: http://www.mentor.com/embedded-software/sourcery-tools/sourcery-codebench/editions/lite-edition/ -2) Symbian Qt libraries. You can find these in the final Nokia Qt SDK. -Then simply compile the PPSSPPQt.pro with qmake from the SDK or QtCreator 2.6+. +1) [GCC 4.6.3][symbian-gcc] from Mentor Graphics. + +2) Symbian Qt libraries. You can find these in the final Nokia Qt SDK or online. + +Then simply compile the PPSSPPQt.pro with `qmake` from the SDK or [QtCreator 2.6+][qt-creator]. [ppsspp-repo]: "https://github.com/hrydgard/ppsspp" [ppsspp-devel]: "http://www.ppsspp.org/development.html" +[qt-creator]: + "http://qt-project.org/downloads" +[blackberry-ndk]: + "http://developer.blackberry.com/native" +[symbian-gcc]: + "http://www.mentor.com/embedded-software/sourcery-tools/sourcery-codebench/editions/lite-edition/" diff --git a/Tools/SaveTool/Makefile b/Tools/SaveTool/Makefile new file mode 100644 index 0000000000..fc7a38f73c --- /dev/null +++ b/Tools/SaveTool/Makefile @@ -0,0 +1,44 @@ +SUBDIRS = kernelcall +release: + @for i in $(SUBDIRS); do echo "make all in $$i..."; (cd $$i; $(MAKE) release; cp $$i.prx ..); done + make all + +allclean: + make clean + @for i in $(SUBDIRS); do echo "Clearing in $$i..."; (cd $$i; $(MAKE) clean; rm -rf $$i.prx); done + +TARGET = ppssppsavetool +OBJS = main.o decrypt.o encrypt.o hash.o psf.o $(KERNELCALL_OBJS) + +BUILD_PRX=1 + + +EXTRA_TARGETS = EBOOT.PBP +PSP_EBOOT_TITLE = PPSSPP Save Tool + +PSP_FW_VERSION=350 + +INCDIR = +CFLAGS = -O0 -G0 -Wall -g +CXXFLAGS = $(CFLAGS) -fno-exceptions -fno-rtti +ASFLAGS = $(CFLAGS) + +KERNELCALL_OBJS = kernelcall_0000.o \ + kernelcall_0001.o \ + kernelcall_0002.o \ + kernelcall_0003.o \ + kernelcall_0004.o \ + kernelcall_0005.o \ + kernelcall_0006.o \ + kernelcall_0007.o + +$(KERNELCALL_OBJS): kernelcall/kernelcall.S + psp-gcc $(CFLAGS) -DF_$* $< -c -o $@ + +LIBDIR = +LDFLAGS = + +LIBS = -lpsputility -lpspgum -lpspgu -lm + +PSPSDK=$(shell psp-config --pspsdk-path) +include $(PSPSDK)/lib/build.mak diff --git a/Tools/SaveTool/README b/Tools/SaveTool/README new file mode 100644 index 0000000000..0581e316b8 --- /dev/null +++ b/Tools/SaveTool/README @@ -0,0 +1,77 @@ + +PSP Tool for encoding save data from PPSSPP to read on PSP and decoding +PSP save to read on PPSSPP. + +Alpha version. Was only tested on Project Dive Extend. + + +Build +===== + +Require PSP Toolchain with PSP SDK + + +make release +This will build ppssppsavetool.prx and kernelcall.prx. + +clean : make allclean + + +How to use +========== + +Require PSP Toolchain with PRX Link and usb link, pspsh to work. Seems +there is limitation for directories listing when running directly the +tool from the PSP. + +A) PREPARING DATA +================= + +1) Play the game on PPSSPP until a game save is done. This will create +the file "ENCRYPT_INFO.BIN" in the save directory. It contain the +encoding key and sdk version of the game. This file is the same for all +save of the game, so you can use it on different save folder without +the need to make them all on PPSSPP. + +2) Run usbhostfs_pc. Then mount .ppsspp/PSP/SAVEDATA directory : +mount 1 +If you enter "drives", you should see host1: mapped to the directory. + +3) Run PRX Link on the psp, and pspsh on the PC. + +B) ENCODING A SAVE FROM PPSSPP TO PSP +===================================== + +1) From pspsh, run ppssppsavetool.prx. + +2) In the menu, select "Encrypt", "host1:/". This will list the +directories in ppsspp save directory which can be encoded. +For a directory to appear, it must contain the ENCRYPT_INFO.BIN file, +and the files of a ppsspp save. + +3) After selecting the directory to encode, it will be copied into +the PSP/SAVEGAME directory and encoded. Now you can play the save on +the PSP. + +C) DECODING A SAVE FROM PSP TO PPSSPP +===================================== + +1) You should have a directory in the PPSSPP save directory with the +same name that the PSP save you want to convert. And in it, the +"ENCRYPT_INFO.BIN" file. + +Ex : You want to decrypt the save in ms0:/PSP/SAVEDATA/XXXXYYY/ +You create .ppsspp/PSP/SAVEDATA/XXXXYYY/ if not existing, and put the +"ENCRYPT_INFO.BIN" generated before in it. + +2) From pspsh, run ppssppsavetool.prx. + +3) In the menu, select "decrypt", "host1:/". You should see your +directory in the list. + +4) After selecting it, the game decode the save and save it into the +PPSSPP save directory. You can now launch your game in PPSSPP and load +the save. + + + diff --git a/Tools/SaveTool/decrypt.c b/Tools/SaveTool/decrypt.c new file mode 100644 index 0000000000..146572199d --- /dev/null +++ b/Tools/SaveTool/decrypt.c @@ -0,0 +1,164 @@ +/* + * PSP Software Development Kit - http://www.pspdev.org + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPSDK root for details. + * + * decrypt.c - Decryption routines using sceChnnlsv + * + * Copyright (c) 2005 Jim Paris + * Coypright (c) 2005 psp123 + * + * $Id: decrypt.c 1562 2005-12-10 20:52:45Z jim $ + */ + +#include "decrypt.h" +#include +#include +#include +#include +#include +#include "kernelcall/kernelcall.h" + +unsigned int align16(unsigned int v) +{ + return ((v + 0xF) >> 4) << 4; +} + +/* Read, decrypt, and write a savedata file. See main.c for example usage. */ +int decrypt_file(const char *decrypted_filename, + const char *encrypted_filename, + const unsigned char *gamekey, + const int mainSdkVersion) +{ + FILE *in, *out; + int len, aligned_len; + unsigned char *data, *cryptkey; + int retval; + + /* Open file and get size */ + + if ((in = fopen(encrypted_filename, "r")) == NULL) { + retval = -1; + goto out; + } + + fseek(in, 0, SEEK_END); + len = ftell(in); + fseek(in, 0, SEEK_SET); + + if (len <= 0) { + retval = -2; + goto out1; + } + + /* Allocate buffers */ + + aligned_len = align16(len); + + if ((data = (unsigned char *) memalign(0x10, aligned_len)) == NULL) { + retval = -3; + goto out1; + } + + if ((cryptkey = (unsigned char *) memalign(0x10, 0x10)) == NULL) { + retval = -4; + goto out2; + } + + /* Fill buffers */ + + if (gamekey != NULL) + memcpy(cryptkey, gamekey, 0x10); + + memset(data + len, 0, aligned_len - len); + if (fread(data, 1, len, in) != len) { + retval = -5; + goto out3; + } + + /* Do the decryption */ + + if ((retval = decrypt_data( gamekey ? (mainSdkVersion >= 4 ? 5 : 3) : 1, // 5 for sdk >= 4, else 3 + data, &len, &aligned_len, + gamekey ? cryptkey : NULL)) < 0) { + retval -= 100; + goto out3; + } + + /* Write the data out. decrypt_data has set len correctly. */ + + if ((out = fopen(decrypted_filename, "w")) == NULL) { + retval = -6; + goto out3; + } + + if (fwrite(data, 1, len, out) != len) { + retval = -7; + goto out4; + } + + /* All done. Return file length. */ + retval = len; + out4: + fclose(out); + out3: + free(cryptkey); + out2: + free(data); + out1: + fclose(in); + out: + return retval; +} + +/* Do the actual hardware decryption. + mode is 3 for saves with a cryptkey, or 1 otherwise + data, dataLen, and cryptkey must be multiples of 0x10. + cryptkey is NULL if mode == 1. +*/ +int decrypt_data(unsigned int mode, + unsigned char *data, + int *dataLen, + int *alignedLen, + unsigned char *cryptkey) +{ + pspChnnlsvContext1 ctx1; + pspChnnlsvContext2 ctx2; + + /* Need a 16-byte IV plus some data */ + if (*alignedLen <= 0x10) + return -1; + *dataLen -= 0x10; + *alignedLen -= 0x10; + + /* Set up buffers */ + memset(&ctx1, 0, sizeof(pspChnnlsvContext1)); + memset(&ctx2, 0, sizeof(pspChnnlsvContext2)); + + /* Perform the magic */ + if (sceChnnlsv_E7833020_(&ctx1, mode) < 0) + return -2; + if (sceChnnlsv_ABFDFC8B_(&ctx2, mode, 2, data, cryptkey) < 0) + return -3; + if (sceChnnlsv_F21A1FCA_(&ctx1, data, 0x10) < 0) + return -4; + if (sceChnnlsv_F21A1FCA_(&ctx1, data + 0x10, *alignedLen) < 0) + return -5; + if (sceChnnlsv_850A7FA1_(&ctx2, data + 0x10, *alignedLen) < 0) + return -6; + + /* Verify that it decrypted correctly */ + if (sceChnnlsv_21BE78B4_(&ctx2) < 0) + return -7; + + /* If desired, a new file hash from this PSP can be computed now: + if (sceChnnlsv_C4C494F8(ctx1, newhash, cryptkey) < 0) + return -8; + */ + + /* The decrypted data starts at data + 0x10, so shift it back. */ + memmove(data, data + 0x10, *dataLen); + + /* All done */ + return 0; +} diff --git a/Tools/SaveTool/decrypt.h b/Tools/SaveTool/decrypt.h new file mode 100644 index 0000000000..57b8881d6c --- /dev/null +++ b/Tools/SaveTool/decrypt.h @@ -0,0 +1,31 @@ +/* + * PSP Software Development Kit - http://www.pspdev.org + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPSDK root for details. + * + * decrypt.h - Declarations for functions in decrypt.c + * + * Copyright (c) 2005 Jim Paris + * Coypright (c) 2005 psp123 + * + * $Id: decrypt.h 1562 2005-12-10 20:52:45Z jim $ + */ + +#include + +/* Detect the samegame format and decrypt it. See main.c for an example. */ +int decrypt_file(const char *decrypted_filename, + const char *encrypted_filename, + const unsigned char *gamekey, + const int mainSdkVersion); + +/* Do the actual hardware decryption. + mode is 3 for saves with a cryptkey, or 1 otherwise. + data, alignedLen, and cryptkey must be multiples of 0x10. + cryptkey is NULL if mode == 1. +*/ +int decrypt_data(unsigned int mode, + unsigned char *data, + int *dataLen, + int *alignedLen, + unsigned char *cryptkey); diff --git a/Tools/SaveTool/encrypt.c b/Tools/SaveTool/encrypt.c new file mode 100644 index 0000000000..8da226e153 --- /dev/null +++ b/Tools/SaveTool/encrypt.c @@ -0,0 +1,233 @@ +/* + * PSP Software Development Kit - http://www.pspdev.org + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPSDK root for details. + * + * encrypt.c - Encryption routines using sceChnnlsv + * + * Copyright (c) 2005 Jim Paris + * Coypright (c) 2005 psp123 + * + * $Id: encrypt.c 1560 2005-12-10 01:16:32Z jim $ + */ + +#include "encrypt.h" +#include "hash.h" +#include +#include +#include +#include +#include +#include "kernelcall/kernelcall.h" + +static inline int align16(unsigned int v) +{ + return ((v + 0xF) >> 4) << 4; +} + +int fopen_getsize(const char *filename, FILE **fd, int *size) +{ + if ((*fd = fopen(filename, "r")) == NULL) + return -1; + + fseek(*fd, 0, SEEK_END); + *size = ftell(*fd); + fseek(*fd, 0, SEEK_SET); + + if (*size <= 0) { + fclose(*fd); + return -2; + } + + return 0; +} + +/* Encrypt the given plaintext file, and update the message + authentication hashes in the param.sfo. The data_filename is + usually the final component of encrypted_filename, e.g. "DATA.BIN". + See main.c for an example of usage. */ +int encrypt_file(const char *plaintext_filename, + const char *encrypted_filename, + const char *data_filename, + const char *paramsfo_filename, + const char *paramsfo_filename_out, + const unsigned char *gamekey, + const int mainSdkVersion) +{ + FILE *in = NULL, *out = NULL, *sfo = NULL; + unsigned char *data = NULL, *cryptkey = NULL, *hash = NULL; + unsigned char paramsfo[0x1330]; + int len, aligned_len, tmp; + int retval; + + /* Open plaintext and param.sfo files and get size */ + + if (fopen_getsize(plaintext_filename, &in, &len) < 0) { + retval = -1; + goto out; + } + + if (fopen_getsize(paramsfo_filename, &sfo, &tmp) < 0) { + retval = -2; + goto out; + } + + /* Verify size of param.sfo; all known saves use this size */ + + if (tmp != 0x1330) { + retval = -3; + goto out; + } + + /* Allocate buffers. data has 0x10 bytes extra for the IV. */ + + aligned_len = align16(len); + + if ((data = + (unsigned char *) memalign(0x10, aligned_len + 0x10)) == NULL) { + retval = -4; + goto out; + } + + if ((cryptkey = (unsigned char *) memalign(0x10, 0x10)) == NULL) { + retval = -5; + goto out; + } + + if ((hash = (unsigned char *) memalign(0x10, 0x10)) == NULL) { + retval = -6; + goto out; + } + + /* Fill buffers. */ + + memset(data + len, 0, aligned_len - len); + if (fread(data, 1, len, in) != len) { + retval = -7; + goto out; + } + + if (fread(paramsfo, 1, 0x1330, sfo) != 0x1330) { + retval = -8; + goto out; + } + + if (gamekey != NULL) + memcpy(cryptkey, gamekey, 0x10); + + /* Do the encryption */ + + if ((retval = encrypt_data( gamekey ? (mainSdkVersion >= 4 ? 5 : 3) : 1, // 5 for sdk >= 4, 3 otherwise + data, + &len, &aligned_len, + hash, + gamekey ? cryptkey : NULL)) < 0) { + retval -= 1000; + goto out; + } + + /* Update the param.sfo hashes */ + + if ((retval = update_hashes(paramsfo, 0x1330, + data_filename, hash, + gamekey ? 3 : 1)) < 0) { + retval -= 2000; + goto out; + } + + /* Write the data to the file. encrypt_data has already set len. */ + + if ((out = fopen(encrypted_filename, "w")) == NULL) { + retval = -9; + goto out; + } + + if (fwrite(data, 1, len, out) != len) { + retval = -10; + goto out; + } + + /* Reopen param.sfo, and write the updated copy out. */ + + fclose(sfo); + if ((sfo = fopen(paramsfo_filename_out, "w")) == NULL) { + retval = -11; + goto out; + } + + if (fwrite(paramsfo, 1, 0x1330, sfo) != 0x1330) { + retval = -12; + goto out; + } + + /* All done. Return file length. */ + + retval = len; + + out: + if(out) fclose(out); + if(hash) free(hash); + if(cryptkey) free(cryptkey); + if(data) free(data); + if(sfo) fclose(sfo); + if(in) fclose(in); + + return retval; +} + +/* Do the actual hardware encryption. + mode is 3 for saves with a cryptkey, or 1 otherwise + data, dataLen, and cryptkey must be multiples of 0x10. + cryptkey is NULL if mode == 1. +*/ +int encrypt_data(unsigned int mode, + unsigned char *data, + int *dataLen, + int *alignedLen, + unsigned char *hash, + unsigned char *cryptkey) +{ + pspChnnlsvContext1 ctx1; + pspChnnlsvContext2 ctx2; + + /* Make room for the IV in front of the data. */ + memmove(data + 0x10, data, *alignedLen); + + /* Set up buffers */ + memset(&ctx1, 0, sizeof(pspChnnlsvContext1)); + memset(&ctx2, 0, sizeof(pspChnnlsvContext2)); + memset(hash, 0, 0x10); + memset(data, 0, 0x10); + + /* Build the 0x10-byte IV and setup encryption */ + if (sceChnnlsv_ABFDFC8B_(&ctx2, mode, 1, data, cryptkey) < 0) + return -1; + if (sceChnnlsv_E7833020_(&ctx1, mode) < 0) + return -2; + if (sceChnnlsv_F21A1FCA_(&ctx1, data, 0x10) < 0) + return -3; + if (sceChnnlsv_850A7FA1_(&ctx2, data + 0x10, *alignedLen) < 0) + return -4; + + /* Clear any extra bytes left from the previous steps */ + memset(data + 0x10 + *dataLen, 0, *alignedLen - *dataLen); + + /* Encrypt the data */ + if (sceChnnlsv_F21A1FCA_(&ctx1, data + 0x10, *alignedLen) < 0) + return -5; + + /* Verify encryption */ + if (sceChnnlsv_21BE78B4_(&ctx2) < 0) + return -6; + + /* Build the file hash from this PSP */ + if (sceChnnlsv_C4C494F8_(&ctx1, hash, cryptkey) < 0) + return -7; + + /* Adjust sizes to account for IV */ + *alignedLen += 0x10; + *dataLen += 0x10; + + /* All done */ + return 0; +} diff --git a/Tools/SaveTool/encrypt.h b/Tools/SaveTool/encrypt.h new file mode 100644 index 0000000000..2b06da21e7 --- /dev/null +++ b/Tools/SaveTool/encrypt.h @@ -0,0 +1,38 @@ +/* + * PSP Software Development Kit - http://www.pspdev.org + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPSDK root for details. + * + * encrypt.h - Declarations for functions in encrypt.c + * + * Copyright (c) 2005 Jim Paris + * Coypright (c) 2005 psp123 + * + * $Id: encrypt.h 1559 2005-12-10 01:10:11Z jim $ + */ + +#include + +/* Encrypt the given plaintext file, and update the message + authentication hashes in the param.sfo. The data_filename is + usually the final component of encrypted_filename, e.g. "DATA.BIN". + See main.c for an example of usage. */ +int encrypt_file(const char *plaintext_filename, + const char *encrypted_filename, + const char *data_filename, + const char *paramsfo_filename, + const char *paramsfo_filename_out, + const unsigned char *gamekey, + const int mainSdkVersion); + +/* Do the actual hardware encryption. + mode is 3 for saves with a cryptkey, or 1 otherwise. + data, alignedLen, cryptkey, and hash must be multiples of 0x10. + cryptkey is NULL if mode == 1. +*/ +int encrypt_data(unsigned int mode, + unsigned char *data, + int *dataLen, + int *alignedLen, + unsigned char *hash, + unsigned char *cryptkey); diff --git a/Tools/SaveTool/hash.c b/Tools/SaveTool/hash.c new file mode 100644 index 0000000000..0a83a4ca3a --- /dev/null +++ b/Tools/SaveTool/hash.c @@ -0,0 +1,129 @@ +/* + * PSP Software Development Kit - http://www.pspdev.org + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPSDK root for details. + * + * hash.c - Hashing routines using sceChnnlsv + * + * Copyright (c) 2005 Jim Paris + * Coypright (c) 2005 psp123 + * + * $Id: hash.c 1560 2005-12-10 01:16:32Z jim $ + */ + +#include "hash.h" +#include "psf.h" +#include +#include +#include +#include +#include +#include "kernelcall/kernelcall.h" + +static inline int align16(unsigned int v) +{ + return ((v + 0xF) >> 4) << 4; +} + +/* Update the hashes in the param.sfo data, using + the given file hash, and by computing the param.sfo hashes. + filehash must be a multiple of 16 bytes, and is reused to + store other hashes. The filename is e.g. "DATA.BIN". */ +int update_hashes(unsigned char *data, + int len, + const char *filename, + unsigned char *filehash, + int encryptmode) +{ + int alignedLen = align16(len); + unsigned char *datafile, *savedata_params; + int listLen, paramsLen; + int ret; + + /* Locate SAVEDATA_PARAM section in the param.sfo. */ + if ((ret = find_psf_section("SAVEDATA_PARAMS", data, 0x1330, + &savedata_params, ¶msLen)) < 0) { + return ret - 100; + } + + /* Locate the pointer for this DATA.BIN equivalent */ + if ((ret = find_psf_section("SAVEDATA_FILE_LIST", data, 0x1330, + &datafile, &listLen)) < 0) { + return ret - 200; + } + + if ((ret = find_psf_datafile(filename, datafile, + listLen, &datafile)) < 0) { + return ret - 300; + } + + /* Check minimum sizes based on where we want to write */ + if ((listLen < 0x20) || (paramsLen < 0x80)) { + return -1; + } + + /* Clear params and insert file hash */ + memset(savedata_params, 0, paramsLen); + memcpy(datafile + 0x0D, filehash, 0x10); + + /* Compute 11D0 hash over entire file */ + if ((ret = build_hash(filehash, data, len, alignedLen, + (encryptmode & 2) ? 4 : 2, NULL)) < 0) { // Not sure about "2" + return ret - 400; + } + + /* Copy 11D0 hash to param.sfo and set flag indicating it's there */ + memcpy(savedata_params + 0x20, filehash, 0x10); + *savedata_params |= 0x01; + + /* If new encryption mode, compute and insert the 1220 hash. */ + if (encryptmode & 2) { + + /* Enable the hash bit first */ + *savedata_params |= 0x20; + + if ((ret = build_hash(filehash, data, len, alignedLen, + 3, 0)) < 0) { + return ret - 500; + } + memcpy(savedata_params + 0x70, filehash, 0x10); + } + + /* Compute and insert the 11C0 hash. */ + if ((ret = build_hash(filehash, data, len, alignedLen, 1, 0)) < 0) { + return ret - 600; + } + memcpy(savedata_params + 0x10, filehash, 0x10); + + /* All done. */ + return 0; +} + +/* Build a single hash using the given data and mode. + data and alignedLen must be multiples of 0x10. + cryptkey is NULL for savedata. */ +int build_hash(unsigned char *output, + unsigned char *data, + unsigned int len, + unsigned int alignedLen, + int mode, + unsigned char *cryptkey) +{ + pspChnnlsvContext1 ctx1; + + /* Set up buffers */ + memset(&ctx1, 0, sizeof(pspChnnlsvContext1)); + memset(output, 0, 0x10); + memset(data + len, 0, alignedLen - len); + + /* Perform the magic */ + if (sceChnnlsv_E7833020_(&ctx1, mode & 0xFF) < 0) + return -1; + if (sceChnnlsv_F21A1FCA_(&ctx1, data, alignedLen) < 0) + return -2; + if (sceChnnlsv_C4C494F8_(&ctx1, output, cryptkey) < 0) + return -3; + + /* All done. */ + return 0; +} diff --git a/Tools/SaveTool/hash.h b/Tools/SaveTool/hash.h new file mode 100644 index 0000000000..978627a693 --- /dev/null +++ b/Tools/SaveTool/hash.h @@ -0,0 +1,34 @@ +/* + * PSP Software Development Kit - http://www.pspdev.org + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPSDK root for details. + * + * hash.h - Declarations for functions in hash.c + * + * Copyright (c) 2005 Jim Paris + * Coypright (c) 2005 psp123 + * + * $Id: hash.h 1559 2005-12-10 01:10:11Z jim $ + */ + +#include + +/* Update the hashes in the param.sfo data, using + the given file hash, and by computing the param.sfo hashes. + filehash must be a multiple of 16 bytes, and is reused to + store other hashes. The filename is e.g. "DATA.BIN". */ +int update_hashes(unsigned char *data, + int len, + const char *filename, + unsigned char *filehash, + int encryptmode); + +/* Build a single hash using the given data and mode. + data, and alignedLen must be multiples of 0x10. + cryptkey is NULL for savedata.*/ +int build_hash(unsigned char *output, + unsigned char *data, + unsigned int len, + unsigned int alignedLen, + int mode, + unsigned char *cryptkey); diff --git a/Tools/SaveTool/kernelcall.prx b/Tools/SaveTool/kernelcall.prx new file mode 100644 index 0000000000..350ee40170 Binary files /dev/null and b/Tools/SaveTool/kernelcall.prx differ diff --git a/Tools/SaveTool/kernelcall/Makefile b/Tools/SaveTool/kernelcall/Makefile new file mode 100644 index 0000000000..a7138d7cd1 --- /dev/null +++ b/Tools/SaveTool/kernelcall/Makefile @@ -0,0 +1,24 @@ +release: all + psp-build-exports -k exports.exp + psp-build-exports -s -k -v exports.exp + +TARGET = kernelcall +OBJS = kernelcall.o + +BUILD_PRX=1 +PRX_EXPORTS=exports.exp +USE_KERNEL_LIBS = 1 +USE_KERNEL_LIBC = 1 + +INCDIR = +CFLAGS = -O0 -G0 -Wall -g +CXXFLAGS = $(CFLAGS) -fno-exceptions -fno-rtti +ASFLAGS = $(CFLAGS) + +LIBDIR = +LDFLAGS = -mno-crt0 -nostartfiles + +LIBS = -lpspchnnlsv + +PSPSDK=$(shell psp-config --pspsdk-path) +include $(PSPSDK)/lib/build.mak diff --git a/Tools/SaveTool/kernelcall/kernelcall.c b/Tools/SaveTool/kernelcall/kernelcall.c new file mode 100644 index 0000000000..32f0291682 --- /dev/null +++ b/Tools/SaveTool/kernelcall/kernelcall.c @@ -0,0 +1,84 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +PSP_MODULE_INFO("kernelcall", 0x1006, 2, 0); + + +u64 sceSysreg_driver_4F46EEDE(); + + +u64 GetFuseId() +{ + u64 fuseId = sceSysreg_driver_4F46EEDE(); + return fuseId; +} + +int sceChnnlsv_E7833020_(pspChnnlsvContext1 *ctx, int mode) +{ + int k1 = pspSdkSetK1(0); + int res = sceChnnlsv_E7833020(ctx,mode); + + pspSdkSetK1(k1); + return res; +} + +int sceChnnlsv_F21A1FCA_(pspChnnlsvContext1 *ctx, unsigned char *data, int len) +{ + int k1 = pspSdkSetK1(0); + int res = sceChnnlsv_F21A1FCA(ctx,data,len); + pspSdkSetK1(k1); + return res; +} + +int sceChnnlsv_C4C494F8_(pspChnnlsvContext1 *ctx, + unsigned char *hash, unsigned char *cryptkey) +{ + int k1 = pspSdkSetK1(0); + int res = sceChnnlsv_C4C494F8(ctx,hash,cryptkey); + pspSdkSetK1(k1); + return res; +} + +int sceChnnlsv_ABFDFC8B_(pspChnnlsvContext2 *ctx, int mode1, int mode2, + unsigned char *hashkey, unsigned char *cipherkey) +{ + int k1 = pspSdkSetK1(0); + int res = sceChnnlsv_ABFDFC8B(ctx,mode1,mode2,hashkey,cipherkey); + pspSdkSetK1(k1); + return res; +} + +int sceChnnlsv_850A7FA1_(pspChnnlsvContext2 *ctx, unsigned char *data, int len) +{ + int k1 = pspSdkSetK1(0); + int res = sceChnnlsv_850A7FA1(ctx,data,len); + pspSdkSetK1(k1); + return res; +} + +int sceChnnlsv_21BE78B4_(pspChnnlsvContext2 *ctx) +{ + int k1 = pspSdkSetK1(0); + int res = sceChnnlsv_21BE78B4(ctx); + pspSdkSetK1(k1); + return res; +} + +int module_start(SceSize args, void *argp) +{ + return 0; +} + +int module_stop() +{ + return 0; +} diff --git a/Tools/SaveTool/kernelcall/kernelcall.h b/Tools/SaveTool/kernelcall/kernelcall.h new file mode 100644 index 0000000000..4baf4c2ed3 --- /dev/null +++ b/Tools/SaveTool/kernelcall/kernelcall.h @@ -0,0 +1,9 @@ +u64 GetFuseId(); +int sceChnnlsv_E7833020_(pspChnnlsvContext1 *ctx, int mode); +int sceChnnlsv_F21A1FCA_(pspChnnlsvContext1 *ctx, unsigned char *data, int len); +int sceChnnlsv_C4C494F8_(pspChnnlsvContext1 *ctx, + unsigned char *hash, unsigned char *cryptkey); +int sceChnnlsv_ABFDFC8B_(pspChnnlsvContext2 *ctx, int mode1, int mode2, + unsigned char *hashkey, unsigned char *cipherkey); +int sceChnnlsv_850A7FA1_(pspChnnlsvContext2 *ctx, unsigned char *data, int len); +int sceChnnlsv_21BE78B4_(pspChnnlsvContext2 *ctx); diff --git a/Tools/SaveTool/main.c b/Tools/SaveTool/main.c new file mode 100644 index 0000000000..54a2b34e96 --- /dev/null +++ b/Tools/SaveTool/main.c @@ -0,0 +1,579 @@ +#include +#include +#include +#include +#include +#include +#include +#include "kernelcall/kernelcall.h" +#include +#include +#include +#include "encrypt.h" +#include "decrypt.h" +#include "psf.h" + +#define printf pspDebugScreenPrintf + +/* Define the module info section */ +PSP_MODULE_INFO("ppssppsavetool", 0, 1, 0); +PSP_MAIN_THREAD_ATTR(PSP_THREAD_ATTR_USER); +PSP_HEAP_SIZE_KB(-64); + +#define ENCRYPT_FILE_VERSION 1 + + +int currentMenu = 0; +int selectedOption = 0; +int basePath = 0; +int workDir = 0; + +char *menuList0[] = {"Encrypt","Decrypt", "Exit", NULL}; +char *menuList1[] = {"ms0:/PSP/SAVEDATAPPSSPP/","host0:/","host1:/", "host2:/", "Back", NULL}; +char *menuList2[] = {"Back", NULL}; + +int GetSDKMainVersion(int sdkVersion) +{ + if(sdkVersion > 0x307FFFF) + return 6; + if(sdkVersion > 0x300FFFF) + return 5; + if(sdkVersion > 0x206FFFF) + return 4; + if(sdkVersion > 0x205FFFF) + return 3; + if(sdkVersion >= 0x2000000) + return 2; + if(sdkVersion >= 0x1000000) + return 1; + return 0; +}; + +int ProcessInput(int maxOption, int *selectedOption) +{ + SceCtrlData pad, oldpad; + sceCtrlReadBufferPositive(&oldpad, 1); + while(1) + { + sceCtrlReadBufferPositive(&pad, 1); + + if (pad.Buttons != 0) + { + if (!(oldpad.Buttons & PSP_CTRL_CROSS) && pad.Buttons & PSP_CTRL_CROSS) + { + return *selectedOption; + } + else if (!(oldpad.Buttons & PSP_CTRL_UP) && pad.Buttons & PSP_CTRL_UP && *selectedOption > 0) + { + *selectedOption = *selectedOption-1; + return -1; + } + else if (!(oldpad.Buttons & PSP_CTRL_DOWN) && pad.Buttons & PSP_CTRL_DOWN && *selectedOption < maxOption-1) + { + *selectedOption = *selectedOption + 1; + return -1; + } + } + oldpad = pad; + } +} + +typedef struct +{ + char name[30]; + char saveFile[30]; + int errorId; +} DirInfo; + +typedef struct +{ + int fileVersion; + u8 key[16]; + int sdkVersion; +} EncryptFileInfo; + +DirInfo dirList[128]; +int numDirList; +DirInfo invalidDirList[128]; +int numInvalidDirList; + +int FileExist(char* basePath, char* dirPath, char* fileName) +{ + SceIoStat fileStat; + char path[1024]; + sprintf(path,"%s%s/%s",basePath, dirPath, fileName); + if(sceIoGetstat(path, &fileStat) < 0) // no file + return 0; + return 1; +} + +int FileRead(char* basePath, char* dirPath, char* fileName, u8* dataout, int size) +{ + char path[1024]; + sprintf(path,"%s%s/%s",basePath, dirPath, fileName); + SceUID fileId = sceIoOpen(path, PSP_O_RDONLY, 0777); + if(fileId < 0) + return -1; + sceIoRead(fileId, dataout, size); + sceIoClose(fileId); + return 0; +} + +void AddErrorDir(char* dirName, int error) +{ + if(numInvalidDirList >= 128) + return; + DirInfo *inf = &invalidDirList[numInvalidDirList]; + strcpy(inf->name,dirName); + inf->errorId = error; + numInvalidDirList++; +} + +int UpdateValidDir(int isEncrypt) +{ + numDirList = 0; + numInvalidDirList = 0; + + const char* pspPath = "ms0:/PSP/SAVEDATA/"; + + char* pathSrc; + char* pathDst; + if(isEncrypt) + { + pathSrc = menuList1[basePath]; + pathDst = pspPath; + } + else + { + pathSrc = pspPath; + pathDst = menuList1[basePath]; + } + + int dfd; + dfd = sceIoDopen(menuList1[basePath]); + if(dfd >= 0) + { + SceIoDirent data; + while(sceIoDread(dfd, &data) > 0 && numDirList < 128) + { + if(!(data.d_stat.st_attr & 0x10)) // is not a directory + { + continue; + } + + if(data.d_name[0] == '.') // ignore "." and ".." + continue; + + if(FileExist(menuList1[basePath], data.d_name, "ENCRYPT_INFO.BIN") < 0) + { + AddErrorDir(data.d_name,1); + continue; + } + + EncryptFileInfo encryptInfo; + if(FileRead(menuList1[basePath], data.d_name, "ENCRYPT_INFO.BIN",(u8*)&encryptInfo,sizeof(encryptInfo)) < 0) + { + AddErrorDir(data.d_name,2); + continue; + } + + if(encryptInfo.fileVersion != ENCRYPT_FILE_VERSION) // Not good version + { + AddErrorDir(data.d_name,3); + continue; + } + + if(FileExist(pathSrc, data.d_name, "PARAM.SFO") < 0) + { + AddErrorDir(data.d_name,4); + continue; + } + + u8 paramsfo[0x1330]; + if(FileRead(pathSrc, data.d_name, "PARAM.SFO",(u8*)¶msfo,0x1330) < 0) + { + AddErrorDir(data.d_name,5); + continue; + } + + u8 *datafile; + int listLen; + if (find_psf_section("SAVEDATA_FILE_LIST", paramsfo, 0x1330, + &datafile, &listLen) < 0) + { + AddErrorDir(data.d_name,6); + continue; + } + if(datafile[0] == 0) + { + AddErrorDir(data.d_name,7); + continue; + } + + char filename[32]; + strcpy(filename, (char*)datafile); + + if(FileExist(pathSrc, data.d_name, filename) < 0) + { + AddErrorDir(data.d_name,8); + continue; + } + + DirInfo *inf = &dirList[numDirList]; + inf->errorId = 0; + strcpy(inf->name, data.d_name); + strcpy(inf->saveFile, filename); + + numDirList++; + + } + sceIoDclose(dfd); + if(numDirList == 0) + { + return -1; + } + } + else + { + return -2; + } + return 0; +} + +int FileCopy(char* srcPath, char* destPath, char* fileName) +{ + SceIoStat fileStat; + char path[258]; + sprintf(path,"%s/%s",srcPath, fileName); + + if(sceIoGetstat(path, &fileStat) < 0) + return -1; + u8* data = malloc(fileStat.st_size); + + SceUID fileId = sceIoOpen(path, PSP_O_RDONLY, 0777); + if(fileId < 0) + { + printf("Fail opening %s\n",path); + return -1; + } + sceIoRead(fileId, data, fileStat.st_size); + sceIoClose(fileId); + + sprintf(path,"%s/%s",destPath, fileName); + + fileId = sceIoOpen(path, PSP_O_WRONLY | PSP_O_CREAT, 0777); + if(fileId < 0) + { + printf("Fail opening %s\n",path); + return -1; + } + sceIoWrite(fileId, data, fileStat.st_size); + sceIoClose(fileId); + + free(data); + return 0; +} + +int main(int argc, char *argv[]) +{ + int i; + pspDebugScreenInit(); + + SceUID mod = pspSdkLoadStartModule ("flash0:/kd/chnnlsv.prx",PSP_MEMORY_PARTITION_KERNEL); + if (mod < 0) { + printf("Error 0x%08X loading/starting chnnlsv.prx.\n", mod); + } + + mod = pspSdkLoadStartModule ("kernelcall.prx",PSP_MEMORY_PARTITION_KERNEL); + if (mod < 0) { + printf("Error 0x%08X loading/starting kernelcall.prx.\n", mod); + } + + sceCtrlSetSamplingCycle(0); + sceCtrlSetSamplingMode(PSP_CTRL_MODE_ANALOG); + for(;;) + { + printf("===================================================================="); + printf("PPSSPP Save Tool\n"); + printf("====================================================================\n\n\n"); + + switch(currentMenu) + { + + case 0: + { + int maxOption = 0; + for(i = 0; menuList0[i]; i++) + { + if(i == selectedOption) + printf(" > %s\n",menuList0[i]); + else + printf(" %s\n",menuList0[i]); + maxOption++; + } + + int input = ProcessInput(maxOption, &selectedOption); + if(input == 0) + { + currentMenu = 1; + selectedOption = 0; + } + else if(input == 1) + { + currentMenu = 4; + selectedOption = 0; + } + else if(input == 2) + { + sceKernelExitGame(); + } + } + break; + case 4: + case 1: + { + int maxOption = 0; + printf("PPSSPP Decrypted Save Directory : \n"); + for(i = 0; menuList1[i]; i++) + { + if(i == selectedOption) + printf(" > %s\n",menuList1[i]); + else + printf(" %s\n",menuList1[i]); + maxOption++; + } + + int input = ProcessInput(maxOption, &selectedOption); + if(input == maxOption-1) + { + if(currentMenu == 1) + selectedOption = 0; + else + selectedOption = 1; + currentMenu = 0; + } + else if(input >= 0) + { + basePath = selectedOption; + if(currentMenu == 1) + { + currentMenu = 2; + UpdateValidDir(1); + } + else + { + currentMenu = 5; + UpdateValidDir(0); + } + selectedOption = 0; + } + } + break; + case 5: + case 2: + { + int maxOption = 0; + if(currentMenu == 2) + printf("Save to encrypt : \n"); + else + printf("Save to decrypt : \n"); + + if(numDirList == 0) + { + printf("No compatible data, see README for help on use\n"); + } + for(i = 0; i < numDirList; i++) + { + if(i == selectedOption) + printf(" > %s\n",dirList[i].name); + else + printf(" %s\n",dirList[i].name); + maxOption++; + } + + for(i = 0; menuList2[i]; i++) + { + if((i+numDirList) == selectedOption) + printf(" > %s\n",menuList2[i]); + else + printf(" %s\n",menuList2[i]); + maxOption++; + } + + printf("\n Invalid path : \n"); + for(i = 0; i < numInvalidDirList && i < (22-numDirList); i++) + { + switch(invalidDirList[i].errorId) + { + case 1: + printf(" %s : ENCRYPT_INFO.BIN not found\n",invalidDirList[i].name); + break; + case 2: + printf(" %s : ENCRYPT_INFO.BIN read error\n",invalidDirList[i].name); + break; + case 3: + printf(" %s : ENCRYPT_INFO.BIN wrong version\n",invalidDirList[i].name); + break; + case 4: + printf(" %s : PARAM.SFO not found\n",invalidDirList[i].name); + break; + case 5: + printf(" %s : PARAM.SFO read error\n",invalidDirList[i].name); + break; + case 6: + printf(" %s : SAVEDATA_FILE_LIST not found in PARAM.SFO\n",invalidDirList[i].name); + break; + case 7: + printf(" %s : no save name in SAVEDATA_FILE_LIST\n",invalidDirList[i].name); + break; + case 8: + printf(" %s : no save found\n",invalidDirList[i].name); + break; + default: + break; + } + } + + int input = ProcessInput(maxOption, &selectedOption); + if(input == numDirList) + { + if(currentMenu == 2) + currentMenu = 1; + else + currentMenu = 4; + selectedOption = basePath; + } + else if(input >= 0) + { + if(currentMenu == 2) + currentMenu = 3; + else + currentMenu = 6; + workDir = input; + selectedOption = 0; + } + } + break; + case 6: + case 3: + { + + EncryptFileInfo encryptInfo; + if(FileRead(menuList1[basePath], dirList[workDir].name, "ENCRYPT_INFO.BIN",(u8*)&encryptInfo,sizeof(encryptInfo)) < 0) + { + printf("Can't read encrypt file\n"); + } + else + { + printf("Key : "); + for(i = 0; i < 16; i++) + printf(" %02x",(u8)encryptInfo.key[i]); + printf("\n"); + printf("SDK Version : 0x%x\n",encryptInfo.sdkVersion); + + char srcPath[128]; + char dstPath[128]; + if(currentMenu == 3) + { + sprintf(srcPath,"%s%s",menuList1[basePath], dirList[workDir].name); + sprintf(dstPath,"ms0:/PSP/SAVEDATA/%s",dirList[workDir].name); + sceIoMkdir(dstPath,0777); + } + else + { + sprintf(srcPath,"ms0:/PSP/SAVEDATA/%s",dirList[workDir].name); + sprintf(dstPath,"%s%s",menuList1[basePath], dirList[workDir].name); + } + + int dfd; + dfd = sceIoDopen(srcPath); + if(dfd >= 0) + { + SceIoDirent dirinfo; + while(sceIoDread(dfd, &dirinfo) > 0) + { + + if(!(dirinfo.d_stat.st_mode & 0x2000)) // is not a file + continue; + + if(strcmp(dirinfo.d_name,"ENCRYPT_INFO.BIN") == 0) // don't copy encrypt info + continue; + + FileCopy(srcPath, dstPath, dirinfo.d_name); + + } + sceIoDclose(dfd); + } + + if(currentMenu == 3) + { + + char decryptedFile[258], encryptedFile[258], srcSFO[258], dstSFO[258]; + sprintf(decryptedFile,"%s/%s",srcPath ,dirList[workDir].saveFile); + sprintf(srcSFO,"%s/PARAM.SFO",srcPath); + + sprintf(encryptedFile,"%s/%s",dstPath ,dirList[workDir].saveFile); + sprintf(dstSFO,"%s/PARAM.SFO",dstPath); + + printf("Encoding %s into %s\n",decryptedFile, encryptedFile); + + int ret = encrypt_file(decryptedFile, + encryptedFile, + dirList[workDir].saveFile, + srcSFO, + dstSFO, + encryptInfo.key[0] != 0 ? encryptInfo.key : NULL, + GetSDKMainVersion(encryptInfo.sdkVersion) + ); + + if(ret < 0) { + printf("Error: encrypt_file() returned %d\n\n", ret); + } else { + printf("Successfully wrote %d bytes to\n", ret); + printf(" %s\n", encryptedFile); + printf("and updated hashes in\n"); + printf(" %s\n\n", dstSFO); + } + } + else + { + char decryptedFile[258], encryptedFile[258]; + sprintf(encryptedFile,"%s/%s",srcPath ,dirList[workDir].saveFile); + sprintf(decryptedFile,"%s/%s",dstPath ,dirList[workDir].saveFile); + + printf("Decoding %s into %s\n",encryptedFile, decryptedFile); + + int ret = decrypt_file(decryptedFile, encryptedFile, encryptInfo.key[0] != 0 ? encryptInfo.key : NULL, GetSDKMainVersion(encryptInfo.sdkVersion)); + + if(ret < 0) { + printf("Error: decrypt_file() returned %d\n\n", ret); + } else { + printf("Successfully wrote %d bytes to\n", ret); + printf(" %s\n", decryptedFile); + } + } + printf(" > Back\n"); + + int input = ProcessInput(1, &selectedOption); + if(input >= 0) + { + if(currentMenu == 3) + currentMenu = 2; + else + currentMenu = 5; + selectedOption = 0; + } + } + } + break; + default: + sceKernelExitGame(); + break; + } + + pspDebugScreenClear(); + sceDisplayWaitVblankStart(); + sceGuSwapBuffers(); + } + return 0; +} diff --git a/Tools/SaveTool/psf.c b/Tools/SaveTool/psf.c new file mode 100644 index 0000000000..5f68b1cc74 --- /dev/null +++ b/Tools/SaveTool/psf.c @@ -0,0 +1,114 @@ +/* + * PSP Software Development Kit - http://www.pspdev.org + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPSDK root for details. + * + * psf.c - PSF parsing routines + * + * Copyright (c) 2005 Jim Paris + * Coypright (c) 2005 psp123 + * + * $Id: psf.c 1560 2005-12-10 01:16:32Z jim $ + */ + +#include "psf.h" +#include +#include +#include +#include +#include + +/* Find to the named section in the PSF file, and return an + absolute pointer to it and the section size. */ +int find_psf_section(const char *name, + unsigned char *data, + int dataLen, + unsigned char **location, + int *size) +{ + unsigned short int nameLoc; + int i, magicHead, strLoc, headLen, numSects; + int sectCurLen, sectBufLen, sectBufLoc, curPos; + + if (dataLen < 0x14) + return -1; + + /* Get the basics from the header */ + magicHead = *(unsigned int *)&data[0x00]; + strLoc = *(unsigned int *)&data[0x08]; + headLen = *(unsigned int *)&data[0x0C]; + numSects = *(unsigned int *)&data[0x10]; + + /* Do some error checking */ + if (magicHead != 0x46535000) + return -2; + + /* Verify strLoc is proper */ + if ((strLoc > headLen) || (strLoc >= dataLen)) + return -3; + + /* Verify headLen is proper */ + if (headLen >= dataLen) + return -4; + + /* Verify numSects is proper */ + if (numSects != ((strLoc - 0x14) / 0x10)) + return -5; + + /* Process all sections */ + for (i = 0; i < numSects; i++) + { + /* Get the curPos */ + curPos = 0x14 + (i * 0x10); + + /* Verify curPos is proper */ + if (curPos >= strLoc) + return -6; + + /* Get some basic info about this section */ + nameLoc = *(unsigned short *)&data[curPos]; + sectCurLen = *(unsigned short *)&data[curPos + 0x04]; + sectBufLen = *(unsigned short *)&data[curPos + 0x08]; + sectBufLoc = *(unsigned short *)&data[curPos + 0x0C]; + + /* Do some error checking */ + if ((nameLoc < dataLen) && (sectCurLen < dataLen) + && (sectBufLen < dataLen) && (sectBufLoc < dataLen)) + { + /* Check if this is the section we want */ + if (!stricmp((char *)&data[strLoc + nameLoc], name)) + { + /* Update the location and size */ + *location = &data[headLen + sectBufLoc]; + *size = sectBufLen; + return 0; + } + } + } + + /* Section was not found if it makes it here */ + return -7; +} + +/* Find the named file inside the FILE_LIST, and return + an absolute pointer to it. */ +int find_psf_datafile(const char *name, + unsigned char *filelist, + int size, + unsigned char **location) +{ + int i; + + /* Process all files */ + for (i = 0; (i + 0x0d) <= size; i += 0x20) + { + /* Check if this is the filename we want */ + if (!strncasecmp((char *)&filelist[i], name, 0x0d)) { + *location = &filelist[i]; + return 0; + } + } + + /* File was not found if it makes it here */ + return -1; +} diff --git a/Tools/SaveTool/psf.h b/Tools/SaveTool/psf.h new file mode 100644 index 0000000000..261e241e84 --- /dev/null +++ b/Tools/SaveTool/psf.h @@ -0,0 +1,29 @@ +/* + * PSP Software Development Kit - http://www.pspdev.org + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPSDK root for details. + * + * psf.h - Declarations for functions in psf.c + * + * Copyright (c) 2005 Jim Paris + * Coypright (c) 2005 psp123 + * + * $Id: psf.h 1559 2005-12-10 01:10:11Z jim $ + */ + +#include + +/* Find the named section in the PSF file, and return an + absolute pointer to it and the section size. */ +int find_psf_section(const char *name, + unsigned char *data, + int dataLen, + unsigned char **location, + int *size); + +/* Find the named file inside the FILE_LIST, and return + an absolute pointer to it. */ +int find_psf_datafile(const char *name, + unsigned char *filelist, + int size, + unsigned char **location); diff --git a/Windows/Debugger/CtrlRegisterList.cpp b/Windows/Debugger/CtrlRegisterList.cpp index db610d1028..b86b0b7dca 100644 --- a/Windows/Debugger/CtrlRegisterList.cpp +++ b/Windows/Debugger/CtrlRegisterList.cpp @@ -128,12 +128,19 @@ CtrlRegisterList::CtrlRegisterList(HWND _wnd) category=0; showHex=false; cpu=0; + lastPC = 0; + lastCat0Values = NULL; + changedCat0Regs = NULL; } CtrlRegisterList::~CtrlRegisterList() { DeleteObject(font); + if (lastCat0Values != NULL) + delete [] lastCat0Values; + if (changedCat0Regs != NULL) + delete [] changedCat0Regs; } void fillRect(HDC hdc, RECT *rect, COLORREF colour); @@ -214,6 +221,18 @@ void CtrlRegisterList::onPaint(WPARAM wParam, LPARAM lParam) Rectangle(hdc,16,rowY1,width,rowY2); + // Check for any changes in the registers. + if (lastPC != cpu->GetPC()) + { + for (int i = 0, n = cpu->GetNumRegsInCategory(0); i < n; ++i) + { + u32 v = cpu->GetRegValue(0, i); + changedCat0Regs[i] = v != lastCat0Values[i]; + lastCat0Values[i] = v; + } + lastPC = cpu->GetPC(); + } + SelectObject(hdc,currentBrush); DeleteObject(mojsBrush); if (iGetNumRegsInCategory(category)) @@ -225,10 +244,13 @@ void CtrlRegisterList::onPaint(WPARAM wParam, LPARAM lParam) SetTextColor(hdc,0x000000); cpu->PrintRegValue(category,i,temp); - SetTextColor(hdc,0x004000); + if (category == 0 && changedCat0Regs[i]) + SetTextColor(hdc, 0x0000FF); + else + SetTextColor(hdc,0x004000); TextOut(hdc,77,rowY1,temp,strlen(temp)); } - + /* } diff --git a/Windows/Debugger/CtrlRegisterList.h b/Windows/Debugger/CtrlRegisterList.h index ff55ff39ef..f45f980f6b 100644 --- a/Windows/Debugger/CtrlRegisterList.h +++ b/Windows/Debugger/CtrlRegisterList.h @@ -40,6 +40,10 @@ class CtrlRegisterList DebugInterface *cpu; static TCHAR szClassName[]; + u32 lastPC; + u32 *lastCat0Values; + bool *changedCat0Regs; + public: CtrlRegisterList(HWND _wnd); ~CtrlRegisterList(); @@ -60,6 +64,12 @@ public: void setCPU(DebugInterface *deb) { cpu = deb; + + int regs = cpu->GetNumRegsInCategory(0); + lastCat0Values = new u32[regs]; + changedCat0Regs = new bool[regs]; + memset(lastCat0Values, 0, regs * sizeof(u32)); + memset(changedCat0Regs, 0, regs * sizeof(bool)); } DebugInterface *getCPU() { diff --git a/Windows/Debugger/Debugger_Disasm.cpp b/Windows/Debugger/Debugger_Disasm.cpp index b326bc75bc..6d2097f1ee 100644 --- a/Windows/Debugger/Debugger_Disasm.cpp +++ b/Windows/Debugger/Debugger_Disasm.cpp @@ -14,6 +14,7 @@ #include "../../Core/Core.h" #include "../../Core/CPU.h" +#include "../../Core/HLE/HLE.h" #include "base/stringutil.h" @@ -136,10 +137,10 @@ BOOL CDisasm::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) break; case IDC_FUNCTIONLIST: - switch (HIWORD(wParam)) - { - case CBN_DBLCLK: - case CBN_SELCHANGE: + switch (HIWORD(wParam)) + { + case CBN_DBLCLK: + case CBN_SELCHANGE: { HWND lb = GetDlgItem(m_hDlg,LOWORD(wParam)); int n = ListBox_GetCurSel(lb); @@ -154,9 +155,9 @@ BOOL CDisasm::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) break; case IDC_GOTOINT: - switch (HIWORD(wParam)) - { - case LBN_SELCHANGE: + switch (HIWORD(wParam)) + { + case LBN_SELCHANGE: { HWND lb =GetDlgItem(m_hDlg,LOWORD(wParam)); int n = ComboBox_GetCurSel(lb); @@ -192,7 +193,7 @@ BOOL CDisasm::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) SetDebugMode(false); CBreakPoints::AddBreakPoint(cpu->GetPC()+cpu->getInstructionSize(0),true); _dbg_update_(); - Core_EnableStepping(false); + Core_EnableStepping(false); MainWindow::UpdateMenus(); Sleep(1); ptr->gotoPC(); @@ -200,6 +201,16 @@ BOOL CDisasm::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) } break; + case IDC_STEPHLE: + { + hleDebugBreak(); + SetDebugMode(false); + _dbg_update_(); + Core_EnableStepping(false); + MainWindow::UpdateMenus(); + } + break; + case IDC_STOP: { SetDebugMode(true); @@ -241,8 +252,8 @@ BOOL CDisasm::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) HWND list = GetDlgItem(hDlg,IDC_CALLSTACK); ComboBox_ResetContent(list); - u32 pc = currentMIPS->pc; - u32 ra = currentMIPS->r[MIPS_REG_RA]; + u32 pc = currentMIPS->pc; + u32 ra = currentMIPS->r[MIPS_REG_RA]; DWORD addr = Memory::ReadUnchecked_U32(pc); int count=1; ComboBox_SetItemData(list,ComboBox_AddString(list,symbolMap.GetDescription(pc)),pc); @@ -341,6 +352,7 @@ void CDisasm::SetDebugMode(bool _bDebug) EnableWindow( GetDlgItem(hDlg, IDC_GO), TRUE); EnableWindow( GetDlgItem(hDlg, IDC_STEP), TRUE); EnableWindow( GetDlgItem(hDlg, IDC_STEPOVER), TRUE); + EnableWindow( GetDlgItem(hDlg, IDC_STEPHLE), TRUE); EnableWindow( GetDlgItem(hDlg, IDC_STOP), FALSE); EnableWindow( GetDlgItem(hDlg, IDC_SKIP), TRUE); CtrlDisAsmView *ptr = CtrlDisAsmView::getFrom(GetDlgItem(m_hDlg,IDC_DISASMVIEW)); @@ -353,6 +365,7 @@ void CDisasm::SetDebugMode(bool _bDebug) EnableWindow( GetDlgItem(hDlg, IDC_GO), FALSE); EnableWindow( GetDlgItem(hDlg, IDC_STEP), FALSE); EnableWindow( GetDlgItem(hDlg, IDC_STEPOVER), FALSE); + EnableWindow( GetDlgItem(hDlg, IDC_STEPHLE), FALSE); EnableWindow( GetDlgItem(hDlg, IDC_STOP), TRUE); EnableWindow( GetDlgItem(hDlg, IDC_SKIP), FALSE); } diff --git a/Windows/EmuThread.cpp b/Windows/EmuThread.cpp index 4815a26296..f6795df085 100644 --- a/Windows/EmuThread.cpp +++ b/Windows/EmuThread.cpp @@ -29,7 +29,8 @@ DWORD TheThread(LPVOID x); void EmuThread_Start(const char *filename) { // _dbg_clear_(); - _tcscpy(fileToStart, filename); + _tcsncpy(fileToStart, filename, sizeof(fileToStart) - 1); + fileToStart[sizeof(fileToStart) - 1] = 0; unsigned int i; emuThread = (HANDLE)_beginthreadex(0,0,(unsigned int (__stdcall *)(void *))TheThread,(LPVOID)0,0,&i); diff --git a/Windows/WndMainWindow.cpp b/Windows/WndMainWindow.cpp index dfcbb99462..5237026b8b 100644 --- a/Windows/WndMainWindow.cpp +++ b/Windows/WndMainWindow.cpp @@ -18,8 +18,9 @@ #include "main.h" #include "../Core/Core.h" -#include "../Core/System.h" #include "../Core/MemMap.h" +#include "../Core/SaveState.h" +#include "../Core/System.h" #include "EmuThread.h" #include "resource.h" @@ -36,8 +37,8 @@ #include "XPTheme.h" #endif -BOOL g_bFullScreen = FALSE; -RECT rc = {0}; +BOOL g_bFullScreen = FALSE; +RECT g_normalRC = {0}; namespace MainWindow { @@ -46,6 +47,7 @@ namespace MainWindow HWND hwndGameList; HMENU menu; BOOL skinMode = FALSE; + CoreState nextState = CORE_POWERDOWN; HINSTANCE hInst; @@ -112,14 +114,15 @@ namespace MainWindow AdjustWindowRect(&rcOuter, WS_OVERLAPPEDWINDOW, TRUE); } - void SetZoom(int zoom) { - g_Config.iWindowZoom = zoom; + void SetZoom(float zoom) { + if (zoom < 5) + g_Config.iWindowZoom = (int) zoom; RECT rc, rcOuter; - GetWindowRectAtZoom(zoom, rc, rcOuter); + GetWindowRectAtZoom((int) zoom, rc, rcOuter); MoveWindow(hwndMain, rcOuter.left, rcOuter.top, rcOuter.right - rcOuter.left, rcOuter.bottom - rcOuter.top, TRUE); MoveWindow(hwndDisplay, 0, 0, rc.right - rc.left, rc.bottom - rc.top, TRUE); - PSP_CoreParameter().pixelWidth = 480 * zoom; - PSP_CoreParameter().pixelHeight = 272 * zoom; + PSP_CoreParameter().pixelWidth = (int) (480 * zoom); + PSP_CoreParameter().pixelHeight = (int) (272 * zoom); GL_Resized(); } @@ -243,7 +246,6 @@ namespace MainWindow switch (message) { case WM_CREATE: - PostMessage(hWnd, WM_COMMAND, ID_FILE_LOAD, 0); break; case WM_MOVE: @@ -311,18 +313,18 @@ namespace MainWindow break; case ID_FILE_LOADSTATE: - if (W32Util::BrowseForFileName(true, hWnd, "Load state",0,"Save States (*.gcs)\0*.gcs\0All files\0*.*\0\0","gcs",fn)) + if (W32Util::BrowseForFileName(true, hWnd, "Load state",0,"Save States (*.ppst)\0*.ppst\0All files\0*.*\0\0","ppst",fn)) { SetCursor(LoadCursor(0,IDC_WAIT)); - SetCursor(LoadCursor(0,IDC_ARROW)); + SaveState::Load(fn, SaveStateActionFinished); } break; case ID_FILE_SAVESTATE: - if (W32Util::BrowseForFileName(false, hWnd, "Save state",0,"Save States (*.gcs)\0*.gcs\0All files\0*.*\0\0","gcs",fn)) + if (W32Util::BrowseForFileName(false, hWnd, "Save state",0,"Save States (*.ppst)\0*.ppst\0All files\0*.*\0\0","ppst",fn)) { SetCursor(LoadCursor(0,IDC_WAIT)); - SetCursor(LoadCursor(0,IDC_ARROW)); + SaveState::Save(fn, SaveStateActionFinished); } break; @@ -353,6 +355,11 @@ namespace MainWindow UpdateMenus(); break; + case ID_OPTIONS_HARDWARETRANSFORM: + g_Config.bHardwareTransform = !g_Config.bHardwareTransform; + UpdateMenus(); + break; + case ID_FILE_EXIT: DestroyWindow(hWnd); break; @@ -461,7 +468,7 @@ namespace MainWindow memoryWindow[0]->Show(true); break; case ID_DEBUG_LOG: - LogManager::GetInstance()->GetConsoleListener()->Show(LogManager::GetInstance()->GetConsoleListener()->Hidden()); + LogManager::GetInstance()->GetConsoleListener()->Show(LogManager::GetInstance()->GetConsoleListener()->Hidden()); break; ////////////////////////////////////////////////////////////////////////// @@ -472,16 +479,30 @@ namespace MainWindow UpdateMenus(); break; case ID_OPTIONS_FULLSCREEN: - if(g_bFullScreen) + if(g_bFullScreen) { _ViewNormal(hWnd); - else + SetZoom(1); //restore window to original size + } + else { + int cx = ::GetSystemMetrics(SM_CXSCREEN); + float screenfactor = cx / 480.0f; + SetZoom(screenfactor); _ViewFullScreen(hWnd); + } + break; + case ID_OPTIONS_WIREFRAME: + g_Config.bDrawWireframe = !g_Config.bDrawWireframe; + UpdateMenus(); break; case ID_OPTIONS_DISPLAYRAWFRAMEBUFFER: g_Config.bDisplayFramebuffer = !g_Config.bDisplayFramebuffer; UpdateMenus(); break; + case ID_OPTIONS_FASTMEMORY: + g_Config.bFastMemory = !g_Config.bFastMemory; + UpdateMenus(); + break; ////////////////////////////////////////////////////////////////////////// @@ -597,6 +618,9 @@ namespace MainWindow disasmWindow[0]->NotifyMapLoaded(); if (memoryWindow[0]) memoryWindow[0]->NotifyMapLoaded(); + + if (nextState == CORE_RUNNING) + PostMessage(hwndMain, WM_COMMAND, ID_EMULATION_RUN, 0); break; default: @@ -621,12 +645,16 @@ namespace MainWindow CHECKITEM(ID_CPU_DYNAREC,g_Config.iCpuCore == CPU_JIT); CHECKITEM(ID_OPTIONS_BUFFEREDRENDERING, g_Config.bBufferedRendering); CHECKITEM(ID_OPTIONS_SHOWDEBUGSTATISTICS, g_Config.bShowDebugStats); + CHECKITEM(ID_OPTIONS_WIREFRAME, g_Config.bDrawWireframe); + CHECKITEM(ID_OPTIONS_HARDWARETRANSFORM, g_Config.bHardwareTransform); + CHECKITEM(ID_OPTIONS_FASTMEMORY, g_Config.bFastMemory); - BOOL enable = !Core_IsStepping(); - EnableMenuItem(menu,ID_EMULATION_RUN,enable); - EnableMenuItem(menu,ID_EMULATION_PAUSE,!enable); + UINT enable = !Core_IsStepping() ? MF_GRAYED : MF_ENABLED; + EnableMenuItem(menu,ID_EMULATION_RUN, g_State.bEmuThreadStarted ? enable : MF_GRAYED); + EnableMenuItem(menu,ID_EMULATION_PAUSE, g_State.bEmuThreadStarted ? !enable : MF_GRAYED); + EnableMenuItem(menu,ID_EMULATION_RESET, g_State.bEmuThreadStarted ? MF_ENABLED : MF_GRAYED); - enable = g_State.bEmuThreadStarted; + enable = g_State.bEmuThreadStarted ? MF_GRAYED : MF_ENABLED; EnableMenuItem(menu,ID_FILE_LOAD,enable); EnableMenuItem(menu,ID_CPU_DYNAREC,enable); EnableMenuItem(menu,ID_CPU_INTERPRETER,enable); @@ -682,51 +710,50 @@ namespace MainWindow } void _ViewNormal(HWND hWnd) { - // put caption and border styles back - DWORD dwOldStyle = ::GetWindowLong(hWnd, GWL_STYLE); - DWORD dwNewStyle = dwOldStyle | WS_CAPTION | WS_THICKFRAME; - ::SetWindowLong(hWnd, GWL_STYLE, dwNewStyle); + // put caption and border styles back + DWORD dwOldStyle = ::GetWindowLong(hWnd, GWL_STYLE); + DWORD dwNewStyle = dwOldStyle | WS_CAPTION | WS_THICKFRAME; + ::SetWindowLong(hWnd, GWL_STYLE, dwNewStyle); - // put back the menu bar - ::SetMenu(hWnd, menu); + // put back the menu bar + ::SetMenu(hWnd, menu); - // resize to normal view - // NOTE: use SWP_FRAMECHANGED to force redraw non-client - const int x = rc.left; - const int y = rc.top; - const int cx = rc.right - rc.left; - const int cy = rc.bottom - rc.top; - ::SetWindowPos(hWnd, HWND_NOTOPMOST, x, y, cx, cy, SWP_FRAMECHANGED); + // resize to normal view + // NOTE: use SWP_FRAMECHANGED to force redraw non-client + const int x = g_normalRC.left; + const int y = g_normalRC.top; + const int cx = g_normalRC.right - g_normalRC.left; + const int cy = g_normalRC.bottom - g_normalRC.top; + ::SetWindowPos(hWnd, HWND_NOTOPMOST, x, y, cx, cy, SWP_FRAMECHANGED); - // reset full screen indicator - g_bFullScreen = FALSE; + // reset full screen indicator + g_bFullScreen = FALSE; } -void _ViewFullScreen(HWND hWnd) -{ - // keep in mind normal window rectangle - ::GetWindowRect(hWnd, &rc); + void _ViewFullScreen(HWND hWnd) + { + // keep in mind normal window rectangle + ::GetWindowRect(hWnd, &g_normalRC); - // remove caption and border styles - DWORD dwOldStyle = ::GetWindowLong(hWnd, GWL_STYLE); - DWORD dwNewStyle = dwOldStyle & ~(WS_CAPTION | WS_THICKFRAME); - ::SetWindowLong(hWnd, GWL_STYLE, dwNewStyle); + // remove caption and border styles + DWORD dwOldStyle = ::GetWindowLong(hWnd, GWL_STYLE); + DWORD dwNewStyle = dwOldStyle & ~(WS_CAPTION | WS_THICKFRAME); + ::SetWindowLong(hWnd, GWL_STYLE, dwNewStyle); - // remove the menu bar - ::SetMenu(hWnd, NULL); + // remove the menu bar + ::SetMenu(hWnd, NULL); - // resize to full screen view - // NOTE: use SWP_FRAMECHANGED to force redraw non-client - const int x = 0; - const int y = 0; - const int cx = ::GetSystemMetrics(SM_CXSCREEN); - const int cy = ::GetSystemMetrics(SM_CYSCREEN); - ::SetWindowPos(hWnd, HWND_TOPMOST, x, y, cx, cy, SWP_FRAMECHANGED); - - // set full screen indicator - g_bFullScreen = TRUE; -} + // resize to full screen view + // NOTE: use SWP_FRAMECHANGED to force redraw non-client + const int x = 0; + const int y = 0; + const int cx = ::GetSystemMetrics(SM_CXSCREEN); + const int cy = ::GetSystemMetrics(SM_CYSCREEN); + ::SetWindowPos(hWnd, HWND_TOPMOST, x, y, cx, cy, SWP_FRAMECHANGED); + // set full screen indicator + g_bFullScreen = TRUE; + } void SetPlaying(const char *text) { @@ -740,6 +767,19 @@ void _ViewFullScreen(HWND hWnd) } } + void SaveStateActionFinished(bool result) + { + // TODO: Improve messaging? + if (!result) + MessageBox(0, "Savestate failure. Please try again later.", "Sorry", MB_OK); + SetCursor(LoadCursor(0, IDC_ARROW)); + } + + void SetNextState(CoreState state) + { + nextState = state; + } + HINSTANCE GetHInstance() { return hInst; diff --git a/Windows/WndMainWindow.h b/Windows/WndMainWindow.h index 0e01330bc4..b58823ca25 100644 --- a/Windows/WndMainWindow.h +++ b/Windows/WndMainWindow.h @@ -1,6 +1,7 @@ #pragma once #include +#include namespace MainWindow { @@ -14,6 +15,9 @@ namespace MainWindow HINSTANCE GetHInstance(); HWND GetDisplayHWND(); void SetPlaying(const char*text); + void BrowseAndBoot(); + void SetNextState(CoreState state); + void SaveStateActionFinished(bool result); void _ViewFullScreen(HWND hWnd); void _ViewNormal(HWND hWnd); } diff --git a/Windows/XinputDevice.cpp b/Windows/XinputDevice.cpp index 86fc0dab64..6f7687f49b 100644 --- a/Windows/XinputDevice.cpp +++ b/Windows/XinputDevice.cpp @@ -41,7 +41,7 @@ int XinputDevice::UpdateState() { if ( dwResult == ERROR_SUCCESS ) { this->ApplyDiff(state); Stick left = NormalizedDeadzoneFilter(state); - __CtrlSetAnalog(left.x, left.y); + __CtrlSetAnalog(left.x, -left.y); this->prevState = state; this->check_delay = 0; return 0; diff --git a/Windows/main.cpp b/Windows/main.cpp index 1a859b1a89..d2f9b186ac 100644 --- a/Windows/main.cpp +++ b/Windows/main.cpp @@ -21,6 +21,7 @@ #include "file/zip_read.h" #include "../Core/Config.h" +#include "EmuThread.h" #include "LogManager.h" #include "ConsoleListener.h" @@ -50,23 +51,65 @@ int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLin { Common::EnableCrashingOnCrashes(); - char *token = szCmdLine; - char fileToLoad[256] = ""; + const char *fileToStart = NULL; + const char *fileToLog = NULL; + bool hideLog = true; + bool autoRun = true; - token = strtok(szCmdLine," "); +#ifdef _DEBUG + hideLog = false; +#endif g_Config.Load(); VFSRegister("", new DirectoryAssetReader("assets/")); VFSRegister("", new DirectoryAssetReader("")); - while (token) + for (int i = 1; i < __argc; ++i) { - if (strcmp(token,"-run")) - { - //run immediately - } + if (__argv[i][0] == '\0') + continue; - token = strtok(NULL," "); + if (__argv[i][0] == '-') + { + switch (__argv[i][1]) + { + case 'j': + g_Config.iCpuCore = CPU_JIT; + break; + case 'i': + g_Config.iCpuCore = CPU_INTERPRETER; + break; + case 'f': + g_Config.iCpuCore = CPU_FASTINTERPRETER; + break; + case 'l': + hideLog = false; + break; + case 's': + autoRun = false; + break; + case '-': + if (!strcmp(__argv[i], "--log") && i < __argc - 1) + fileToLog = __argv[++i]; + if (!strncmp(__argv[i], "--log=", strlen("--log=")) && strlen(__argv[i]) > strlen("--log=")) + fileToLog = __argv[i] + strlen("--log="); + break; + } + } + else if (fileToStart == NULL) + { + fileToStart = __argv[i]; + if (!File::Exists(fileToStart)) + { + fprintf(stderr, "File not found: %s\n", fileToStart); + exit(1); + } + } + else + { + fprintf(stderr, "Can only boot one file"); + exit(1); + } } //Windows, API init stuff @@ -98,16 +141,23 @@ int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLin MainWindow::UpdateMenus(); LogManager::Init(); - bool hidden = false; -#ifndef _DEBUG - hidden = true; -#endif - LogManager::GetInstance()->GetConsoleListener()->Open(hidden, 150, 120, "PPSSPP Debug Console"); + if (fileToLog != NULL) + LogManager::GetInstance()->ChangeFileLog(fileToLog); + LogManager::GetInstance()->GetConsoleListener()->Open(hideLog, 150, 120, "PPSSPP Debug Console"); LogManager::GetInstance()->SetLogLevel(LogTypes::G3D, LogTypes::LERROR); - if (strlen(fileToLoad)) + if (fileToStart != NULL) { - // TODO: load the thing + MainWindow::SetPlaying(fileToStart); + MainWindow::Update(); + MainWindow::UpdateMenus(); + + EmuThread_Start(fileToStart); } + else + MainWindow::BrowseAndBoot(); + + if (autoRun) + MainWindow::SetNextState(CORE_RUNNING); //so.. we're at the message pump of the GUI thread MSG msg; diff --git a/Windows/ppsspp.rc b/Windows/ppsspp.rc index 8ddb89699d..e9561bdac4 100644 Binary files a/Windows/ppsspp.rc and b/Windows/ppsspp.rc differ diff --git a/Windows/resource.h b/Windows/resource.h index 594a397e12..134447f2a5 100644 --- a/Windows/resource.h +++ b/Windows/resource.h @@ -243,13 +243,17 @@ #define ID_EMULATION_FASTINTERPRETER 40120 #define ID_CPU_FASTINTERPRETER 40121 #define ID_OPTIONS_SHOWDEBUGSTATISTICS 40122 +#define ID_OPTIONS_WIREFRAME 40123 +#define ID_OPTIONS_HARDWARETRANSFORM 40124 +#define ID_OPTIONS_FASTMEMORY 40125 +#define IDC_STEPHLE 40126 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 232 -#define _APS_NEXT_COMMAND_VALUE 40123 +#define _APS_NEXT_COMMAND_VALUE 40127 #define _APS_NEXT_CONTROL_VALUE 1162 #define _APS_NEXT_SYMED_VALUE 101 #endif diff --git a/android/jni/Android.mk b/android/jni/Android.mk index ca69ab1388..634edc6bf1 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -69,9 +69,11 @@ LOCAL_SRC_FILES := \ $(SRC)/Common/MathUtil.cpp \ $(SRC)/GPU/Math3D.cpp \ $(SRC)/GPU/GPUState.cpp \ + $(SRC)/GPU/GeDisasm.cpp \ $(SRC)/GPU/GLES/Framebuffer.cpp \ $(SRC)/GPU/GLES/DisplayListInterpreter.cpp \ $(SRC)/GPU/GLES/TextureCache.cpp \ + $(SRC)/GPU/GLES/IndexGenerator.cpp \ $(SRC)/GPU/GLES/TransformPipeline.cpp \ $(SRC)/GPU/GLES/StateMapping.cpp \ $(SRC)/GPU/GLES/VertexDecoder.cpp \ @@ -94,6 +96,7 @@ LOCAL_SRC_FILES := \ $(SRC)/Core/PSPLoaders.cpp \ $(SRC)/Core/MemMap.cpp \ $(SRC)/Core/MemMapFunctions.cpp \ + $(SRC)/Core/SaveState.cpp \ $(SRC)/Core/System.cpp \ $(SRC)/Core/PSPMixer.cpp \ $(SRC)/Core/Debugger/Breakpoints.cpp \ diff --git a/android/jni/EmuScreen.cpp b/android/jni/EmuScreen.cpp index b21ab0b36c..69a3696b09 100644 --- a/android/jni/EmuScreen.cpp +++ b/android/jni/EmuScreen.cpp @@ -29,9 +29,8 @@ #include "../../Core/Host.h" #include "../../Core/System.h" #include "../../Core/MIPS/MIPS.h" -#include "../../GPU/GLES/TextureCache.h" -#include "../../GPU/GLES/ShaderManager.h" #include "../../GPU/GPUState.h" +#include "../../GPU/GPUInterface.h" #include "../../Core/HLE/sceCtrl.h" #include "GamepadEmu.h" @@ -40,8 +39,6 @@ #include "MenuScreens.h" #include "EmuScreen.h" -extern ShaderManager shaderManager; - EmuScreen::EmuScreen(const std::string &filename) : invalid_(true) { std::string fileToStart = filename; @@ -112,6 +109,7 @@ void EmuScreen::update(InputState &input) if (invalid_) return; + // First translate touches into pad input. UpdateGamepad(input); UpdateInputState(&input); @@ -120,8 +118,8 @@ void EmuScreen::update(InputState &input) static const int mapping[12][2] = { {PAD_BUTTON_A, CTRL_CROSS}, - {PAD_BUTTON_B, CTRL_SQUARE}, - {PAD_BUTTON_X, CTRL_CIRCLE}, + {PAD_BUTTON_B, CTRL_CIRCLE}, + {PAD_BUTTON_X, CTRL_SQUARE}, {PAD_BUTTON_Y, CTRL_TRIANGLE}, {PAD_BUTTON_UP, CTRL_UP}, {PAD_BUTTON_DOWN, CTRL_DOWN}, @@ -130,15 +128,18 @@ void EmuScreen::update(InputState &input) {PAD_BUTTON_LBUMPER, CTRL_LTRIGGER}, {PAD_BUTTON_RBUMPER, CTRL_RTRIGGER}, {PAD_BUTTON_START, CTRL_START}, - {PAD_BUTTON_BACK, CTRL_SELECT}, + {PAD_BUTTON_SELECT, CTRL_SELECT}, }; for (int i = 0; i < 12; i++) { - if (input.pad_buttons_down & mapping[i][0]) + if (input.pad_buttons_down & mapping[i][0]) { __CtrlButtonDown(mapping[i][1]); - if (input.pad_buttons_up & mapping[i][0]) + } + if (input.pad_buttons_up & mapping[i][0]) { __CtrlButtonUp(mapping[i][1]); + } } + __CtrlSetAnalog(input.pad_lstick_x, input.pad_lstick_y); if (input.pad_buttons_down & (PAD_BUTTON_MENU | PAD_BUTTON_BACK)) { fbo_unbind(); @@ -179,7 +180,6 @@ void EmuScreen::render() ui_draw2d.Begin(DBMODE_NORMAL); - // Make this configurable. if (g_Config.bShowTouchControls) DrawGamepad(ui_draw2d); @@ -201,6 +201,5 @@ void EmuScreen::render() void EmuScreen::deviceLost() { - TextureCache_Clear(false); // This doesn't seem to help? - shaderManager.ClearCache(false); + gpu->DeviceLost(); } diff --git a/android/jni/GamepadEmu.cpp b/android/jni/GamepadEmu.cpp index 49ce14a09c..84e45e565b 100644 --- a/android/jni/GamepadEmu.cpp +++ b/android/jni/GamepadEmu.cpp @@ -21,8 +21,8 @@ #include "ui_atlas.h" TouchButton buttonX(&ui_atlas, I_ROUND, I_CROSS, PAD_BUTTON_A); -TouchButton buttonO(&ui_atlas, I_ROUND, I_CIRCLE, PAD_BUTTON_X); -TouchButton buttonSq(&ui_atlas, I_ROUND, I_SQUARE, PAD_BUTTON_B); +TouchButton buttonO(&ui_atlas, I_ROUND, I_CIRCLE, PAD_BUTTON_B); +TouchButton buttonSq(&ui_atlas, I_ROUND, I_SQUARE, PAD_BUTTON_X); TouchButton buttonTri(&ui_atlas, I_ROUND, I_TRIANGLE, PAD_BUTTON_Y); TouchButton buttonSelect(&ui_atlas, I_RECT, I_SELECT, PAD_BUTTON_SELECT); TouchButton buttonStart(&ui_atlas, I_RECT, I_START, PAD_BUTTON_START); diff --git a/android/jni/MenuScreens.cpp b/android/jni/MenuScreens.cpp index 6cdbd87488..b35ae132a7 100644 --- a/android/jni/MenuScreens.cpp +++ b/android/jni/MenuScreens.cpp @@ -31,6 +31,9 @@ #include "util/random/rng.h" #include "UIShader.h" +#include "../../GPU/ge_constants.h" +#include "../../GPU/GPUState.h" +#include "../../GPU/GPUInterface.h" #include "../../Core/Config.h" #include "../../Core/CoreParameter.h" @@ -62,7 +65,6 @@ static void DrawBackground(float alpha) { static float ybase[100] = {0}; if (xbase[0] == 0.0f) { GMRng rng; - printf("%i %i AAAH\n", dp_xres, dp_yres); for (int i = 0; i < 100; i++) { xbase[i] = rng.F() * dp_xres; ybase[i] = rng.F() * dp_yres; @@ -109,9 +111,9 @@ void LogoScreen::render() { UIBegin(); DrawBackground(alpha); - ui_draw2d.SetFontScale(1.5f,1.5f); + ui_draw2d.SetFontScale(1.5f, 1.5f); ui_draw2d.DrawText(UBUNTU48, "PPSSPP", dp_xres / 2, dp_yres / 2 - 30, colorAlpha(0xFFFFFFFF, alphaText), ALIGN_CENTER); - ui_draw2d.SetFontScale(1.0f,1.0f); + ui_draw2d.SetFontScale(1.0f, 1.0f); ui_draw2d.DrawText(UBUNTU24, "Created by Henrik Rydgard", dp_xres / 2, dp_yres / 2 + 40, colorAlpha(0xFFFFFFFF, alphaText), ALIGN_CENTER); ui_draw2d.DrawText(UBUNTU24, "Free Software under GPL 2.0", dp_xres / 2, dp_yres / 2 + 70, colorAlpha(0xFFFFFFFF, alphaText), ALIGN_CENTER); ui_draw2d.DrawText(UBUNTU24, "www.ppsspp.org", dp_xres / 2, dp_yres / 2 + 130, colorAlpha(0xFFFFFFFF, alphaText), ALIGN_CENTER); @@ -207,6 +209,11 @@ void InGameMenuScreen::render() { ui_draw2d.DrawText(UBUNTU48, "Emulation Paused", dp_xres / 2, 30, 0xFFFFFFFF, ALIGN_HCENTER); + int x = 30; + int y = 50; + UICheckBox(GEN_ID, x, y += 50, "Show Debug Statistics (experimental)", ALIGN_TOPLEFT, &g_Config.bShowDebugStats); + UICheckBox(GEN_ID, x, y += 50, "Hardware Transform (experimental)", ALIGN_TOPLEFT, &g_Config.bHardwareTransform); + VLinear vlinear(dp_xres - 10, 160, 20); if (UIButton(GEN_ID, vlinear, LARGE_BUTTON_WIDTH, "Continue", ALIGN_RIGHT)) { screenManager()->finishDialog(this, DR_CANCEL); @@ -215,6 +222,11 @@ void InGameMenuScreen::render() { if (UIButton(GEN_ID, vlinear, LARGE_BUTTON_WIDTH, "Return to Menu", ALIGN_RIGHT)) { screenManager()->finishDialog(this, DR_OK); } + + if (UIButton(GEN_ID, vlinear, LARGE_BUTTON_WIDTH, "Dump Next Frame", ALIGN_RIGHT)) { + gpu->DumpNextFrame(); + } + DrawWatermark(); UIEnd(); @@ -239,9 +251,9 @@ void SettingsScreen::render() { // VLinear vlinear(10, 80, 10); int x = 30; int y = 50; - UICheckBox(GEN_ID, x, y += 50, "Enable Sound Emulation", ALIGN_TOPLEFT, &g_Config.bEnableSound); + UICheckBox(GEN_ID, x, y += 50, "Sound Emulation", ALIGN_TOPLEFT, &g_Config.bEnableSound); UICheckBox(GEN_ID, x, y += 50, "Buffered Rendering (may fix flicker)", ALIGN_TOPLEFT, &g_Config.bBufferedRendering); - + UICheckBox(GEN_ID, x, y += 50, "Hardware Transform (experimental)", ALIGN_TOPLEFT, &g_Config.bHardwareTransform); bool useFastInt = g_Config.iCpuCore == CPU_FASTINTERPRETER; UICheckBox(GEN_ID, x, y += 50, "Slightly faster interpreter (may crash)", ALIGN_TOPLEFT, &useFastInt); diff --git a/android/jni/NativeApp.cpp b/android/jni/NativeApp.cpp index 6f0ae119e7..d732413783 100644 --- a/android/jni/NativeApp.cpp +++ b/android/jni/NativeApp.cpp @@ -40,6 +40,7 @@ #include "../../Core/PSPMixer.h" #include "../../Core/CPU.h" #include "../../Core/Config.h" +#include "../../Core/HLE/sceCtrl.h" #include "../../Core/Host.h" #include "../../Common/MemArena.h" @@ -133,7 +134,7 @@ void NativeMix(short *audio, int num_samples) { if (g_mixer) { - g_mixer->Mix(audio, num_samples/2); + g_mixer->Mix(audio, num_samples); } else { @@ -169,6 +170,12 @@ void NativeInit(int argc, const char *argv[], const char *savegame_directory, co LogManager *logman = LogManager::GetInstance(); ILOG("Logman: %p", logman); + config_filename = user_data_path + "ppsspp.ini"; + + g_Config.Load(config_filename.c_str()); + + const char *fileToLog = 0; + bool gfxLog = false; // Parse command line LogTypes::LOG_LEVELS logLevel = LogTypes::LINFO; @@ -184,9 +191,19 @@ void NativeInit(int argc, const char *argv[], const char *savegame_directory, co break; case 'j': g_Config.iCpuCore = CPU_JIT; + g_Config.bSaveSettings = false; + break; + case 'f': + g_Config.iCpuCore = CPU_FASTINTERPRETER; + g_Config.bSaveSettings = false; break; case 'i': g_Config.iCpuCore = CPU_INTERPRETER; + g_Config.bSaveSettings = false; + break; + case '-': + if (!strncmp(argv[i], "--log=", strlen("--log=")) && strlen(argv[i]) > strlen("--log=")) + fileToLog = argv[i] + strlen("--log="); break; } } else { @@ -204,9 +221,8 @@ void NativeInit(int argc, const char *argv[], const char *savegame_directory, co } } - config_filename = user_data_path + "ppsspp.ini"; - - g_Config.Load(config_filename.c_str()); + if (fileToLog != NULL) + LogManager::GetInstance()->ChangeFileLog(fileToLog); if (g_Config.currentDirectory == "") { #if defined(ANDROID) || defined(BLACKBERRY) || defined(__SYMBIAN32__) @@ -216,7 +232,7 @@ void NativeInit(int argc, const char *argv[], const char *savegame_directory, co #endif } -#if defined(ANDROID) || defined(BLACKBERRY) +#if defined(ANDROID) || defined(BLACKBERRY) || defined(__SYMBIAN32__) g_Config.memCardDirectory = user_data_path; g_Config.flashDirectory = user_data_path+"/flash/"; #else diff --git a/android/src/org/ppsspp/ppsspp/PpssppActivity.java b/android/src/org/ppsspp/ppsspp/PpssppActivity.java index be46ac6038..bd7cd15d12 100644 --- a/android/src/org/ppsspp/ppsspp/PpssppActivity.java +++ b/android/src/org/ppsspp/ppsspp/PpssppActivity.java @@ -15,4 +15,4 @@ public class PpssppActivity extends NativeActivity { { return false; } -} \ No newline at end of file +} \ No newline at end of file diff --git a/ext/libkirk/libkirk.vcxproj b/ext/libkirk/libkirk.vcxproj index 03cc26bc5c..3c248e88ea 100644 --- a/ext/libkirk/libkirk.vcxproj +++ b/ext/libkirk/libkirk.vcxproj @@ -91,6 +91,9 @@ MaxSpeed true true + false + StreamingSIMDExtensions2 + Fast true diff --git a/ext/zlib/zlib.vcxproj b/ext/zlib/zlib.vcxproj index b35269cd97..be9c34926c 100644 --- a/ext/zlib/zlib.vcxproj +++ b/ext/zlib/zlib.vcxproj @@ -129,6 +129,8 @@ true WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) StreamingSIMDExtensions2 + false + Fast Windows diff --git a/headless/Headless.cpp b/headless/Headless.cpp index 7510f6527e..dabd2586e7 100644 --- a/headless/Headless.cpp +++ b/headless/Headless.cpp @@ -74,8 +74,8 @@ void printUsage(const char *progname, const char *reason) if (reason != NULL) fprintf(stderr, "Error: %s\n\n", reason); fprintf(stderr, "PPSSPP Headless\n"); - fprintf(stderr, "This is primarily meant for non-inactive test tool.\n\n"); - fprintf(stderr, "Usage: %s [options] file.elf\n\n", progname); + fprintf(stderr, "This is primarily meant as a non-interactive test tool.\n\n"); + fprintf(stderr, "Usage: %s file.elf [options]\n\n", progname); fprintf(stderr, "Options:\n"); fprintf(stderr, " -m, --mount umd.cso mount iso on umd:\n"); fprintf(stderr, " -l, --log full log output, not just emulated printfs\n"); diff --git a/headless/Headless.vcxproj b/headless/Headless.vcxproj index 6fe00a43be..e4e8907ff7 100644 --- a/headless/Headless.vcxproj +++ b/headless/Headless.vcxproj @@ -113,6 +113,9 @@ true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../Common;..;../Core;../native/ext/glew; + false + StreamingSIMDExtensions2 + Fast Console diff --git a/native b/native index 0de5e114f3..1556328129 160000 --- a/native +++ b/native @@ -1 +1 @@ -Subproject commit 0de5e114f337859a03d0763c30beaf6e03af03c4 +Subproject commit 15563281297adb62c8e690fc063a9bf4442fcfc7 diff --git a/pspautotests b/pspautotests index 8c1284c758..5e5ae52067 160000 --- a/pspautotests +++ b/pspautotests @@ -1 +1 @@ -Subproject commit 8c1284c758c05811517c043bbc781ba8ab724d5e +Subproject commit 5e5ae520672b816943acabed1f223ebcd7cff16f diff --git a/test.py b/test.py index 4ac98e263e..2e1248690b 100755 --- a/test.py +++ b/test.py @@ -49,7 +49,9 @@ tests_good = [ "cpu/fpu/fpu", "ctrl/ctrl", + "ctrl/idle/idle", "ctrl/sampling/sampling", + "ctrl/sampling2/sampling2", "display/display", "dmac/dmactest", "loader/bss/bss", @@ -58,6 +60,10 @@ tests_good = [ "misc/testgp", "string/string", "gpu/callbacks/ge_callbacks", + "threads/alarm/alarm", + "threads/alarm/cancel/cancel", + "threads/alarm/refer/refer", + "threads/alarm/set/set", "threads/events/events", "threads/events/cancel/cancel", "threads/events/clear/clear",