diff --git a/CMakeLists.txt b/CMakeLists.txt index 5fd1b01c88..d12ef4cb9c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -273,6 +273,17 @@ add_library(zlib STATIC ) include_directories(ext/zlib) +add_library(snappy STATIC + ext/snappy/snappy-c.cpp + ext/snappy/snappy-internal.h + ext/snappy/snappy-sinksource.h + ext/snappy/snappy-stubs-internal.h + ext/snappy/snappy-stubs-public.h + ext/snappy/snappy.cpp + ext/snappy/snappy.h +) +include_directories(ext/snappy) + add_library(etcpack STATIC native/ext/etcpack/etcdec.cpp native/ext/etcpack/etcdec.h @@ -535,6 +546,8 @@ add_library(native STATIC native/util/random/perlin.cpp native/util/random/perlin.h native/util/random/rng.h + native/util/text/utf8.h + native/util/text/utf8.cpp native/ext/rapidxml/rapidxml.hpp native/ext/rapidxml/rapidxml_iterators.hpp native/ext/rapidxml/rapidxml_print.hpp @@ -543,7 +556,7 @@ include_directories(native) # rapidxml is headers only so we can't make a lib specific for it include_directories(native/ext/rapidxml) target_link_libraries(native ${LIBZIP} etcpack sha1 stb_image stb_vorbis #vjson - zlib ${GLEW_LIBRARIES}) + zlib snappy ${GLEW_LIBRARIES}) if(ANDROID) target_link_libraries(native log) endif() @@ -718,8 +731,6 @@ add_library(${CoreLibName} ${CoreLinkType} Core/HLE/sceSas.h Core/HLE/sceSsl.cpp Core/HLE/sceSsl.h - Core/HLE/scesupPreAcc.cpp - Core/HLE/scesupPreAcc.h Core/HLE/sceUmd.cpp Core/HLE/sceUmd.h Core/HLE/sceUsb.cpp @@ -806,6 +817,8 @@ add_library(GPU OBJECT GPU/GPUInterface.h GPU/GeDisasm.cpp GPU/GeDisasm.h + GPU/GPUCommon.cpp + GPU/GPUCommon.h GPU/GPUState.cpp GPU/GPUState.h GPU/Math3D.cpp @@ -884,7 +897,7 @@ if(WIN32) endif() if(HEADLESS) - add_executable(PPSSPPHeadless headless/Headless.cpp) + add_executable(PPSSPPHeadless headless/Headless.cpp headless/StubHost.h) target_link_libraries(PPSSPPHeadless ${CoreLibName} ${COCOA_LIBRARY} ${CMAKE_THREAD_LIBS_INIT}) setup_target_project(PPSSPPHeadless headless) diff --git a/Common/ChunkFile.h b/Common/ChunkFile.h index 1671cecc8a..d9866e3f8d 100644 --- a/Common/ChunkFile.h +++ b/Common/ChunkFile.h @@ -36,6 +36,7 @@ #include "Common.h" #include "FileUtil.h" +#include "../ext/snappy/snappy-c.h" template struct LinkedListItem : public T @@ -419,9 +420,22 @@ public: } u8 *ptr = buffer; + u8 *buf = buffer; + if (header.Compress) { + u8 *uncomp_buffer = new u8[header.UncompressedSize]; + size_t uncomp_size = header.UncompressedSize; + snappy_uncompress((const char *)buffer, sz, (char *)uncomp_buffer, &uncomp_size); + if (uncomp_size != header.UncompressedSize) { + ERROR_LOG(COMMON,"Size mismatch: file: %i calc: %i", (int)header.UncompressedSize, (int)uncomp_size); + } + ptr = uncomp_buffer; + buf = uncomp_buffer; + delete [] buffer; + } + PointerWrap p(&ptr, PointerWrap::MODE_READ); _class.DoState(p); - delete[] buffer; + delete[] buf; INFO_LOG(COMMON, "ChunkReader: Done loading %s" , _rFilename.c_str()); return true; @@ -439,33 +453,57 @@ public: return false; } + bool compress = true; + // Get data u8 *ptr = 0; PointerWrap p(&ptr, PointerWrap::MODE_MEASURE); _class.DoState(p); size_t const sz = (size_t)ptr; - std::vector buffer(sz); + + u8 * buffer = new u8[sz]; ptr = &buffer[0]; p.SetMode(PointerWrap::MODE_WRITE); _class.DoState(p); - + // Create header SChunkHeader header; - header.Compress = 0; + header.Compress = compress ? 1 : 0; header.Revision = _Revision; header.ExpectedSize = (int)sz; + header.UncompressedSize = (int)sz; // Write to file - if (!pFile.WriteArray(&header, 1)) - { - ERROR_LOG(COMMON,"ChunkReader: Failed writing header"); - return false; - } - - if (!pFile.WriteBytes(&buffer[0], sz)) - { - ERROR_LOG(COMMON,"ChunkReader: Failed writing data"); - return false; + if (compress) { + size_t comp_len = snappy_max_compressed_length(sz); + u8 *compressed_buffer = new u8[comp_len]; + snappy_compress((const char *)buffer, sz, (char *)compressed_buffer, &comp_len); + delete [] buffer; + header.ExpectedSize = comp_len; + if (!pFile.WriteArray(&header, 1)) + { + ERROR_LOG(COMMON,"ChunkReader: Failed writing header"); + return false; + } + if (!pFile.WriteBytes(&compressed_buffer[0], comp_len)) { + ERROR_LOG(COMMON,"ChunkReader: Failed writing compressed data"); + return false; + } else { + INFO_LOG(COMMON, "Savestate: Compressed %i bytes into %i", (int)sz, (int)comp_len); + } + delete [] compressed_buffer; + } else { + if (!pFile.WriteArray(&header, 1)) + { + ERROR_LOG(COMMON,"ChunkReader: Failed writing header"); + return false; + } + if (!pFile.WriteBytes(&buffer[0], sz)) + { + ERROR_LOG(COMMON,"ChunkReader: Failed writing data"); + return false; + } + delete [] buffer; } INFO_LOG(COMMON,"ChunkReader: Done writing %s", @@ -503,6 +541,7 @@ private: int Revision; int Compress; int ExpectedSize; + int UncompressedSize; }; }; diff --git a/Common/MemoryUtil.cpp b/Common/MemoryUtil.cpp index 282ffe2ee4..f4ff4da1a5 100644 --- a/Common/MemoryUtil.cpp +++ b/Common/MemoryUtil.cpp @@ -115,7 +115,11 @@ void* AllocateMemoryPages(size_t size) #ifdef _WIN32 void* ptr = VirtualAlloc(0, size, MEM_COMMIT, PAGE_READWRITE); #else - void* ptr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); + void* ptr = mmap(0, size, PROT_READ | PROT_WRITE, +#ifndef __SYMBIAN32__ + MAP_ANON | +#endif + MAP_PRIVATE, -1, 0); #endif // printf("Mapped memory at %p (size %ld)\n", ptr, diff --git a/Common/StringUtil.cpp b/Common/StringUtil.cpp index 8d96eae2b8..ae0a09b0c4 100644 --- a/Common/StringUtil.cpp +++ b/Common/StringUtil.cpp @@ -376,3 +376,11 @@ std::string UriEncode(const std::string & sSrc) delete [] pStart; return sResult; } + +bool StringEndsWith(std::string const &fullString, std::string const &ending) { + if (fullString.length() >= ending.length()) { + return (0 == fullString.compare (fullString.length() - ending.length(), ending.length(), ending)); + } else { + return false; + } +} diff --git a/Common/StringUtil.h b/Common/StringUtil.h index a5477a3996..b4282c3533 100644 --- a/Common/StringUtil.h +++ b/Common/StringUtil.h @@ -129,4 +129,6 @@ std::string ReplaceAll(std::string result, const std::string& src, const std::st std::string UriDecode(const std::string & sSrc); std::string UriEncode(const std::string & sSrc); +bool StringEndsWith(std::string const &fullString, std::string const &ending); + #endif // _STRINGUTIL_H_ diff --git a/Core/CMakeLists.txt b/Core/CMakeLists.txt index 771ba59694..7891ef0271 100644 --- a/Core/CMakeLists.txt +++ b/Core/CMakeLists.txt @@ -61,7 +61,6 @@ set(SRCS HLE/sceSsl.cpp HLE/sceParseUri.cpp HLE/sceParseHttp.cpp - HLE/scesupPreAcc.cpp HLE/sceVaudio.cpp HW/MemoryStick.cpp HW/MediaEngine.cpp diff --git a/Core/Config.cpp b/Core/Config.cpp index e0ea8cff4e..bc68d1ba5f 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -33,18 +33,21 @@ CConfig::~CConfig() void CConfig::Load(const char *iniFileName) { iniFilename_ = iniFileName; - NOTICE_LOG(LOADER, "Loading config: %s", iniFileName); + INFO_LOG(LOADER, "Loading config: %s", iniFileName); bSaveSettings = true; IniFile iniFile; - iniFile.Load(iniFileName); + if (!iniFile.Load(iniFileName)) { + ERROR_LOG(LOADER, "Failed to read %s. Setting config to default.", iniFileName); + // Continue anyway to initialize the config. + } IniFile::Section *general = iniFile.GetOrCreateSection("General"); bSpeedLimit = false; general->Get("FirstRun", &bFirstRun, true); general->Get("AutoLoadLast", &bAutoLoadLast, false); - general->Get("AutoRun", &bAutoRun, false); + general->Get("AutoRun", &bAutoRun, true); general->Get("ConfirmOnQuit", &bConfirmOnQuit, false); general->Get("IgnoreBadMemAccess", &bIgnoreBadMemAccess, true); general->Get("CurrentDirectory", ¤tDirectory, ""); @@ -59,7 +62,7 @@ void CConfig::Load(const char *iniFileName) graphics->Get("DisplayFramebuffer", &bDisplayFramebuffer, false); graphics->Get("WindowZoom", &iWindowZoom, 1); graphics->Get("BufferedRendering", &bBufferedRendering, true); - graphics->Get("HardwareTransform", &bHardwareTransform, false); + graphics->Get("HardwareTransform", &bHardwareTransform, true); graphics->Get("LinearFiltering", &bLinearFiltering, false); IniFile::Section *sound = iniFile.GetOrCreateSection("Sound"); @@ -69,17 +72,17 @@ void CConfig::Load(const char *iniFileName) control->Get("ShowStick", &bShowAnalogStick, false); control->Get("ShowTouchControls", &bShowTouchControls, true); - // Ephemeral settings bDrawWireframe = false; } void CConfig::Save() { - if (g_Config.bSaveSettings && iniFilename_.size()) - { + if (iniFilename_.size() && g_Config.bSaveSettings) { IniFile iniFile; - iniFile.Load(iniFilename_.c_str()); + if (!iniFile.Load(iniFilename_.c_str())) { + ERROR_LOG(LOADER, "Error saving config - can't read ini %s", iniFilename_.c_str()); + } IniFile::Section *general = iniFile.GetOrCreateSection("General"); general->Set("FirstRun", bFirstRun); @@ -108,9 +111,12 @@ void CConfig::Save() control->Set("ShowStick", bShowAnalogStick); control->Set("ShowTouchControls", bShowTouchControls); - iniFile.Save(iniFilename_.c_str()); - NOTICE_LOG(LOADER, "Config saved: %s", iniFilename_.c_str()); + if (!iniFile.Save(iniFilename_.c_str())) { + ERROR_LOG(LOADER, "Error saving config - can't write ini %s", iniFilename_.c_str()); + return; + } + INFO_LOG(LOADER, "Config saved: %s", iniFilename_.c_str()); } else { - NOTICE_LOG(LOADER, "Error saving config: %s", iniFilename_.c_str()); + INFO_LOG(LOADER, "Not saving config"); } } diff --git a/Core/Config.h b/Core/Config.h index 030d3ea8c1..2bfbfe1ea7 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -38,9 +38,9 @@ public: // These are broken bool bAutoLoadLast; bool bFirstRun; - bool bAutoRun; bool bSpeedLimit; bool bConfirmOnQuit; + bool bAutoRun; // start immediately // Core bool bIgnoreBadMemAccess; diff --git a/Core/Core.cpp b/Core/Core.cpp index 33124240f6..a94e5f8d7c 100644 --- a/Core/Core.cpp +++ b/Core/Core.cpp @@ -88,7 +88,7 @@ void Core_Run() while (true) { reswitch: - switch(coreState) + switch (coreState) { case CORE_RUNNING: //1: enter a fast runloop @@ -132,7 +132,7 @@ void Core_EnableStepping(bool step) #if defined(_DEBUG) host->SetDebugMode(true); #endif - coreState=CORE_STEPPING; + coreState = CORE_STEPPING; } else { @@ -145,5 +145,4 @@ void Core_EnableStepping(bool step) m_hStepEvent.notify_one(); } - } diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index 5e73a44574..8e3574417c 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -116,6 +116,8 @@ + + @@ -171,7 +173,6 @@ - @@ -261,6 +262,11 @@ + + + + + @@ -320,7 +326,6 @@ - diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index 5419b5cc77..c0c03cd556 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -43,6 +43,12 @@ {41034c99-9b76-477f-8a77-bffaaffd5e82} + + {1966d4a4-9a34-4a6c-946a-ebaf33633276} + + + {0b77054f-7fc7-4c33-ada3-762aecde69e5} + @@ -321,9 +327,6 @@ HLE\Libraries - - HLE\Libraries - HLE\Libraries @@ -354,6 +357,12 @@ Core + + Ext\Snappy + + + Ext\Snappy + @@ -620,9 +629,6 @@ HLE\Libraries - - HLE\Libraries - HLE\Libraries @@ -653,6 +659,21 @@ Core + + Ext\Snappy + + + Ext\Snappy + + + Ext\Snappy + + + Ext\Snappy + + + Ext\Snappy + diff --git a/Core/Dialog/PSPDialog.cpp b/Core/Dialog/PSPDialog.cpp index 9750fb917d..183ce97f3c 100644 --- a/Core/Dialog/PSPDialog.cpp +++ b/Core/Dialog/PSPDialog.cpp @@ -50,7 +50,7 @@ void PSPDialog::EndDraw() void PSPDialog::DisplayMessage(std::string text) { - PPGeDrawText(text.c_str(), 480/2, 100, PPGE_ALIGN_CENTER, 0.5f, 0xFFFFFFFF); + PPGeDrawText(text.c_str(), 40, 30, PPGE_ALIGN_LEFT, 0.5f, 0xFFFFFFFF); } int PSPDialog::Shutdown() diff --git a/Core/Dialog/PSPMsgDialog.cpp b/Core/Dialog/PSPMsgDialog.cpp index a5ef5ceb6e..f72a246484 100644 --- a/Core/Dialog/PSPMsgDialog.cpp +++ b/Core/Dialog/PSPMsgDialog.cpp @@ -44,6 +44,13 @@ int PSPMsgDialog::Init(unsigned int paramAddr) } Memory::ReadStruct(messageDialogAddr, &messageDialog); + // debug info + int optionsNotCoded = ((messageDialog.options | SCE_UTILITY_MSGDIALOG_DEBUG_OPTION_CODED) ^ SCE_UTILITY_MSGDIALOG_DEBUG_OPTION_CODED); + if(optionsNotCoded) + { + ERROR_LOG(HLE,"PSPMsgDialog options not coded : 0x%08x",optionsNotCoded); + } + yesnoChoice = 1; if (messageDialog.type == 0) // number { @@ -56,6 +63,8 @@ int PSPMsgDialog::Init(unsigned int paramAddr) display = DS_MESSAGE; if(messageDialog.options & SCE_UTILITY_MSGDIALOG_OPTION_YESNO) display = DS_YESNO; + if(messageDialog.options & SCE_UTILITY_MSGDIALOG_OPTION_OK) + display = DS_OK; if(messageDialog.options & SCE_UTILITY_MSGDIALOG_OPTION_DEFAULT_NO) yesnoChoice = 0; } @@ -189,6 +198,25 @@ int PSPMsgDialog::Update() } EndDraw(); break; + case DS_OK: + StartDraw(); + + DisplayMessage(text); + + // TODO : Dialogs should take control over input and not send them to the game while displaying + DisplayEnterBack(); + if (IsButtonPressed(cancelButtonFlag)) + { + status = SCE_UTILITY_STATUS_FINISHED; + messageDialog.buttonPressed = 3; + } + else if (IsButtonPressed(okButtonFlag)) + { + status = SCE_UTILITY_STATUS_FINISHED; + messageDialog.buttonPressed = 1; + } + EndDraw(); + break; default: status = SCE_UTILITY_STATUS_FINISHED; return 0; @@ -208,6 +236,7 @@ int PSPMsgDialog::Shutdown() void PSPMsgDialog::DoState(PointerWrap &p) { + PSPDialog::DoState(p); p.Do(display); p.Do(messageDialog); p.Do(messageDialogAddr); diff --git a/Core/Dialog/PSPMsgDialog.h b/Core/Dialog/PSPMsgDialog.h index 779a77f4e6..c738f5e8d5 100644 --- a/Core/Dialog/PSPMsgDialog.h +++ b/Core/Dialog/PSPMsgDialog.h @@ -22,8 +22,11 @@ #define SCE_UTILITY_MSGDIALOG_OPTION_ERROR 0 // Do nothing #define SCE_UTILITY_MSGDIALOG_OPTION_TEXT 0x00000001 #define SCE_UTILITY_MSGDIALOG_OPTION_YESNO 0x00000010 +#define SCE_UTILITY_MSGDIALOG_OPTION_OK 0x00000020 #define SCE_UTILITY_MSGDIALOG_OPTION_DEFAULT_NO 0x00000100 +#define SCE_UTILITY_MSGDIALOG_DEBUG_OPTION_CODED 0x00000131 // OR of all options coded to display warning + struct pspMessageDialog { pspUtilityDialogCommon common; @@ -32,7 +35,7 @@ struct pspMessageDialog unsigned int errorNum; char string[512]; unsigned int options; - unsigned int buttonPressed; // 0=?, 1=Yes, 2=No, 3=Back + unsigned int buttonPressed; // 0=?, 1=Yes/OK, 2=No, 3=Back }; @@ -58,6 +61,7 @@ private : DS_MESSAGE, DS_ERROR, DS_YESNO, + DS_OK }; DisplayState display; diff --git a/Core/Dialog/PSPOskDialog.cpp b/Core/Dialog/PSPOskDialog.cpp index abe367b96e..00b539fc4b 100644 --- a/Core/Dialog/PSPOskDialog.cpp +++ b/Core/Dialog/PSPOskDialog.cpp @@ -236,6 +236,7 @@ int PSPOskDialog::Update() void PSPOskDialog::DoState(PointerWrap &p) { + PSPDialog::DoState(p); p.Do(oskParams); p.Do(oskData); p.Do(oskDesc); diff --git a/Core/Dialog/PSPSaveDialog.cpp b/Core/Dialog/PSPSaveDialog.cpp index 4c02582986..675d3ec9ae 100644 --- a/Core/Dialog/PSPSaveDialog.cpp +++ b/Core/Dialog/PSPSaveDialog.cpp @@ -22,8 +22,8 @@ PSPSaveDialog::PSPSaveDialog() : PSPDialog() - , currentSelectedSave(0) , display(DS_NONE) + , currentSelectedSave(0) { param.SetPspParam(0); } @@ -81,9 +81,12 @@ int PSPSaveDialog::Init(int paramAddr) display = DS_DELETE_LIST_CHOICE; break; case SCE_UTILITY_SAVEDATA_TYPE_SIZES: - display = DS_NONE; - break; case SCE_UTILITY_SAVEDATA_TYPE_LIST: + case SCE_UTILITY_SAVEDATA_TYPE_FILES: + case SCE_UTILITY_SAVEDATA_TYPE_SIZES22: + case SCE_UTILITY_SAVEDATA_TYPE_MAKEDATASECURE: + case SCE_UTILITY_SAVEDATA_TYPE_WRITEDATASECURE: + case SCE_UTILITY_SAVEDATA_TYPE_READDATASECURE: display = DS_NONE; break; case SCE_UTILITY_SAVEDATA_TYPE_DELETE: // This run on PSP display a list of all save on the PSP. Weird. (Not really, it's to let you free up space) @@ -242,7 +245,7 @@ void PSPSaveDialog::DisplaySaveDataInfo1() 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" + snprintf(txt,2048,"%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 @@ -266,7 +269,7 @@ void PSPSaveDialog::DisplaySaveDataInfo2() else { char txt[1024]; - sprintf(txt,"%s\n%02d/%02d/%d %02d:%02d\n%lld KB" + snprintf(txt,1024,"%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 @@ -671,10 +674,41 @@ int PSPSaveDialog::Update() param.GetPspParam()->result = 0; status = SCE_UTILITY_STATUS_FINISHED; break; - // TODO: Don't know the name? - case 12: - // Pretend we have nothing, always. - param.GetPspParam()->result = SCE_UTILITY_SAVEDATA_ERROR_RW_NO_DATA; + case SCE_UTILITY_SAVEDATA_TYPE_FILES: + if(param.GetFilesList(param.GetPspParam())) + { + param.GetPspParam()->result = 0; + } + else + { + param.GetPspParam()->result = SCE_UTILITY_SAVEDATA_ERROR_RW_NO_DATA; + } + status = SCE_UTILITY_STATUS_FINISHED; + break; + case SCE_UTILITY_SAVEDATA_TYPE_SIZES22: + if(param.GetSizes22(param.GetPspParam())) + { + param.GetPspParam()->result = 0; + } + else + { + param.GetPspParam()->result = SCE_UTILITY_SAVEDATA_ERROR_RW_NO_DATA; + } + status = SCE_UTILITY_STATUS_FINISHED; + break; + case SCE_UTILITY_SAVEDATA_TYPE_MAKEDATASECURE: + case SCE_UTILITY_SAVEDATA_TYPE_WRITEDATASECURE: + if(param.Save(param.GetPspParam(),param.GetSelectedSave())) + param.GetPspParam()->result = 0; + else + param.GetPspParam()->result = SCE_UTILITY_SAVEDATA_ERROR_RW_NO_DATA; + status = SCE_UTILITY_STATUS_FINISHED; + break; + case SCE_UTILITY_SAVEDATA_TYPE_READDATASECURE: + if(param.Load(param.GetPspParam(),param.GetSelectedSave())) + param.GetPspParam()->result = 0; + else + param.GetPspParam()->result = SCE_UTILITY_SAVEDATA_ERROR_RW_NO_DATA; // not sure if correct code status = SCE_UTILITY_STATUS_FINISHED; break; default: @@ -708,11 +742,15 @@ int PSPSaveDialog::Shutdown() void PSPSaveDialog::DoState(PointerWrap &p) { + PSPDialog::DoState(p); p.Do(display); param.DoState(p); p.Do(request); // Just reset it. - param.SetPspParam(&request); + bool hasParam = param.GetPspParam() != NULL; + p.Do(hasParam); + if (hasParam) + param.SetPspParam(&request); p.Do(requestAddr); p.Do(currentSelectedSave); p.Do(yesnoChoice); diff --git a/Core/Dialog/SavedataParam.cpp b/Core/Dialog/SavedataParam.cpp index 4abafe0c73..a3206736da 100644 --- a/Core/Dialog/SavedataParam.cpp +++ b/Core/Dialog/SavedataParam.cpp @@ -45,7 +45,7 @@ namespace str[strLength - 1] = 0; } - bool ReadPSPFile(std::string filename, u8 *data, s64 dataSize) + bool ReadPSPFile(std::string filename, u8 *data, s64 dataSize, s64 *readSize) { u32 handle = pspFileSystem.OpenFile(filename, FILEACCESS_READ); if (handle == 0) @@ -53,6 +53,8 @@ namespace int result = pspFileSystem.ReadFile(handle, data, dataSize); pspFileSystem.CloseFile(handle); + if(readSize) + *readSize = result; return result != 0; } @@ -75,14 +77,47 @@ namespace u8 key[16]; int sdkVersion; }; + + bool PSPMatch(std::string text, std::string regexp) + { + if(text.empty() && regexp.empty()) + return true; + else if(regexp == "*") + return true; + else if(text.empty()) + return false; + else if(regexp.empty()) + return false; + else if(regexp == "?" && text.length() == 1) + return true; + else if(text == regexp) + return true; + else if(regexp.data()[0] == '*') + { + bool res = PSPMatch(text.substr(1),regexp.substr(1)); + if(!res) + res = PSPMatch(text.substr(1),regexp); + return res; + } + else if(regexp.data()[0] == '?') + { + return PSPMatch(text.substr(1),regexp.substr(1)); + } + else if(regexp.data()[0] == text.data()[0]) + { + return PSPMatch(text.substr(1),regexp.substr(1)); + } + + return false; + } } SavedataParam::SavedataParam() : pspParam(0) , selectedSave(0) , saveDataList(0) - , saveNameListDataCount(0) , saveDataListCount(0) + , saveNameListDataCount(0) { } @@ -130,6 +165,8 @@ std::string SavedataParam::GetSaveName(SceUtilitySavedataParam* param) char saveName[21]; memcpy(saveName,param->saveName,20); saveName[20] = 0; + if(strcmp(saveName,"<>") == 0) + return ""; return saveName; } @@ -167,95 +204,137 @@ bool SavedataParam::Save(SceUtilitySavedataParam* param, int saveId) return false; } - u8 *data_ = (u8*)Memory::GetPointer(*((unsigned int*)¶m->dataBuf)); - std::string dirPath = GetSaveFilePath(param, saveId); 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()); - if (!WritePSPFile(filePath, data_, param->dataBufSize)) + if(param->dataBuf != 0) // Can launch save without save data in mode 13 { - ERROR_LOG(HLE,"Error writing file %s",filePath.c_str()); - return false; + std::string filePath = dirPath+"/"+GetFileName(param); + int saveSize = param->dataSize; + if(saveSize == 0 || saveSize > param->dataBufSize) + saveSize = param->dataBufSize; // fallback, should never use this + INFO_LOG(HLE,"Saving file with size %u in %s",saveSize,filePath.c_str()); + u8 *data_ = (u8*)Memory::GetPointer(param->dataBuf); + + if (!WritePSPFile(filePath, data_, saveSize)) + { + ERROR_LOG(HLE,"Error writing file %s",filePath.c_str()); + return false; + } } - else + + // SAVE PARAM.SFO + ParamSFOData sfoFile; + std::string sfopath = dirPath+"/"+sfoName; + PSPFileInfo sfoInfo = pspFileSystem.GetFileInfo(sfopath); + if(sfoInfo.exists) // Read old sfo if exist { - // SAVE PARAM.SFO - ParamSFOData sfoFile; - sfoFile.SetValue("TITLE",param->sfoParam.title,128); - sfoFile.SetValue("SAVEDATA_TITLE",param->sfoParam.savedataTitle,128); - sfoFile.SetValue("SAVEDATA_DETAIL",param->sfoParam.detail,1024); - sfoFile.SetValue("PARENTAL_LEVEL",param->sfoParam.parentalLevel,4); - sfoFile.SetValue("CATEGORY","MS",4); - sfoFile.SetValue("SAVEDATA_DIRECTORY",GetSaveDir(param,saveId),64); - - // 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; - WritePSPFile(sfopath, sfoData, sfoSize); - delete[] sfoData; - - // SAVE ICON0 - if (param->icon0FileData.buf) + u8 *sfoData = new u8[(size_t)sfoInfo.size]; + size_t sfoSize = (size_t)sfoInfo.size; + if(ReadPSPFile(sfopath,sfoData,sfoSize, NULL)) { - data_ = (u8*)Memory::GetPointer(*((unsigned int*)¶m->icon0FileData.buf)); - std::string icon0path = dirPath+"/"+icon0Name; - WritePSPFile(icon0path, data_, param->icon0FileData.bufSize); + sfoFile.ReadSFO(sfoData,sfoSize); + delete[] sfoData; } - // SAVE ICON1 - if (param->icon1FileData.buf) + } + + // Update values + sfoFile.SetValue("TITLE",param->sfoParam.title,128); + sfoFile.SetValue("SAVEDATA_TITLE",param->sfoParam.savedataTitle,128); + sfoFile.SetValue("SAVEDATA_DETAIL",param->sfoParam.detail,1024); + sfoFile.SetValue("PARENTAL_LEVEL",param->sfoParam.parentalLevel,4); + sfoFile.SetValue("CATEGORY","MS",4); + sfoFile.SetValue("SAVEDATA_DIRECTORY",GetSaveDir(param,saveId),64); + + // For each file, 13 bytes for filename, 16 bytes for file hash (0 in PPSSPP), 3 byte for padding + const int FILE_LIST_ITEM_SIZE = 13 + 16 + 3; + const int FILE_LIST_COUNT_MAX = 99; + const int FILE_LIST_TOTAL_SIZE = FILE_LIST_ITEM_SIZE * FILE_LIST_COUNT_MAX; + u32 tmpDataSize = 0; + u8* tmpDataOrig = sfoFile.GetValueData("SAVEDATA_FILE_LIST", &tmpDataSize); + u8* tmpData = new u8[FILE_LIST_TOTAL_SIZE]; + + if (tmpDataOrig != NULL) + memcpy(tmpData, tmpDataOrig, tmpDataSize > FILE_LIST_TOTAL_SIZE ? FILE_LIST_TOTAL_SIZE : tmpDataSize); + else + memset(tmpData, 0, FILE_LIST_TOTAL_SIZE); + + if (param->dataBuf != 0) + { + char *fName = (char*)tmpData; + for(int i = 0; i < FILE_LIST_COUNT_MAX; i++) { - data_ = (u8*)Memory::GetPointer(*((unsigned int*)¶m->icon1FileData.buf)); - std::string icon1path = dirPath+"/"+icon1Name; - WritePSPFile(icon1path, data_, param->icon1FileData.bufSize); - } - // SAVE PIC1 - if (param->pic1FileData.buf) - { - data_ = (u8*)Memory::GetPointer(*((unsigned int*)¶m->pic1FileData.buf)); - std::string pic1path = dirPath+"/"+pic1Name; - WritePSPFile(pic1path, data_, param->pic1FileData.bufSize); + if(fName[0] == 0) + break; // End of list + if(strncmp(fName,GetFileName(param).c_str(),20) == 0) + break; // File already in SFO + + fName += FILE_LIST_ITEM_SIZE; } - // Save SND - if (param->snd0FileData.buf) - { - data_ = (u8*)Memory::GetPointer(*((unsigned int*)¶m->snd0FileData.buf)); - std::string snd0path = dirPath+"/"+snd0Name; - WritePSPFile(snd0path, data_, param->snd0FileData.bufSize); - } + if (fName + 20 <= (char*)tmpData + FILE_LIST_TOTAL_SIZE) + snprintf(fName, 20, "%s",GetFileName(param).c_str()); + } + sfoFile.SetValue("SAVEDATA_FILE_LIST", tmpData, FILE_LIST_TOTAL_SIZE, FILE_LIST_TOTAL_SIZE); + delete[] tmpData; - // Save Encryption Data - { - EncryptFileInfo encryptInfo; - int dataSize = sizeof(encryptInfo); // version + key + sdkVersion - memset(&encryptInfo,0,dataSize); + // No crypted save, so fill with 0 + tmpData = new u8[128]; + memset(tmpData, 0, 128); + sfoFile.SetValue("SAVEDATA_PARAMS", tmpData, 128, 128); + delete[] tmpData; - encryptInfo.fileVersion = 1; - encryptInfo.sdkVersion = sceKernelGetCompiledSdkVersion(); - if(param->size > 1500) - memcpy(encryptInfo.key,param->key,16); + u8 *sfoData; + size_t sfoSize; + sfoFile.WriteSFO(&sfoData,&sfoSize); + WritePSPFile(sfopath, sfoData, sfoSize); + delete[] sfoData; - std::string encryptInfoPath = dirPath+"/"+"ENCRYPT_INFO.BIN"; - WritePSPFile(encryptInfoPath, (u8*)&encryptInfo, dataSize); - } + // SAVE ICON0 + if (param->icon0FileData.buf) + { + u8* data_ = (u8*)Memory::GetPointer(param->icon0FileData.buf); + std::string icon0path = dirPath+"/"+icon0Name; + WritePSPFile(icon0path, data_, param->icon0FileData.bufSize); + } + // SAVE ICON1 + if (param->icon1FileData.buf) + { + u8* data_ = (u8*)Memory::GetPointer(param->icon1FileData.buf); + std::string icon1path = dirPath+"/"+icon1Name; + WritePSPFile(icon1path, data_, param->icon1FileData.bufSize); + } + // SAVE PIC1 + if (param->pic1FileData.buf) + { + u8* data_ = (u8*)Memory::GetPointer(param->pic1FileData.buf); + std::string pic1path = dirPath+"/"+pic1Name; + WritePSPFile(pic1path, data_, param->pic1FileData.bufSize); + } + + // Save SND + if (param->snd0FileData.buf) + { + u8* data_ = (u8*)Memory::GetPointer(param->snd0FileData.buf); + std::string snd0path = dirPath+"/"+snd0Name; + 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; } @@ -266,7 +345,7 @@ bool SavedataParam::Load(SceUtilitySavedataParam *param, int saveId) return false; } - u8 *data_ = (u8*)Memory::GetPointer(*((unsigned int*)¶m->dataBuf)); + u8 *data_ = (u8*)Memory::GetPointer(param->dataBuf); std::string dirPath = GetSaveFilePath(param, saveId); if (saveId >= 0 && saveNameListDataCount > 0) // if user selection, use it @@ -278,12 +357,14 @@ bool SavedataParam::Load(SceUtilitySavedataParam *param, int saveId) } std::string filePath = dirPath+"/"+GetFileName(param); + s64 readSize; INFO_LOG(HLE,"Loading file with size %u in %s",param->dataBufSize,filePath.c_str()); - if (!ReadPSPFile(filePath, data_, param->dataBufSize)) + if (!ReadPSPFile(filePath, data_, param->dataBufSize, &readSize)) { ERROR_LOG(HLE,"Error reading file %s",filePath.c_str()); return false; } + param->dataSize = readSize; return true; } @@ -318,9 +399,6 @@ std::string SavedataParam::GetSpaceText(int 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) { @@ -394,11 +472,105 @@ bool SavedataParam::GetList(SceUtilitySavedataParam *param) if (Memory::IsValidAddress(param->idListAddr)) { - Memory::Write_U32(0,param->idListAddr+4); + u32 outputBuffer = Memory::Read_U32(param->idListAddr + 8); + u32 maxFile = Memory::Read_U32(param->idListAddr + 0); + + std::vector validDir; + std::vector allDir = pspFileSystem.GetDirListing(savePath); + + if (Memory::IsValidAddress(outputBuffer)) + { + std::string searchString = GetGameName(param)+GetSaveName(param); + for (size_t i = 0; i < allDir.size() && i < maxFile; i++) + { + std::string dirName = allDir[i].name; + if(PSPMatch(dirName, searchString)) + { + validDir.push_back(allDir[i]); + } + } + + for (size_t i = 0; i < validDir.size(); i++) + { + u32 baseAddr = outputBuffer + (i*72); + Memory::Write_U32(0x11FF,baseAddr + 0); // mode + Memory::Write_U64(0,baseAddr + 4); // TODO ctime + Memory::Write_U64(0,baseAddr + 12); // TODO unknow + Memory::Write_U64(0,baseAddr + 20); // TODO atime + Memory::Write_U64(0,baseAddr + 28); // TODO unknow + Memory::Write_U64(0,baseAddr + 36); // TODO mtime + Memory::Write_U64(0,baseAddr + 44); // TODO unknow + // folder name without gamename (max 20 u8) + std::string outName = validDir[i].name.substr(GetGameName(param).size()); + Memory::Memset(baseAddr + 52,0,20); + Memory::Memcpy(baseAddr + 52, outName.c_str(), outName.size()); + } + } + // Save num of folder found + Memory::Write_U32(validDir.size(),param->idListAddr+4); } return true; } +bool SavedataParam::GetFilesList(SceUtilitySavedataParam *param) +{ + if (!param) + { + return false; + } + + u32 dataAddr = param->fileListAddr; + if (!Memory::IsValidAddress(dataAddr)) + return false; + + // TODO : Need to be checked against more game + + u32 fileInfosAddr = Memory::Read_U32(dataAddr + 24); + + //for Valkyria2, dataAddr+0 and dataAddr+12 has "5" for 5 files + int numFiles = Memory::Read_U32(dataAddr+12); + int foundFiles = 0; + for (int i = 0; i < numFiles; i++) + { + // for each file (80 bytes): + // u32 mode, u32 ??, u64 size, u64 ctime, u64 ??, u64 atime, u64 ???, u64 mtime, u64 ??? + // u8[16] filename (or 13 + padding?) + u32 curFileInfoAddr = fileInfosAddr + i*80; + + char fileName[16]; + strncpy(fileName, Memory::GetCharPointer(curFileInfoAddr + 64),16); + std::string filePath = savePath + GetGameName(param) + GetSaveName(param) + "/" + fileName; + PSPFileInfo info = pspFileSystem.GetFileInfo(filePath); + if (info.exists) + { + Memory::Write_U32(0x21FF, curFileInfoAddr+0); + Memory::Write_U64(info.size, curFileInfoAddr+8); + Memory::Write_U64(0,curFileInfoAddr + 16); // TODO ctime + Memory::Write_U64(0,curFileInfoAddr + 24); // TODO unknow + Memory::Write_U64(0,curFileInfoAddr + 32); // TODO atime + Memory::Write_U64(0,curFileInfoAddr + 40); // TODO unknow + Memory::Write_U64(0,curFileInfoAddr + 48); // TODO mtime + Memory::Write_U64(0,curFileInfoAddr + 56); // TODO unknow + foundFiles++; + } + } + + // TODO : verify if return true if at least 1 file found or only if all found + return foundFiles > 0; +} + +bool SavedataParam::GetSizes22(SceUtilitySavedataParam *param) +{ + if (!param) + { + return false; + } + + // TODO code this + + return false; +} + void SavedataParam::Clear() { if (saveDataList) @@ -412,6 +584,7 @@ void SavedataParam::Clear() delete[] saveDataList; saveDataList = 0; + saveDataListCount = 0; } } @@ -434,6 +607,8 @@ int SavedataParam::SetPspParam(SceUtilitySavedataParam *param) char (*saveNameListData)[20]; if (param->saveNameList != 0) { + Clear(); + saveNameListData = (char(*)[20])Memory::GetPointer(param->saveNameList); // Get number of fileName in array @@ -443,7 +618,6 @@ int SavedataParam::SetPspParam(SceUtilitySavedataParam *param) saveDataListCount++; } while(saveNameListData[saveDataListCount][0] != 0); - Clear(); saveDataList = new SaveFileInfo[saveDataListCount]; // get and stock file info for each file @@ -482,6 +656,7 @@ int SavedataParam::SetPspParam(SceUtilitySavedataParam *param) Clear(); saveDataList = new SaveFileInfo[1]; + saveDataListCount = 1; // get and stock file info for each file DEBUG_LOG(HLE,"Name : %s",GetSaveName(param).c_str()); @@ -532,7 +707,7 @@ void SavedataParam::SetFileInfo(int idx, PSPFileInfo &info, std::string saveName if (info2.exists) { u8 *textureDataPNG = new u8[(size_t)info2.size]; - ReadPSPFile(fileDataPath2, textureDataPNG, info2.size); + ReadPSPFile(fileDataPath2, textureDataPNG, info2.size, NULL); unsigned char *textureData; int w,h; @@ -561,7 +736,7 @@ void SavedataParam::SetFileInfo(int idx, PSPFileInfo &info, std::string saveName if (info2.exists) { u8 *sfoParam = new u8[(size_t)info2.size]; - ReadPSPFile(fileDataPath2, sfoParam, info2.size); + ReadPSPFile(fileDataPath2, sfoParam, info2.size, NULL); ParamSFOData sfoFile; if (sfoFile.ReadSFO(sfoParam,(size_t)info2.size)) { @@ -608,6 +783,12 @@ void SavedataParam::DoState(PointerWrap &p) p.Do(selectedSave); p.Do(saveDataListCount); p.Do(saveNameListDataCount); + if (p.mode == p.MODE_READ) + { + if (saveDataList != NULL) + delete [] saveDataList; + saveDataList = new SaveFileInfo[saveDataListCount]; + } p.DoArray(saveDataList, saveDataListCount); p.DoMarker("SavedataParam"); } diff --git a/Core/Dialog/SavedataParam.h b/Core/Dialog/SavedataParam.h index 4a1384a8ac..592c1d3bbc 100644 --- a/Core/Dialog/SavedataParam.h +++ b/Core/Dialog/SavedataParam.h @@ -31,7 +31,12 @@ enum SceUtilitySavedataType SCE_UTILITY_SAVEDATA_TYPE_LISTDELETE = 6, SCE_UTILITY_SAVEDATA_TYPE_DELETE = 7, SCE_UTILITY_SAVEDATA_TYPE_SIZES = 8, - SCE_UTILITY_SAVEDATA_TYPE_LIST = 11 + SCE_UTILITY_SAVEDATA_TYPE_LIST = 11, + SCE_UTILITY_SAVEDATA_TYPE_FILES = 12, + SCE_UTILITY_SAVEDATA_TYPE_MAKEDATASECURE = 13, + SCE_UTILITY_SAVEDATA_TYPE_READDATASECURE = 15, + SCE_UTILITY_SAVEDATA_TYPE_WRITEDATASECURE = 17, + SCE_UTILITY_SAVEDATA_TYPE_SIZES22 = 22 } ; // title, savedataTitle, detail: parts of the unencrypted SFO @@ -81,7 +86,7 @@ struct SceUtilitySavedataParam char unused2[3]; /** pointer to a buffer that will contain data file unencrypted data */ - int dataBuf; // Initially void*, but void* in 64bit system take 8 bytes. + u32 dataBuf; // Initially void*, but void* in 64bit system take 8 bytes. /** size of allocated space to dataBuf */ SceSize dataBufSize; SceSize dataSize; // Size of the actual save data @@ -98,9 +103,9 @@ struct SceUtilitySavedataParam int abortStatus; // Function SCE_UTILITY_SAVEDATA_TYPE_SIZES - int msFree; - int msData; - int utilityData; + u32 msFree; + u32 msData; + u32 utilityData; char key[16]; @@ -108,13 +113,13 @@ struct SceUtilitySavedataParam int multiStatus; // Function 11 LIST - int idListAddr; + u32 idListAddr; // Function 12 FILES - int fileListAddr; + u32 fileListAddr; // Function 22 GETSIZES - int sizeAddr; + u32 sizeAddr; }; @@ -135,10 +140,12 @@ struct SaveFileInfo int textureWidth; int textureHeight; }; - + class SavedataParam { public: + SavedataParam(); + static void Init(); std::string GetSaveFilePath(SceUtilitySavedataParam* param, int saveId = -1); std::string GetSaveDir(SceUtilitySavedataParam* param, int saveId = -1); @@ -147,6 +154,8 @@ public: bool Load(SceUtilitySavedataParam* param, int saveId = -1); bool GetSizes(SceUtilitySavedataParam* param); bool GetList(SceUtilitySavedataParam* param); + bool GetFilesList(SceUtilitySavedataParam* param); + bool GetSizes22(SceUtilitySavedataParam* param); std::string GetGameName(SceUtilitySavedataParam* param); std::string GetSaveName(SceUtilitySavedataParam* param); @@ -154,8 +163,6 @@ public: static std::string GetSpaceText(int size); - SavedataParam(); - int SetPspParam(SceUtilitySavedataParam* param); SceUtilitySavedataParam* GetPspParam(); @@ -177,5 +184,4 @@ private: SaveFileInfo* saveDataList; int saveDataListCount; int saveNameListDataCount; - }; diff --git a/Core/ELF/ElfReader.cpp b/Core/ELF/ElfReader.cpp index 2969b975e5..77ff90de4d 100644 --- a/Core/ELF/ElfReader.cpp +++ b/Core/ELF/ElfReader.cpp @@ -201,10 +201,21 @@ bool ElfReader::LoadInto(u32 loadAddress) } } u32 totalSize = totalEnd - totalStart; - if (loadAddress) - vaddr = userMemory.AllocAt(loadAddress, totalSize, "ELF"); + if (!bRelocate) + { + // Binary is prerelocated, load it where the first segment starts + vaddr = userMemory.AllocAt(totalStart, totalSize, "ELF"); + } + else if (loadAddress) + { + // Binary needs to be relocated: add loadAddress to the binary start address + vaddr = userMemory.AllocAt(loadAddress + totalStart, totalSize, "ELF"); + } else + { + // Just put it where there is room vaddr = userMemory.Alloc(totalSize, false, "ELF"); + } if (vaddr == -1) { ERROR_LOG(LOADER, "Failed to allocate memory for ELF!"); @@ -299,7 +310,7 @@ bool ElfReader::LoadInto(u32 loadAddress) else if (s->sh_type == SHT_REL) { DEBUG_LOG(LOADER, "Traditional relocation section found."); - if (bRelocate) + if (!bRelocate) { DEBUG_LOG(LOADER, "Binary is prerelocated. Skipping relocations."); } diff --git a/Core/ELF/ParamSFO.cpp b/Core/ELF/ParamSFO.cpp index 7965c1455b..bfba51a6cd 100644 --- a/Core/ELF/ParamSFO.cpp +++ b/Core/ELF/ParamSFO.cpp @@ -147,7 +147,7 @@ bool ParamSFOData::WriteSFO(u8 **paramsfo, size_t *size) total_size += sizeof(Header); // Get size info - for(std::map::iterator it = values.begin(); it != values.end(); it++) + for (std::map::iterator it = values.begin(); it != values.end(); it++) { key_size += it->first.size()+1; data_size += it->second.max_size; @@ -168,29 +168,29 @@ bool ParamSFOData::WriteSFO(u8 **paramsfo, size_t *size) u8* data = new u8[total_size]; *paramsfo = data; - memset(data,0,total_size); - memcpy(data,&header,sizeof(Header)); + memset(data, 0, total_size); + memcpy(data, &header, sizeof(Header)); // Now fill IndexTable *index_ptr = (IndexTable*)(data + sizeof(Header)); u8* key_ptr = data + header.key_table_start; u8* data_ptr = data + header.data_table_start; - for(std::map::iterator it = values.begin(); it != values.end(); it++) + for (std::map::iterator it = values.begin(); it != values.end(); it++) { u16 offset = (u16)(key_ptr - (data+header.key_table_start)); index_ptr->key_table_offset = offset; offset = (u16)(data_ptr - (data+header.data_table_start)); index_ptr->data_table_offset = offset; index_ptr->param_max_len = it->second.max_size; - if(it->second.type == VT_INT) + if (it->second.type == VT_INT) { index_ptr->param_fmt = 0x0404; index_ptr->param_len = 4; *(int*)data_ptr = it->second.i_value; } - else if(it->second.type == VT_UTF8_SPE) + else if (it->second.type == VT_UTF8_SPE) { index_ptr->param_fmt = 0x0004; index_ptr->param_len = it->second.u_size; @@ -198,7 +198,7 @@ bool ParamSFOData::WriteSFO(u8 **paramsfo, size_t *size) 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) + else if (it->second.type == VT_UTF8) { index_ptr->param_fmt = 0x0204; index_ptr->param_len = it->second.s_value.size()+1; diff --git a/Core/ELF/ParamSFO.h b/Core/ELF/ParamSFO.h index fc4e07efc7..1620563dd0 100644 --- a/Core/ELF/ParamSFO.h +++ b/Core/ELF/ParamSFO.h @@ -33,14 +33,15 @@ public: bool ReadSFO(const u8 *paramsfo, size_t size); bool WriteSFO(u8 **paramsfo, size_t *size); -private: +private: enum ValueType { VT_INT, VT_UTF8, VT_UTF8_SPE // raw data in u8 }; + class ValueData { public: @@ -62,7 +63,7 @@ private: if(size > 0) { u_value = new u8[size]; - memcpy(u_value,data,size); + memcpy(u_value, data, size); } u_size = size; } diff --git a/Core/ELF/PrxDecrypter.cpp b/Core/ELF/PrxDecrypter.cpp index 8541c0358c..f9a643463b 100644 --- a/Core/ELF/PrxDecrypter.cpp +++ b/Core/ELF/PrxDecrypter.cpp @@ -6,6 +6,9 @@ extern "C" } #include "../../Globals.h" +#include "PrxDecrypter.h" + +#define ROUNDUP16(x) (((x)+15)&~15) // Thank you PSARDUMPER & JPCSP keys @@ -287,6 +290,22 @@ static const TAG_INFO g_tagInfo[] = { 0xBB67C59F, g_key_GAMESHARE2xx, 0x5E, 0x5E } }; +bool HasKey(int key) +{ + switch (key) + { + case 0x02: case 0x03: case 0x04: case 0x05: case 0x07: case 0x0C: case 0x0D: case 0x0E: case 0x0F: + case 0x10: case 0x11: case 0x12: + case 0x38: case 0x39: case 0x3A: case 0x44: case 0x4B: + case 0x53: case 0x57: case 0x5D: + case 0x63: case 0x64: + return true; + default: + INFO_LOG(HLE, "Missing key %02X, cannot decrypt module", key); + return false; + } +} + static const TAG_INFO *GetTagInfo(u32 tagFind) { for (u32 iTag = 0; iTag < sizeof(g_tagInfo)/sizeof(TAG_INFO); iTag++) @@ -297,21 +316,17 @@ static const TAG_INFO *GetTagInfo(u32 tagFind) static void ExtraV2Mangle(u8* buffer1, u8 codeExtra) { -#ifdef _MSC_VER - static u8 __declspec(align(64)) g_dataTmp[20+0xA0]; -#else - static u8 g_dataTmp[20+0xA0] __attribute__((aligned(0x40))); -#endif - u8* buffer2 = g_dataTmp; // aligned + u8 buffer2[ROUNDUP16(0x14+0xA0)]; + + memcpy(buffer2+0x14, buffer1, 0xA0); - memcpy(buffer2+20, buffer1, 0xA0); u32* pl2 = (u32*)buffer2; pl2[0] = 5; pl2[1] = pl2[2] = 0; pl2[3] = codeExtra; pl2[4] = 0xA0; - sceUtilsBufferCopyWithRange(buffer2, 20+0xA0, buffer2, 20+0xA0, 7); + sceUtilsBufferCopyWithRange(buffer2, 20+0xA0, buffer2, 20+0xA0, KIRK_CMD_DECRYPT_IV_0); // copy result back memcpy(buffer1, buffer2, 0xA0); } @@ -323,7 +338,7 @@ static int Scramble(u32 *buf, u32 size, u32 code) buf[3] = code; buf[4] = size; - if (sceUtilsBufferCopyWithRange((u8*)buf, size+0x14, (u8*)buf, size+0x14, 7) < 0) + if (sceUtilsBufferCopyWithRange((u8*)buf, size+0x14, (u8*)buf, size+0x14, KIRK_CMD_DECRYPT_IV_0) < 0) { return -1; } @@ -341,6 +356,11 @@ static int DecryptPRX1(const u8* pbIn, u8* pbOut, int cbTotal, u32 tag) { return -1; } + if (!HasKey(pti->code) || + (pti->codeExtra != 0 && !HasKey(pti->codeExtra))) + { + return MISSING_KEY; + } retsize = *(u32*)&pbIn[0xB0]; @@ -432,7 +452,7 @@ struct TAG_INFO2 u8 type; }; -static TAG_INFO2 g_tagInfo2[] = +static const TAG_INFO2 g_tagInfo2[] = { { 0x4C9494F0, keys660_k1, 0x43 }, { 0x4C9495F0, keys660_k2, 0x43 }, @@ -568,7 +588,7 @@ static TAG_INFO2 g_tagInfo2[] = }; -static TAG_INFO2 *GetTagInfo2(u32 tagFind) +static const TAG_INFO2 *GetTagInfo2(u32 tagFind) { for (u32 iTag = 0; iTag < sizeof(g_tagInfo2) / sizeof(TAG_INFO2); iTag++) { @@ -581,22 +601,25 @@ static TAG_INFO2 *GetTagInfo2(u32 tagFind) return NULL; // not found } + static int DecryptPRX2(const u8 *inbuf, u8 *outbuf, u32 size, u32 tag) { - TAG_INFO2 * pti = GetTagInfo2(tag); + const TAG_INFO2 *pti = GetTagInfo2(tag); if (!pti) { return -1; } + if (!HasKey(pti->code)) + { + return MISSING_KEY; + } - 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, 0x90+0x14); - memset(tmp4, 0, 0x20); + int retsize = *(const int *)&inbuf[0xB0]; + u8 tmp1[0x150] = {0}; + u8 tmp2[ROUNDUP16(0x90+0x14)] = {0}; + u8 tmp3[ROUNDUP16(0x90+0x14)] = {0}; + u8 tmp4[ROUNDUP16(0x20)] = {0}; if (inbuf != outbuf) memcpy(outbuf, inbuf, size); @@ -606,7 +629,7 @@ static int DecryptPRX2(const u8 *inbuf, u8 *outbuf, u32 size, u32 tag) return -2; } - if ((size - 0x150) < retsize) + if (((int)size - 0x150) < retsize) { return -4; } @@ -614,15 +637,16 @@ static int DecryptPRX2(const u8 *inbuf, u8 *outbuf, u32 size, u32 tag) memcpy(tmp1, outbuf, 0x150); int i, j; - u8 *p = tmp2+0x14; + u8 *p = tmp2 + 0x14; + // Writes 0x90 bytes to tmp2 + 0x14. for (i = 0; i < 9; i++) { for (j = 0; j < 0x10; j++) { p[(i << 4) + j] = pti->key[j]; } - p[(i << 4)] = i; // really? + p[(i << 4)] = i; // really? this is very odd } if (Scramble((u32 *)tmp2, 0x90, pti->code) < 0) @@ -724,6 +748,10 @@ int pspDecryptPRX(const u8 *inbuf, u8 *outbuf, u32 size) { kirk_init(); int retsize = DecryptPRX1(inbuf, outbuf, size, *(u32 *)&inbuf[0xD0]); + if (retsize == MISSING_KEY) + { + return MISSING_KEY; + } if (retsize <= 0) { diff --git a/Core/ELF/PrxDecrypter.h b/Core/ELF/PrxDecrypter.h index 23cbedbc1a..afb4f2d555 100644 --- a/Core/ELF/PrxDecrypter.h +++ b/Core/ELF/PrxDecrypter.h @@ -19,6 +19,8 @@ #include "../../Globals.h" +#define MISSING_KEY -10 + #ifdef _MSC_VER #pragma pack(push, 1) #endif diff --git a/Core/FileSystems/BlockDevices.h b/Core/FileSystems/BlockDevices.h index 626c11e6ed..48faa59cad 100644 --- a/Core/FileSystems/BlockDevices.h +++ b/Core/FileSystems/BlockDevices.h @@ -38,28 +38,32 @@ public: class CISOFileBlockDevice : public BlockDevice { +public: + CISOFileBlockDevice(std::string _filename); + ~CISOFileBlockDevice(); + bool ReadBlock(int blockNumber, u8 *outPtr); + int GetNumBlocks() { return numBlocks;} + +private: std::string filename; FILE *f; u32 *index; int indexShift; u32 blockSize; int numBlocks; -public: - CISOFileBlockDevice(std::string _filename); - ~CISOFileBlockDevice(); - bool ReadBlock(int blockNumber, u8 *outPtr); - int GetNumBlocks() { return numBlocks;} }; class FileBlockDevice : public BlockDevice { - std::string filename; - FILE *f; - size_t filesize; public: FileBlockDevice(std::string _filename); ~FileBlockDevice(); bool ReadBlock(int blockNumber, u8 *outPtr); - int GetNumBlocks() {return (int)(filesize/GetBlockSize());} + int GetNumBlocks() {return (int)(filesize / GetBlockSize());} + +private: + std::string filename; + FILE *f; + size_t filesize; }; diff --git a/Core/FileSystems/DirectoryFileSystem.cpp b/Core/FileSystems/DirectoryFileSystem.cpp index e57be86eb2..523661e9fd 100644 --- a/Core/FileSystems/DirectoryFileSystem.cpp +++ b/Core/FileSystems/DirectoryFileSystem.cpp @@ -490,23 +490,31 @@ PSPFileInfo DirectoryFileSystem::GetFileInfo(std::string filename) { return x; #endif } - x.type = File::IsDirectory(fullName) ? FILETYPE_NORMAL : FILETYPE_DIRECTORY; + x.type = File::IsDirectory(fullName) ? FILETYPE_DIRECTORY : FILETYPE_NORMAL; x.exists = true; + if (x.type != FILETYPE_DIRECTORY) + { #ifdef _WIN32 - WIN32_FILE_ATTRIBUTE_DATA data; - GetFileAttributesEx(fullName.c_str(), GetFileExInfoStandard, &data); + WIN32_FILE_ATTRIBUTE_DATA data; + GetFileAttributesEx(fullName.c_str(), GetFileExInfoStandard, &data); - x.size = data.nFileSizeLow | ((u64)data.nFileSizeHigh<<32); + x.size = data.nFileSizeLow | ((u64)data.nFileSizeHigh<<32); #else - x.size = File::GetSize(fullName); - //TODO + x.size = File::GetSize(fullName); + //TODO #endif - x.mtime = File::GetModifTime(fullName); + x.mtime = File::GetModifTime(fullName); + } return x; } +bool DirectoryFileSystem::GetHostPath(const std::string &inpath, std::string &outpath) { + outpath = GetLocalPath(inpath); + return true; +} + std::vector DirectoryFileSystem::GetDirListing(std::string path) { std::vector myVector; #ifdef _WIN32 @@ -541,7 +549,23 @@ std::vector DirectoryFileSystem::GetDirListing(std::string path) { break; } #else - ERROR_LOG(HLE, "GetDirListing not implemented on non-Windows"); + DIR *dp; + dirent *dirp; + if((dp = opendir(GetLocalPath(path).c_str())) == NULL) { + ERROR_LOG(HLE,"Error opening directory %s\n",path.c_str()); + return myVector; + } + + while ((dirp = readdir(dp)) != NULL) { + PSPFileInfo entry; + if(dirp->d_type == DT_DIR) + entry.type = FILETYPE_DIRECTORY; + else + entry.type = FILETYPE_NORMAL; + entry.name = dirp->d_name; + myVector.push_back(entry); + } + closedir(dp); #endif return myVector; } diff --git a/Core/FileSystems/DirectoryFileSystem.h b/Core/FileSystems/DirectoryFileSystem.h index 6cb334fc53..739a8427ad 100644 --- a/Core/FileSystems/DirectoryFileSystem.h +++ b/Core/FileSystems/DirectoryFileSystem.h @@ -67,6 +67,7 @@ public: bool RmDir(const std::string &dirname); bool RenameFile(const std::string &from, const std::string &to); bool DeleteFile(const std::string &filename); + bool GetHostPath(const std::string &inpath, std::string &outpath); private: struct OpenFileEntry { diff --git a/Core/FileSystems/FileSystem.h b/Core/FileSystems/FileSystem.h index 4d8670effd..87fc1b3f83 100644 --- a/Core/FileSystems/FileSystem.h +++ b/Core/FileSystems/FileSystem.h @@ -103,6 +103,7 @@ public: virtual bool RmDir(const std::string &dirname) = 0; virtual bool RenameFile(const std::string &from, const std::string &to) = 0; virtual bool DeleteFile(const std::string &filename) = 0; + virtual bool GetHostPath(const std::string &inpath, std::string &outpath) = 0; }; @@ -122,6 +123,7 @@ public: virtual bool RmDir(const std::string &dirname) {return false;} virtual bool RenameFile(const std::string &from, const std::string &to) {return false;} virtual bool DeleteFile(const std::string &filename) {return false;} + virtual bool GetHostPath(const std::string &inpath, std::string &outpath) {return false;} }; diff --git a/Core/FileSystems/ISOFileSystem.cpp b/Core/FileSystems/ISOFileSystem.cpp index b054b6bf72..a8ea984e43 100644 --- a/Core/FileSystems/ISOFileSystem.cpp +++ b/Core/FileSystems/ISOFileSystem.cpp @@ -209,7 +209,7 @@ nextblock: if (strlen(name) == 1 && name[0] == '\x01') // ".." record { strcpy(name,".."); - relative=true; + relative = true; } TreeEntry *e = new TreeEntry; @@ -240,7 +240,7 @@ nextblock: } -ISOFileSystem::TreeEntry *ISOFileSystem::GetFromPath(std::string path) +ISOFileSystem::TreeEntry *ISOFileSystem::GetFromPath(std::string path, bool catchError) { if (path.length() == 0) { @@ -297,7 +297,10 @@ ISOFileSystem::TreeEntry *ISOFileSystem::GetFromPath(std::string path) } else { - ERROR_LOG(FILESYS,"File %s not found", path.c_str()); + if (catchError) + { + ERROR_LOG(FILESYS,"File %s not found", path.c_str()); + } return 0; } } @@ -502,7 +505,7 @@ PSPFileInfo ISOFileSystem::GetFileInfo(std::string filename) return fileInfo; } - TreeEntry *entry = GetFromPath(filename); + TreeEntry *entry = GetFromPath(filename, false); PSPFileInfo x; if (!entry) { diff --git a/Core/FileSystems/ISOFileSystem.h b/Core/FileSystems/ISOFileSystem.h index 96e88e0710..ab90080829 100644 --- a/Core/FileSystems/ISOFileSystem.h +++ b/Core/FileSystems/ISOFileSystem.h @@ -27,6 +27,26 @@ class ISOFileSystem : public IFileSystem { +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); + size_t ReadFile(u32 handle, u8 *pointer, s64 size); + size_t SeekFile(u32 handle, s32 position, FileMove type); + PSPFileInfo GetFileInfo(std::string filename); + bool OwnsHandle(u32 handle); + + size_t WriteFile(u32 handle, const u8 *pointer, s64 size); + bool GetHostPath(const std::string &inpath, std::string &outpath) {return false;} + virtual bool MkDir(const std::string &dirname) {return false;} + virtual bool RmDir(const std::string &dirname) {return false;} + virtual bool RenameFile(const std::string &from, const std::string &to) {return false;} + virtual bool DeleteFile(const std::string &filename) {return false;} + +private: struct TreeEntry { TreeEntry(){} @@ -67,24 +87,6 @@ class ISOFileSystem : public IFileSystem TreeEntry entireISO; void ReadDirectory(u32 startsector, u32 dirsize, TreeEntry *root); - TreeEntry *GetFromPath(std::string path); + TreeEntry *GetFromPath(std::string path, bool catchError=true); 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); - size_t ReadFile(u32 handle, u8 *pointer, s64 size); - size_t WriteFile(u32 handle, const u8 *pointer, s64 size); - size_t SeekFile(u32 handle, s32 position, FileMove type); - PSPFileInfo GetFileInfo(std::string filename); - bool OwnsHandle(u32 handle); - - virtual bool MkDir(const std::string &dirname) {return false;} - virtual bool RmDir(const std::string &dirname) {return false;} - virtual bool RenameFile(const std::string &from, const std::string &to) {return false;} - virtual bool DeleteFile(const std::string &filename) {return false;} }; diff --git a/Core/FileSystems/MetaFileSystem.cpp b/Core/FileSystems/MetaFileSystem.cpp index 58ae33fae1..ad144396d8 100644 --- a/Core/FileSystems/MetaFileSystem.cpp +++ b/Core/FileSystems/MetaFileSystem.cpp @@ -16,6 +16,7 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include +#include "Common/StringUtil.h" #include "MetaFileSystem.h" static bool ApplyPathStringToComponentsVector(std::vector &vector, const std::string &pathString) @@ -251,14 +252,15 @@ PSPFileInfo MetaFileSystem::GetFileInfo(std::string filename) } } -//TODO: Not sure where this should live. Seems a bit wrong putting it in common -bool stringEndsWith (std::string const &fullString, std::string const &ending) +bool MetaFileSystem::GetHostPath(const std::string &inpath, std::string &outpath) { - if (fullString.length() >= ending.length()) { - return (0 == fullString.compare (fullString.length() - ending.length(), ending.length(), ending)); - } else { - return false; - } + std::string of; + IFileSystem *system; + if (MapFilePath(inpath, of, &system)) { + return system->GetHostPath(of, outpath); + } else { + return false; + } } std::vector MetaFileSystem::GetDirListing(std::string path) diff --git a/Core/FileSystems/MetaFileSystem.h b/Core/FileSystems/MetaFileSystem.h index 7fd0a96db2..88fc50d23c 100644 --- a/Core/FileSystems/MetaFileSystem.h +++ b/Core/FileSystems/MetaFileSystem.h @@ -41,6 +41,9 @@ public: IFileSystem *GetHandleOwner(u32 handle); bool MapFilePath(const std::string &inpath, std::string &outpath, IFileSystem **system); + // Only possible if a file system is a DirectoryFileSystem or similar. + bool GetHostPath(const std::string &inpath, std::string &outpath); + std::vector GetDirListing(std::string path); u32 OpenFile(std::string filename, FileAccess access); void CloseFile(u32 handle); diff --git a/Core/HLE/FunctionWrappers.h b/Core/HLE/FunctionWrappers.h index d88e99090c..f57029c1ec 100644 --- a/Core/HLE/FunctionWrappers.h +++ b/Core/HLE/FunctionWrappers.h @@ -29,6 +29,12 @@ template void WrapU64_V() { currentMIPS->r[3] = (retval >> 32) & 0xFFFFFFFF; } +template void WrapU64_U() { + u64 retval = func(PARAM(0)); + currentMIPS->r[2] = retval & 0xFFFFFFFF; + currentMIPS->r[3] = (retval >> 32) & 0xFFFFFFFF; +} + template void WrapI_UU64() { u64 param_one = currentMIPS->r[6]; param_one |= (u64)(currentMIPS->r[7]) << 32; @@ -36,6 +42,13 @@ template void WrapI_UU64() { RETURN(retval); } +template void WrapU_UU64() { + u64 param_one = currentMIPS->r[6]; + param_one |= (u64)(currentMIPS->r[7]) << 32; + u32 retval = func(PARAM(0), param_one); + RETURN(retval); +} + template void WrapI_UUU64() { u64 param_two = currentMIPS->r[6]; param_two |= (u64)(currentMIPS->r[7]) << 32; @@ -50,6 +63,13 @@ template void WrapU_II64I() { RETURN(retval); } +template void WrapU_UU64UU() { + u64 param_one = currentMIPS->r[6]; + param_one |= (u64)(currentMIPS->r[7]) << 32; + u32 retval = func(PARAM(0), param_one, PARAM(4), PARAM(5)); + RETURN(retval); +} + template void WrapI64_II64I() { s64 param_one = currentMIPS->r[6]; param_one |= (s64)(currentMIPS->r[7]) << 32; @@ -414,6 +434,11 @@ template void WrapU_UUUI() { RETURN(retval); } +template void WrapU_UUIU() { + u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3)); + RETURN(retval); +} + template void WrapU_UIII() { u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3)); RETURN(retval); diff --git a/Core/HLE/HLETables.cpp b/Core/HLE/HLETables.cpp index 7a8aba3661..27f916faeb 100644 --- a/Core/HLE/HLETables.cpp +++ b/Core/HLE/HLETables.cpp @@ -51,7 +51,6 @@ #include "sceParseUri.h" #include "sceSsl.h" #include "sceParseHttp.h" -#include "scesupPreAcc.h" #include "sceVaudio.h" #include "sceUsb.h" @@ -75,9 +74,9 @@ const HLEFunction FakeSysCalls[] = const HLEFunction UtilsForUser[] = { {0x91E4F6A7, WrapU_V, "sceKernelLibcClock"}, - {0x27CC57F0, sceKernelLibcTime, "sceKernelLibcTime"}, + {0x27CC57F0, WrapU_U, "sceKernelLibcTime"}, {0x71EC4271, sceKernelLibcGettimeofday, "sceKernelLibcGettimeofday"}, - {0xBFA98062, WrapV_UI, "sceKernelDcacheInvalidateRange"}, + {0xBFA98062, WrapI_UI, "sceKernelDcacheInvalidateRange"}, {0xC8186A58, 0, "sceKernelUtilsMd5Digest"}, {0x9E5C5086, 0, "sceKernelUtilsMd5BlockInit"}, {0x61E1E525, 0, "sceKernelUtilsMd5BlockUpdate"}, @@ -90,10 +89,10 @@ const HLEFunction UtilsForUser[] = {0x06FB8A63, 0, "sceKernelUtilsMt19937UInt"}, {0x37FB5C42, sceKernelGetGPI, "sceKernelGetGPI"}, {0x6AD345D7, sceKernelSetGPO, "sceKernelSetGPO"}, - {0x79D1C3FA, sceKernelDcacheWritebackAll, "sceKernelDcacheWritebackAll"}, - {0xB435DEC5, sceKernelDcacheWritebackInvalidateAll, "sceKernelDcacheWritebackInvalidateAll"}, - {0x3EE30821, WrapV_UI, "sceKernelDcacheWritebackRange"}, - {0x34B9FA9E, WrapV_UI, "sceKernelDcacheWritebackInvalidateRange"}, + {0x79D1C3FA, WrapI_V, "sceKernelDcacheWritebackAll"}, + {0xB435DEC5, WrapI_V, "sceKernelDcacheWritebackInvalidateAll"}, + {0x3EE30821, WrapI_UI, "sceKernelDcacheWritebackRange"}, + {0x34B9FA9E, WrapI_UI, "sceKernelDcacheWritebackInvalidateRange"}, {0xC2DF770E, 0, "sceKernelIcacheInvalidateRange"}, {0x80001C4C, 0, "sceKernelDcacheProbe"}, {0x16641D70, 0, "sceKernelDcacheReadTag"}, @@ -243,7 +242,6 @@ void RegisterAllModules() { Register_sceParseUri(); Register_sceSsl(); Register_sceParseHttp(); - Register_scesupPreAcc(); Register_sceVaudio(); Register_sceUsb(); diff --git a/Core/HLE/sceCtrl.cpp b/Core/HLE/sceCtrl.cpp index 234bb3dc5f..9a89bdd39c 100644 --- a/Core/HLE/sceCtrl.cpp +++ b/Core/HLE/sceCtrl.cpp @@ -163,7 +163,7 @@ void __CtrlSetAnalog(float x, float y) if (x < -1.0f) x = -1.0f; if (y < -1.0f) y = -1.0f; ctrlCurrent.analog[0] = (u8)(x * 127.f + 128.f); - ctrlCurrent.analog[1] = (u8)(y * 127.f + 128.f); + ctrlCurrent.analog[1] = (u8)(-y * 127.f + 128.f); } int __CtrlReadSingleBuffer(u32 ctrlDataPtr, bool negative) @@ -361,7 +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); + DEBUG_LOG(HLE, "%d=sceCtrlGetSamplingMode(%08x)", retVal, modePtr); if (Memory::IsValidAddress(modePtr)) Memory::Write_U32(retVal, modePtr); diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index ddd793b43b..a217aaa258 100644 --- a/Core/HLE/sceDisplay.cpp +++ b/Core/HLE/sceDisplay.cpp @@ -109,7 +109,6 @@ void __DisplayInit() { vCount = 0; hCount = 0; hCountTotal = 0; - hasSetMode = false; lastFrameTime = 0; InitGfxState(); @@ -133,6 +132,20 @@ void __DisplayDoState(PointerWrap &p) { p.Do(leaveVblankEvent); CoreTiming::RestoreRegisterEvent(leaveVblankEvent, "LeaveVBlank", &hleLeaveVblank); + p.Do(gstate); + p.Do(gstate_c); + p.Do(gpuStats); + gpu->DoState(p); + + ReapplyGfxState(); + + if (p.mode == p.MODE_READ) { + if (hasSetMode) { + gpu->InitClear(); + } + gpu->SetDisplayFramebuffer(framebuf.topaddr, framebuf.pspFramebufLinesize, framebuf.pspFramebufFormat); + } + p.DoMarker("sceDisplay"); } diff --git a/Core/HLE/sceFont.cpp b/Core/HLE/sceFont.cpp index 8c026e1913..98163f7ed2 100644 --- a/Core/HLE/sceFont.cpp +++ b/Core/HLE/sceFont.cpp @@ -271,6 +271,19 @@ int sceFontGetFontInfo(u32 fontHandle, u32 fontInfoPtr) fi.minGlyphCenterXF = 16; fi.minGlyphCenterXI = 16; fi.shadowMapLength = 0; + fi.fontStyle.fontAttributes=1; + fi.fontStyle.fontCountry= 1; + fi.fontStyle.fontExpire= 1; + fi.fontStyle.fontFamily= 1; + //fi.fontStyle.fontFileName="asd"; + fi.fontStyle.fontH=32; + fi.fontStyle.fontHRes=32; + fi.fontStyle.fontLanguage=1; + // fi.fontStyle.fontName="ppsspp"; + fi.fontStyle.fontRegion=9; + fi.fontStyle.fontV=32; + fi.fontStyle.fontVRes=32; + fi.fontStyle.fontWeight= 32; Memory::WriteStruct(fontInfoPtr, &fi); } @@ -316,7 +329,13 @@ int sceFontGetCharGlyphImage(u32 libHandler, u32 charCode, u32 glyphImagePtr) int bytesPerLine = Memory::Read_U16(glyphImagePtr+16); int buffer =Memory::Read_U32(glyphImagePtr+20); - Memory::Memset(buffer, 0x7F, bytesPerLine*bufHeight*pixelFormat); + for (int y= 0; y < bufHeight; y++) + { + for (int x=0; xInvalidateCache(0, -1); + p.DoArray(ge_callback_data, ARRAY_SIZE(ge_callback_data)); + p.DoArray(ge_used_callbacks, ARRAY_SIZE(ge_used_callbacks)); + // Everything else is done in sceDisplay. p.DoMarker("sceGe"); } @@ -66,7 +63,29 @@ u32 sceGeEdramGetSize() return retVal; } -u32 sceGeListEnQueue(u32 listAddress, u32 stallAddress, u32 callbackId, +// TODO: Probably shouldn't use an interrupt? +int __GeSubIntrBase(int callbackId) +{ + // Negative means don't use. + if (callbackId < 0) + return 0; + + if (callbackId >= (int)(ARRAY_SIZE(ge_used_callbacks))) + { + WARN_LOG(HLE, "Unexpected (too high) GE callback id %d, ignoring", callbackId); + return 0; + } + + if (!ge_used_callbacks[callbackId]) + { + WARN_LOG(HLE, "Unregistered GE callback id %d, ignoring", callbackId); + return 0; + } + + return (callbackId + 1) << 16; +} + +u32 sceGeListEnQueue(u32 listAddress, u32 stallAddress, int callbackId, u32 optParamAddr) { DEBUG_LOG(HLE, @@ -74,19 +93,14 @@ u32 sceGeListEnQueue(u32 listAddress, u32 stallAddress, u32 callbackId, listAddress, stallAddress, callbackId, optParamAddr); //if (!stallAddress) // stallAddress = listAddress; - u32 listID = gpu->EnqueueList(listAddress, stallAddress); - // HACKY - if (listID) - state = SCE_GE_LIST_STALLING; - else - state = SCE_GE_LIST_COMPLETED; + u32 listID = gpu->EnqueueList(listAddress, stallAddress, __GeSubIntrBase(callbackId), false); DEBUG_LOG(HLE, "List %i enqueued.", listID); //return display list ID return listID; } -u32 sceGeListEnQueueHead(u32 listAddress, u32 stallAddress, u32 callbackId, +u32 sceGeListEnQueueHead(u32 listAddress, u32 stallAddress, int callbackId, u32 optParamAddr) { DEBUG_LOG(HLE, @@ -94,29 +108,34 @@ u32 sceGeListEnQueueHead(u32 listAddress, u32 stallAddress, u32 callbackId, listAddress, stallAddress, callbackId, optParamAddr); //if (!stallAddress) // stallAddress = listAddress; - u32 listID = gpu->EnqueueList(listAddress, stallAddress); - // HACKY - if (listID) - state = SCE_GE_LIST_STALLING; - else - state = SCE_GE_LIST_COMPLETED; + u32 listID = gpu->EnqueueList(listAddress, stallAddress, __GeSubIntrBase(callbackId), true); DEBUG_LOG(HLE, "List %i enqueued.", listID); //return display list ID return listID; } -void sceGeListUpdateStallAddr(u32 displayListID, u32 stallAddress) +int sceGeListDeQueue(u32 listID) +{ + ERROR_LOG(HLE, "UNIMPL sceGeListDeQueue(%08x)", listID); + return 0; +} + +int sceGeListUpdateStallAddr(u32 displayListID, u32 stallAddress) { DEBUG_LOG(HLE, "sceGeListUpdateStallAddr(dlid=%i,stalladdr=%08x)", displayListID, stallAddress); gpu->UpdateStall(displayListID, stallAddress); + return 0; } int sceGeListSync(u32 displayListID, u32 mode) //0 : wait for completion 1:check and return { DEBUG_LOG(HLE, "sceGeListSync(dlid=%08x, mode=%08x)", displayListID, mode); + if(mode == 1) { + return gpu->listStatus(displayListID); + } return 0; } @@ -129,47 +148,82 @@ u32 sceGeDrawSync(u32 mode) return 0; } -void sceGeContinue() +int sceGeContinue() { ERROR_LOG(HLE, "UNIMPL sceGeContinue"); // no arguments + return 0; } -void sceGeBreak(u32 mode) +int sceGeBreak(u32 mode) { //mode => 0 : current dlist 1: all drawing ERROR_LOG(HLE, "UNIMPL sceGeBreak(mode=%d)", mode); + return 0; } - u32 sceGeSetCallback(u32 structAddr) { DEBUG_LOG(HLE, "sceGeSetCallback(struct=%08x)", structAddr); - PspGeCallbackData ge_callback_data; - Memory::ReadStruct(structAddr, &ge_callback_data); + int cbID = -1; + for (int i = 0; i < ARRAY_SIZE(ge_used_callbacks); ++i) + if (!ge_used_callbacks[i]) + { + cbID = i; + break; + } - if (ge_callback_data.finish_func) + if (cbID == -1) { - sceKernelRegisterSubIntrHandler(PSP_GE_INTR, PSP_GE_SUBINTR_FINISH, - ge_callback_data.finish_func, ge_callback_data.finish_arg); - sceKernelEnableSubIntr(PSP_GE_INTR, PSP_GE_SUBINTR_FINISH); - } - if (ge_callback_data.signal_func) - { - sceKernelRegisterSubIntrHandler(PSP_GE_INTR, PSP_GE_SUBINTR_SIGNAL, - ge_callback_data.signal_func, ge_callback_data.signal_arg); - sceKernelEnableSubIntr(PSP_GE_INTR, PSP_GE_SUBINTR_SIGNAL); + WARN_LOG(HLE, "sceGeSetCallback(): out of callback ids"); + return SCE_KERNEL_ERROR_OUT_OF_MEMORY; } - // TODO: This should return a callback ID - return 0; + ge_used_callbacks[cbID] = true; + Memory::ReadStruct(structAddr, &ge_callback_data[cbID]); + + int subIntrBase = __GeSubIntrBase(cbID); + + if (ge_callback_data[cbID].finish_func) + { + sceKernelRegisterSubIntrHandler(PSP_GE_INTR, subIntrBase | PSP_GE_SUBINTR_FINISH, + ge_callback_data[cbID].finish_func, ge_callback_data[cbID].finish_arg); + sceKernelEnableSubIntr(PSP_GE_INTR, subIntrBase | PSP_GE_SUBINTR_FINISH); + } + if (ge_callback_data[cbID].signal_func) + { + sceKernelRegisterSubIntrHandler(PSP_GE_INTR, subIntrBase | PSP_GE_SUBINTR_SIGNAL, + ge_callback_data[cbID].signal_func, ge_callback_data[cbID].signal_arg); + sceKernelEnableSubIntr(PSP_GE_INTR, subIntrBase | PSP_GE_SUBINTR_SIGNAL); + } + + return cbID; } -void sceGeUnsetCallback(u32 cbID) { +int sceGeUnsetCallback(u32 cbID) +{ DEBUG_LOG(HLE, "sceGeUnsetCallback(cbid=%08x)", cbID); - sceKernelReleaseSubIntrHandler(PSP_GE_INTR, PSP_GE_SUBINTR_FINISH); - sceKernelReleaseSubIntrHandler(PSP_GE_INTR, PSP_GE_SUBINTR_SIGNAL); + + if (cbID >= ARRAY_SIZE(ge_used_callbacks)) + { + WARN_LOG(HLE, "sceGeUnsetCallback(cbid=%08x): invalid callback id", cbID); + return SCE_KERNEL_ERROR_INVALID_ID; + } + + if (ge_used_callbacks[cbID]) + { + int subIntrBase = __GeSubIntrBase(cbID); + + sceKernelReleaseSubIntrHandler(PSP_GE_INTR, subIntrBase | PSP_GE_SUBINTR_FINISH); + sceKernelReleaseSubIntrHandler(PSP_GE_INTR, subIntrBase | PSP_GE_SUBINTR_SIGNAL); + } + else + WARN_LOG(HLE, "sceGeUnsetCallback(cbid=%08x): ignoring unregistered callback id", cbID); + + ge_used_callbacks[cbID] = false; + + return 0; } // Points to 512 32-bit words, where we can probably layout the context however we want @@ -215,9 +269,16 @@ u32 sceGeRestoreContext(u32 ctxAddr) return 0; } -void sceGeGetMtx() +int sceGeGetMtx(int type, u32 matrixPtr) { - ERROR_LOG(HLE, "UNIMPL sceGeGetMtx()"); + ERROR_LOG(HLE, "UNIMPL sceGeGetMtx(%d, %08x)", type, matrixPtr); + return 0; +} + +u32 sceGeGetCmd(int cmd) +{ + ERROR_LOG(HLE, "UNIMPL sceGeGetCmd()"); + return 0; } u32 sceGeEdramSetAddrTranslation(int new_size) @@ -231,23 +292,23 @@ u32 sceGeEdramSetAddrTranslation(int new_size) const HLEFunction sceGe_user[] = { - {0xE47E40E4,&WrapU_V, "sceGeEdramGetAddr"}, - {0xAB49E76A,&WrapU_UUUU, "sceGeListEnQueue"}, - {0x1C0D95A6,&WrapU_UUUU, "sceGeListEnQueueHead"}, - {0xE0D68148,&WrapV_UU, "sceGeListUpdateStallAddr"}, - {0x03444EB4,&WrapI_UU, "sceGeListSync"}, - {0xB287BD61,&WrapU_U, "sceGeDrawSync"}, - {0xB448EC0D,&WrapV_U, "sceGeBreak"}, - {0x4C06E472,sceGeContinue, "sceGeContinue"}, - {0xA4FC06A4,&WrapU_U, "sceGeSetCallback"}, - {0x05DB22CE,&WrapV_U, "sceGeUnsetCallback"}, - {0x1F6752AD,&WrapU_V, "sceGeEdramGetSize"}, - {0xB77905EA,&WrapU_I,"sceGeEdramSetAddrTranslation"}, - {0xDC93CFEF,0,"sceGeGetCmd"}, - {0x57C8945B,&sceGeGetMtx,"sceGeGetMtx"}, - {0x438A385A,&WrapU_U,"sceGeSaveContext"}, - {0x0BF608FB,&WrapU_U,"sceGeRestoreContext"}, - {0x5FB86AB0,0,"sceGeListDeQueue"}, + {0xE47E40E4, WrapU_V, "sceGeEdramGetAddr"}, + {0xAB49E76A, WrapU_UUIU, "sceGeListEnQueue"}, + {0x1C0D95A6, WrapU_UUIU, "sceGeListEnQueueHead"}, + {0xE0D68148, WrapI_UU, "sceGeListUpdateStallAddr"}, + {0x03444EB4, WrapI_UU, "sceGeListSync"}, + {0xB287BD61, WrapU_U, "sceGeDrawSync"}, + {0xB448EC0D, WrapI_U, "sceGeBreak"}, + {0x4C06E472, WrapI_V, "sceGeContinue"}, + {0xA4FC06A4, WrapU_U, "sceGeSetCallback"}, + {0x05DB22CE, WrapI_U, "sceGeUnsetCallback"}, + {0x1F6752AD, WrapU_V, "sceGeEdramGetSize"}, + {0xB77905EA, WrapU_I, "sceGeEdramSetAddrTranslation"}, + {0xDC93CFEF, WrapU_I, "sceGeGetCmd"}, + {0x57C8945B, WrapI_IU, "sceGeGetMtx"}, + {0x438A385A, WrapU_U, "sceGeSaveContext"}, + {0x0BF608FB, WrapU_U, "sceGeRestoreContext"}, + {0x5FB86AB0, WrapI_U, "sceGeListDeQueue"}, }; void Register_sceGe_user() diff --git a/Core/HLE/sceGe.h b/Core/HLE/sceGe.h index d6a19a0767..b37508a672 100644 --- a/Core/HLE/sceGe.h +++ b/Core/HLE/sceGe.h @@ -45,4 +45,4 @@ void __GeShutdown(); u32 sceGeRestoreContext(u32 ctxAddr); u32 sceGeSaveContext(u32 ctxAddr); -u32 sceGeListEnQueue(u32 listAddress, u32 stallAddress, u32 callbackId, u32 optParamAddr); +u32 sceGeListEnQueue(u32 listAddress, u32 stallAddress, int callbackId, u32 optParamAddr); diff --git a/Core/HLE/sceHprm.cpp b/Core/HLE/sceHprm.cpp index 63490c960d..6d1d3196a6 100644 --- a/Core/HLE/sceHprm.cpp +++ b/Core/HLE/sceHprm.cpp @@ -20,29 +20,44 @@ #include "sceCtrl.h" -u32 sceHprmPeekCurrentKey(u32 keyAddress) -{ +u32 sceHprmPeekCurrentKey(u32 keyAddress) { INFO_LOG(HLE,"0=sceHprmPeekCurrentKey(ptr)"); Memory::Write_U32(0, keyAddress); return 0; } +// TODO: Might make sense to reflect the headphone status of the host here, +// if the games adjust their sound. +u32 sceHprmIsHeadphoneExist() { + DEBUG_LOG(HLE, "sceHprmIsHeadphoneExist()"); + return 0; +} + +u32 sceHprmIsMicrophoneExist() { + DEBUG_LOG(HLE, "sceHprmIsMicrophoneExist()"); + return 0; +} + +u32 sceHprmIsRemoteExist() { + DEBUG_LOG(HLE, "sceHprmIsRemoteExist()"); + return 0; +} + const HLEFunction sceHprm[] = { {0x089fdfa4, 0, "sceHprm_0x089fdfa4"}, {0x1910B327, &WrapU_U, "sceHprmPeekCurrentKey"}, - {0x208DB1BD, 0, "sceHprmIsRemoteExist"}, - {0x7E69EDA4, 0, "sceHprmIsHeadphoneExist"}, - {0x219C58F1, 0, "sceHprmIsMicrophoneExist"}, + {0x208DB1BD, WrapU_V, "sceHprmIsRemoteExist"}, + {0x7E69EDA4, WrapU_V, "sceHprmIsHeadphoneExist"}, + {0x219C58F1, WrapU_V, "sceHprmIsMicrophoneExist"}, {0xC7154136, 0, "sceHprmRegisterCallback"}, {0x444ED0B7, 0, "sceHprmUnregisterCallback"}, {0x1910B327, 0, "sceHprmPeekCurrentKey"}, {0x2BCEC83E, 0, "sceHprmPeekLatch"}, {0x40D2F9F0, 0, "sceHprmReadLatch"}, }; -const int sceHprmCount = ARRAY_SIZE(sceHprm); void Register_sceHprm() { - RegisterModule("sceHprm", sceHprmCount, sceHprm); + RegisterModule("sceHprm", ARRAY_SIZE(sceHprm), sceHprm); } diff --git a/Core/HLE/sceIo.cpp b/Core/HLE/sceIo.cpp index a7f8e00564..11578688c6 100644 --- a/Core/HLE/sceIo.cpp +++ b/Core/HLE/sceIo.cpp @@ -20,8 +20,8 @@ #undef DeleteFile #endif -#include "../System.h" #include "../Config.h" +#include "../Host.h" #include "../SaveState.h" #include "HLE.h" #include "../MIPS/MIPS.h" @@ -77,12 +77,6 @@ typedef s32 SceMode; typedef s64 SceOff; typedef u64 SceIores; -std::string emuDebugOutput; - -const std::string &EmuDebugOutput() { - return emuDebugOutput; -} - typedef u32 (*DeferredAction)(SceUID id, int param); DeferredAction defAction = 0; u32 defParam = 0; @@ -218,6 +212,15 @@ void __IoShutdown() { defParam = 0; } +u32 __IoGetFileHandleFromId(u32 id, u32 &outError) +{ + FileNode *f = kernelObjects.Get < FileNode > (id, outError); + if (!f) { + return -1; + } + return f->handle; +} + u32 sceIoAssign(const char *aliasname, const char *physname, const char *devname, u32 flag) { ERROR_LOG(HLE, "UNIMPL sceIoAssign(%s, %s, %s, %08x, ...)", aliasname, physname, devname, flag); @@ -644,12 +647,7 @@ u32 sceIoDevctl(const char *name, int cmd, u32 argAddr, int argLen, u32 outPtr, std::string data(Memory::GetCharPointer(argAddr), argLen); if (PSP_CoreParameter().printfEmuLog) { - printf("%s", data.c_str()); -#ifdef _WIN32 - OutputDebugString(data.c_str()); -#endif - // Also collect the debug output - emuDebugOutput += data; + host->SendDebugOutput(data.c_str()); } else { @@ -865,6 +863,7 @@ public: virtual void DoState(PointerWrap &p) { p.Do(name); + p.Do(index); // TODO: Is this the right way for it to wake up? int count = listing.size(); diff --git a/Core/HLE/sceIo.h b/Core/HLE/sceIo.h index 0f87cde613..2945a396c1 100644 --- a/Core/HLE/sceIo.h +++ b/Core/HLE/sceIo.h @@ -18,16 +18,17 @@ #pragma once #include + +#include "../System.h" #include "HLE.h" #include "sceKernel.h" void __IoInit(); void __IoDoState(PointerWrap &p); void __IoShutdown(); +u32 __IoGetFileHandleFromId(u32 id, u32 &outError); KernelObject *__KernelFileNodeObject(); KernelObject *__KernelDirListingObject(); void Register_IoFileMgrForUser(); void Register_StdioForUser(); - -const std::string &EmuDebugOutput(); diff --git a/Core/HLE/sceKernel.cpp b/Core/HLE/sceKernel.cpp index cafd9038a2..6de646ec49 100644 --- a/Core/HLE/sceKernel.cpp +++ b/Core/HLE/sceKernel.cpp @@ -80,7 +80,7 @@ void __KernelInit() return; } - SaveState::Init(); + __KernelTimeInit(); __InterruptsInit(); __KernelMemoryInit(); __KernelThreadingInit(); @@ -104,6 +104,7 @@ void __KernelInit() __ImposeInit(); __UsbInit(); __FontInit(); + SaveState::Init(); // Must be after IO, as it may create a directory // "Internal" PSP libraries __PPGeInit(); @@ -160,6 +161,7 @@ void __KernelDoState(PointerWrap &p) __KernelModuleDoState(p); __KernelMutexDoState(p); __KernelSemaDoState(p); + __KernelTimeDoState(p); __AudioDoState(p); __CtrlDoState(p); @@ -190,20 +192,18 @@ bool __KernelIsRunning() { void sceKernelExitGame() { INFO_LOG(HLE,"sceKernelExitGame"); - if (PSP_CoreParameter().headLess) - exit(0); - else + if (!PSP_CoreParameter().headLess) PanicAlert("Game exited"); + __KernelSwitchOffThread("game exited"); Core_Stop(); } void sceKernelExitGameWithStatus() { INFO_LOG(HLE,"sceKernelExitGameWithStatus"); - if (PSP_CoreParameter().headLess) - exit(0); - else + if (!PSP_CoreParameter().headLess) PanicAlert("Game exited (with status)"); + __KernelSwitchOffThread("game exited"); Core_Stop(); } @@ -247,23 +247,38 @@ void sceKernelGetGPI() // Don't even log these, they're spammy and we probably won't // 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) +int sceKernelDcacheInvalidateRange(u32 addr, int size) { - gpu->InvalidateCache(addr, size); + if (size > 0 && addr != 0) { + gpu->InvalidateCache(addr, size); + } + return 0; } -void sceKernelDcacheWritebackAll() +int sceKernelDcacheWritebackAll() { + // Some games seem to use this a lot, it doesn't make sense + // to zap the whole texture cache. + // gpu->InvalidateCache(0, -1); + return 0; } -void sceKernelDcacheWritebackRange(u32 addr, int size) +int sceKernelDcacheWritebackRange(u32 addr, int size) { + if (size > 0 && addr != 0) { + gpu->InvalidateCache(addr, size); + } + return 0; } -void sceKernelDcacheWritebackInvalidateRange(u32 addr, int size) +int sceKernelDcacheWritebackInvalidateRange(u32 addr, int size) { - gpu->InvalidateCache(addr, size); + if (size > 0 && addr != 0) { + gpu->InvalidateCache(addr, size); + } + return 0; } -void sceKernelDcacheWritebackInvalidateAll() +int sceKernelDcacheWritebackInvalidateAll() { gpu->InvalidateCache(0, -1); + return 0; } KernelObjectPool::KernelObjectPool() @@ -537,21 +552,6 @@ const HLEFunction ThreadManForUser[] = {0x94416130,WrapU_UUUU,"sceKernelGetThreadmanIdList"}, {0x57CF62DD,WrapU_U,"sceKernelGetThreadmanIdType"}, - {0x20fff560,sceKernelCreateVTimer,"sceKernelCreateVTimer"}, - {0x328F9E52,0,"sceKernelDeleteVTimer"}, - {0xc68d9437,sceKernelStartVTimer,"sceKernelStartVTimer"}, - {0xD0AEEE87,0,"sceKernelStopVTimer"}, - {0xD2D615EF,0,"sceKernelCancelVTimerHandler"}, - {0xB3A59970,0,"sceKernelGetVTimerBase"}, - {0xB7C18B77,0,"sceKernelGetVTimerBaseWide"}, - {0x034A921F,0,"sceKernelGetVTimerTime"}, - {0xC0B3FFD2,0,"sceKernelGetVTimerTimeWide"}, - {0x5F32BEAA,0,"sceKernelReferVTimerStatus"}, - {0x542AD630,0,"sceKernelSetVTimerTime"}, - {0xFB6425C3,0,"sceKernelSetVTimerTimeWide"}, - {0xd8b299ae,sceKernelSetVTimerHandler,"sceKernelSetVTimerHandler"}, - {0x53B00E9A,0,"sceKernelSetVTimerHandlerWide"}, - {0x82BC5777,sceKernelGetSystemTimeWide,"sceKernelGetSystemTimeWide"}, {0xdb738f35,sceKernelGetSystemTime,"sceKernelGetSystemTime"}, {0x369ed59d,sceKernelGetSystemTimeLow,"sceKernelGetSystemTimeLow"}, @@ -625,6 +625,21 @@ const HLEFunction ThreadManForUser[] = {0xA8AA591F,sceKernelCancelFpl,"sceKernelCancelFpl"}, {0xD8199E4C,sceKernelReferFplStatus,"sceKernelReferFplStatus"}, + {0x20fff560,WrapU_CU,"sceKernelCreateVTimer"}, + {0x328F9E52,WrapU_U,"sceKernelDeleteVTimer"}, + {0xc68d9437,WrapU_U,"sceKernelStartVTimer"}, + {0xD0AEEE87,WrapU_U,"sceKernelStopVTimer"}, + {0xD2D615EF,WrapU_U,"sceKernelCancelVTimerHandler"}, + {0xB3A59970,WrapU_UU,"sceKernelGetVTimerBase"}, + {0xB7C18B77,WrapU64_U,"sceKernelGetVTimerBaseWide"}, + {0x034A921F,WrapU_UU,"sceKernelGetVTimerTime"}, + {0xC0B3FFD2,WrapU64_U,"sceKernelGetVTimerTimeWide"}, + {0x5F32BEAA,WrapU_UU,"sceKernelReferVTimerStatus"}, + {0x542AD630,WrapU_UU,"sceKernelSetVTimerTime"}, + {0xFB6425C3,WrapU_UU64,"sceKernelSetVTimerTimeWide"}, + {0xd8b299ae,WrapU_UUUU,"sceKernelSetVTimerHandler"}, + {0x53B00E9A,WrapU_UU64UU,"sceKernelSetVTimerHandlerWide"}, + // Not sure if these should be hooked up. See below. {0x0E927AED, _sceKernelReturnFromTimerHandler, "_sceKernelReturnFromTimerHandler"}, {0x532A522E, _sceKernelExitThread,"_sceKernelExitThread"}, diff --git a/Core/HLE/sceKernel.h b/Core/HLE/sceKernel.h index 90835442d1..e958bc92fd 100644 --- a/Core/HLE/sceKernel.h +++ b/Core/HLE/sceKernel.h @@ -23,7 +23,9 @@ enum { - SCE_KERNEL_ERROR_OK = 0, + SCE_KERNEL_ERROR_OK = 0, + SCE_KERNEL_ERROR_OUT_OF_MEMORY = 0x80000022, + SCE_KERNEL_ERROR_INVALID_ID = 0x80000100, SCE_KERNEL_ERROR_INVALID_VALUE = 0x800001fe, SCE_KERNEL_ERROR_INVALID_ARGUMENT = 0x800001ff, SCE_KERNEL_ERROR_ERROR = 0x80020001, @@ -292,11 +294,11 @@ void sceKernelFindModuleByName(); void sceKernelSetGPO(); void sceKernelGetGPI(); -void sceKernelDcacheInvalidateRange(u32 addr, int size); -void sceKernelDcacheWritebackAll(); -void sceKernelDcacheWritebackRange(u32 addr, int size); -void sceKernelDcacheWritebackInvalidateRange(u32 addr, int size); -void sceKernelDcacheWritebackInvalidateAll(); +int sceKernelDcacheInvalidateRange(u32 addr, int size); +int sceKernelDcacheWritebackAll(); +int sceKernelDcacheWritebackRange(u32 addr, int size); +int sceKernelDcacheWritebackInvalidateRange(u32 addr, int size); +int sceKernelDcacheWritebackInvalidateAll(); void sceKernelGetThreadStackFreeSize(); void sceKernelIcacheInvalidateAll(); void sceKernelIcacheClearAll(); diff --git a/Core/HLE/sceKernelInterrupt.cpp b/Core/HLE/sceKernelInterrupt.cpp index 77e7e707a9..ddec34669c 100644 --- a/Core/HLE/sceKernelInterrupt.cpp +++ b/Core/HLE/sceKernelInterrupt.cpp @@ -547,24 +547,37 @@ void QueryIntrHandlerInfo() RETURN(0); } -// TODO: speedup u32 sceKernelMemset(u32 addr, u32 fillc, u32 n) { 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 + Memory::Memset(addr, c, n); + return addr; } u32 sceKernelMemcpy(u32 dst, u32 src, u32 size) { DEBUG_LOG(HLE, "sceKernelMemcpy(dest=%08x, src=%08x, size=%i)", dst, src, size); - if (Memory::IsValidAddress(dst) && Memory::IsValidAddress(src+size)) // a bit of bound checking. Wrong?? + // Technically should crash if these are invalid and size > 0... + if (Memory::IsValidAddress(dst) && Memory::IsValidAddress(src + size - 1)) { - Memory::Memcpy(dst, Memory::GetPointer(src), size); + u8 *dstp = Memory::GetPointer(dst); + u8 *srcp = Memory::GetPointer(src); + u32 size64 = size / 8; + u32 size8 = size % 8; + + // Try to handle overlapped copies with similar properties to hardware, just in case. + // Not that anyone ought to rely on it. + while (size64-- > 0) + { + *(u64 *) dstp = *(u64 *) srcp; + srcp += 8; + dstp += 8; + } + while (size8-- > 0) + *dstp++ = *srcp++; } - return 0; + return dst; } const HLEFunction Kernel_Library[] = diff --git a/Core/HLE/sceKernelInterrupt.h b/Core/HLE/sceKernelInterrupt.h index 6b60a7870f..f93bbf2ca4 100644 --- a/Core/HLE/sceKernelInterrupt.h +++ b/Core/HLE/sceKernelInterrupt.h @@ -71,10 +71,10 @@ struct PendingInterrupt { PendingInterrupt(int intr_, int subintr_, int arg_) : intr(intr_), subintr(subintr_), hasArg(true), arg(arg_) {} - int arg; + u32 intr; + u32 subintr; bool hasArg; - int intr; - int subintr; + int arg; }; class SubIntrHandler diff --git a/Core/HLE/sceKernelMemory.cpp b/Core/HLE/sceKernelMemory.cpp index b2669893ae..bc0e742188 100644 --- a/Core/HLE/sceKernelMemory.cpp +++ b/Core/HLE/sceKernelMemory.cpp @@ -55,6 +55,12 @@ struct NativeFPL //FPL - Fixed Length Dynamic Memory Pool - every item has the same length struct FPL : public KernelObject { + FPL() : blocks(NULL) {} + ~FPL() { + if (blocks != NULL) { + delete [] blocks; + } + } const char *GetName() {return nf.name;} const char *GetTypeName() {return "FPL";} static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_FPLID; } @@ -86,6 +92,8 @@ struct FPL : public KernelObject virtual void DoState(PointerWrap &p) { p.Do(nf); + if (p.mode == p.MODE_READ) + blocks = new bool[nf.numBlocks]; p.DoArray(blocks, nf.numBlocks); p.Do(address); p.DoMarker("FPL"); @@ -398,7 +406,7 @@ public: virtual void DoState(PointerWrap &p) { p.Do(address); - p.Do(name); + p.DoArray(name, sizeof(name)); p.DoMarker("PMB"); } @@ -587,6 +595,53 @@ void sceKernelSetCompiledSdkVersion600_602(int sdkVersion) return; } +void sceKernelSetCompiledSdkVersion500_505(int sdkVersion) +{ + int sdkMainVersion = sdkVersion & 0xFFFF0000; + if(sdkMainVersion == 0x5000000 + || sdkMainVersion == 0x5050000) + { + sdkVersion_ = sdkVersion; + flags_ |= SCE_KERNEL_HASCOMPILEDSDKVERSION; + } + else + { + ERROR_LOG(HLE,"sceKernelSetCompiledSdkVersion500_505 unknown SDK : %x\n",sdkVersion); + } + return; +} + +void sceKernelSetCompiledSdkVersion401_402(int sdkVersion) +{ + int sdkMainVersion = sdkVersion & 0xFFFF0000; + if(sdkMainVersion == 0x4010000 + || sdkMainVersion == 0x4020000) + { + sdkVersion_ = sdkVersion; + flags_ |= SCE_KERNEL_HASCOMPILEDSDKVERSION; + } + else + { + ERROR_LOG(HLE,"sceKernelSetCompiledSdkVersion401_402 unknown SDK : %x\n",sdkVersion); + } + return; +} + +void sceKernelSetCompiledSdkVersion507(int sdkVersion) +{ + int sdkMainVersion = sdkVersion & 0xFFFF0000; + if(sdkMainVersion == 0x5070000) + { + sdkVersion_ = sdkVersion; + flags_ |= SCE_KERNEL_HASCOMPILEDSDKVERSION; + } + else + { + ERROR_LOG(HLE,"sceKernelSetCompiledSdkVersion507 unknown SDK : %x\n",sdkVersion); + } + return; +} + void sceKernelSetCompiledSdkVersion603_605(int sdkVersion) { int sdkMainVersion = sdkVersion & 0xFFFF0000; @@ -877,7 +932,10 @@ const HLEFunction SysMemUserForUser[] = { {0x342061E5,&WrapV_I,"sceKernelSetCompiledSdkVersion370"}, {0x315AD3A0,&WrapV_I,"sceKernelSetCompiledSdkVersion380_390"}, {0xEBD5C3E6,&WrapV_I,"sceKernelSetCompiledSdkVersion395"}, + {0x057E7380,&WrapV_I,"sceKernelSetCompiledSdkVersion401_402"}, {0xf77d77cb,&WrapV_I,"sceKernelSetCompilerVersion"}, + {0x91de343c,&WrapV_I,"sceKernelSetCompiledSdkVersion500_505"}, + {0x7893f79a,&WrapV_I,"sceKernelSetCompiledSdkVersion507"}, {0x35669d4c,&WrapV_I,"sceKernelSetCompiledSdkVersion600_602"}, //?? {0x1b4217bc,&WrapV_I,"sceKernelSetCompiledSdkVersion603_605"}, {0x358ca1bb,&WrapV_I,"sceKernelSetCompiledSdkVersion606"}, diff --git a/Core/HLE/sceKernelModule.cpp b/Core/HLE/sceKernelModule.cpp index 8f35526859..c4895299d2 100644 --- a/Core/HLE/sceKernelModule.cpp +++ b/Core/HLE/sceKernelModule.cpp @@ -39,6 +39,7 @@ #include "sceKernelModule.h" #include "sceKernelThread.h" #include "sceKernelMemory.h" +#include "sceIo.h" enum { PSP_THREAD_ATTR_USER = 0x80000000 @@ -100,7 +101,7 @@ struct NativeModule { class Module : public KernelObject { public: - Module() : memoryBlockAddr(0) {} + Module() : memoryBlockAddr(0), isFake(false) {} ~Module() { if (memoryBlockAddr) { userMemory.Free(memoryBlockAddr); @@ -111,7 +112,8 @@ public: void GetQuickInfo(char *ptr, int size) { // ignore size - sprintf(ptr, "name=%s gp=%08x entry=%08x", + sprintf(ptr, "%sname=%s gp=%08x entry=%08x", + isFake ? "faked " : "", nm.name, nm.gp_value, nm.entry_addr); @@ -129,6 +131,7 @@ public: NativeModule nm; u32 memoryBlockAddr; + bool isFake; }; KernelObject *__KernelModuleObject() @@ -212,6 +215,10 @@ Module *__KernelLoadELFFromPtr(const u8 *ptr, u32 loadAddress, std::string *erro kernelObjects.Create(module); u8 *newptr = 0; + if (*(u32*)ptr == 0x4543537e) { // "~SCE" + INFO_LOG(HLE, "~SCE module, skipping header"); + ptr += *(u32*)(ptr + 4); + } if (*(u32*)ptr == 0x5053507e) { // "~PSP" // Decrypt module! YAY! @@ -225,20 +232,23 @@ Module *__KernelLoadELFFromPtr(const u8 *ptr, u32 loadAddress, std::string *erro } newptr = new u8[head->elf_size + head->psp_size]; ptr = newptr; - pspDecryptPRX(in, (u8*)ptr, head->psp_size); + int ret = pspDecryptPRX(in, (u8*)ptr, head->psp_size); + if (ret == MISSING_KEY) + { + *error_string = "Missing key"; + delete [] newptr; + module->isFake = true; + strncpy(module->nm.name, head->modname, 28); + module->nm.entry_addr = -1; + module->nm.gp_value = -1; + return module; + } + else if (ret <= 0) + { + ERROR_LOG(HLE, "Failed decrypting PRX! That's not normal!\n"); + } } - if (*(u32*)ptr == 0x4543537e) { // "~SCE" - ERROR_LOG(HLE, "Wrong magic number %08x (~SCE, kernel module?)",*(u32*)ptr); - *error_string = "Kernel module?"; - if (newptr) - { - delete [] newptr; - } - kernelObjects.Destroy(module->GetUID()); - return 0; - } - if (*(u32*)ptr != 0x464c457f) { ERROR_LOG(HLE, "Wrong magic number %08x",*(u32*)ptr); @@ -297,6 +307,9 @@ Module *__KernelLoadELFFromPtr(const u8 *ptr, u32 loadAddress, std::string *erro else modinfo = (PspModuleInfo *)Memory::GetPointer(reader.GetSegmentVaddr(0) + (reader.GetSegmentPaddr(0) & 0x7FFFFFFF) - reader.GetSegmentOffset(0)); + module->nm.gp_value = modinfo->gp; + strncpy(module->nm.name, modinfo->name, 28); + // 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 // directly access hardware or for other reasons. @@ -307,8 +320,9 @@ Module *__KernelLoadELFFromPtr(const u8 *ptr, u32 loadAddress, std::string *erro { delete [] newptr; } - kernelObjects.Destroy(module->GetUID()); - return 0; + module->isFake = true; + module->nm.entry_addr = -1; + return module; } } @@ -337,9 +351,6 @@ Module *__KernelLoadELFFromPtr(const u8 *ptr, u32 loadAddress, std::string *erro } } - module->nm.gp_value = modinfo->gp; - strncpy(module->nm.name, modinfo->name, 28); - INFO_LOG(LOADER,"Module %s: %08x %08x %08x", modinfo->name, modinfo->gp, modinfo->libent,modinfo->libstub); struct PspLibStubEntry @@ -688,9 +699,6 @@ u32 sceKernelLoadModule(const char *name, u32 flags) 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)", - moduleId,argsize,argAddr,returnValueAddr,optionAddr); - // Dunno what these three defaults should be... u32 priority = 0x20; u32 stacksize = 0x40000; @@ -707,12 +715,15 @@ void sceKernelStartModule(u32 moduleId, u32 argsize, u32 argAddr, u32 returnValu u32 error; Module *module = kernelObjects.Get(moduleId, error); if (!module) { - // TODO: Try not to lie so much. - /* RETURN(error); return; - */ + } else if (module->isFake) { + INFO_LOG(HLE,"sceKernelStartModule(%d,asize=%08x,aptr=%08x,retptr=%08x,%08x): faked (undecryptable module)", + moduleId,argsize,argAddr,returnValueAddr,optionAddr); } else { + ERROR_LOG(HLE,"UNIMPL sceKernelStartModule(%d,asize=%08x,aptr=%08x,retptr=%08x,%08x)", + moduleId,argsize,argAddr,returnValueAddr,optionAddr); + u32 entryAddr = module->nm.entry_addr; if (entryAddr == -1) { entryAddr = module->nm.module_start_func; @@ -785,10 +796,44 @@ void sceKernelFindModuleByName() RETURN(1); } -u32 sceKernelLoadModuleByID(u32 id, u32 flags, u32 lmoptionPtr) { - ERROR_LOG(HLE,"UNIMPL %008x=sceKernelLoadModuleById(%08x, %08x)",id,id,lmoptionPtr); - // Apparenty, ID is a sceIo File UID. So this shouldn't be too hard when needed. - return id; +u32 sceKernelLoadModuleByID(u32 id, u32 flags, u32 lmoptionPtr) +{ + u32 error; + u32 handle = __IoGetFileHandleFromId(id, error); + if (handle < 0) { + ERROR_LOG(HLE,"sceKernelLoadModuleByID(%08x, %08x, %08x): could not open file id",id,flags,lmoptionPtr); + return error; + } + SceKernelLMOption *lmoption = 0; + if (lmoptionPtr) { + lmoption = (SceKernelLMOption *)Memory::GetPointer(lmoptionPtr); + } + u32 pos = pspFileSystem.SeekFile(handle, 0, FILEMOVE_CURRENT); + u32 size = pspFileSystem.SeekFile(handle, 0, FILEMOVE_END); + std::string error_string; + pspFileSystem.SeekFile(handle, pos, FILEMOVE_BEGIN); + Module *module = 0; + u8 *temp = new u8[size]; + pspFileSystem.ReadFile(handle, temp, size); + module = __KernelLoadELFFromPtr(temp, 0, &error_string); + delete [] temp; + + if (!module) { + // Module was blacklisted or couldn't be decrypted, which means it's a kernel module we don't want to run. + // Let's just act as if it worked. + NOTICE_LOG(LOADER, "Module %d is blacklisted or undecryptable - we lie about success", id); + return 1; + } + + if (lmoption) { + INFO_LOG(HLE,"%i=sceKernelLoadModuleByID(%d,flag=%08x,%08x,%08x,%08x,position = %08x)", + module->GetUID(),id,flags, + lmoption->size,lmoption->mpidtext,lmoption->mpiddata,lmoption->position); + } else { + INFO_LOG(HLE,"%i=sceKernelLoadModuleByID(%d,flag=%08x,(...))", module->GetUID(), id, flags); + } + + return module->GetUID(); } u32 sceKernelLoadModuleDNAS(const char *name, u32 flags) diff --git a/Core/HLE/sceKernelMutex.cpp b/Core/HLE/sceKernelMutex.cpp index a591e4ce6d..4469dc0601 100644 --- a/Core/HLE/sceKernelMutex.cpp +++ b/Core/HLE/sceKernelMutex.cpp @@ -456,7 +456,10 @@ int sceKernelLockMutex(SceUID id, int count, u32 timeoutPtr) return error; else { - mutex->waitingThreads.push_back(__KernelGetCurThread()); + SceUID threadID = __KernelGetCurThread(); + // May be in a tight loop timing out (where we don't remove from waitingThreads yet), don't want to add duplicates. + if (std::find(mutex->waitingThreads.begin(), mutex->waitingThreads.end(), threadID) == mutex->waitingThreads.end()) + mutex->waitingThreads.push_back(threadID); __KernelWaitMutex(mutex, timeoutPtr); __KernelWaitCurThread(WAITTYPE_MUTEX, id, count, timeoutPtr, false); @@ -481,7 +484,10 @@ int sceKernelLockMutexCB(SceUID id, int count, u32 timeoutPtr) return error; else { - mutex->waitingThreads.push_back(__KernelGetCurThread()); + SceUID threadID = __KernelGetCurThread(); + // May be in a tight loop timing out (where we don't remove from waitingThreads yet), don't want to add duplicates. + if (std::find(mutex->waitingThreads.begin(), mutex->waitingThreads.end(), threadID) == mutex->waitingThreads.end()) + mutex->waitingThreads.push_back(threadID); __KernelWaitMutex(mutex, timeoutPtr); __KernelWaitCurThread(WAITTYPE_MUTEX, id, count, timeoutPtr, true); @@ -813,7 +819,10 @@ int sceKernelLockLwMutex(u32 workareaPtr, int count, u32 timeoutPtr) LwMutex *mutex = kernelObjects.Get(workarea.uid, error); if (mutex) { - mutex->waitingThreads.push_back(__KernelGetCurThread()); + SceUID threadID = __KernelGetCurThread(); + // May be in a tight loop timing out (where we don't remove from waitingThreads yet), don't want to add duplicates. + if (std::find(mutex->waitingThreads.begin(), mutex->waitingThreads.end(), threadID) == mutex->waitingThreads.end()) + mutex->waitingThreads.push_back(threadID); __KernelWaitLwMutex(mutex, timeoutPtr); __KernelWaitCurThread(WAITTYPE_LWMUTEX, workarea.uid, count, timeoutPtr, false); @@ -846,7 +855,10 @@ int sceKernelLockLwMutexCB(u32 workareaPtr, int count, u32 timeoutPtr) LwMutex *mutex = kernelObjects.Get(workarea.uid, error); if (mutex) { - mutex->waitingThreads.push_back(__KernelGetCurThread()); + SceUID threadID = __KernelGetCurThread(); + // May be in a tight loop timing out (where we don't remove from waitingThreads yet), don't want to add duplicates. + if (std::find(mutex->waitingThreads.begin(), mutex->waitingThreads.end(), threadID) == mutex->waitingThreads.end()) + mutex->waitingThreads.push_back(threadID); __KernelWaitLwMutex(mutex, timeoutPtr); __KernelWaitCurThread(WAITTYPE_LWMUTEX, workarea.uid, count, timeoutPtr, true); diff --git a/Core/HLE/sceKernelSemaphore.cpp b/Core/HLE/sceKernelSemaphore.cpp index f9e5cd4e0b..8fbeb4e674 100644 --- a/Core/HLE/sceKernelSemaphore.cpp +++ b/Core/HLE/sceKernelSemaphore.cpp @@ -360,7 +360,11 @@ int __KernelWaitSema(SceUID id, int wantedCount, u32 timeoutPtr, const char *bad else { s->ns.numWaitThreads++; - s->waitingThreads.push_back(__KernelGetCurThread()); + + SceUID threadID = __KernelGetCurThread(); + // May be in a tight loop timing out (where we don't remove from waitingThreads yet), don't want to add duplicates. + if (std::find(s->waitingThreads.begin(), s->waitingThreads.end(), threadID) == s->waitingThreads.end()) + s->waitingThreads.push_back(threadID); __KernelSetSemaTimeout(s, timeoutPtr); __KernelWaitCurThread(WAITTYPE_SEMA, id, wantedCount, timeoutPtr, processCallbacks); } diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index b247736056..6811048dfd 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -75,11 +75,6 @@ const char *waitTypeStrings[] = { "Ctrl", }; -struct SceKernelSysClock { - u32 low; - u32 hi; -}; - struct NativeCallback { SceUInt size; @@ -371,11 +366,19 @@ public: // Fill the stack. Memory::Memset(stackBlock, 0xFF, stackSize); context.r[MIPS_REG_SP] = stackBlock + stackSize; - nt.initialStack = context.r[MIPS_REG_SP]; + nt.initialStack = stackBlock; nt.stackSize = stackSize; // What's this 512? - context.r[MIPS_REG_K0] = context.r[MIPS_REG_SP] - 512; + context.r[MIPS_REG_K0] = context.r[MIPS_REG_SP] - 256; context.r[MIPS_REG_SP] -= 512; + u32 k0 = context.r[MIPS_REG_K0]; + Memory::Memset(k0, 0, 0x100); + Memory::Write_U32(nt.initialStack, k0 + 0xc0); + Memory::Write_U32(GetUID(), k0 + 0xca); + Memory::Write_U32(0xffffffff, k0 + 0xf8); + Memory::Write_U32(0xffffffff, k0 + 0xfc); + + Memory::Write_U32(GetUID(), nt.initialStack); return true; } @@ -1145,9 +1148,15 @@ 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); - RETURN(diff); //Blatant lie + if (t) { + u32 diff = abs((long)((s64)t->stackBlock - (s64)currentMIPS->r[MIPS_REG_SP])); + WARN_LOG(HLE, "%i=sceKernelCheckThreadStack()", diff); + RETURN(diff); + } else { + // WTF? + ERROR_LOG(HLE, "sceKernelCheckThreadStack() - not on thread"); + RETURN(-1); + } } void ThreadContext::reset() @@ -1225,7 +1234,7 @@ Thread *__KernelCreateThread(SceUID &id, SceUID moduleId, const char *name, u32 t->nt.numInterruptPreempts = 0; t->nt.numReleases = 0; t->nt.numThreadPreempts = 0; - t->nt.runForClocks.low = 0; + t->nt.runForClocks.lo = 0; t->nt.runForClocks.hi = 0; t->nt.wakeupCount = 0; if (moduleId) diff --git a/Core/HLE/sceKernelThread.h b/Core/HLE/sceKernelThread.h index a491506bf0..7272b8d90d 100644 --- a/Core/HLE/sceKernelThread.h +++ b/Core/HLE/sceKernelThread.h @@ -54,6 +54,11 @@ void sceKernelGetThreadExitStatus(); u32 sceKernelGetThreadmanIdType(u32); u32 sceKernelGetThreadmanIdList(u32 type, u32 readBufPtr, u32 readBufSize, u32 idCountPtr); +struct SceKernelSysClock { + u32 lo; + u32 hi; +}; + enum WaitType //probably not the real values { @@ -79,19 +84,19 @@ enum WaitType //probably not the real values struct ThreadContext { - void reset(); - u32 r[32]; - float f[32]; - float v[128]; - u32 vfpuCtrl[16]; + void reset(); + u32 r[32]; + float f[32]; + float v[128]; + u32 vfpuCtrl[16]; - u32 hi; - u32 lo; - u32 pc; - u32 fpcond; + u32 hi; + u32 lo; + u32 pc; + u32 fpcond; - u32 fcr0; - u32 fcr31; + u32 fcr0; + u32 fcr31; }; // Internal API, used by implementations of kernel functions @@ -126,15 +131,15 @@ void __KernelReSchedule(bool doCallbacks, const char *reason); // Registered callback types enum RegisteredCallbackType { - THREAD_CALLBACK_UMD = 0, - THREAD_CALLBACK_IO = 1, - THREAD_CALLBACK_MEMORYSTICK = 2, - THREAD_CALLBACK_MEMORYSTICK_FAT = 3, - THREAD_CALLBACK_POWER = 4, - THREAD_CALLBACK_EXIT = 5, - THREAD_CALLBACK_USER_DEFINED = 6, - THREAD_CALLBACK_SIZE = 7, - THREAD_CALLBACK_NUM_TYPES = 8, + THREAD_CALLBACK_UMD = 0, + THREAD_CALLBACK_IO = 1, + THREAD_CALLBACK_MEMORYSTICK = 2, + THREAD_CALLBACK_MEMORYSTICK_FAT = 3, + THREAD_CALLBACK_POWER = 4, + THREAD_CALLBACK_EXIT = 5, + THREAD_CALLBACK_USER_DEFINED = 6, + THREAD_CALLBACK_SIZE = 7, + THREAD_CALLBACK_NUM_TYPES = 8, }; // These operate on the current thread @@ -214,6 +219,7 @@ struct MipsCall { void DoState(PointerWrap &p); }; + enum ThreadStatus { THREADSTATUS_RUNNING = 1, diff --git a/Core/HLE/sceKernelTime.cpp b/Core/HLE/sceKernelTime.cpp index e7c6be0c4a..e5cb09a9f5 100644 --- a/Core/HLE/sceKernelTime.cpp +++ b/Core/HLE/sceKernelTime.cpp @@ -30,10 +30,28 @@ #include "../CoreTiming.h" +////////////////////////////////////////////////////////////////////////// +// State +////////////////////////////////////////////////////////////////////////// + +// The time when the game started. +time_t start_time; + ////////////////////////////////////////////////////////////////////////// // Other clock stuff ////////////////////////////////////////////////////////////////////////// +void __KernelTimeInit() +{ + time(&start_time); +} + +void __KernelTimeDoState(PointerWrap &p) +{ + p.Do(start_time); + p.DoMarker("sceKernelTime"); +} + struct SceKernelSysClock { u32 lo; @@ -107,19 +125,23 @@ u32 sceKernelUSec2SysClockWide(u32 usec) u32 sceKernelLibcClock() { - u32 retVal = clock()*1000; // TODO: This can't be right - DEBUG_LOG(HLE,"%i = sceKernelLibcClock",retVal); + u32 retVal = (u32) (CoreTiming::GetTicks() / CoreTiming::GetClockFrequencyMHz()); + DEBUG_LOG(HLE, "%i = sceKernelLibcClock", retVal); return retVal; } -void sceKernelLibcTime() +u32 sceKernelLibcTime(u32 outPtr) { - time_t *t = 0; - if (PARAM(0)) - t = (time_t*)Memory::GetPointer(PARAM(0)); - u32 retVal = (u32)time(t); - DEBUG_LOG(HLE,"%i = sceKernelLibcTime()",retVal); - RETURN(retVal); + u32 t = (u32) start_time + (u32) (CoreTiming::GetTicks() / CPU_HZ); + + DEBUG_LOG(HLE, "%i = sceKernelLibcTime(%08X)", t, outPtr); + + if (Memory::IsValidAddress(outPtr)) + Memory::Write_U32(t, outPtr); + else if (outPtr != 0) + return 0; + + return t; } void sceKernelLibcGettimeofday() diff --git a/Core/HLE/sceKernelTime.h b/Core/HLE/sceKernelTime.h index bf53e76b5f..4ab8e59ec6 100644 --- a/Core/HLE/sceKernelTime.h +++ b/Core/HLE/sceKernelTime.h @@ -18,7 +18,7 @@ #pragma once void sceKernelLibcGettimeofday(); -void sceKernelLibcTime(); +u32 sceKernelLibcTime(u32 outPtr); void sceKernelUSec2SysClock(); void sceKernelGetSystemTime(); void sceKernelGetSystemTimeLow(); @@ -27,3 +27,6 @@ void sceKernelSysClock2USec(); void sceKernelSysClock2USecWide(); u32 sceKernelUSec2SysClockWide(u32 usec); u32 sceKernelLibcClock(); + +void __KernelTimeInit(); +void __KernelTimeDoState(PointerWrap &p); diff --git a/Core/HLE/sceKernelVTimer.cpp b/Core/HLE/sceKernelVTimer.cpp index 35174f34f1..22a4dd8884 100644 --- a/Core/HLE/sceKernelVTimer.cpp +++ b/Core/HLE/sceKernelVTimer.cpp @@ -16,77 +16,224 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include "sceKernel.h" +#include "sceKernelThread.h" #include "sceKernelVTimer.h" #include "HLE.h" -////////////////////////////////////////////////////////////////////////// -// VTIMER -////////////////////////////////////////////////////////////////////////// +// Using ERROR_LOG liberally when this is in development. When done, +// should be changed to DEBUG_LOG wherever applicable. -struct VTimer : public KernelObject -{ - const char *GetName() {return name;} +struct NativeVTimer { + SceSize size; + char name[KERNELOBJECT_MAX_NAME_LENGTH+1]; + int running; + SceKernelSysClock basetime; + SceKernelSysClock curtime; + SceKernelSysClock scheduletime; + u32 handlerAddr; + u32 argument; +}; + +struct VTimer : public KernelObject { + const char *GetName() {return nvt.name;} const char *GetTypeName() {return "VTimer";} 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); + virtual void DoState(PointerWrap &p) { + p.Do(nvt); p.DoMarker("VTimer"); } - SceSize size; - char name[KERNELOBJECT_MAX_NAME_LENGTH+1]; - u64 startTime; - bool running; - u32 handler; - u64 handlerTime; - u32 argument; + NativeVTimer nvt; }; -KernelObject *__KernelVTimerObject() -{ +KernelObject *__KernelVTimerObject() { return new VTimer; } -void sceKernelCreateVTimer() -{ - DEBUG_LOG(HLE,"sceKernelCreateVTimer"); - const char *name = Memory::GetCharPointer(PARAM(0)); +u32 sceKernelCreateVTimer(const char *name, u32 optParamAddr) { + ERROR_LOG(HLE,"FAKE sceKernelCreateVTimer(%s, %08x)", name, optParamAddr); VTimer *vt = new VTimer(); SceUID uid = kernelObjects.Create(vt); - strncpy(vt->name, name, 32); - vt->running = true; - vt->startTime = 0; //TODO fix - RETURN(uid); //TODO: return timer ID + memset(&vt->nvt, 0, sizeof(vt->nvt)); + if (name) + strncpy(vt->nvt.name, name, 32); + vt->nvt.running = 1; + return uid; //TODO: return timer ID } -void sceKernelStartVTimer() -{ - int timerID = PARAM(0); - DEBUG_LOG(HLE,"sceKernelStartVTimer(%i)", timerID); +u32 sceKernelDeleteVTimer(u32 uid) { + ERROR_LOG(HLE,"FAKE sceKernelDeleteVTimer(%i)", uid); - RETURN(0); //ok; 1=alreadyrunning + u32 error; + VTimer *vt = kernelObjects.Get(uid, error); + if (!vt) { + return error; + } + // TODO: Deschedule events here. Might share code with Stop. + + return kernelObjects.Destroy(uid); } -void sceKernelSetVTimerHandler() -{ - DEBUG_LOG(HLE,"sceKernelSetVTimerHandler"); +u32 sceKernelStartVTimer(u32 uid) { + ERROR_LOG(HLE,"FAKE sceKernelStartVTimer(%i)", uid); + u32 error; + VTimer *vt = kernelObjects.Get(uid, error); + if (!vt) { + return error; + } + if (vt->nvt.running) { + // Already running + return 1; + } else { + vt->nvt.running = 1; + // TODO: Schedule events etc. + return 0; + } +} + +u32 sceKernelStopVTimer(u32 uid) { + ERROR_LOG(HLE,"FAKE sceKernelStartVTimer(%i)", uid); + u32 error; + VTimer *vt = kernelObjects.Get(uid, error); + if (!vt) { + return error; + } + if (vt->nvt.running) { + // Already running + return 0; + } else { + vt->nvt.running = 0; + // TODO: Deschedule events etc. + return 1; + } +} + +u32 sceKernelSetVTimerHandler(u32 uid, u32 scheduleAddr, u32 handlerFuncAddr, u32 commonAddr) { + ERROR_LOG(HLE,"UNIMPL sceKernelSetVTimerHandler(%i, %08x, %08x, %08x)", + uid, scheduleAddr, handlerFuncAddr, commonAddr); + u32 error; + VTimer *vt = kernelObjects.Get(uid, error); + if (!vt) { + return error; + } + // TODO + return 0; +} + +u32 sceKernelSetVTimerHandlerWide(u32 uid, u64 schedule, u32 handlerFuncAddr, u32 commonAddr) { + ERROR_LOG(HLE,"UNIMPL sceKernelSetVTimerHandlerWide(%i, %llu, %08x, %08x)", + uid, schedule, handlerFuncAddr, commonAddr); + u32 error; + VTimer *vt = kernelObjects.Get(uid, error); + if (!vt) { + return error; + } + // TODO + return 0; +} + +u32 sceKernelCancelVTimerHandler(u32 uid) { + ERROR_LOG(HLE,"UNIMPL sceKernelCancelVTimerHandler(%i)", uid); + u32 error; + VTimer *vt = kernelObjects.Get(uid, error); + if (!vt) { + return error; + } + // TODO + return 0; +} + +u32 sceKernelReferVTimerStatus(u32 uid, u32 statusAddr) { + ERROR_LOG(HLE,"sceKernelReferVTimerStatus(%i, %08x)", uid, statusAddr); + u32 error; + VTimer *vt = kernelObjects.Get(uid, error); + if (!vt) { + return error; + } + // TODO: possibly update time values here? + Memory::WriteStruct(statusAddr, &vt->nvt); + return 0; +} + +u32 sceKernelGetVTimerBase(u32 uid, u32 baseClockAddr) { + ERROR_LOG(HLE,"sceKernelGetVTimerBase(%i, %08x)", uid, baseClockAddr); + u32 error; + VTimer *vt = kernelObjects.Get(uid, error); + if (!vt) { + return error; + } + Memory::WriteStruct(baseClockAddr, &vt->nvt.basetime); + return 0; +} + +u64 sceKernelGetVTimerBaseWide(u32 uid) { + ERROR_LOG(HLE,"sceKernelGetVTimerWide(%i)", uid); + u32 error; + VTimer *vt = kernelObjects.Get(uid, error); + if (!vt) { + return error; + } + // TODO: probably update the timer somehow? + u64 t = vt->nvt.curtime.lo; + t |= (u64)(vt->nvt.curtime.hi) << 32; + return t; +} + +u32 sceKernelGetVTimerTime(u32 uid, u32 timeClockAddr) { + ERROR_LOG(HLE,"sceKernelGetVTimerTime(%i, %08x)", uid, timeClockAddr); + u32 error; + VTimer *vt = kernelObjects.Get(uid, error); + if (!vt) { + return error; + } + // TODO: probably update the timer somehow? + Memory::WriteStruct(timeClockAddr, &vt->nvt.curtime); + return 0; +} + +u64 sceKernelGetVTimerTimeWide(u32 uid) { + ERROR_LOG(HLE,"sceKernelGetVTimerTimeWide(%i)", uid); + u32 error; + VTimer *vt = kernelObjects.Get(uid, error); + if (!vt) { + return error; + } + // TODO: probably update the timer somehow? + u64 t = vt->nvt.curtime.lo; + t |= (u64)(vt->nvt.curtime.hi) << 32; + return t; +} + +u32 sceKernelSetVTimerTime(u32 uid, u32 timeClockAddr) { + ERROR_LOG(HLE,"sceKernelSetVTimerTime(%i, %08x)", uid, timeClockAddr); + u32 error; + VTimer *vt = kernelObjects.Get(uid, error); + if (!vt) { + return error; + } + Memory::ReadStruct(timeClockAddr, &vt->nvt.curtime); + return 0; +} + +u32 sceKernelSetVTimerTimeWide(u32 uid, u64 timeClock) { + ERROR_LOG(HLE,"sceKernelSetVTimerTime(%i, %llu)", uid, timeClock); + u32 error; + VTimer *vt = kernelObjects.Get(uid, error); + if (!vt) { + return error; + } + vt->nvt.curtime.lo = timeClock & 0xFFFFFFFF; + vt->nvt.curtime.hi = timeClock >> 32; + return 0; } // Not sure why this is exposed... void _sceKernelReturnFromTimerHandler() { - DEBUG_LOG(HLE,"_sceKernelReturnFromTimerHandler"); - + ERROR_LOG(HLE,"_sceKernelReturnFromTimerHandler - should not be called!"); } diff --git a/Core/HLE/sceKernelVTimer.h b/Core/HLE/sceKernelVTimer.h index 3597dfeb18..9cde7913e5 100644 --- a/Core/HLE/sceKernelVTimer.h +++ b/Core/HLE/sceKernelVTimer.h @@ -17,9 +17,20 @@ #pragma once -void sceKernelCreateVTimer(); -void sceKernelStartVTimer(); -void sceKernelSetVTimerHandler(); +u32 sceKernelCreateVTimer(const char *name, u32 optParamAddr); +u32 sceKernelDeleteVTimer(u32 uid); +u32 sceKernelStartVTimer(u32 uid); +u32 sceKernelStopVTimer(u32 uid); +u32 sceKernelSetVTimerHandler(u32 uid, u32 scheduleAddr, u32 handlerFuncAddr, u32 commonAddr); +u32 sceKernelSetVTimerHandlerWide(u32 uid, u64 schedule, u32 handlerFuncAddr, u32 commonAddr); +u32 sceKernelCancelVTimerHandler(u32 uid); +u32 sceKernelReferVTimerStatus(u32 uid, u32 statusAddr); +u32 sceKernelGetVTimerBase(u32 uid, u32 baseClockAddr); //SceKernelSysClock +u64 sceKernelGetVTimerBaseWide(u32 uid); +u32 sceKernelGetVTimerTime(u32 uid, u32 timeClockAddr); +u64 sceKernelGetVTimerTimeWide(u32 uid); +u32 sceKernelSetVTimerTime(u32 uid, u32 timeClockAddr); +u32 sceKernelSetVTimerTimeWide(u32 uid, u64 timeClock); // TODO void _sceKernelReturnFromTimerHandler(); diff --git a/Core/HLE/scePower.cpp b/Core/HLE/scePower.cpp index f486c064f9..13fbcaf85a 100644 --- a/Core/HLE/scePower.cpp +++ b/Core/HLE/scePower.cpp @@ -181,7 +181,7 @@ u32 scePowerSetBusClockFrequency(u32 busfreq) { u32 scePowerGetCpuClockFrequencyInt() { int freq = CoreTiming::GetClockFrequencyMHz(); - INFO_LOG(HLE,"%i=scePowerGetCpuClockFrequencyInt()", freq); + DEBUG_LOG(HLE,"%i=scePowerGetCpuClockFrequencyInt()", freq); return freq; } diff --git a/Core/HLE/sceSas.cpp b/Core/HLE/sceSas.cpp index 45693a80e5..b802088edb 100644 --- a/Core/HLE/sceSas.cpp +++ b/Core/HLE/sceSas.cpp @@ -226,17 +226,19 @@ u32 sceSasSetKeyOn(u32 core, int voiceNum) // sceSasSetKeyOff can be used to start sounds, that just sound during the Release phase! u32 sceSasSetKeyOff(u32 core, int voiceNum) { - DEBUG_LOG(HLE,"0=sceSasSetKeyOff(core=%08x, voiceNum=%i)", core, voiceNum); - - if (voiceNum >= PSP_SAS_VOICES_MAX || voiceNum < 0) - { + if (voiceNum == -1) { + // TODO: Some games (like Every Extend Extra) deliberately pass voiceNum = -1. Does that mean all voices? for now let's ignore. + DEBUG_LOG(HLE,"sceSasSetKeyOff(core=%08x, voiceNum=%i) - voiceNum = -1???", core, voiceNum); + return 0; + } else if (voiceNum < 0 || voiceNum >= PSP_SAS_VOICES_MAX) { WARN_LOG(HLE, "%s: invalid voicenum %d", __FUNCTION__, voiceNum); return ERROR_SAS_INVALID_VOICE; + } else { + DEBUG_LOG(HLE,"0=sceSasSetKeyOff(core=%08x, voiceNum=%i)", core, voiceNum); + SasVoice &v = sas->voices[voiceNum]; + v.KeyOff(); + return 0; } - - SasVoice &v = sas->voices[voiceNum]; - v.KeyOff(); - return 0; } u32 sceSasSetNoise(u32 core, int voiceNum, int freq) diff --git a/Core/HLE/sceUtility.cpp b/Core/HLE/sceUtility.cpp index c5dbc84934..bb727f9989 100644 --- a/Core/HLE/sceUtility.cpp +++ b/Core/HLE/sceUtility.cpp @@ -88,20 +88,32 @@ int sceUtilitySavedataUpdate(int animSpeed) #define PSP_AV_MODULE_AAC 6 #define PSP_AV_MODULE_G729 7 -//TODO: Shouldn't be void -void sceUtilityLoadAvModule(u32 module) +u32 sceUtilityLoadAvModule(u32 module) { DEBUG_LOG(HLE,"sceUtilityLoadAvModule(%i)", module); - RETURN(0); - __KernelReSchedule("utilityloadavmodule"); + hleReSchedule("utilityloadavmodule"); + return 0; } -//TODO: Shouldn't be void -void sceUtilityLoadModule(u32 module) +u32 sceUtilityUnloadAvModule(u32 module) +{ + DEBUG_LOG(HLE,"sceUtilityUnloadAvModule(%i)", module); + hleReSchedule("utilityunloadavmodule"); + return 0; +} + +u32 sceUtilityLoadModule(u32 module) { DEBUG_LOG(HLE,"sceUtilityLoadModule(%i)", module); - RETURN(0); - __KernelReSchedule("utilityloadmodule"); + hleReSchedule("utilityloadmodule"); + return 0; +} + +u32 sceUtilityUnloadModule(u32 module) +{ + DEBUG_LOG(HLE,"sceUtilityUnloadModule(%i)", module); + hleReSchedule("utilityunloadmodule"); + return 0; } int sceUtilityMsgDialogInitStart(u32 structAddr) @@ -406,11 +418,11 @@ const HLEFunction sceUtility[] = {0xf5ce1134, 0, "sceUtilityHtmlViewerShutdownStart"}, {0x05afb9e4, 0, "sceUtilityHtmlViewerUpdate"}, - {0xc629af26, &WrapV_U, "sceUtilityLoadAvModule"}, - {0xf7d8d092, 0, "sceUtilityUnloadAvModule"}, + {0xc629af26, &WrapU_U, "sceUtilityLoadAvModule"}, + {0xf7d8d092, &WrapU_U, "sceUtilityUnloadAvModule"}, - {0x2a2b3de0, &WrapV_U, "sceUtilityLoadModule"}, - {0xe49bfe92, 0, "sceUtilityUnloadModule"}, + {0x2a2b3de0, &WrapU_U, "sceUtilityLoadModule"}, + {0xe49bfe92, &WrapU_U, "sceUtilityUnloadModule"}, {0x0251B134, 0, "sceUtilityScreenshotInitStart"}, {0xF9E0008C, 0, "sceUtilityScreenshotShutdownStart"}, diff --git a/Core/HLE/scesupPreAcc.cpp b/Core/HLE/scesupPreAcc.cpp deleted file mode 100644 index 564b656b39..0000000000 --- a/Core/HLE/scesupPreAcc.cpp +++ /dev/null @@ -1,38 +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 "HLE.h" - -#include "scesupPreAcc.h" - -const HLEFunction scesupPreAcc[] = -{ - {0x110e318b, 0, "scesupPreAcc_0x110e318b"}, - {0x13ae25b3, 0, "scesupPreAcc_0x13ae25b3"}, - {0x28c5f696, 0, "scesupPreAcc_0x28c5f696"}, - {0x348ba3e2, 0, "scesupPreAcc_0x348ba3e2"}, - {0x86debd66, 0, "scesupPreAcc_0x86debd66"}, - {0xa0eaf444, 0, "scesupPreAcc_0xa0eaf444"}, - {0xb03ff882, 0, "scesupPreAcc_0xb03ff882"}, - {0x2ec3f4d9, 0, "scesupPreAcc_0x2ec3f4d9"}, - -}; - -void Register_scesupPreAcc() -{ - RegisterModule("scesupPreAcc",ARRAY_SIZE(scesupPreAcc), scesupPreAcc ); -} diff --git a/Core/Host.h b/Core/Host.h index 5600e01393..056395184c 100644 --- a/Core/Host.h +++ b/Core/Host.h @@ -60,6 +60,9 @@ public: virtual bool IsDebuggingEnabled() {return true;} virtual bool AttemptLoadSymbolMap() {return false;} virtual void SetWindowTitle(const char *message) {} + + // Used for headless. + virtual void SendDebugOutput(const std::string &output) {} }; extern Host *host; diff --git a/Core/MIPS/ARM/CompLoadStore.cpp b/Core/MIPS/ARM/CompLoadStore.cpp index 9db18b946c..e8d8430447 100644 --- a/Core/MIPS/ARM/CompLoadStore.cpp +++ b/Core/MIPS/ARM/CompLoadStore.cpp @@ -42,6 +42,10 @@ namespace MIPSComp int rt = _RT; int rs = _RS; int o = op>>26; + if (((op >> 29) & 1) == 0 && rt == 0) { + // Don't load anything into $zr + return; + } switch (o) { case 37: //R(rt) = ReadMem16(addr); break; //lhu diff --git a/Core/MIPS/ARM/JitCache.cpp b/Core/MIPS/ARM/JitCache.cpp index 5df0381fa1..68a5aa5d3c 100644 --- a/Core/MIPS/ARM/JitCache.cpp +++ b/Core/MIPS/ARM/JitCache.cpp @@ -362,12 +362,8 @@ void JitBlockCache::DestroyBlock(int block_num, bool invalidate) return; } b.invalid = true; -#ifdef JIT_UNLIMITED_ICACHE - Memory::Write_Opcode_JIT(b.originalAddress, b.originalFirstOpcode?b.originalFirstOpcode:JIT_ICACHE_INVALID_WORD); -#else - if ((int)Memory::ReadUnchecked_U32(b.originalAddress) == block_num) + if ((int)Memory::ReadUnchecked_U32(b.originalAddress) == (MIPS_EMUHACK_OPCODE | block_num)) Memory::WriteUnchecked_U32(b.originalFirstOpcode, b.originalAddress); -#endif UnlinkBlock(block_num); diff --git a/Core/MIPS/MIPSInt.cpp b/Core/MIPS/MIPSInt.cpp index e97746cd58..c3bcfe4ab9 100644 --- a/Core/MIPS/MIPSInt.cpp +++ b/Core/MIPS/MIPSInt.cpp @@ -301,8 +301,10 @@ namespace MIPSInt int rt = _RT; int rs = _RS; - if (rt == 0) //destination register is zero register + if (rt == 0) { //destination register is zero register + PC += 4; return; //nop + } switch (op>>26) { @@ -398,6 +400,12 @@ namespace MIPSInt int rs = _RS; u32 addr = R(rs) + imm; + if (((op >> 29) & 1) == 0 && rt == 0) { + // Don't load anything into $zr + PC += 4; + return; + } + switch (op >> 26) { case 32: R(rt) = (u32)(s32)(s8) Memory::Read_U8(addr); break; //lb diff --git a/Core/MIPS/x86/Asm.cpp b/Core/MIPS/x86/Asm.cpp index 67a6611e64..c2d57b6017 100644 --- a/Core/MIPS/x86/Asm.cpp +++ b/Core/MIPS/x86/Asm.cpp @@ -62,7 +62,7 @@ void Jit() void ImHere() { - DEBUG_LOG(CPU, "I'm Here: %08x", currentMIPS->pc); + DEBUG_LOG(CPU, "JIT Here: %08x", currentMIPS->pc); } void AsmRoutineManager::Generate(MIPSState *mips, MIPSComp::Jit *jit) @@ -107,7 +107,7 @@ void AsmRoutineManager::Generate(MIPSState *mips, MIPSComp::Jit *jit) dispatcherNoCheck = GetCodePtr(); // Debug - //CALL(&ImHere); + // CALL(&ImHere); MOV(32, R(EAX), M(&mips->pc)); #ifdef _M_IX86 diff --git a/Core/MIPS/x86/CompLoadStore.cpp b/Core/MIPS/x86/CompLoadStore.cpp index 631dc803aa..b262b11699 100644 --- a/Core/MIPS/x86/CompLoadStore.cpp +++ b/Core/MIPS/x86/CompLoadStore.cpp @@ -52,6 +52,11 @@ namespace MIPSComp int rt = _RT; int rs = _RS; int o = op>>26; + if (((op >> 29) & 1) == 0 && rt == 0) { + // Don't load anything into $zr + return; + } + switch (o) { case 37: //R(rt) = ReadMem16(addr); break; //lhu diff --git a/Core/MIPS/x86/Jit.cpp b/Core/MIPS/x86/Jit.cpp index e79e624e2d..58b04562fc 100644 --- a/Core/MIPS/x86/Jit.cpp +++ b/Core/MIPS/x86/Jit.cpp @@ -88,6 +88,14 @@ const u8 *Jit::DoJit(u32 em_address, JitBlock *b) js.compiling = true; js.inDelaySlot = false; + // We add a check before the block, used when entering from a linked block. + b->checkedEntry = GetCodePtr(); + // Downcount flag check. The last block decremented downcounter, and the flag should still be available. + FixupBranch skip = J_CC(CC_NBE); + MOV(32, M(&mips_->pc), Imm32(js.blockStart)); + JMP(asm_.outerLoop, true); // downcount hit zero - go advance. + SetJumpTarget(skip); + b->normalEntry = GetCodePtr(); // TODO: this needs work diff --git a/Core/MIPS/x86/JitCache.cpp b/Core/MIPS/x86/JitCache.cpp index c710923972..cd3d60039b 100644 --- a/Core/MIPS/x86/JitCache.cpp +++ b/Core/MIPS/x86/JitCache.cpp @@ -347,12 +347,8 @@ void JitBlockCache::DestroyBlock(int block_num, bool invalidate) return; } b.invalid = true; -#ifdef JIT_UNLIMITED_ICACHE - Memory::Write_Opcode_JIT(b.originalAddress, b.originalFirstOpcode?b.originalFirstOpcode:JIT_ICACHE_INVALID_WORD); -#else - if (Memory::ReadUnchecked_U32(b.originalAddress) == (u32)block_num) + if ((int)Memory::ReadUnchecked_U32(b.originalAddress) == (MIPS_EMUHACK_OPCODE | block_num)) Memory::WriteUnchecked_U32(b.originalFirstOpcode, b.originalAddress); -#endif UnlinkBlock(block_num); diff --git a/Core/PSPLoaders.cpp b/Core/PSPLoaders.cpp index afc8cad456..7915b80d66 100644 --- a/Core/PSPLoaders.cpp +++ b/Core/PSPLoaders.cpp @@ -77,11 +77,10 @@ bool Load_PSP_ISO(const char *filename, std::string *error_string) u32 fd = pspFileSystem.OpenFile(sfoPath, FILEACCESS_READ); pspFileSystem.ReadFile(fd, paramsfo, fileInfo.size); pspFileSystem.CloseFile(fd); - ParamSFOData data; - if (data.ReadSFO(paramsfo, (size_t)fileInfo.size)) + if (g_paramSFO.ReadSFO(paramsfo, (size_t)fileInfo.size)) { char title[1024]; - sprintf(title, "%s : %s", data.GetValueString("DISC_ID").c_str(), data.GetValueString("TITLE").c_str()); + sprintf(title, "%s : %s", g_paramSFO.GetValueString("DISC_ID").c_str(), g_paramSFO.GetValueString("TITLE").c_str()); INFO_LOG(LOADER, "%s", title); host->SetWindowTitle(title); } diff --git a/Core/SaveState.cpp b/Core/SaveState.cpp index aa74d0cebe..bf4a2a8fde 100644 --- a/Core/SaveState.cpp +++ b/Core/SaveState.cpp @@ -16,6 +16,7 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include "../Common/StdMutex.h" +#include "../Common/FileUtil.h" #include #include "SaveState.h" @@ -45,23 +46,24 @@ namespace SaveState struct Operation { - Operation(OperationType t, const std::string &f, Callback cb) - : type(t), filename(f), callback(cb) + Operation(OperationType t, const std::string &f, Callback cb, void *cbUserData_) + : type(t), filename(f), callback(cb), cbUserData(cbUserData_) { } OperationType type; std::string filename; Callback callback; + void *cbUserData; }; static int timer; + static bool needsProcess = false; 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. @@ -87,28 +89,105 @@ namespace SaveState // 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()) + if (Core_IsStepping() && __KernelIsRunning()) { // Warning: this may run on a different thread. Process(0, 0); } - else + else if (__KernelIsRunning()) CoreTiming::ScheduleEvent_Threadsafe(0, timer); + else + needsProcess = true; } - void Load(const std::string &filename, Callback callback) + void Load(const std::string &filename, Callback callback, void *cbUserData) { - Enqueue(Operation(SAVESTATE_LOAD, filename, callback)); + Enqueue(Operation(SAVESTATE_LOAD, filename, callback, cbUserData)); } - void Save(const std::string &filename, Callback callback) + void Save(const std::string &filename, Callback callback, void *cbUserData) { - Enqueue(Operation(SAVESTATE_SAVE, filename, callback)); + Enqueue(Operation(SAVESTATE_SAVE, filename, callback, cbUserData)); } - void Verify(Callback callback) + + // Slot utilities + + std::string GenerateSaveSlotFilename(int slot) { - Enqueue(Operation(SAVESTATE_VERIFY, std::string(""), callback)); + char discID[256]; + char temp[256]; + sprintf(discID, "%s_%s", + g_paramSFO.GetValueString("DISC_ID").c_str(), + g_paramSFO.GetValueString("DISC_VERSION").c_str()); + sprintf(temp, "ms0:/PSP/PPSSPP_STATE/%s_%i.ppst", discID, slot); + std::string hostPath; + if (pspFileSystem.GetHostPath(std::string(temp), hostPath)) { + return hostPath; + } else { + return ""; + } + } + + void LoadSlot(int slot, Callback callback, void *cbUserData) + { + std::string fn = GenerateSaveSlotFilename(slot); + if (!fn.empty()) + Load(fn, callback, cbUserData); + else + (*callback)(false, cbUserData); + } + + void SaveSlot(int slot, Callback callback, void *cbUserData) + { + std::string fn = GenerateSaveSlotFilename(slot); + if (!fn.empty()) + Save(fn, callback, cbUserData); + else + (*callback)(false, cbUserData); + } + + void HasSaveInSlot(int slot) + { + std::string fn = GenerateSaveSlotFilename(slot); + } + + bool operator < (const tm &t1, const tm &t2) { + if (t1.tm_year < t2.tm_year) return true; + if (t1.tm_year > t2.tm_year) return false; + if (t1.tm_mon < t2.tm_mon) return true; + if (t1.tm_mon > t2.tm_mon) return false; + if (t1.tm_mday < t2.tm_mday) return true; + if (t1.tm_mday > t2.tm_mday) return false; + if (t1.tm_hour < t2.tm_hour) return true; + if (t1.tm_hour > t2.tm_hour) return false; + if (t1.tm_min < t2.tm_min) return true; + if (t1.tm_min > t2.tm_min) return false; + if (t1.tm_sec < t2.tm_sec) return true; + if (t1.tm_sec > t2.tm_sec) return false; + return false; + } + + int GetMostRecentSaveSlot() { + int newestSlot = -1; + tm newestDate = {0}; + for (int i = 0; i < SAVESTATESLOTS; i++) { + std::string fn = GenerateSaveSlotFilename(i); + if (File::Exists(fn)) { + tm time = File::GetModifTime(fn); + if (newestDate < time) { + newestDate = time; + newestSlot = i; + } + } + } + return newestSlot; + } + + + void Verify(Callback callback, void *cbUserData) + { + Enqueue(Operation(SAVESTATE_VERIFY, std::string(""), callback, cbUserData)); } std::vector Flush() @@ -122,6 +201,12 @@ namespace SaveState void Process(u64 userdata, int cyclesLate) { + if (!__KernelIsRunning()) + { + ERROR_LOG(COMMON, "Savestate failure: Unable to load without kernel, this should never happen."); + return; + } + std::vector operations = Flush(); SaveStart state; @@ -158,12 +243,21 @@ namespace SaveState } if (op.callback != NULL) - op.callback(result); + op.callback(result, op.cbUserData); } } void Init() { timer = CoreTiming::RegisterEvent("SaveState", Process); + // Make sure there's a directory for save slots + pspFileSystem.MkDir("ms0:/PSP/PPSSPP_STATE"); + + std::lock_guard guard(mutex); + if (needsProcess) + { + CoreTiming::ScheduleEvent(0, timer); + needsProcess = false; + } } } diff --git a/Core/SaveState.h b/Core/SaveState.h index 82a7021040..2edecfe930 100644 --- a/Core/SaveState.h +++ b/Core/SaveState.h @@ -20,20 +20,28 @@ namespace SaveState { - typedef void (*Callback)(bool status); + typedef void (*Callback)(bool status, void *cbUserData); // TODO: Better place for this? const int REVISION = 1; + const int SAVESTATESLOTS = 4; void Init(); + void SaveSlot(int slot, Callback callback, void *cbUserData = 0); + void LoadSlot(int slot, Callback callback, void *cbUserData = 0); + void HasSaveInSlot(int slot); + int GetNewestSlot(); + // 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); + void Load(const std::string &filename, Callback callback = 0, void *cbUserData = 0); + // 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); + void Save(const std::string &filename, Callback callback = 0, void *cbUserData = 0); + // 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); + void Verify(Callback callback = 0, void *cbUserData = 0); }; diff --git a/Core/System.cpp b/Core/System.cpp index 9110dcc245..67c3b74cd6 100644 --- a/Core/System.cpp +++ b/Core/System.cpp @@ -37,10 +37,10 @@ #include "CoreParameter.h" #include "FileSystems/MetaFileSystem.h" #include "Loaders.h" - +#include "ELF/ParamSFO.h" MetaFileSystem pspFileSystem; - +ParamSFOData g_paramSFO; static CoreParameter coreParameter; bool PSP_Init(const CoreParameter &coreParam, std::string *error_string) diff --git a/Core/System.h b/Core/System.h index a3340b2a21..a47d1622fe 100644 --- a/Core/System.h +++ b/Core/System.h @@ -21,8 +21,10 @@ #include "MemMap.h" #include "FileSystems/MetaFileSystem.h" #include "CoreParameter.h" +#include "ELF/ParamSFO.h" extern MetaFileSystem pspFileSystem; +extern ParamSFOData g_paramSFO; bool PSP_Init(const CoreParameter &coreParam, std::string *error_string); bool PSP_IsInited(); diff --git a/Core/Util/PPGeDraw.cpp b/Core/Util/PPGeDraw.cpp index bd8032dd0b..cbd3bce81f 100644 --- a/Core/Util/PPGeDraw.cpp +++ b/Core/Util/PPGeDraw.cpp @@ -238,18 +238,20 @@ static void PPGeMeasureText(const char *text, float scale, float *w, float *h) { const AtlasFont &atlasfont = *ppge_atlas.fonts[0]; unsigned char cval; float wacc = 0; + float maxw = 0; int lines = 1; while ((cval = *text++) != '\0') { if (cval < 32) continue; if (cval > 127) continue; if (cval == '\n') { + if (wacc > maxw) maxw = wacc; wacc = 0; lines++; } AtlasChar c = atlasfont.chars[cval - 32]; wacc += c.wx * scale; } - if (w) *w = wacc; + if (w) *w = maxw; if (h) *h = atlasfont.height * scale * lines; } @@ -278,6 +280,7 @@ void PPGeDrawText(const char *text, float x, float y, int align, float scale, u3 float sx = x; while ((cval = *text++) != '\0') { if (cval == '\n') { + // This is not correct when centering or right-justifying, need to set x depending on line width (tricky) y += atlasfont.height * scale; x = sx; continue; diff --git a/GPU/CMakeLists.txt b/GPU/CMakeLists.txt index ad56972231..19beca69da 100644 --- a/GPU/CMakeLists.txt +++ b/GPU/CMakeLists.txt @@ -1,4 +1,5 @@ set(SRCS + GPUCommon.cpp GPUState.cpp Math3D.cpp GLES/DisplayListInterpreter.cpp diff --git a/GPU/GLES/DisplayListInterpreter.cpp b/GPU/GLES/DisplayListInterpreter.cpp index af4966ee18..761dab5fc4 100644 --- a/GPU/GLES/DisplayListInterpreter.cpp +++ b/GPU/GLES/DisplayListInterpreter.cpp @@ -161,10 +161,8 @@ GLES_GPU::GLES_GPU(int renderWidth, int renderHeight) : interruptsEnabled_(true), displayFramebufPtr_(0), renderWidth_(renderWidth), - renderHeight_(renderHeight), - dlIdGenerator(1), - dumpThisFrame_(false), - dumpNextFrame_(false) { + renderHeight_(renderHeight) +{ renderWidthFactor_ = (float)renderWidth / 480.0f; renderHeightFactor_ = (float)renderHeight / 272.0f; shaderManager_ = new ShaderManager(); @@ -177,10 +175,10 @@ GLES_GPU::GLES_GPU(int renderWidth, int renderHeight) flushBeforeCommand_ = new u8[256]; memset(flushBeforeCommand_, 0, 256 * sizeof(bool)); - for (int i = 0; i < ARRAY_SIZE(flushOnChangedBeforeCommandList); i++) { + for (size_t i = 0; i < ARRAY_SIZE(flushOnChangedBeforeCommandList); i++) { flushBeforeCommand_[flushOnChangedBeforeCommandList[i]] = 2; } - for (int i = 0; i < ARRAY_SIZE(flushBeforeCommandList); i++) { + for (size_t i = 0; i < ARRAY_SIZE(flushBeforeCommandList); i++) { flushBeforeCommand_[flushBeforeCommandList[i]] = 1; } flushBeforeCommand_[1] = 0; @@ -304,7 +302,16 @@ GLES_GPU::VirtualFramebuffer *GLES_GPU::GetDisplayFBO() { return *iter; } } - + DEBUG_LOG(HLE, "Finding no FBO matching address %08x", displayFramebufPtr_); +#ifdef _DEBUG + std::string debug = "FBOs: "; + for (auto iter = vfbs_.begin(); iter != vfbs_.end(); ++iter) { + char temp[256]; + sprintf(temp, "%08x %i %i", (*iter)->fb_address, (*iter)->width, (*iter)->height); + debug += std::string(temp); + } + ERROR_LOG(HLE, "FBOs: %s", debug.c_str()); +#endif return 0; } @@ -322,13 +329,19 @@ void GLES_GPU::SetRenderFrameBuffer() { int drawing_width = ((gstate.region2) & 0x3FF) + 1; int drawing_height = ((gstate.region2 >> 10) & 0x3FF) + 1; + // HACK for first frame where some games don't init things right + if (drawing_width == 1 && drawing_height == 1) { + drawing_width = 480; + drawing_height = 272; + } + int fmt = gstate.framebufpixformat & 3; // Find a matching framebuffer VirtualFramebuffer *vfb = 0; for (auto iter = vfbs_.begin(); iter != vfbs_.end(); ++iter) { VirtualFramebuffer *v = *iter; - if (v->fb_address == fb_address) { + if (v->fb_address == fb_address && v->width == drawing_width && v->height == drawing_height) { // Let's not be so picky for now. Let's say this is the one. vfb = v; // Update fb stride in case it changed @@ -355,7 +368,7 @@ void GLES_GPU::SetRenderFrameBuffer() { glViewport(0, 0, renderWidth_, renderHeight_); currentRenderVfb_ = vfb; glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); - DEBUG_LOG(HLE, "Creating FBO for %08x", vfb->fb_address); + INFO_LOG(HLE, "Creating FBO for %08x : %i x %i", vfb->fb_address, vfb->width, vfb->height); return; } @@ -386,53 +399,8 @@ void GLES_GPU::EndDebugDraw() { // Render queue -bool GLES_GPU::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 (!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 GLES_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 GLES_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(); -} - -void GLES_GPU::DrawSync(int mode) { +void GLES_GPU::DrawSync(int mode) +{ transformDraw_.Flush(); } @@ -464,6 +432,13 @@ static void LeaveClearMode() { // dirtyshader? } +void GLES_GPU::PreExecuteOp(u32 op, u32 diff) { + u32 cmd = op >> 24; + + if (flushBeforeCommand_[cmd] == 1 || (diff && flushBeforeCommand_[cmd] == 2)) + transformDraw_.Flush(); +} + void GLES_GPU::ExecuteOp(u32 op, u32 diff) { u32 cmd = op >> 24; u32 data = op & 0xFFFFFF; @@ -509,7 +484,7 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) { } int bytesRead; - transformDraw_.SubmitPrim(verts, inds, type, count, gstate.vertType, 0, -1, &bytesRead); + transformDraw_.SubmitPrim(verts, inds, type, count, gstate.vertType, -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. @@ -547,7 +522,7 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) { { u32 target = (((gstate.base & 0x00FF0000) << 8) | (op & 0xFFFFFC)) & 0x0FFFFFFF; if (Memory::IsValidAddress(target)) { - dcontext.pc = target - 4; // pc will be increased after we return, counteract that + currentList->pc = target - 4; // pc will be increased after we return, counteract that } else { ERROR_LOG(G3D, "JUMP to illegal address %08x - ignoring??", target); } @@ -556,21 +531,21 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) { case GE_CMD_CALL: { - u32 retval = dcontext.pc + 4; + u32 retval = currentList->pc + 4; if (stackptr == ARRAY_SIZE(stack)) { ERROR_LOG(G3D, "CALL: Stack full!"); } else { stack[stackptr++] = retval; u32 target = (((gstate.base & 0x00FF0000) << 8) | (op & 0xFFFFFC)) & 0xFFFFFFF; - dcontext.pc = target - 4; // pc will be increased after we return, counteract that + currentList->pc = target - 4; // pc will be increased after we return, counteract that } } break; case GE_CMD_RET: { - u32 target = (dcontext.pc & 0xF0000000) | (stack[--stackptr] & 0x0FFFFFFF); - dcontext.pc = target - 4; + u32 target = (currentList->pc & 0xF0000000) | (stack[--stackptr] & 0x0FFFFFFF); + currentList->pc = target - 4; } break; @@ -583,13 +558,14 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) { case GE_CMD_FINISH: // TODO: Should this run while interrupts are suspended? if (interruptsEnabled_) - __TriggerInterruptWithArg(PSP_INTR_HLE, PSP_GE_INTR, PSP_GE_SUBINTR_FINISH, 0); + __TriggerInterruptWithArg(PSP_INTR_HLE, PSP_GE_INTR, currentList->subIntrBase | PSP_GE_SUBINTR_FINISH, 0); break; case GE_CMD_END: switch (prev >> 24) { case GE_CMD_SIGNAL: { + currentList->status = PSP_GE_LIST_END_REACHED; // TODO: see http://code.google.com/p/jpcsp/source/detail?r=2935# int behaviour = (prev >> 16) & 0xFF; int signal = prev & 0xFFFF; @@ -620,10 +596,11 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) { } // TODO: Should this run while interrupts are suspended? if (interruptsEnabled_) - __TriggerInterruptWithArg(PSP_INTR_HLE, PSP_GE_INTR, PSP_GE_SUBINTR_SIGNAL, signal); + __TriggerInterruptWithArg(PSP_INTR_HLE, PSP_GE_INTR, currentList->subIntrBase | PSP_GE_SUBINTR_SIGNAL, signal); } break; case GE_CMD_FINISH: + currentList->status = PSP_GE_LIST_DONE; finished = true; break; default: @@ -641,7 +618,7 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) { break; case GE_CMD_ORIGIN: - gstate.offsetAddr = dcontext.pc & 0xFFFFFF; + gstate.offsetAddr = currentList->pc & 0xFFFFFF; break; case GE_CMD_VERTEXTYPE: @@ -1095,49 +1072,11 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) { break; default: - DEBUG_LOG(G3D,"DL Unknown: %08x @ %08x", op, dcontext.pc); + DEBUG_LOG(G3D,"DL Unknown: %08x @ %08x", op, currentList == NULL ? 0 : currentList->pc); break; } } -bool GLES_GPU::InterpretList() -{ - // Reset stackptr for safety - stackptr = 0; - u32 op = 0; - prev = 0; - finished = false; - while (!finished) - { - if (!Memory::IsValidAddress(dcontext.pc)) { - ERROR_LOG(G3D, "DL PC = %08x WTF!!!!", dcontext.pc); - return true; - } - 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]; - 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); - - dcontext.pc += 4; - prev = op; - } - return true; -} - void GLES_GPU::UpdateStats() { gpuStats.numVertexShaders = shaderManager_->NumVertexShaders(); gpuStats.numFragmentShaders = shaderManager_->NumFragmentShaders(); @@ -1196,3 +1135,16 @@ void GLES_GPU::InvalidateCache(u32 addr, int size) { void GLES_GPU::Flush() { transformDraw_.Flush(); } + +void GLES_GPU::DoState(PointerWrap &p) { + GPUCommon::DoState(p); + + TextureCache_Clear(true); + gstate_c.textureChanged = true; + for (auto iter = vfbs_.begin(); iter != vfbs_.end(); ++iter) { + fbo_destroy((*iter)->fbo); + delete (*iter); + } + vfbs_.clear(); + shaderManager_->ClearCache(true); +} diff --git a/GPU/GLES/DisplayListInterpreter.h b/GPU/GLES/DisplayListInterpreter.h index ae22e56188..fdceb8a551 100644 --- a/GPU/GLES/DisplayListInterpreter.h +++ b/GPU/GLES/DisplayListInterpreter.h @@ -18,9 +18,9 @@ #pragma once #include -#include +#include -#include "../GPUInterface.h" +#include "../GPUCommon.h" #include "Framebuffer.h" #include "VertexDecoder.h" #include "TransformPipeline.h" @@ -29,16 +29,14 @@ class ShaderManager; class LinkedShader; -class GLES_GPU : public GPUInterface +class GLES_GPU : public GPUCommon { public: GLES_GPU(int renderWidth, int renderHeight); ~GLES_GPU(); virtual void InitClear(); - virtual u32 EnqueueList(u32 listpc, u32 stall); - virtual void UpdateStall(int listid, u32 newstall); + virtual void PreExecuteOp(u32 op, u32 diff); virtual void ExecuteOp(u32 op, u32 diff); - virtual bool InterpretList(); virtual void DrawSync(int mode); virtual void Continue(); virtual void Break(); @@ -55,10 +53,10 @@ public: virtual void DumpNextFrame(); virtual void Flush(); + virtual void DoState(PointerWrap &p); private: void DoBlockTransfer(); - bool ProcessDLQueue(); // Applies states for debugging if enabled. void BeginDebugDraw(); @@ -80,31 +78,12 @@ private: float renderWidthFactor_; float renderHeightFactor_; - bool dumpNextFrame_; - bool dumpThisFrame_; - struct CmdProcessorState { u32 pc; u32 stallAddr; + int subIntrBase; }; - CmdProcessorState dcontext; - - int dlIdGenerator; - - struct DisplayList { - int id; - u32 listpc; - u32 stall; - }; - - std::vector dlQueue; - - u32 prev; - u32 stack[2]; - u32 stackptr; - bool finished; - struct VirtualFramebuffer { u32 fb_address; u32 z_address; diff --git a/GPU/GLES/FragmentShaderGenerator.cpp b/GPU/GLES/FragmentShaderGenerator.cpp index 7cd7e2302b..143e3b2cf5 100644 --- a/GPU/GLES/FragmentShaderGenerator.cpp +++ b/GPU/GLES/FragmentShaderGenerator.cpp @@ -156,7 +156,7 @@ char *GenerateFragmentShader() } // Color doubling if (gstate.texfunc & 0x10000) { - WRITE(p, " v = v * vec4(2.0, 2.0, 2.0, 1.0);"); + WRITE(p, " v = v * vec4(2.0, 2.0, 2.0, 2.0);"); } if (gstate.alphaTestEnable & 1) { diff --git a/GPU/GLES/StateMapping.cpp b/GPU/GLES/StateMapping.cpp index 1ea3b7cc26..95792d31e3 100644 --- a/GPU/GLES/StateMapping.cpp +++ b/GPU/GLES/StateMapping.cpp @@ -181,7 +181,6 @@ void UpdateViewportAndProjection() { 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. @@ -203,8 +202,6 @@ void UpdateViewportAndProjection() { gstate_c.vpWidth = vpXa * 2; gstate_c.vpHeight = -vpYa * 2; - return; - float vpWidth = fabsf(gstate_c.vpWidth); float vpHeight = fabsf(gstate_c.vpHeight); diff --git a/GPU/GLES/TextureCache.cpp b/GPU/GLES/TextureCache.cpp index 3e13c466e3..6322d9a62c 100644 --- a/GPU/GLES/TextureCache.cpp +++ b/GPU/GLES/TextureCache.cpp @@ -428,16 +428,12 @@ 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); - // 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); + 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); } } diff --git a/GPU/GLES/TransformPipeline.cpp b/GPU/GLES/TransformPipeline.cpp index 03c3c1901f..e849032a94 100644 --- a/GPU/GLES/TransformPipeline.cpp +++ b/GPU/GLES/TransformPipeline.cpp @@ -64,8 +64,7 @@ TransformDrawEngine::~TransformDrawEngine() { 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]; + // Generate indices for a rectangular mesh. int c = 0; for (int y = 0; y < 3; y++) { for (int x = 0; x < 3; x++) { @@ -78,6 +77,14 @@ void TransformDrawEngine::DrawBezier(int ucount, int vcount) { } } + // We are free to use the "decoded" buffer here. + // Let's split it into two to get a second buffer, there's enough space. + u8 *decoded2 = decoded + 65536 * 24; + + // Alright, now for the vertex data. + // For now, we will simply inject UVs. + + float customUV[4 * 4 * 2]; for (int y = 0; y < 4; y++) { for (int x = 0; x < 4; x++) { customUV[(y * 4 + x) * 2 + 0] = (float)x/3.0f; @@ -85,8 +92,13 @@ void TransformDrawEngine::DrawBezier(int ucount, int vcount) { } } - int vertexCount = 3 * 3 * 6; - SubmitPrim(Memory::GetPointer(gstate_c.vertexAddr), &indices[0], GE_PRIM_TRIANGLES, vertexCount, customVertType, customUV, GE_VTYPE_IDX_16BIT, 0); + if (!(gstate.vertType & GE_VTYPE_TC_MASK)) { + dec.SetVertexType(gstate.vertType); + u32 newVertType = dec.InjectUVs(decoded2, Memory::GetPointer(gstate_c.vertexAddr), customUV, 16); + SubmitPrim(decoded2, &indices[0], GE_PRIM_TRIANGLES, c, newVertType, GE_VTYPE_IDX_16BIT, 0); + } else { + SubmitPrim(Memory::GetPointer(gstate_c.vertexAddr), &indices[0], GE_PRIM_TRIANGLES, c, gstate.vertType, GE_VTYPE_IDX_16BIT, 0); + } } void TransformDrawEngine::DrawSpline(int ucount, int vcount, int utype, int vtype) { @@ -227,7 +239,7 @@ void Lighter::Light(float colorOut0[4], float colorOut1[4], const float colorIn[ dots[l] = dot; if (gstate.lightEnable[l] & 1) { - Color4 lightAmbient(gstate_c.lightColor[2][l], 1.0f); + Color4 lightAmbient(gstate_c.lightColor[0][l], 1.0f); lightSum0 += lightAmbient * *ambient + diff; } } @@ -330,18 +342,6 @@ static void RotateUVs(TransformedVertex v[4]) { // 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]); - */ - - // Temporary storage for RECTANGLES emulation - float v2[3] = {0}; - float uv2[2] = {0}; bool throughmode = (vertType & GE_VTYPE_THROUGH_MASK) != 0; @@ -372,9 +372,9 @@ void TransformDrawEngine::SoftwareTransformAndDraw( c1[j] = 0.0f; } } 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[0] = ((gstate.materialambient >> 16) & 0xFF) / 255.f; + c0[1] = ((gstate.materialambient >> 8) & 0xFF) / 255.f; + c0[2] = (gstate.materialambient & 0xFF) / 255.f; c0[3] = (gstate.materialalpha & 0xFF) / 255.f; } @@ -431,9 +431,9 @@ void TransformDrawEngine::SoftwareTransformAndDraw( 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[0] = ((gstate.materialambient >> 16) & 0xFF) / 255.f; + unlitColor[1] = ((gstate.materialambient >> 8) & 0xFF) / 255.f; + unlitColor[2] = (gstate.materialambient & 0xFF) / 255.f; unlitColor[3] = (gstate.materialalpha & 0xFF) / 255.f; } float litColor0[4]; @@ -462,9 +462,9 @@ void TransformDrawEngine::SoftwareTransformAndDraw( c1[j] = 0.0f; } } 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[0] = ((gstate.materialambient >> 16) & 0xFF) / 255.f; + c0[1] = ((gstate.materialambient >> 8) & 0xFF) / 255.f; + c0[2] = (gstate.materialambient & 0xFF) / 255.f; c0[3] = (gstate.materialalpha & 0xFF) / 255.f; } } @@ -541,6 +541,10 @@ void TransformDrawEngine::SoftwareTransformAndDraw( numTrans = vertexCount; drawIndexed = true; } else { + // Temporary storage for RECTANGLES emulation + float v2[3] = {0}; + float uv2[2] = {0}; + numTrans = 0; drawBuffer = transformedExpanded; TransformedVertex *trans = &transformedExpanded[0]; @@ -615,7 +619,7 @@ void TransformDrawEngine::SoftwareTransformAndDraw( if (program->a_color1 != -1) glDisableVertexAttribArray(program->a_color1); } -void TransformDrawEngine::SubmitPrim(void *verts, void *inds, int prim, int vertexCount, u32 vertType, float *customUV, int forceIndexType, int *bytesRead) { +void TransformDrawEngine::SubmitPrim(void *verts, void *inds, int prim, int vertexCount, u32 vertType, int forceIndexType, int *bytesRead) { // For the future if (!indexGen.PrimCompatible(prim)) Flush(); diff --git a/GPU/GLES/TransformPipeline.h b/GPU/GLES/TransformPipeline.h index 3be12aaa1c..829c3152b6 100644 --- a/GPU/GLES/TransformPipeline.h +++ b/GPU/GLES/TransformPipeline.h @@ -29,7 +29,7 @@ class TransformDrawEngine { public: TransformDrawEngine(); ~TransformDrawEngine(); - void SubmitPrim(void *verts, void *inds, int prim, int vertexCount, u32 vertexType, float *customUV, int forceIndexType, int *bytesRead); + void SubmitPrim(void *verts, void *inds, int prim, int vertexCount, u32 vertexType, int forceIndexType, int *bytesRead); void DrawBezier(int ucount, int vcount); void DrawSpline(int ucount, int vcount, int utype, int vtype); void Flush(); diff --git a/GPU/GLES/VertexDecoder.cpp b/GPU/GLES/VertexDecoder.cpp index 3a3ffe650c..100640c424 100644 --- a/GPU/GLES/VertexDecoder.cpp +++ b/GPU/GLES/VertexDecoder.cpp @@ -645,8 +645,7 @@ void VertexDecoder::SetVertexType(u32 fmt) { DEBUG_LOG(G3D,"SVT : size = %i, aligned to biggest %i", size, biggest); } -void VertexDecoder::DecodeVerts(u8 *decodedptr, 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 { // 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; @@ -677,8 +676,7 @@ void VertexDecoder::DecodeVerts(u8 *decodedptr, const void *verts, const void *i // 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++) - { + for (int index = lowerBound; index <= upperBound; index++) { for (int i = 0; i < numSteps_; i++) { ((*this).*steps_[i])(); } @@ -686,3 +684,24 @@ void VertexDecoder::DecodeVerts(u8 *decodedptr, const void *verts, const void *i decoded_ += decFmt.stride; } } + +// TODO: Does not support morphs, skinning etc. +u32 VertexDecoder::InjectUVs(u8 *decoded, const void *verts, float *customuv, int count) const { + u32 customVertType = (gstate.vertType & ~GE_VTYPE_TC_MASK) | GE_VTYPE_TC_FLOAT; + VertexDecoder decOut; + decOut.SetVertexType(customVertType); + + const u8 *inp = (const u8 *)verts; + u8 *out = decoded; + for (int i = 0; i < count; i++) { + if (pos) memcpy(out + decOut.posoff, inp + posoff, possize[pos]); + if (nrm) memcpy(out + decOut.nrmoff, inp + nrmoff, nrmsize[pos]); + if (col) memcpy(out + decOut.coloff, inp + coloff, colsize[pos]); + // Ignore others for now, this is all we need for puzbob. + // Inject! + memcpy(out + decOut.tcoff, &customuv[i * 2], tcsize[decOut.tc]); + inp += this->onesize_; + out += decOut.onesize_; + } + return customVertType; +} diff --git a/GPU/GLES/VertexDecoder.h b/GPU/GLES/VertexDecoder.h index 406b668428..771e1e35a7 100644 --- a/GPU/GLES/VertexDecoder.h +++ b/GPU/GLES/VertexDecoder.h @@ -88,6 +88,10 @@ public: const DecVtxFormat &GetDecVtxFmt() { return decFmt; } void DecodeVerts(u8 *decoded, const void *verts, const void *inds, int prim, int count, int *indexLowerBound, int *indexUpperBound) const; + + // This could be easily generalized to inject any one component. Don't know another use for it though. + u32 InjectUVs(u8 *decoded, const void *verts, float *customuv, int count) const; + bool hasColor() const { return col != 0; } int VertexSize() const { return size; } diff --git a/GPU/GPU.vcxproj b/GPU/GPU.vcxproj index a88d235a12..4a6262b411 100644 --- a/GPU/GPU.vcxproj +++ b/GPU/GPU.vcxproj @@ -128,6 +128,7 @@ + @@ -147,6 +148,7 @@ + @@ -162,4 +164,4 @@ - + \ No newline at end of file diff --git a/GPU/GPU.vcxproj.filters b/GPU/GPU.vcxproj.filters index e5a783590f..5f4cbc3c56 100644 --- a/GPU/GPU.vcxproj.filters +++ b/GPU/GPU.vcxproj.filters @@ -60,6 +60,10 @@ GLES + + + Common + @@ -101,6 +105,10 @@ GLES + + + Common + diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp new file mode 100644 index 0000000000..d439de6cb1 --- /dev/null +++ b/GPU/GPUCommon.cpp @@ -0,0 +1,127 @@ +#include "../Core/MemMap.h" +#include "GeDisasm.h" +#include "GPUCommon.h" +#include "GPUState.h" + + + +static int dlIdGenerator = 1; + +void init() { + dlIdGenerator = 1; +} + +int GPUCommon::listStatus(int listid) +{ + for(DisplayListQueue::iterator it(dlQueue.begin()); it != dlQueue.end(); ++it) + { + if(it->id == listid) + { + return it->status; + } + } + return 0x80000100; // INVALID_ID +} + +u32 GPUCommon::EnqueueList(u32 listpc, u32 stall, int subIntrBase, bool head) +{ + DisplayList dl; + dl.id = dlIdGenerator++; + dl.pc = listpc & 0xFFFFFFF; + dl.stall = stall & 0xFFFFFFF; + dl.status = PSP_GE_LIST_QUEUED; + dl.subIntrBase = subIntrBase; + if(head) + dlQueue.push_front(dl); + else + dlQueue.push_back(dl); + ProcessDLQueue(); + return dl.id; +} + +void GPUCommon::UpdateStall(int listid, u32 newstall) +{ + // this needs improvement.... + for (DisplayListQueue::iterator iter = dlQueue.begin(); iter != dlQueue.end(); iter++) + { + DisplayList &l = *iter; + if (l.id == listid) + { + l.stall = newstall & 0xFFFFFFF; + } + } + + ProcessDLQueue(); +} + +bool GPUCommon::InterpretList(DisplayList &list) +{ + currentList = &list; + // Reset stackptr for safety + stackptr = 0; + u32 op = 0; + prev = 0; + finished = false; + while (!finished) + { + list.status = PSP_GE_LIST_DRAWING; + if (!Memory::IsValidAddress(list.pc)) { + ERROR_LOG(G3D, "DL PC = %08x WTF!!!!", list.pc); + return true; + } + if (list.pc == list.stall) + { + list.status = PSP_GE_LIST_STALL_REACHED; + return false; + } + op = Memory::ReadUnchecked_U32(list.pc); //read from memory + u32 cmd = op >> 24; + u32 diff = op ^ gstate.cmdmem[cmd]; + PreExecuteOp(op, diff); + // TODO: Add a compiler flag to remove stuff like this at very-final build time. + if (dumpThisFrame_) { + char temp[256]; + GeDisassembleOp(list.pc, op, prev, temp); + NOTICE_LOG(G3D, "%s", temp); + } + gstate.cmdmem[cmd] = op; // crashes if I try to put the whole op there?? + + ExecuteOp(op, diff); + + list.pc += 4; + prev = op; + } + return true; +} + +bool GPUCommon::ProcessDLQueue() +{ + DisplayListQueue::iterator iter = dlQueue.begin(); + while (!(iter == dlQueue.end())) + { + DisplayList &l = *iter; + DEBUG_LOG(G3D,"Okay, starting DL execution at %08x - stall = %08x", l.pc, l.stall); + if (!InterpretList(l)) + { + 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! +} + +void GPUCommon::PreExecuteOp(u32 op, u32 diff) { + // Nothing to do +} + +void GPUCommon::DoState(PointerWrap &p) { + p.Do(dlIdGenerator); + p.Do(dlQueue); + p.DoMarker("GPUCommon"); +} diff --git a/GPU/GPUCommon.h b/GPU/GPUCommon.h new file mode 100644 index 0000000000..803f63efd5 --- /dev/null +++ b/GPU/GPUCommon.h @@ -0,0 +1,38 @@ +#pragma once + +#include "GPUInterface.h" + +class GPUCommon : public GPUInterface +{ +public: + GPUCommon() : + dlIdGenerator(1), + currentList(NULL), + stackptr(0), + dumpNextFrame_(false), + dumpThisFrame_(false) + {} + + virtual void PreExecuteOp(u32 op, u32 diff); + virtual bool InterpretList(DisplayList &list); + virtual bool ProcessDLQueue(); + virtual void UpdateStall(int listid, u32 newstall); + virtual u32 EnqueueList(u32 listpc, u32 stall, int subIntrBase, bool head); + virtual int listStatus(int listid); + virtual void DoState(PointerWrap &p); + +protected: + typedef std::deque DisplayListQueue; + + int dlIdGenerator; + DisplayList *currentList; + DisplayListQueue dlQueue; + + u32 prev; + u32 stack[2]; + u32 stackptr; + bool finished; + + bool dumpNextFrame_; + bool dumpThisFrame_; +}; \ No newline at end of file diff --git a/GPU/GPUInterface.h b/GPU/GPUInterface.h index 81d71d9a52..94d0fc9d77 100644 --- a/GPU/GPUInterface.h +++ b/GPU/GPUInterface.h @@ -18,6 +18,27 @@ #pragma once #include "../Globals.h" +#include "../Common/ChunkFile.h" +#include + +enum DisplayListStatus +{ + PSP_GE_LIST_DONE = 0, // reached finish+end + PSP_GE_LIST_QUEUED = 1, // in queue, not stalled + PSP_GE_LIST_DRAWING = 2, // drawing + PSP_GE_LIST_STALL_REACHED = 3, // stalled + PSP_GE_LIST_END_REACHED = 4, // reached signal+end, in jpcsp but not in pspsdk? + PSP_GE_LIST_CANCEL_DONE = 5, // canceled? +}; + +struct DisplayList +{ + int id; + u32 pc; + u32 stall; + DisplayListStatus status; + int subIntrBase; +}; class GPUInterface { @@ -29,13 +50,15 @@ public: // Draw queue management // TODO: Much of this should probably be shared between the different GPU implementations. - virtual u32 EnqueueList(u32 listpc, u32 stall) = 0; + virtual u32 EnqueueList(u32 listpc, u32 stall, int subIntrBase, bool head) = 0; virtual void UpdateStall(int listid, u32 newstall) = 0; virtual void DrawSync(int mode) = 0; virtual void Continue() = 0; + virtual void PreExecuteOp(u32 op, u32 diff) = 0; virtual void ExecuteOp(u32 op, u32 diff) = 0; - virtual bool InterpretList() = 0; + virtual bool InterpretList(DisplayList& list) = 0; + virtual int listStatus(int listid) = 0; // Framebuffer management virtual void SetDisplayFramebuffer(u32 framebuf, u32 stride, int format) = 0; @@ -54,6 +77,7 @@ public: virtual void DeviceLost() = 0; virtual void Flush() = 0; + virtual void DoState(PointerWrap &p) = 0; // Debugging virtual void DumpNextFrame() = 0; diff --git a/GPU/GPUState.cpp b/GPU/GPUState.cpp index c45d65ae88..d984cf0b33 100644 --- a/GPU/GPUState.cpp +++ b/GPU/GPUState.cpp @@ -95,7 +95,8 @@ void ReapplyGfxState() for (int i = GE_CMD_VERTEXTYPE; i < GE_CMD_BONEMATRIXNUMBER; i++) { - gpu->ExecuteOp(gstate.cmdmem[i], 0xFFFFFFFF); + if(i != GE_CMD_ORIGIN) + gpu->ExecuteOp(gstate.cmdmem[i], 0xFFFFFFFF); } // Can't write to bonematrixnumber here diff --git a/GPU/Null/NullGpu.cpp b/GPU/Null/NullGpu.cpp index 80d7fc9652..706caa616e 100644 --- a/GPU/Null/NullGpu.cpp +++ b/GPU/Null/NullGpu.cpp @@ -22,93 +22,13 @@ #include "../../Core/MemMap.h" #include "../../Core/HLE/sceKernelInterrupt.h" -struct DisplayState -{ - u32 pc; - u32 stallAddr; -}; - -static DisplayState dcontext; - -struct DisplayList -{ - int id; - u32 listpc; - u32 stall; -}; - -static std::vector dlQueue; - -static u32 prev; -static u32 stack[2]; -static u32 stackptr = 0; -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(); - 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()) - { - 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 NullGPU::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 NullGPU::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(); } void NullGPU::DrawSync(int mode) @@ -124,7 +44,6 @@ void NullGPU::Continue() } - void NullGPU::ExecuteOp(u32 op, u32 diff) { u32 cmd = op >> 24; @@ -187,18 +106,18 @@ void NullGPU::ExecuteOp(u32 op, u32 diff) 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 + DEBUG_LOG(G3D,"DL CMD JUMP - %08x to %08x", currentList->pc, target); + currentList->pc = target - 4; // pc will be increased after we return, counteract that } break; case GE_CMD_CALL: { - u32 retval = dcontext.pc + 4; + u32 retval = currentList->pc + 4; 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 + DEBUG_LOG(G3D,"DL CMD CALL - %08x to %08x, ret=%08x", currentList->pc, target, retval); + currentList->pc = target - 4; // pc will be increased after we return, counteract that } break; @@ -206,8 +125,8 @@ void NullGPU::ExecuteOp(u32 op, u32 diff) //TODO : debug! { u32 target = stack[--stackptr] & 0xFFFFFFF; - DEBUG_LOG(G3D,"DL CMD RET - from %08x to %08x", dcontext.pc, target); - dcontext.pc = target - 4; + DEBUG_LOG(G3D,"DL CMD RET - from %08x to %08x", currentList->pc, target); + currentList->pc = target - 4; } break; @@ -219,7 +138,7 @@ void NullGPU::ExecuteOp(u32 op, u32 diff) // TODO: Should this run while interrupts are suspended? if (interruptsEnabled_) - __TriggerInterruptWithArg(PSP_INTR_HLE, PSP_GE_INTR, PSP_GE_SUBINTR_SIGNAL, signal); + __TriggerInterruptWithArg(PSP_INTR_HLE, PSP_GE_INTR, currentList->subIntrBase | PSP_GE_SUBINTR_SIGNAL, signal); } break; @@ -234,7 +153,7 @@ void NullGPU::ExecuteOp(u32 op, u32 diff) break; case GE_CMD_ORIGIN: - gstate.offsetAddr = dcontext.pc & 0xFFFFFF; + gstate.offsetAddr = currentList->pc & 0xFFFFFF; break; case GE_CMD_VERTEXTYPE: @@ -251,7 +170,7 @@ void NullGPU::ExecuteOp(u32 op, u32 diff) DEBUG_LOG(G3D,"DL CMD FINISH"); // TODO: Should this run while interrupts are suspended? if (interruptsEnabled_) - __TriggerInterruptWithArg(PSP_INTR_HLE, PSP_GE_INTR, PSP_GE_SUBINTR_FINISH, 0); + __TriggerInterruptWithArg(PSP_INTR_HLE, PSP_GE_INTR, currentList->subIntrBase | PSP_GE_SUBINTR_FINISH, 0); break; case GE_CMD_END: @@ -807,38 +726,13 @@ void NullGPU::ExecuteOp(u32 op, u32 diff) break; default: - DEBUG_LOG(G3D,"DL Unknown: %08x @ %08x", op, dcontext.pc); + DEBUG_LOG(G3D,"DL Unknown: %08x @ %08x", op, currentList->pc); break; //ETC... } } -bool NullGPU::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; -} - void NullGPU::UpdateStats() { gpuStats.numVertexShaders = 0; diff --git a/GPU/Null/NullGpu.h b/GPU/Null/NullGpu.h index 5e9f163713..1dabc32e42 100644 --- a/GPU/Null/NullGpu.h +++ b/GPU/Null/NullGpu.h @@ -17,20 +17,17 @@ #pragma once -#include "../GPUInterface.h" +#include "../GPUCommon.h" class ShaderManager; -class NullGPU : public GPUInterface +class NullGPU : public GPUCommon { public: NullGPU(); ~NullGPU(); virtual void InitClear() {} - virtual u32 EnqueueList(u32 listpc, u32 stall); - virtual void UpdateStall(int listid, u32 newstall); virtual void ExecuteOp(u32 op, u32 diff); - virtual bool InterpretList(); virtual void Continue(); virtual void DrawSync(int mode); virtual void EnableInterrupts(bool enable) { @@ -48,6 +45,5 @@ public: virtual void DumpNextFrame() {} private: - bool ProcessDLQueue(); bool interruptsEnabled_; }; diff --git a/Qt/Core.pro b/Qt/Core.pro index 6c2cada922..d3fadba4bc 100755 --- a/Qt/Core.pro +++ b/Qt/Core.pro @@ -136,6 +136,8 @@ SOURCES += ../Core/CPU.cpp \ # Core ../GPU/GLES/TransformPipeline.cpp \ ../GPU/GLES/VertexDecoder.cpp \ ../GPU/GLES/VertexShaderGenerator.cpp \ + ../GPU/GeDisasm.cpp \ + ../GPU/GPUCommon.cpp \ ../GPU/GPUState.cpp \ ../GPU/Math3D.cpp \ ../GPU/Null/NullGpu.cpp \ # Kirk @@ -245,6 +247,8 @@ HEADERS += ../Core/CPU.h \ ../GPU/GLES/VertexDecoder.h \ ../GPU/GLES/VertexShaderGenerator.h \ ../GPU/GPUInterface.h \ + ../GPU/GeDisasm.h \ + ../GPU/GPUCommon.h \ ../GPU/GPUState.h \ ../GPU/Math3D.h \ ../GPU/Null/NullGpu.h \ diff --git a/Windows/EmuThread.cpp b/Windows/EmuThread.cpp index f6795df085..b03761a956 100644 --- a/Windows/EmuThread.cpp +++ b/Windows/EmuThread.cpp @@ -78,6 +78,7 @@ DWORD TheThread(LPVOID x) coreParameter.outputHeight = 272 * g_Config.iWindowZoom; coreParameter.pixelWidth = 480 * g_Config.iWindowZoom; coreParameter.pixelHeight = 272 * g_Config.iWindowZoom; + coreParameter.startPaused = !g_Config.bAutoRun; std::string error_string; if (!PSP_Init(coreParameter, &error_string)) @@ -89,20 +90,8 @@ DWORD TheThread(LPVOID x) INFO_LOG(BOOT, "Done."); _dbg_update_(); - if (g_Config.bAutoRun) - { -#ifdef _DEBUG - host->UpdateDisassembly(); -#endif - Core_EnableStepping(FALSE); - } - else - { -#ifdef _DEBUG - host->UpdateDisassembly(); -#endif - Core_EnableStepping(TRUE); - } + host->UpdateDisassembly(); + Core_EnableStepping(coreParameter.startPaused ? TRUE : FALSE); g_State.bBooted = true; #ifdef _DEBUG diff --git a/Windows/KeyboardDevice.cpp b/Windows/KeyboardDevice.cpp index 8d3cf30c37..dfbf67e39e 100644 --- a/Windows/KeyboardDevice.cpp +++ b/Windows/KeyboardDevice.cpp @@ -43,10 +43,10 @@ int KeyboardDevice::UpdateState() { switch (analog_ctrl_map[i + 1]) { case CTRL_UP: - analogY -= .8f; + analogY += .8f; break; case CTRL_DOWN: - analogY += .8f; + analogY -= .8f; break; case CTRL_LEFT: analogX -= .8f; diff --git a/Windows/PPSSPP.vcxproj b/Windows/PPSSPP.vcxproj index 4d14a6d2e3..bd233cf1af 100644 --- a/Windows/PPSSPP.vcxproj +++ b/Windows/PPSSPP.vcxproj @@ -112,9 +112,10 @@ Windows MachineX86 $(OutDir)$(TargetName)$(TargetExt) - true + false true 0x00400000 + true @@ -139,7 +140,7 @@ $(OutDir)$(ProjectName).pdb Windows MachineX64 - true + false true 0x00400000 @@ -175,6 +176,11 @@ MachineX86 /ignore:4049 %(AdditionalOptions) UseLinkTimeCodeGeneration + 0x00400000 + false + true + true + true diff --git a/Windows/WindowsHost.cpp b/Windows/WindowsHost.cpp index 7dc6b109ee..f82d722f9a 100644 --- a/Windows/WindowsHost.cpp +++ b/Windows/WindowsHost.cpp @@ -38,7 +38,7 @@ void WindowsHost::ShutdownGL() void WindowsHost::SetWindowTitle(const char *message) { // Really need a better way to deal with versions. - std::string title = "PPSSPP v0.4 - "; + std::string title = "PPSSPP v0.5 - "; title += message; int size = MultiByteToWideChar(CP_UTF8, 0, message, title.size(), NULL, 0); diff --git a/Windows/WndMainWindow.cpp b/Windows/WndMainWindow.cpp index 5237026b8b..76dcd65786 100644 --- a/Windows/WndMainWindow.cpp +++ b/Windows/WndMainWindow.cpp @@ -1,7 +1,7 @@ // NOTE: Apologies for the quality of this code, this is really from pre-opensource Dolphin - that is, 2003. -#define programname "PPSSPP v0.4" +#define programname "PPSSPP v0.5" #include @@ -61,6 +61,7 @@ namespace MainWindow LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK DisplayProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK About(HWND, UINT, WPARAM, LPARAM); + LRESULT CALLBACK Controls(HWND, UINT, WPARAM, LPARAM); HWND GetHWND() { @@ -312,7 +313,14 @@ namespace MainWindow UpdateMenus(); break; - case ID_FILE_LOADSTATE: + case ID_FILE_LOADSTATEFILE: + if (g_State.bEmuThreadStarted) + { + nextState = Core_IsStepping() ? CORE_STEPPING : CORE_RUNNING; + for (int i=0; iGetDlgHandle(), WM_COMMAND, IDC_STOP, 0); + } 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)); @@ -320,7 +328,14 @@ namespace MainWindow } break; - case ID_FILE_SAVESTATE: + case ID_FILE_SAVESTATEFILE: + if (g_State.bEmuThreadStarted) + { + nextState = Core_IsStepping() ? CORE_STEPPING : CORE_RUNNING; + for (int i=0; iGetDlgHandle(), WM_COMMAND, IDC_STOP, 0); + } 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)); @@ -328,6 +343,32 @@ namespace MainWindow } break; + // TODO: Add UI for multiple slots + + case ID_FILE_QUICKLOADSTATE: + if (g_State.bEmuThreadStarted) + { + nextState = Core_IsStepping() ? CORE_STEPPING : CORE_RUNNING; + for (int i=0; iGetDlgHandle(), WM_COMMAND, IDC_STOP, 0); + } + SetCursor(LoadCursor(0,IDC_WAIT)); + SaveState::LoadSlot(0, SaveStateActionFinished); + break; + + case ID_FILE_QUICKSAVESTATE: + if (g_State.bEmuThreadStarted) + { + nextState = Core_IsStepping() ? CORE_STEPPING : CORE_RUNNING; + for (int i=0; iGetDlgHandle(), WM_COMMAND, IDC_STOP, 0); + } + SetCursor(LoadCursor(0,IDC_WAIT)); + SaveState::SaveSlot(0, SaveStateActionFinished); + break; + case ID_OPTIONS_SCREEN1X: SetZoom(1); UpdateMenus(); @@ -380,6 +421,10 @@ namespace MainWindow UpdateMenus(); break; + case ID_EMULATION_RUNONLOAD: + g_Config.bAutoRun = !g_Config.bAutoRun; + UpdateMenus(); + break; //case ID_CPU_RESET: // MessageBox(hwndMain,"Use the controls in the disasm window for now..","Sorry",0); // Update(); @@ -503,6 +548,16 @@ namespace MainWindow g_Config.bFastMemory = !g_Config.bFastMemory; UpdateMenus(); break; + case ID_OPTIONS_LINEARFILTERING: + g_Config.bLinearFiltering = !g_Config.bLinearFiltering; + UpdateMenus(); + break; + case ID_OPTIONS_CONTROLS: + DialogManager::EnableAll(FALSE); + DialogBox(hInst, (LPCTSTR)IDD_CONTROLS, hWnd, (DLGPROC)Controls); + DialogManager::EnableAll(TRUE); + break; + ////////////////////////////////////////////////////////////////////////// @@ -593,7 +648,7 @@ namespace MainWindow } else */ - return DefWindowProc(hWnd,message,wParam,lParam); + return DefWindowProc(hWnd,message,wParam,lParam); // case WM_LBUTTONDOWN: // TrackPopupMenu(menu,0,0,0,0,hWnd,0); // break; @@ -648,6 +703,8 @@ namespace MainWindow CHECKITEM(ID_OPTIONS_WIREFRAME, g_Config.bDrawWireframe); CHECKITEM(ID_OPTIONS_HARDWARETRANSFORM, g_Config.bHardwareTransform); CHECKITEM(ID_OPTIONS_FASTMEMORY, g_Config.bFastMemory); + CHECKITEM(ID_OPTIONS_LINEARFILTERING, g_Config.bLinearFiltering); + CHECKITEM(ID_EMULATION_RUNONLOAD, g_Config.bAutoRun); UINT enable = !Core_IsStepping() ? MF_GRAYED : MF_ENABLED; EnableMenuItem(menu,ID_EMULATION_RUN, g_State.bEmuThreadStarted ? enable : MF_GRAYED); @@ -656,6 +713,10 @@ namespace MainWindow enable = g_State.bEmuThreadStarted ? MF_GRAYED : MF_ENABLED; EnableMenuItem(menu,ID_FILE_LOAD,enable); + EnableMenuItem(menu,ID_FILE_SAVESTATEFILE,!enable); + EnableMenuItem(menu,ID_FILE_LOADSTATEFILE,!enable); + EnableMenuItem(menu,ID_FILE_QUICKSAVESTATE,!enable); + EnableMenuItem(menu,ID_FILE_QUICKLOADSTATE,!enable); EnableMenuItem(menu,ID_CPU_DYNAREC,enable); EnableMenuItem(menu,ID_CPU_INTERPRETER,enable); EnableMenuItem(menu,ID_CPU_FASTINTERPRETER,enable); @@ -697,6 +758,53 @@ namespace MainWindow return FALSE; } + const char *controllist[] = { + "Start\tSpace", + "Select\tV", + "Square\tA", + "Triangle\tS", + "Circle\tX", + "Cross\tZ", + "Left Trigger\tQ", + "Right Trigger\tW", + "Up\tArrow Up", + "Down\tArrow Down", + "Left\tArrow Left", + "Right\tArrow Right", + "Analog Up\tI", + "Analog Down\tK", + "Analog Left\tJ", + "Analog Right\tL", + }; + // Message handler for about box. + LRESULT CALLBACK Controls(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) + { + switch (message) + { + case WM_INITDIALOG: + W32Util::CenterWindow(hDlg); + { + // TODO: connect to keyboard device instead + HWND list = GetDlgItem(hDlg, IDC_LISTCONTROLS); + int stops[1] = {80}; + SendMessage(list, LB_SETTABSTOPS, 1, (LPARAM)stops); + for (int i = 0; i < sizeof(controllist)/sizeof(controllist[0]); i++) { + SendMessage(list, LB_INSERTSTRING, -1, (LPARAM)controllist[i]); + } + } + return TRUE; + + case WM_COMMAND: + if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) + { + EndDialog(hDlg, LOWORD(wParam)); + return TRUE; + } + break; + } + return FALSE; + } + void Update() { InvalidateRect(hwndDisplay,0,0); @@ -767,12 +875,19 @@ namespace MainWindow } } - void SaveStateActionFinished(bool result) + void SaveStateActionFinished(bool result, void *userdata) { // TODO: Improve messaging? if (!result) MessageBox(0, "Savestate failure. Please try again later.", "Sorry", MB_OK); SetCursor(LoadCursor(0, IDC_ARROW)); + + if (g_State.bEmuThreadStarted && nextState == CORE_RUNNING) + { + for (int i=0; iGetDlgHandle(), WM_COMMAND, IDC_GO, 0); + } } void SetNextState(CoreState state) diff --git a/Windows/WndMainWindow.h b/Windows/WndMainWindow.h index b58823ca25..11e7d4c2cb 100644 --- a/Windows/WndMainWindow.h +++ b/Windows/WndMainWindow.h @@ -17,7 +17,7 @@ namespace MainWindow void SetPlaying(const char*text); void BrowseAndBoot(); void SetNextState(CoreState state); - void SaveStateActionFinished(bool result); + void SaveStateActionFinished(bool result, void *userdata); void _ViewFullScreen(HWND hWnd); void _ViewNormal(HWND hWnd); } diff --git a/Windows/XinputDevice.cpp b/Windows/XinputDevice.cpp index 6f7687f49b..86fc0dab64 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 d2f9b186ac..8bc5bb5260 100644 --- a/Windows/main.cpp +++ b/Windows/main.cpp @@ -21,6 +21,7 @@ #include "file/zip_read.h" #include "../Core/Config.h" +#include "../Core/SaveState.h" #include "EmuThread.h" #include "LogManager.h" @@ -53,8 +54,8 @@ int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLin const char *fileToStart = NULL; const char *fileToLog = NULL; + const char *stateToLoad = NULL; bool hideLog = true; - bool autoRun = true; #ifdef _DEBUG hideLog = false; @@ -75,24 +76,32 @@ int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLin { case 'j': g_Config.iCpuCore = CPU_JIT; + g_Config.bSaveSettings = false; break; case 'i': g_Config.iCpuCore = CPU_INTERPRETER; + g_Config.bSaveSettings = false; break; case 'f': g_Config.iCpuCore = CPU_FASTINTERPRETER; + g_Config.bSaveSettings = false; break; case 'l': hideLog = false; break; case 's': - autoRun = false; + g_Config.bAutoRun = false; + g_Config.bSaveSettings = 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="); + if (!strcmp(__argv[i], "--state") && i < __argc - 1) + stateToLoad = __argv[++i]; + if (!strncmp(__argv[i], "--state=", strlen("--state=")) && strlen(__argv[i]) > strlen("--state=")) + stateToLoad = __argv[i] + strlen("--state="); break; } } @@ -156,8 +165,8 @@ int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLin else MainWindow::BrowseAndBoot(); - if (autoRun) - MainWindow::SetNextState(CORE_RUNNING); + if (fileToStart != NULL && stateToLoad != NULL) + SaveState::Load(stateToLoad); //so.. we're at the message pump of the GUI thread MSG msg; diff --git a/Windows/ppsspp.rc b/Windows/ppsspp.rc index e9561bdac4..6b535444c5 100644 Binary files a/Windows/ppsspp.rc and b/Windows/ppsspp.rc differ diff --git a/Windows/resource.h b/Windows/resource.h index 134447f2a5..83c39ed5be 100644 --- a/Windows/resource.h +++ b/Windows/resource.h @@ -53,8 +53,8 @@ #define ID_DEBUG_BREAKPOINTS 122 #define ID_FILE_LOAD_BIN 123 #define ID_FILE_LOAD_ISO 125 -#define ID_FILE_LOADSTATE 126 -#define ID_FILE_SAVESTATE 127 +#define ID_FILE_LOADSTATEFILE 126 +#define ID_FILE_SAVESTATEFILE 127 #define ID_EMULATION_RESET 130 #define IDD_ABOUTBOX 133 #define ID_DEBUG_LOADMAPFILE 134 @@ -107,6 +107,7 @@ #define IDI_STOP 223 #define IDD_INPUTBOX 226 #define IDD_VFPU 231 +#define IDD_CONTROLS 232 #define IDC_GO 1001 #define IDC_ADDRESS 1002 #define IDC_DEBUG_COUNT 1003 @@ -154,6 +155,7 @@ #define IDC_FILELIST 1150 #define IDC_BROWSE 1159 #define IDC_SHOWVFPU 1161 +#define IDC_LISTCONTROLS 1162 #define ID_FILE_BOOTISO 40001 #define ID_FILE_EXIT 40002 #define ID_CONFIG_SELECT_PLUGINS 40003 @@ -247,14 +249,20 @@ #define ID_OPTIONS_HARDWARETRANSFORM 40124 #define ID_OPTIONS_FASTMEMORY 40125 #define IDC_STEPHLE 40126 +#define ID_OPTIONS_LINEARFILTERING 40127 +#define ID_FILE_QUICKSAVESTATE 40128 +#define ID_FILE_QUICKLOADSTATE 40129 +#define ID_OPTIONS_CONTROLS 40130 +#define ID_EMULATION_RUNONLOAD 40131 +#define IDC_STATIC -1 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 232 -#define _APS_NEXT_COMMAND_VALUE 40127 -#define _APS_NEXT_CONTROL_VALUE 1162 +#define _APS_NEXT_RESOURCE_VALUE 233 +#define _APS_NEXT_COMMAND_VALUE 40132 +#define _APS_NEXT_CONTROL_VALUE 1163 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index 719be33b5a..9f3ce7a4e4 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -1,8 +1,8 @@ + android:versionCode="5" + android:versionName="0.5" > diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 634edc6bf1..5265b4bb7b 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -53,6 +53,8 @@ LOCAL_SRC_FILES := \ $(SRC)/ext/libkirk/bn.c \ $(SRC)/ext/libkirk/ec.c \ $(SRC)/ext/libkirk/kirk_engine.c \ + $(SRC)/ext/snappy/snappy-c.cpp \ + $(SRC)/ext/snappy/snappy.cpp \ $(SRC)/Common/ArmABI.cpp \ $(SRC)/Common/ArmEmitter.cpp \ $(SRC)/Common/LogManager.cpp \ @@ -68,6 +70,7 @@ LOCAL_SRC_FILES := \ $(SRC)/Common/Misc.cpp \ $(SRC)/Common/MathUtil.cpp \ $(SRC)/GPU/Math3D.cpp \ + $(SRC)/GPU/GPUCommon.cpp \ $(SRC)/GPU/GPUState.cpp \ $(SRC)/GPU/GeDisasm.cpp \ $(SRC)/GPU/GLES/Framebuffer.cpp \ @@ -148,7 +151,6 @@ LOCAL_SRC_FILES := \ $(SRC)/Core/HLE/sceUsb.cpp \ $(SRC)/Core/HLE/sceUtility.cpp \ $(SRC)/Core/HLE/sceVaudio.cpp \ - $(SRC)/Core/HLE/scesupPreAcc.cpp \ $(SRC)/Core/FileSystems/BlockDevices.cpp \ $(SRC)/Core/FileSystems/ISOFileSystem.cpp \ $(SRC)/Core/FileSystems/MetaFileSystem.cpp \ diff --git a/android/jni/EmuScreen.cpp b/android/jni/EmuScreen.cpp index 69a3696b09..096d80d163 100644 --- a/android/jni/EmuScreen.cpp +++ b/android/jni/EmuScreen.cpp @@ -59,13 +59,14 @@ EmuScreen::EmuScreen(const std::string &filename) : invalid_(true) coreParam.enableDebugging = false; coreParam.printfEmuLog = false; coreParam.headLess = false; - coreParam.renderWidth = 480; - coreParam.renderHeight = 272; + if (g_Config.iWindowZoom < 1 || g_Config.iWindowZoom > 2) + g_Config.iWindowZoom = 1; + coreParam.renderWidth = 480 * g_Config.iWindowZoom; + coreParam.renderHeight = 272 * g_Config.iWindowZoom; coreParam.outputWidth = dp_xres; coreParam.outputHeight = dp_yres; coreParam.pixelWidth = pixel_xres; coreParam.pixelHeight = pixel_yres; - std::string error_string; if (PSP_Init(coreParam, &error_string)) { invalid_ = false; @@ -79,7 +80,6 @@ EmuScreen::EmuScreen(const std::string &filename) : invalid_(true) LayoutGamepad(dp_xres, dp_yres); NOTICE_LOG(BOOT, "Loading %s...", fileToStart.c_str()); - coreState = CORE_RUNNING; } EmuScreen::~EmuScreen() diff --git a/android/jni/MenuScreens.cpp b/android/jni/MenuScreens.cpp index b35ae132a7..da8ca19ddc 100644 --- a/android/jni/MenuScreens.cpp +++ b/android/jni/MenuScreens.cpp @@ -29,6 +29,7 @@ #include "ui/ui.h" #include "ui_atlas.h" #include "util/random/rng.h" +#include "util/text/utf8.h" #include "UIShader.h" #include "../../GPU/ge_constants.h" @@ -36,10 +37,16 @@ #include "../../GPU/GPUInterface.h" #include "../../Core/Config.h" #include "../../Core/CoreParameter.h" +#include "../../Core/SaveState.h" #include "MenuScreens.h" #include "EmuScreen.h" + +// Ugly communication with NativeApp +extern std::string game_title; + + static const int symbols[4] = { I_CROSS, I_CIRCLE, @@ -150,7 +157,7 @@ void MenuScreen::render() { ui_draw2d.DrawTextShadow(UBUNTU48, "PPSSPP", dp_xres + xoff - w/2, 80, 0xFFFFFFFF, ALIGN_HCENTER | ALIGN_BOTTOM); ui_draw2d.SetFontScale(0.7f, 0.7f); - ui_draw2d.DrawTextShadow(UBUNTU24, "V0.4", dp_xres + xoff, 80, 0xFFFFFFFF, ALIGN_RIGHT | ALIGN_BOTTOM); + ui_draw2d.DrawTextShadow(UBUNTU24, "v0.5", dp_xres + xoff, 80, 0xFFFFFFFF, ALIGN_RIGHT | ALIGN_BOTTOM); ui_draw2d.SetFontScale(1.0f, 1.0f); VLinear vlinear(dp_xres + xoff, 95, 20); @@ -207,23 +214,41 @@ void InGameMenuScreen::render() { UIBegin(); DrawBackground(1.0f); - ui_draw2d.DrawText(UBUNTU48, "Emulation Paused", dp_xres / 2, 30, 0xFFFFFFFF, ALIGN_HCENTER); + const char *title; + if (UTF8StringHasNonASCII(game_title.c_str())) { + title = "(can't display japanese title)"; + } else { + title = game_title.c_str(); + } + + ui_draw2d.DrawText(UBUNTU48, title, 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); + UICheckBox(GEN_ID, x, y += 50, "Show Debug Statistics", ALIGN_TOPLEFT, &g_Config.bShowDebugStats); + UICheckBox(GEN_ID, x, y += 50, "Hardware Transform", ALIGN_TOPLEFT, &g_Config.bHardwareTransform); + + // TODO: Add UI for more than one slot. + VLinear vlinear1(x, y + 80, 20); + UIText(UBUNTU24, vlinear1, "Save states are experimental (and large)", 0xFFFFFFFF); + if (UIButton(GEN_ID, vlinear1, LARGE_BUTTON_WIDTH, "Save State", ALIGN_LEFT)) { + SaveState::SaveSlot(0, 0, 0); + screenManager()->finishDialog(this, DR_CANCEL); + } + if (UIButton(GEN_ID, vlinear1, LARGE_BUTTON_WIDTH, "Load State", ALIGN_LEFT)) { + SaveState::LoadSlot(0, 0, 0); + screenManager()->finishDialog(this, DR_CANCEL); + } VLinear vlinear(dp_xres - 10, 160, 20); if (UIButton(GEN_ID, vlinear, LARGE_BUTTON_WIDTH, "Continue", ALIGN_RIGHT)) { screenManager()->finishDialog(this, DR_CANCEL); } - 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)) { + + if (UIButton(GEN_ID, Pos(dp_xres - 10, dp_yres - 10), LARGE_BUTTON_WIDTH*2, "Debug: Dump Next Frame", ALIGN_BOTTOMRIGHT)) { gpu->DumpNextFrame(); } @@ -251,14 +276,19 @@ void SettingsScreen::render() { // VLinear vlinear(10, 80, 10); int x = 30; int y = 50; - 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); + UICheckBox(GEN_ID, x, y += 45, "Sound Emulation", ALIGN_TOPLEFT, &g_Config.bEnableSound); + UICheckBox(GEN_ID, x, y += 45, "Buffered Rendering", ALIGN_TOPLEFT, &g_Config.bBufferedRendering); + if (g_Config.bBufferedRendering) { + bool doubleRes = g_Config.iWindowZoom == 2; + UICheckBox(GEN_ID, x + 50, y += 50, "2x Render Resolution", ALIGN_TOPLEFT, &doubleRes); + g_Config.iWindowZoom = doubleRes ? 2 : 1; + } + UICheckBox(GEN_ID, x, y += 45, "Hardware Transform", 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); + UICheckBox(GEN_ID, x, y += 45, "Slightly faster interpreter (may crash)", ALIGN_TOPLEFT, &useFastInt); // ui_draw2d.DrawText(UBUNTU48, "much faster JIT coming later", x, y+=50, 0xcFFFFFFF, ALIGN_LEFT); - UICheckBox(GEN_ID, x, y += 50, "On-screen Touch Controls", ALIGN_TOPLEFT, &g_Config.bShowTouchControls); + UICheckBox(GEN_ID, x, y += 45, "On-screen Touch Controls", ALIGN_TOPLEFT, &g_Config.bShowTouchControls); if (g_Config.bShowTouchControls) UICheckBox(GEN_ID, x + 50, y += 50, "Show Analog Stick", ALIGN_TOPLEFT, &g_Config.bShowAnalogStick); g_Config.iCpuCore = useFastInt ? CPU_FASTINTERPRETER : CPU_INTERPRETER; @@ -375,7 +405,7 @@ void CreditsScreen::update(InputState &input_state) { static const char *credits[] = { - "PPSSPP v0.4", + "PPSSPP v0.5", "", "", "A fast and portable PSP emulator", diff --git a/android/jni/NativeApp.cpp b/android/jni/NativeApp.cpp index d732413783..9faf23f009 100644 --- a/android/jni/NativeApp.cpp +++ b/android/jni/NativeApp.cpp @@ -53,6 +53,7 @@ Texture *uiTexture; ScreenManager *screenManager; std::string config_filename; +std::string game_title; class AndroidLogger : public LogListener { @@ -112,6 +113,9 @@ public: virtual bool AttemptLoadSymbolMap() {return false;} virtual void ResetSymbolMap() {} virtual void AddSymbol(std::string name, u32 addr, u32 size, int type=0) {} + virtual void SetWindowTitle(const char *message) { + game_title = message; + } }; // globals @@ -232,7 +236,13 @@ void NativeInit(int argc, const char *argv[], const char *savegame_directory, co #endif } -#if defined(ANDROID) || defined(BLACKBERRY) || defined(__SYMBIAN32__) +#if defined(ANDROID) + // Maybe there should be an option to use internal memory instead, but I think + // that for most people, using external memory (SDCard/USB Storage) makes the + // most sense. + g_Config.memCardDirectory = std::string(external_directory) + "/"; + g_Config.flashDirectory = std::string(external_directory)+"/flash/"; +#elif defined(BLACKBERRY) || defined(__SYMBIAN32__) g_Config.memCardDirectory = user_data_path; g_Config.flashDirectory = user_data_path+"/flash/"; #else diff --git a/ext/libkirk/AES.c b/ext/libkirk/AES.c index fc80f348b9..c75748510b 100644 --- a/ext/libkirk/AES.c +++ b/ext/libkirk/AES.c @@ -37,11 +37,11 @@ //CMAC GLOBS #define AES_128 0 -unsigned char const_Rb[16] = { +const unsigned char const_Rb[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87 }; -unsigned char const_Zero[16] = { +const unsigned char const_Zero[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; @@ -1294,7 +1294,7 @@ void AES_encrypt(AES_ctx *ctx, const u8 *src, u8 *dst) rijndaelEncrypt(ctx->ek, ctx->Nr, src, dst); } -void xor_128(unsigned char *a, unsigned char *b, unsigned char *out) +void xor_128(const unsigned char *a, const unsigned char *b, unsigned char *out) { int i; for (i=0;i<16; i++) diff --git a/ext/snappy/AUTHORS b/ext/snappy/AUTHORS new file mode 100644 index 0000000000..4858b377c7 --- /dev/null +++ b/ext/snappy/AUTHORS @@ -0,0 +1 @@ +opensource@google.com diff --git a/ext/snappy/COPYING b/ext/snappy/COPYING new file mode 100644 index 0000000000..8d6bd9fed4 --- /dev/null +++ b/ext/snappy/COPYING @@ -0,0 +1,28 @@ +Copyright 2011, Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/ext/snappy/readme.ppsspp.txt b/ext/snappy/readme.ppsspp.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/ext/snappy/snappy-c.cpp b/ext/snappy/snappy-c.cpp new file mode 100644 index 0000000000..473a0b0978 --- /dev/null +++ b/ext/snappy/snappy-c.cpp @@ -0,0 +1,90 @@ +// Copyright 2011 Martin Gieseking . +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "snappy.h" +#include "snappy-c.h" + +extern "C" { + +snappy_status snappy_compress(const char* input, + size_t input_length, + char* compressed, + size_t *compressed_length) { + if (*compressed_length < snappy_max_compressed_length(input_length)) { + return SNAPPY_BUFFER_TOO_SMALL; + } + snappy::RawCompress(input, input_length, compressed, compressed_length); + return SNAPPY_OK; +} + +snappy_status snappy_uncompress(const char* compressed, + size_t compressed_length, + char* uncompressed, + size_t* uncompressed_length) { + size_t real_uncompressed_length; + if (!snappy::GetUncompressedLength(compressed, + compressed_length, + &real_uncompressed_length)) { + return SNAPPY_INVALID_INPUT; + } + if (*uncompressed_length < real_uncompressed_length) { + return SNAPPY_BUFFER_TOO_SMALL; + } + if (!snappy::RawUncompress(compressed, compressed_length, uncompressed)) { + return SNAPPY_INVALID_INPUT; + } + *uncompressed_length = real_uncompressed_length; + return SNAPPY_OK; +} + +size_t snappy_max_compressed_length(size_t source_length) { + return snappy::MaxCompressedLength(source_length); +} + +snappy_status snappy_uncompressed_length(const char *compressed, + size_t compressed_length, + size_t *result) { + if (snappy::GetUncompressedLength(compressed, + compressed_length, + result)) { + return SNAPPY_OK; + } else { + return SNAPPY_INVALID_INPUT; + } +} + +snappy_status snappy_validate_compressed_buffer(const char *compressed, + size_t compressed_length) { + if (snappy::IsValidCompressedBuffer(compressed, compressed_length)) { + return SNAPPY_OK; + } else { + return SNAPPY_INVALID_INPUT; + } +} + +} // extern "C" diff --git a/ext/snappy/snappy-c.h b/ext/snappy/snappy-c.h new file mode 100644 index 0000000000..c6c2a860a4 --- /dev/null +++ b/ext/snappy/snappy-c.h @@ -0,0 +1,138 @@ +/* + * Copyright 2011 Martin Gieseking . + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Plain C interface (a wrapper around the C++ implementation). + */ + +#ifndef UTIL_SNAPPY_OPENSOURCE_SNAPPY_C_H_ +#define UTIL_SNAPPY_OPENSOURCE_SNAPPY_C_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* + * Return values; see the documentation for each function to know + * what each can return. + */ +typedef enum { + SNAPPY_OK = 0, + SNAPPY_INVALID_INPUT = 1, + SNAPPY_BUFFER_TOO_SMALL = 2 +} snappy_status; + +/* + * Takes the data stored in "input[0..input_length-1]" and stores + * it in the array pointed to by "compressed". + * + * signals the space available in "compressed". + * If it is not at least equal to "snappy_max_compressed_length(input_length)", + * SNAPPY_BUFFER_TOO_SMALL is returned. After successful compression, + * contains the true length of the compressed output, + * and SNAPPY_OK is returned. + * + * Example: + * size_t output_length = snappy_max_compressed_length(input_length); + * char* output = (char*)malloc(output_length); + * if (snappy_compress(input, input_length, output, &output_length) + * == SNAPPY_OK) { + * ... Process(output, output_length) ... + * } + * free(output); + */ +snappy_status snappy_compress(const char* input, + size_t input_length, + char* compressed, + size_t* compressed_length); + +/* + * Given data in "compressed[0..compressed_length-1]" generated by + * calling the snappy_compress routine, this routine stores + * the uncompressed data to + * uncompressed[0..uncompressed_length-1]. + * Returns failure (a value not equal to SNAPPY_OK) if the message + * is corrupted and could not be decrypted. + * + * signals the space available in "uncompressed". + * If it is not at least equal to the value returned by + * snappy_uncompressed_length for this stream, SNAPPY_BUFFER_TOO_SMALL + * is returned. After successful decompression, + * contains the true length of the decompressed output. + * + * Example: + * size_t output_length; + * if (snappy_uncompressed_length(input, input_length, &output_length) + * != SNAPPY_OK) { + * ... fail ... + * } + * char* output = (char*)malloc(output_length); + * if (snappy_uncompress(input, input_length, output, &output_length) + * == SNAPPY_OK) { + * ... Process(output, output_length) ... + * } + * free(output); + */ +snappy_status snappy_uncompress(const char* compressed, + size_t compressed_length, + char* uncompressed, + size_t* uncompressed_length); + +/* + * Returns the maximal size of the compressed representation of + * input data that is "source_length" bytes in length. + */ +size_t snappy_max_compressed_length(size_t source_length); + +/* + * REQUIRES: "compressed[]" was produced by snappy_compress() + * Returns SNAPPY_OK and stores the length of the uncompressed data in + * *result normally. Returns SNAPPY_INVALID_INPUT on parsing error. + * This operation takes O(1) time. + */ +snappy_status snappy_uncompressed_length(const char* compressed, + size_t compressed_length, + size_t* result); + +/* + * Check if the contents of "compressed[]" can be uncompressed successfully. + * Does not return the uncompressed data; if so, returns SNAPPY_OK, + * or if not, returns SNAPPY_INVALID_INPUT. + * Takes time proportional to compressed_length, but is usually at least a + * factor of four faster than actual decompression. + */ +snappy_status snappy_validate_compressed_buffer(const char* compressed, + size_t compressed_length); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif /* UTIL_SNAPPY_OPENSOURCE_SNAPPY_C_H_ */ diff --git a/ext/snappy/snappy-internal.h b/ext/snappy/snappy-internal.h new file mode 100644 index 0000000000..c99d33130b --- /dev/null +++ b/ext/snappy/snappy-internal.h @@ -0,0 +1,150 @@ +// Copyright 2008 Google Inc. All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Internals shared between the Snappy implementation and its unittest. + +#ifndef UTIL_SNAPPY_SNAPPY_INTERNAL_H_ +#define UTIL_SNAPPY_SNAPPY_INTERNAL_H_ + +#include "snappy-stubs-internal.h" + +namespace snappy { +namespace internal { + +class WorkingMemory { + public: + WorkingMemory() : large_table_(NULL) { } + ~WorkingMemory() { delete[] large_table_; } + + // Allocates and clears a hash table using memory in "*this", + // stores the number of buckets in "*table_size" and returns a pointer to + // the base of the hash table. + uint16* GetHashTable(size_t input_size, int* table_size); + + private: + uint16 small_table_[1<<10]; // 2KB + uint16* large_table_; // Allocated only when needed + + DISALLOW_COPY_AND_ASSIGN(WorkingMemory); +}; + +// Flat array compression that does not emit the "uncompressed length" +// prefix. Compresses "input" string to the "*op" buffer. +// +// REQUIRES: "input_length <= kBlockSize" +// REQUIRES: "op" points to an array of memory that is at least +// "MaxCompressedLength(input_length)" in size. +// REQUIRES: All elements in "table[0..table_size-1]" are initialized to zero. +// REQUIRES: "table_size" is a power of two +// +// Returns an "end" pointer into "op" buffer. +// "end - op" is the compressed size of "input". +char* CompressFragment(const char* input, + size_t input_length, + char* op, + uint16* table, + const int table_size); + +// Return the largest n such that +// +// s1[0,n-1] == s2[0,n-1] +// and n <= (s2_limit - s2). +// +// Does not read *s2_limit or beyond. +// Does not read *(s1 + (s2_limit - s2)) or beyond. +// Requires that s2_limit >= s2. +// +// Separate implementation for x86_64, for speed. Uses the fact that +// x86_64 is little endian. +#if defined(ARCH_K8) +static inline int FindMatchLength(const char* s1, + const char* s2, + const char* s2_limit) { + assert(s2_limit >= s2); + int matched = 0; + + // Find out how long the match is. We loop over the data 64 bits at a + // time until we find a 64-bit block that doesn't match; then we find + // the first non-matching bit and use that to calculate the total + // length of the match. + while (PREDICT_TRUE(s2 <= s2_limit - 8)) { + if (PREDICT_FALSE(UNALIGNED_LOAD64(s2) == UNALIGNED_LOAD64(s1 + matched))) { + s2 += 8; + matched += 8; + } else { + // On current (mid-2008) Opteron models there is a 3% more + // efficient code sequence to find the first non-matching byte. + // However, what follows is ~10% better on Intel Core 2 and newer, + // and we expect AMD's bsf instruction to improve. + uint64 x = UNALIGNED_LOAD64(s2) ^ UNALIGNED_LOAD64(s1 + matched); + int matching_bits = Bits::FindLSBSetNonZero64(x); + matched += matching_bits >> 3; + return matched; + } + } + while (PREDICT_TRUE(s2 < s2_limit)) { + if (PREDICT_TRUE(s1[matched] == *s2)) { + ++s2; + ++matched; + } else { + return matched; + } + } + return matched; +} +#else +static inline int FindMatchLength(const char* s1, + const char* s2, + const char* s2_limit) { + // Implementation based on the x86-64 version, above. + assert(s2_limit >= s2); + int matched = 0; + + while (s2 <= s2_limit - 4 && + UNALIGNED_LOAD32(s2) == UNALIGNED_LOAD32(s1 + matched)) { + s2 += 4; + matched += 4; + } + if (LittleEndian::IsLittleEndian() && s2 <= s2_limit - 4) { + uint32 x = UNALIGNED_LOAD32(s2) ^ UNALIGNED_LOAD32(s1 + matched); + int matching_bits = Bits::FindLSBSetNonZero(x); + matched += matching_bits >> 3; + } else { + while ((s2 < s2_limit) && (s1[matched] == *s2)) { + ++s2; + ++matched; + } + } + return matched; +} +#endif + +} // end namespace internal +} // end namespace snappy + +#endif // UTIL_SNAPPY_SNAPPY_INTERNAL_H_ diff --git a/ext/snappy/snappy-sinksource.h b/ext/snappy/snappy-sinksource.h new file mode 100644 index 0000000000..faabfa1e6f --- /dev/null +++ b/ext/snappy/snappy-sinksource.h @@ -0,0 +1,137 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef UTIL_SNAPPY_SNAPPY_SINKSOURCE_H_ +#define UTIL_SNAPPY_SNAPPY_SINKSOURCE_H_ + +#include + + +namespace snappy { + +// A Sink is an interface that consumes a sequence of bytes. +class Sink { + public: + Sink() { } + virtual ~Sink(); + + // Append "bytes[0,n-1]" to this. + virtual void Append(const char* bytes, size_t n) = 0; + + // Returns a writable buffer of the specified length for appending. + // May return a pointer to the caller-owned scratch buffer which + // must have at least the indicated length. The returned buffer is + // only valid until the next operation on this Sink. + // + // After writing at most "length" bytes, call Append() with the + // pointer returned from this function and the number of bytes + // written. Many Append() implementations will avoid copying + // bytes if this function returned an internal buffer. + // + // If a non-scratch buffer is returned, the caller may only pass a + // prefix of it to Append(). That is, it is not correct to pass an + // interior pointer of the returned array to Append(). + // + // The default implementation always returns the scratch buffer. + virtual char* GetAppendBuffer(size_t length, char* scratch); + + + private: + // No copying + Sink(const Sink&); + void operator=(const Sink&); +}; + +// A Source is an interface that yields a sequence of bytes +class Source { + public: + Source() { } + virtual ~Source(); + + // Return the number of bytes left to read from the source + virtual size_t Available() const = 0; + + // Peek at the next flat region of the source. Does not reposition + // the source. The returned region is empty iff Available()==0. + // + // Returns a pointer to the beginning of the region and store its + // length in *len. + // + // The returned region is valid until the next call to Skip() or + // until this object is destroyed, whichever occurs first. + // + // The returned region may be larger than Available() (for example + // if this ByteSource is a view on a substring of a larger source). + // The caller is responsible for ensuring that it only reads the + // Available() bytes. + virtual const char* Peek(size_t* len) = 0; + + // Skip the next n bytes. Invalidates any buffer returned by + // a previous call to Peek(). + // REQUIRES: Available() >= n + virtual void Skip(size_t n) = 0; + + private: + // No copying + Source(const Source&); + void operator=(const Source&); +}; + +// A Source implementation that yields the contents of a flat array +class ByteArraySource : public Source { + public: + ByteArraySource(const char* p, size_t n) : ptr_(p), left_(n) { } + virtual ~ByteArraySource(); + virtual size_t Available() const; + virtual const char* Peek(size_t* len); + virtual void Skip(size_t n); + private: + const char* ptr_; + size_t left_; +}; + +// A Sink implementation that writes to a flat array without any bound checks. +class UncheckedByteArraySink : public Sink { + public: + explicit UncheckedByteArraySink(char* dest) : dest_(dest) { } + virtual ~UncheckedByteArraySink(); + virtual void Append(const char* data, size_t n); + virtual char* GetAppendBuffer(size_t len, char* scratch); + + // Return the current output pointer so that a caller can see how + // many bytes were produced. + // Note: this is not a Sink method. + char* CurrentDestination() const { return dest_; } + private: + char* dest_; +}; + + +} + +#endif // UTIL_SNAPPY_SNAPPY_SINKSOURCE_H_ diff --git a/ext/snappy/snappy-stubs-internal.h b/ext/snappy/snappy-stubs-internal.h new file mode 100644 index 0000000000..12393b6289 --- /dev/null +++ b/ext/snappy/snappy-stubs-internal.h @@ -0,0 +1,491 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Various stubs for the open-source version of Snappy. + +#ifndef UTIL_SNAPPY_OPENSOURCE_SNAPPY_STUBS_INTERNAL_H_ +#define UTIL_SNAPPY_OPENSOURCE_SNAPPY_STUBS_INTERNAL_H_ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include +#include +#include + +#ifdef HAVE_SYS_MMAN_H +#include +#endif + +#include "snappy-stubs-public.h" + +#if defined(__x86_64__) + +// Enable 64-bit optimized versions of some routines. +#define ARCH_K8 1 + +#endif + +// Needed by OS X, among others. +#ifndef MAP_ANONYMOUS +#define MAP_ANONYMOUS MAP_ANON +#endif + +// Pull in std::min, std::ostream, and the likes. This is safe because this +// header file is never used from any public header files. +using namespace std; + +// The size of an array, if known at compile-time. +// Will give unexpected results if used on a pointer. +// We undefine it first, since some compilers already have a definition. +#ifdef ARRAYSIZE +#undef ARRAYSIZE +#endif +#define ARRAYSIZE(a) (sizeof(a) / sizeof(*(a))) + +// Static prediction hints. +#ifdef HAVE_BUILTIN_EXPECT +#define PREDICT_FALSE(x) (__builtin_expect(x, 0)) +#define PREDICT_TRUE(x) (__builtin_expect(!!(x), 1)) +#else +#define PREDICT_FALSE(x) x +#define PREDICT_TRUE(x) x +#endif + +// This is only used for recomputing the tag byte table used during +// decompression; for simplicity we just remove it from the open-source +// version (anyone who wants to regenerate it can just do the call +// themselves within main()). +#define DEFINE_bool(flag_name, default_value, description) \ + bool FLAGS_ ## flag_name = default_value +#define DECLARE_bool(flag_name) \ + extern bool FLAGS_ ## flag_name + +namespace snappy { + +static const uint32 kuint32max = static_cast(0xFFFFFFFF); +static const int64 kint64max = static_cast(0x7FFFFFFFFFFFFFFFLL); + +// Potentially unaligned loads and stores. + +// x86 and PowerPC can simply do these loads and stores native. + +#if defined(__i386__) || defined(__x86_64__) || defined(__powerpc__) + +#define UNALIGNED_LOAD16(_p) (*reinterpret_cast(_p)) +#define UNALIGNED_LOAD32(_p) (*reinterpret_cast(_p)) +#define UNALIGNED_LOAD64(_p) (*reinterpret_cast(_p)) + +#define UNALIGNED_STORE16(_p, _val) (*reinterpret_cast(_p) = (_val)) +#define UNALIGNED_STORE32(_p, _val) (*reinterpret_cast(_p) = (_val)) +#define UNALIGNED_STORE64(_p, _val) (*reinterpret_cast(_p) = (_val)) + +// ARMv7 and newer support native unaligned accesses, but only of 16-bit +// and 32-bit values (not 64-bit); older versions either raise a fatal signal, +// do an unaligned read and rotate the words around a bit, or do the reads very +// slowly (trip through kernel mode). There's no simple #define that says just +// “ARMv7 or higher”, so we have to filter away all ARMv5 and ARMv6 +// sub-architectures. +// +// This is a mess, but there's not much we can do about it. + +#elif defined(__arm__) && \ + !defined(__ARM_ARCH_4__) && \ + !defined(__ARM_ARCH_4T__) && \ + !defined(__ARM_ARCH_5__) && \ + !defined(__ARM_ARCH_5T__) && \ + !defined(__ARM_ARCH_5TE__) && \ + !defined(__ARM_ARCH_5TEJ__) && \ + !defined(__ARM_ARCH_6__) && \ + !defined(__ARM_ARCH_6J__) && \ + !defined(__ARM_ARCH_6K__) && \ + !defined(__ARM_ARCH_6Z__) && \ + !defined(__ARM_ARCH_6ZK__) && \ + !defined(__ARM_ARCH_6T2__) + +#define UNALIGNED_LOAD16(_p) (*reinterpret_cast(_p)) +#define UNALIGNED_LOAD32(_p) (*reinterpret_cast(_p)) + +#define UNALIGNED_STORE16(_p, _val) (*reinterpret_cast(_p) = (_val)) +#define UNALIGNED_STORE32(_p, _val) (*reinterpret_cast(_p) = (_val)) + +// TODO(user): NEON supports unaligned 64-bit loads and stores. +// See if that would be more efficient on platforms supporting it, +// at least for copies. + +inline uint64 UNALIGNED_LOAD64(const void *p) { + uint64 t; + memcpy(&t, p, sizeof t); + return t; +} + +inline void UNALIGNED_STORE64(void *p, uint64 v) { + memcpy(p, &v, sizeof v); +} + +#else + +// These functions are provided for architectures that don't support +// unaligned loads and stores. + +inline uint16 UNALIGNED_LOAD16(const void *p) { + uint16 t; + memcpy(&t, p, sizeof t); + return t; +} + +inline uint32 UNALIGNED_LOAD32(const void *p) { + uint32 t; + memcpy(&t, p, sizeof t); + return t; +} + +inline uint64 UNALIGNED_LOAD64(const void *p) { + uint64 t; + memcpy(&t, p, sizeof t); + return t; +} + +inline void UNALIGNED_STORE16(void *p, uint16 v) { + memcpy(p, &v, sizeof v); +} + +inline void UNALIGNED_STORE32(void *p, uint32 v) { + memcpy(p, &v, sizeof v); +} + +inline void UNALIGNED_STORE64(void *p, uint64 v) { + memcpy(p, &v, sizeof v); +} + +#endif + +// This can be more efficient than UNALIGNED_LOAD64 + UNALIGNED_STORE64 +// on some platforms, in particular ARM. +inline void UnalignedCopy64(const void *src, void *dst) { + if (sizeof(void *) == 8) { + UNALIGNED_STORE64(dst, UNALIGNED_LOAD64(src)); + } else { + const char *src_char = reinterpret_cast(src); + char *dst_char = reinterpret_cast(dst); + + UNALIGNED_STORE32(dst_char, UNALIGNED_LOAD32(src_char)); + UNALIGNED_STORE32(dst_char + 4, UNALIGNED_LOAD32(src_char + 4)); + } +} + +// The following guarantees declaration of the byte swap functions. +#ifdef WORDS_BIGENDIAN + +#ifdef HAVE_SYS_BYTEORDER_H +#include +#endif + +#ifdef HAVE_SYS_ENDIAN_H +#include +#endif + +#ifdef _MSC_VER +#include +#define bswap_16(x) _byteswap_ushort(x) +#define bswap_32(x) _byteswap_ulong(x) +#define bswap_64(x) _byteswap_uint64(x) + +#elif defined(__APPLE__) +// Mac OS X / Darwin features +#include +#define bswap_16(x) OSSwapInt16(x) +#define bswap_32(x) OSSwapInt32(x) +#define bswap_64(x) OSSwapInt64(x) + +#elif defined(HAVE_BYTESWAP_H) +#include + +#elif defined(bswap32) +// FreeBSD defines bswap{16,32,64} in (already #included). +#define bswap_16(x) bswap16(x) +#define bswap_32(x) bswap32(x) +#define bswap_64(x) bswap64(x) + +#elif defined(BSWAP_64) +// Solaris 10 defines BSWAP_{16,32,64} in (already #included). +#define bswap_16(x) BSWAP_16(x) +#define bswap_32(x) BSWAP_32(x) +#define bswap_64(x) BSWAP_64(x) + +#else + +inline uint16 bswap_16(uint16 x) { + return (x << 8) | (x >> 8); +} + +inline uint32 bswap_32(uint32 x) { + x = ((x & 0xff00ff00UL) >> 8) | ((x & 0x00ff00ffUL) << 8); + return (x >> 16) | (x << 16); +} + +inline uint64 bswap_64(uint64 x) { + x = ((x & 0xff00ff00ff00ff00ULL) >> 8) | ((x & 0x00ff00ff00ff00ffULL) << 8); + x = ((x & 0xffff0000ffff0000ULL) >> 16) | ((x & 0x0000ffff0000ffffULL) << 16); + return (x >> 32) | (x << 32); +} + +#endif + +#endif // WORDS_BIGENDIAN + +// Convert to little-endian storage, opposite of network format. +// Convert x from host to little endian: x = LittleEndian.FromHost(x); +// convert x from little endian to host: x = LittleEndian.ToHost(x); +// +// Store values into unaligned memory converting to little endian order: +// LittleEndian.Store16(p, x); +// +// Load unaligned values stored in little endian converting to host order: +// x = LittleEndian.Load16(p); +class LittleEndian { + public: + // Conversion functions. +#ifdef WORDS_BIGENDIAN + + static uint16 FromHost16(uint16 x) { return bswap_16(x); } + static uint16 ToHost16(uint16 x) { return bswap_16(x); } + + static uint32 FromHost32(uint32 x) { return bswap_32(x); } + static uint32 ToHost32(uint32 x) { return bswap_32(x); } + + static bool IsLittleEndian() { return false; } + +#else // !defined(WORDS_BIGENDIAN) + + static uint16 FromHost16(uint16 x) { return x; } + static uint16 ToHost16(uint16 x) { return x; } + + static uint32 FromHost32(uint32 x) { return x; } + static uint32 ToHost32(uint32 x) { return x; } + + static bool IsLittleEndian() { return true; } + +#endif // !defined(WORDS_BIGENDIAN) + + // Functions to do unaligned loads and stores in little-endian order. + static uint16 Load16(const void *p) { + return ToHost16(UNALIGNED_LOAD16(p)); + } + + static void Store16(void *p, uint16 v) { + UNALIGNED_STORE16(p, FromHost16(v)); + } + + static uint32 Load32(const void *p) { + return ToHost32(UNALIGNED_LOAD32(p)); + } + + static void Store32(void *p, uint32 v) { + UNALIGNED_STORE32(p, FromHost32(v)); + } +}; + +// Some bit-manipulation functions. +class Bits { + public: + // Return floor(log2(n)) for positive integer n. Returns -1 iff n == 0. + static int Log2Floor(uint32 n); + + // Return the first set least / most significant bit, 0-indexed. Returns an + // undefined value if n == 0. FindLSBSetNonZero() is similar to ffs() except + // that it's 0-indexed. + static int FindLSBSetNonZero(uint32 n); + static int FindLSBSetNonZero64(uint64 n); + + private: + DISALLOW_COPY_AND_ASSIGN(Bits); +}; + +#ifdef HAVE_BUILTIN_CTZ + +inline int Bits::Log2Floor(uint32 n) { + return n == 0 ? -1 : 31 ^ __builtin_clz(n); +} + +inline int Bits::FindLSBSetNonZero(uint32 n) { + return __builtin_ctz(n); +} + +inline int Bits::FindLSBSetNonZero64(uint64 n) { + return __builtin_ctzll(n); +} + +#else // Portable versions. + +inline int Bits::Log2Floor(uint32 n) { + if (n == 0) + return -1; + int log = 0; + uint32 value = n; + for (int i = 4; i >= 0; --i) { + int shift = (1 << i); + uint32 x = value >> shift; + if (x != 0) { + value = x; + log += shift; + } + } + assert(value == 1); + return log; +} + +inline int Bits::FindLSBSetNonZero(uint32 n) { + int rc = 31; + for (int i = 4, shift = 1 << 4; i >= 0; --i) { + const uint32 x = n << shift; + if (x != 0) { + n = x; + rc -= shift; + } + shift >>= 1; + } + return rc; +} + +// FindLSBSetNonZero64() is defined in terms of FindLSBSetNonZero(). +inline int Bits::FindLSBSetNonZero64(uint64 n) { + const uint32 bottombits = static_cast(n); + if (bottombits == 0) { + // Bottom bits are zero, so scan in top bits + return 32 + FindLSBSetNonZero(static_cast(n >> 32)); + } else { + return FindLSBSetNonZero(bottombits); + } +} + +#endif // End portable versions. + +// Variable-length integer encoding. +class Varint { + public: + // Maximum lengths of varint encoding of uint32. + static const int kMax32 = 5; + + // Attempts to parse a varint32 from a prefix of the bytes in [ptr,limit-1]. + // Never reads a character at or beyond limit. If a valid/terminated varint32 + // was found in the range, stores it in *OUTPUT and returns a pointer just + // past the last byte of the varint32. Else returns NULL. On success, + // "result <= limit". + static const char* Parse32WithLimit(const char* ptr, const char* limit, + uint32* OUTPUT); + + // REQUIRES "ptr" points to a buffer of length sufficient to hold "v". + // EFFECTS Encodes "v" into "ptr" and returns a pointer to the + // byte just past the last encoded byte. + static char* Encode32(char* ptr, uint32 v); + + // EFFECTS Appends the varint representation of "value" to "*s". + static void Append32(string* s, uint32 value); +}; + +inline const char* Varint::Parse32WithLimit(const char* p, + const char* l, + uint32* OUTPUT) { + const unsigned char* ptr = reinterpret_cast(p); + const unsigned char* limit = reinterpret_cast(l); + uint32 b, result; + if (ptr >= limit) return NULL; + b = *(ptr++); result = b & 127; if (b < 128) goto done; + if (ptr >= limit) return NULL; + b = *(ptr++); result |= (b & 127) << 7; if (b < 128) goto done; + if (ptr >= limit) return NULL; + b = *(ptr++); result |= (b & 127) << 14; if (b < 128) goto done; + if (ptr >= limit) return NULL; + b = *(ptr++); result |= (b & 127) << 21; if (b < 128) goto done; + if (ptr >= limit) return NULL; + b = *(ptr++); result |= (b & 127) << 28; if (b < 16) goto done; + return NULL; // Value is too long to be a varint32 + done: + *OUTPUT = result; + return reinterpret_cast(ptr); +} + +inline char* Varint::Encode32(char* sptr, uint32 v) { + // Operate on characters as unsigneds + unsigned char* ptr = reinterpret_cast(sptr); + static const int B = 128; + if (v < (1<<7)) { + *(ptr++) = v; + } else if (v < (1<<14)) { + *(ptr++) = v | B; + *(ptr++) = v>>7; + } else if (v < (1<<21)) { + *(ptr++) = v | B; + *(ptr++) = (v>>7) | B; + *(ptr++) = v>>14; + } else if (v < (1<<28)) { + *(ptr++) = v | B; + *(ptr++) = (v>>7) | B; + *(ptr++) = (v>>14) | B; + *(ptr++) = v>>21; + } else { + *(ptr++) = v | B; + *(ptr++) = (v>>7) | B; + *(ptr++) = (v>>14) | B; + *(ptr++) = (v>>21) | B; + *(ptr++) = v>>28; + } + return reinterpret_cast(ptr); +} + +// If you know the internal layout of the std::string in use, you can +// replace this function with one that resizes the string without +// filling the new space with zeros (if applicable) -- +// it will be non-portable but faster. +inline void STLStringResizeUninitialized(string* s, size_t new_size) { + s->resize(new_size); +} + +// Return a mutable char* pointing to a string's internal buffer, +// which may not be null-terminated. Writing through this pointer will +// modify the string. +// +// string_as_array(&str)[i] is valid for 0 <= i < str.size() until the +// next call to a string method that invalidates iterators. +// +// As of 2006-04, there is no standard-blessed way of getting a +// mutable reference to a string's internal buffer. However, issue 530 +// (http://www.open-std.org/JTC1/SC22/WG21/docs/lwg-defects.html#530) +// proposes this as the method. It will officially be part of the standard +// for C++0x. This should already work on all current implementations. +inline char* string_as_array(string* str) { + return str->empty() ? NULL : &*str->begin(); +} + +} // namespace snappy + +#endif // UTIL_SNAPPY_OPENSOURCE_SNAPPY_STUBS_INTERNAL_H_ diff --git a/ext/snappy/snappy-stubs-public.h b/ext/snappy/snappy-stubs-public.h new file mode 100644 index 0000000000..e97fc24682 --- /dev/null +++ b/ext/snappy/snappy-stubs-public.h @@ -0,0 +1,61 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// Author: sesse@google.com (Steinar H. Gunderson) +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Various type stubs for the open-source version of Snappy. +// +// This file cannot include config.h, as it is included from snappy.h, +// which is a public header. Instead, snappy-stubs-public.h is generated by +// from snappy-stubs-public.h.in at configure time. + +#ifndef UTIL_SNAPPY_OPENSOURCE_SNAPPY_STUBS_PUBLIC_H_ +#define UTIL_SNAPPY_OPENSOURCE_SNAPPY_STUBS_PUBLIC_H_ + +#include "base/basictypes.h" +#include + +namespace snappy { + +typedef int8_t int8; +typedef uint8_t uint8; +typedef int16_t int16; +typedef uint16_t uint16; +typedef int32_t int32; +typedef uint32_t uint32; +typedef int64_t int64; +typedef uint64_t uint64; + +typedef std::string string; + +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&); \ + void operator=(const TypeName&) + +} // namespace snappy + +#endif // UTIL_SNAPPY_OPENSOURCE_SNAPPY_STUBS_PUBLIC_H_ diff --git a/ext/snappy/snappy.cpp b/ext/snappy/snappy.cpp new file mode 100644 index 0000000000..26ef022cca --- /dev/null +++ b/ext/snappy/snappy.cpp @@ -0,0 +1,1166 @@ +// Copyright 2005 Google Inc. All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "snappy.h" +#include "snappy-internal.h" +#include "snappy-sinksource.h" + +#include + +#include +#include +#include + + +namespace snappy { + + Source::~Source() { } + + Sink::~Sink() { } + + char* Sink::GetAppendBuffer(size_t length, char* scratch) { + return scratch; + } + + ByteArraySource::~ByteArraySource() { } + + size_t ByteArraySource::Available() const { return left_; } + + const char* ByteArraySource::Peek(size_t* len) { + *len = left_; + return ptr_; + } + + void ByteArraySource::Skip(size_t n) { + left_ -= n; + ptr_ += n; + } + + UncheckedByteArraySink::~UncheckedByteArraySink() { } + + void UncheckedByteArraySink::Append(const char* data, size_t n) { + // Do no copying if the caller filled in the result of GetAppendBuffer() + if (data != dest_) { + memcpy(dest_, data, n); + } + dest_ += n; + } + + char* UncheckedByteArraySink::GetAppendBuffer(size_t len, char* scratch) { + return dest_; + } + +} + + +namespace snappy { + +// Any hash function will produce a valid compressed bitstream, but a good +// hash function reduces the number of collisions and thus yields better +// compression for compressible input, and more speed for incompressible +// input. Of course, it doesn't hurt if the hash function is reasonably fast +// either, as it gets called a lot. +static inline uint32 HashBytes(uint32 bytes, int shift) { + uint32 kMul = 0x1e35a7bd; + return (bytes * kMul) >> shift; +} +static inline uint32 Hash(const char* p, int shift) { + return HashBytes(UNALIGNED_LOAD32(p), shift); +} + +size_t MaxCompressedLength(size_t source_len) { + // Compressed data can be defined as: + // compressed := item* literal* + // item := literal* copy + // + // The trailing literal sequence has a space blowup of at most 62/60 + // since a literal of length 60 needs one tag byte + one extra byte + // for length information. + // + // Item blowup is trickier to measure. Suppose the "copy" op copies + // 4 bytes of data. Because of a special check in the encoding code, + // we produce a 4-byte copy only if the offset is < 65536. Therefore + // the copy op takes 3 bytes to encode, and this type of item leads + // to at most the 62/60 blowup for representing literals. + // + // Suppose the "copy" op copies 5 bytes of data. If the offset is big + // enough, it will take 5 bytes to encode the copy op. Therefore the + // worst case here is a one-byte literal followed by a five-byte copy. + // I.e., 6 bytes of input turn into 7 bytes of "compressed" data. + // + // This last factor dominates the blowup, so the final estimate is: + return 32 + source_len + source_len/6; +} + +enum { + LITERAL = 0, + COPY_1_BYTE_OFFSET = 1, // 3 bit length + 3 bits of offset in opcode + COPY_2_BYTE_OFFSET = 2, + COPY_4_BYTE_OFFSET = 3 +}; + +void Varint::Append32(string* s, uint32 value) { + char buf[Varint::kMax32]; + const char* p = Varint::Encode32(buf, value); + s->append(buf, p - buf); +} + +// Copy "len" bytes from "src" to "op", one byte at a time. Used for +// handling COPY operations where the input and output regions may +// overlap. For example, suppose: +// src == "ab" +// op == src + 2 +// len == 20 +// After IncrementalCopy(src, op, len), the result will have +// eleven copies of "ab" +// ababababababababababab +// Note that this does not match the semantics of either memcpy() +// or memmove(). +static inline void IncrementalCopy(const char* src, char* op, int len) { + assert(len > 0); + do { + *op++ = *src++; + } while (--len > 0); +} + +// Equivalent to IncrementalCopy except that it can write up to ten extra +// bytes after the end of the copy, and that it is faster. +// +// The main part of this loop is a simple copy of eight bytes at a time until +// we've copied (at least) the requested amount of bytes. However, if op and +// src are less than eight bytes apart (indicating a repeating pattern of +// length < 8), we first need to expand the pattern in order to get the correct +// results. For instance, if the buffer looks like this, with the eight-byte +// and patterns marked as intervals: +// +// abxxxxxxxxxxxx +// [------] src +// [------] op +// +// a single eight-byte copy from to will repeat the pattern once, +// after which we can move two bytes without moving : +// +// ababxxxxxxxxxx +// [------] src +// [------] op +// +// and repeat the exercise until the two no longer overlap. +// +// This allows us to do very well in the special case of one single byte +// repeated many times, without taking a big hit for more general cases. +// +// The worst case of extra writing past the end of the match occurs when +// op - src == 1 and len == 1; the last copy will read from byte positions +// [0..7] and write to [4..11], whereas it was only supposed to write to +// position 1. Thus, ten excess bytes. + +namespace { + +const int kMaxIncrementCopyOverflow = 10; + +} // namespace + +static inline void IncrementalCopyFastPath(const char* src, char* op, int len) { + while (op - src < 8) { + UnalignedCopy64(src, op); + len -= op - src; + op += op - src; + } + while (len > 0) { + UnalignedCopy64(src, op); + src += 8; + op += 8; + len -= 8; + } +} + +static inline char* EmitLiteral(char* op, + const char* literal, + int len, + bool allow_fast_path) { + int n = len - 1; // Zero-length literals are disallowed + if (n < 60) { + // Fits in tag byte + *op++ = LITERAL | (n << 2); + + // The vast majority of copies are below 16 bytes, for which a + // call to memcpy is overkill. This fast path can sometimes + // copy up to 15 bytes too much, but that is okay in the + // main loop, since we have a bit to go on for both sides: + // + // - The input will always have kInputMarginBytes = 15 extra + // available bytes, as long as we're in the main loop, and + // if not, allow_fast_path = false. + // - The output will always have 32 spare bytes (see + // MaxCompressedLength). + if (allow_fast_path && len <= 16) { + UnalignedCopy64(literal, op); + UnalignedCopy64(literal + 8, op + 8); + return op + len; + } + } else { + // Encode in upcoming bytes + char* base = op; + int count = 0; + op++; + while (n > 0) { + *op++ = n & 0xff; + n >>= 8; + count++; + } + assert(count >= 1); + assert(count <= 4); + *base = LITERAL | ((59+count) << 2); + } + memcpy(op, literal, len); + return op + len; +} + +static inline char* EmitCopyLessThan64(char* op, size_t offset, int len) { + assert(len <= 64); + assert(len >= 4); + assert(offset < 65536); + + if ((len < 12) && (offset < 2048)) { + size_t len_minus_4 = len - 4; + assert(len_minus_4 < 8); // Must fit in 3 bits + *op++ = COPY_1_BYTE_OFFSET | ((len_minus_4) << 2) | ((offset >> 8) << 5); + *op++ = offset & 0xff; + } else { + *op++ = COPY_2_BYTE_OFFSET | ((len-1) << 2); + LittleEndian::Store16(op, offset); + op += 2; + } + return op; +} + +static inline char* EmitCopy(char* op, size_t offset, int len) { + // Emit 64 byte copies but make sure to keep at least four bytes reserved + while (len >= 68) { + op = EmitCopyLessThan64(op, offset, 64); + len -= 64; + } + + // Emit an extra 60 byte copy if have too much data to fit in one copy + if (len > 64) { + op = EmitCopyLessThan64(op, offset, 60); + len -= 60; + } + + // Emit remainder + op = EmitCopyLessThan64(op, offset, len); + return op; +} + + +bool GetUncompressedLength(const char* start, size_t n, size_t* result) { + uint32 v = 0; + const char* limit = start + n; + if (Varint::Parse32WithLimit(start, limit, &v) != NULL) { + *result = v; + return true; + } else { + return false; + } +} + +namespace internal { +uint16* WorkingMemory::GetHashTable(size_t input_size, int* table_size) { + // Use smaller hash table when input.size() is smaller, since we + // fill the table, incurring O(hash table size) overhead for + // compression, and if the input is short, we won't need that + // many hash table entries anyway. + assert(kMaxHashTableSize >= 256); + size_t htsize = 256; + while (htsize < kMaxHashTableSize && htsize < input_size) { + htsize <<= 1; + } + + uint16* table; + if (htsize <= ARRAYSIZE(small_table_)) { + table = small_table_; + } else { + if (large_table_ == NULL) { + large_table_ = new uint16[kMaxHashTableSize]; + } + table = large_table_; + } + + *table_size = htsize; + memset(table, 0, htsize * sizeof(*table)); + return table; +} +} // end namespace internal + +// For 0 <= offset <= 4, GetUint32AtOffset(GetEightBytesAt(p), offset) will +// equal UNALIGNED_LOAD32(p + offset). Motivation: On x86-64 hardware we have +// empirically found that overlapping loads such as +// UNALIGNED_LOAD32(p) ... UNALIGNED_LOAD32(p+1) ... UNALIGNED_LOAD32(p+2) +// are slower than UNALIGNED_LOAD64(p) followed by shifts and casts to uint32. +// +// We have different versions for 64- and 32-bit; ideally we would avoid the +// two functions and just inline the UNALIGNED_LOAD64 call into +// GetUint32AtOffset, but GCC (at least not as of 4.6) is seemingly not clever +// enough to avoid loading the value multiple times then. For 64-bit, the load +// is done when GetEightBytesAt() is called, whereas for 32-bit, the load is +// done at GetUint32AtOffset() time. + +#ifdef ARCH_K8 + +typedef uint64 EightBytesReference; + +static inline EightBytesReference GetEightBytesAt(const char* ptr) { + return UNALIGNED_LOAD64(ptr); +} + +static inline uint32 GetUint32AtOffset(uint64 v, int offset) { + assert(offset >= 0); + assert(offset <= 4); + return v >> (LittleEndian::IsLittleEndian() ? 8 * offset : 32 - 8 * offset); +} + +#else + +typedef const char* EightBytesReference; + +static inline EightBytesReference GetEightBytesAt(const char* ptr) { + return ptr; +} + +static inline uint32 GetUint32AtOffset(const char* v, int offset) { + assert(offset >= 0); + assert(offset <= 4); + return UNALIGNED_LOAD32(v + offset); +} + +#endif + +// Flat array compression that does not emit the "uncompressed length" +// prefix. Compresses "input" string to the "*op" buffer. +// +// REQUIRES: "input" is at most "kBlockSize" bytes long. +// REQUIRES: "op" points to an array of memory that is at least +// "MaxCompressedLength(input.size())" in size. +// REQUIRES: All elements in "table[0..table_size-1]" are initialized to zero. +// REQUIRES: "table_size" is a power of two +// +// Returns an "end" pointer into "op" buffer. +// "end - op" is the compressed size of "input". +namespace internal { +char* CompressFragment(const char* input, + size_t input_size, + char* op, + uint16* table, + const int table_size) { + // "ip" is the input pointer, and "op" is the output pointer. + const char* ip = input; + assert(input_size <= kBlockSize); + assert((table_size & (table_size - 1)) == 0); // table must be power of two + const int shift = 32 - Bits::Log2Floor(table_size); + assert(static_cast(kuint32max >> shift) == table_size - 1); + const char* ip_end = input + input_size; + const char* base_ip = ip; + // Bytes in [next_emit, ip) will be emitted as literal bytes. Or + // [next_emit, ip_end) after the main loop. + const char* next_emit = ip; + + const size_t kInputMarginBytes = 15; + if (PREDICT_TRUE(input_size >= kInputMarginBytes)) { + const char* ip_limit = input + input_size - kInputMarginBytes; + + for (uint32 next_hash = Hash(++ip, shift); ; ) { + assert(next_emit < ip); + // The body of this loop calls EmitLiteral once and then EmitCopy one or + // more times. (The exception is that when we're close to exhausting + // the input we goto emit_remainder.) + // + // In the first iteration of this loop we're just starting, so + // there's nothing to copy, so calling EmitLiteral once is + // necessary. And we only start a new iteration when the + // current iteration has determined that a call to EmitLiteral will + // precede the next call to EmitCopy (if any). + // + // Step 1: Scan forward in the input looking for a 4-byte-long match. + // If we get close to exhausting the input then goto emit_remainder. + // + // Heuristic match skipping: If 32 bytes are scanned with no matches + // found, start looking only at every other byte. If 32 more bytes are + // scanned, look at every third byte, etc.. When a match is found, + // immediately go back to looking at every byte. This is a small loss + // (~5% performance, ~0.1% density) for compressible data due to more + // bookkeeping, but for non-compressible data (such as JPEG) it's a huge + // win since the compressor quickly "realizes" the data is incompressible + // and doesn't bother looking for matches everywhere. + // + // The "skip" variable keeps track of how many bytes there are since the + // last match; dividing it by 32 (ie. right-shifting by five) gives the + // number of bytes to move ahead for each iteration. + uint32 skip = 32; + + const char* next_ip = ip; + const char* candidate; + do { + ip = next_ip; + uint32 hash = next_hash; + assert(hash == Hash(ip, shift)); + uint32 bytes_between_hash_lookups = skip++ >> 5; + next_ip = ip + bytes_between_hash_lookups; + if (PREDICT_FALSE(next_ip > ip_limit)) { + goto emit_remainder; + } + next_hash = Hash(next_ip, shift); + candidate = base_ip + table[hash]; + assert(candidate >= base_ip); + assert(candidate < ip); + + table[hash] = ip - base_ip; + } while (PREDICT_TRUE(UNALIGNED_LOAD32(ip) != + UNALIGNED_LOAD32(candidate))); + + // Step 2: A 4-byte match has been found. We'll later see if more + // than 4 bytes match. But, prior to the match, input + // bytes [next_emit, ip) are unmatched. Emit them as "literal bytes." + assert(next_emit + 16 <= ip_end); + op = EmitLiteral(op, next_emit, ip - next_emit, true); + + // Step 3: Call EmitCopy, and then see if another EmitCopy could + // be our next move. Repeat until we find no match for the + // input immediately after what was consumed by the last EmitCopy call. + // + // If we exit this loop normally then we need to call EmitLiteral next, + // though we don't yet know how big the literal will be. We handle that + // by proceeding to the next iteration of the main loop. We also can exit + // this loop via goto if we get close to exhausting the input. + EightBytesReference input_bytes; + uint32 candidate_bytes = 0; + + do { + // We have a 4-byte match at ip, and no need to emit any + // "literal bytes" prior to ip. + const char* base = ip; + int matched = 4 + FindMatchLength(candidate + 4, ip + 4, ip_end); + ip += matched; + size_t offset = base - candidate; + assert(0 == memcmp(base, candidate, matched)); + op = EmitCopy(op, offset, matched); + // We could immediately start working at ip now, but to improve + // compression we first update table[Hash(ip - 1, ...)]. + const char* insert_tail = ip - 1; + next_emit = ip; + if (PREDICT_FALSE(ip >= ip_limit)) { + goto emit_remainder; + } + input_bytes = GetEightBytesAt(insert_tail); + uint32 prev_hash = HashBytes(GetUint32AtOffset(input_bytes, 0), shift); + table[prev_hash] = ip - base_ip - 1; + uint32 cur_hash = HashBytes(GetUint32AtOffset(input_bytes, 1), shift); + candidate = base_ip + table[cur_hash]; + candidate_bytes = UNALIGNED_LOAD32(candidate); + table[cur_hash] = ip - base_ip; + } while (GetUint32AtOffset(input_bytes, 1) == candidate_bytes); + + next_hash = HashBytes(GetUint32AtOffset(input_bytes, 2), shift); + ++ip; + } + } + + emit_remainder: + // Emit the remaining bytes as a literal + if (next_emit < ip_end) { + op = EmitLiteral(op, next_emit, ip_end - next_emit, false); + } + + return op; +} +} // end namespace internal + +// Signature of output types needed by decompression code. +// The decompression code is templatized on a type that obeys this +// signature so that we do not pay virtual function call overhead in +// the middle of a tight decompression loop. +// +// class DecompressionWriter { +// public: +// // Called before decompression +// void SetExpectedLength(size_t length); +// +// // Called after decompression +// bool CheckLength() const; +// +// // Called repeatedly during decompression +// bool Append(const char* ip, size_t length); +// bool AppendFromSelf(uint32 offset, size_t length); +// +// // The difference between TryFastAppend and Append is that TryFastAppend +// // is allowed to read up to bytes from the input buffer, +// // whereas Append is allowed to read . +// // +// // Also, TryFastAppend is allowed to return false, declining the append, +// // without it being a fatal error -- just "return false" would be +// // a perfectly legal implementation of TryFastAppend. The intention +// // is for TryFastAppend to allow a fast path in the common case of +// // a small append. +// // +// // NOTE(user): TryFastAppend must always return decline (return false) +// // if is 61 or more, as in this case the literal length is not +// // decoded fully. In practice, this should not be a big problem, +// // as it is unlikely that one would implement a fast path accepting +// // this much data. +// bool TryFastAppend(const char* ip, size_t available, size_t length); +// }; + +// ----------------------------------------------------------------------- +// Lookup table for decompression code. Generated by ComputeTable() below. +// ----------------------------------------------------------------------- + +// Mapping from i in range [0,4] to a mask to extract the bottom 8*i bits +static const uint32 wordmask[] = { + 0u, 0xffu, 0xffffu, 0xffffffu, 0xffffffffu +}; + +// Data stored per entry in lookup table: +// Range Bits-used Description +// ------------------------------------ +// 1..64 0..7 Literal/copy length encoded in opcode byte +// 0..7 8..10 Copy offset encoded in opcode byte / 256 +// 0..4 11..13 Extra bytes after opcode +// +// We use eight bits for the length even though 7 would have sufficed +// because of efficiency reasons: +// (1) Extracting a byte is faster than a bit-field +// (2) It properly aligns copy offset so we do not need a <<8 +static const uint16 char_table[256] = { + 0x0001, 0x0804, 0x1001, 0x2001, 0x0002, 0x0805, 0x1002, 0x2002, + 0x0003, 0x0806, 0x1003, 0x2003, 0x0004, 0x0807, 0x1004, 0x2004, + 0x0005, 0x0808, 0x1005, 0x2005, 0x0006, 0x0809, 0x1006, 0x2006, + 0x0007, 0x080a, 0x1007, 0x2007, 0x0008, 0x080b, 0x1008, 0x2008, + 0x0009, 0x0904, 0x1009, 0x2009, 0x000a, 0x0905, 0x100a, 0x200a, + 0x000b, 0x0906, 0x100b, 0x200b, 0x000c, 0x0907, 0x100c, 0x200c, + 0x000d, 0x0908, 0x100d, 0x200d, 0x000e, 0x0909, 0x100e, 0x200e, + 0x000f, 0x090a, 0x100f, 0x200f, 0x0010, 0x090b, 0x1010, 0x2010, + 0x0011, 0x0a04, 0x1011, 0x2011, 0x0012, 0x0a05, 0x1012, 0x2012, + 0x0013, 0x0a06, 0x1013, 0x2013, 0x0014, 0x0a07, 0x1014, 0x2014, + 0x0015, 0x0a08, 0x1015, 0x2015, 0x0016, 0x0a09, 0x1016, 0x2016, + 0x0017, 0x0a0a, 0x1017, 0x2017, 0x0018, 0x0a0b, 0x1018, 0x2018, + 0x0019, 0x0b04, 0x1019, 0x2019, 0x001a, 0x0b05, 0x101a, 0x201a, + 0x001b, 0x0b06, 0x101b, 0x201b, 0x001c, 0x0b07, 0x101c, 0x201c, + 0x001d, 0x0b08, 0x101d, 0x201d, 0x001e, 0x0b09, 0x101e, 0x201e, + 0x001f, 0x0b0a, 0x101f, 0x201f, 0x0020, 0x0b0b, 0x1020, 0x2020, + 0x0021, 0x0c04, 0x1021, 0x2021, 0x0022, 0x0c05, 0x1022, 0x2022, + 0x0023, 0x0c06, 0x1023, 0x2023, 0x0024, 0x0c07, 0x1024, 0x2024, + 0x0025, 0x0c08, 0x1025, 0x2025, 0x0026, 0x0c09, 0x1026, 0x2026, + 0x0027, 0x0c0a, 0x1027, 0x2027, 0x0028, 0x0c0b, 0x1028, 0x2028, + 0x0029, 0x0d04, 0x1029, 0x2029, 0x002a, 0x0d05, 0x102a, 0x202a, + 0x002b, 0x0d06, 0x102b, 0x202b, 0x002c, 0x0d07, 0x102c, 0x202c, + 0x002d, 0x0d08, 0x102d, 0x202d, 0x002e, 0x0d09, 0x102e, 0x202e, + 0x002f, 0x0d0a, 0x102f, 0x202f, 0x0030, 0x0d0b, 0x1030, 0x2030, + 0x0031, 0x0e04, 0x1031, 0x2031, 0x0032, 0x0e05, 0x1032, 0x2032, + 0x0033, 0x0e06, 0x1033, 0x2033, 0x0034, 0x0e07, 0x1034, 0x2034, + 0x0035, 0x0e08, 0x1035, 0x2035, 0x0036, 0x0e09, 0x1036, 0x2036, + 0x0037, 0x0e0a, 0x1037, 0x2037, 0x0038, 0x0e0b, 0x1038, 0x2038, + 0x0039, 0x0f04, 0x1039, 0x2039, 0x003a, 0x0f05, 0x103a, 0x203a, + 0x003b, 0x0f06, 0x103b, 0x203b, 0x003c, 0x0f07, 0x103c, 0x203c, + 0x0801, 0x0f08, 0x103d, 0x203d, 0x1001, 0x0f09, 0x103e, 0x203e, + 0x1801, 0x0f0a, 0x103f, 0x203f, 0x2001, 0x0f0b, 0x1040, 0x2040 +}; + +// In debug mode, allow optional computation of the table at startup. +// Also, check that the decompression table is correct. +#ifndef NDEBUG +DEFINE_bool(snappy_dump_decompression_table, false, + "If true, we print the decompression table at startup."); + +static uint16 MakeEntry(unsigned int extra, + unsigned int len, + unsigned int copy_offset) { + // Check that all of the fields fit within the allocated space + assert(extra == (extra & 0x7)); // At most 3 bits + assert(copy_offset == (copy_offset & 0x7)); // At most 3 bits + assert(len == (len & 0x7f)); // At most 7 bits + return len | (copy_offset << 8) | (extra << 11); +} + +static void ComputeTable() { + uint16 dst[256]; + + // Place invalid entries in all places to detect missing initialization + int assigned = 0; + for (int i = 0; i < 256; i++) { + dst[i] = 0xffff; + } + + // Small LITERAL entries. We store (len-1) in the top 6 bits. + for (unsigned int len = 1; len <= 60; len++) { + dst[LITERAL | ((len-1) << 2)] = MakeEntry(0, len, 0); + assigned++; + } + + // Large LITERAL entries. We use 60..63 in the high 6 bits to + // encode the number of bytes of length info that follow the opcode. + for (unsigned int extra_bytes = 1; extra_bytes <= 4; extra_bytes++) { + // We set the length field in the lookup table to 1 because extra + // bytes encode len-1. + dst[LITERAL | ((extra_bytes+59) << 2)] = MakeEntry(extra_bytes, 1, 0); + assigned++; + } + + // COPY_1_BYTE_OFFSET. + // + // The tag byte in the compressed data stores len-4 in 3 bits, and + // offset/256 in 5 bits. offset%256 is stored in the next byte. + // + // This format is used for length in range [4..11] and offset in + // range [0..2047] + for (unsigned int len = 4; len < 12; len++) { + for (unsigned int offset = 0; offset < 2048; offset += 256) { + dst[COPY_1_BYTE_OFFSET | ((len-4)<<2) | ((offset>>8)<<5)] = + MakeEntry(1, len, offset>>8); + assigned++; + } + } + + // COPY_2_BYTE_OFFSET. + // Tag contains len-1 in top 6 bits, and offset in next two bytes. + for (unsigned int len = 1; len <= 64; len++) { + dst[COPY_2_BYTE_OFFSET | ((len-1)<<2)] = MakeEntry(2, len, 0); + assigned++; + } + + // COPY_4_BYTE_OFFSET. + // Tag contents len-1 in top 6 bits, and offset in next four bytes. + for (unsigned int len = 1; len <= 64; len++) { + dst[COPY_4_BYTE_OFFSET | ((len-1)<<2)] = MakeEntry(4, len, 0); + assigned++; + } + + // Check that each entry was initialized exactly once. + if (assigned != 256) { + fprintf(stderr, "ComputeTable: assigned only %d of 256\n", assigned); + abort(); + } + for (int i = 0; i < 256; i++) { + if (dst[i] == 0xffff) { + fprintf(stderr, "ComputeTable: did not assign byte %d\n", i); + abort(); + } + } + + if (FLAGS_snappy_dump_decompression_table) { + printf("static const uint16 char_table[256] = {\n "); + for (int i = 0; i < 256; i++) { + printf("0x%04x%s", + dst[i], + ((i == 255) ? "\n" : (((i%8) == 7) ? ",\n " : ", "))); + } + printf("};\n"); + } + + // Check that computed table matched recorded table + for (int i = 0; i < 256; i++) { + if (dst[i] != char_table[i]) { + fprintf(stderr, "ComputeTable: byte %d: computed (%x), expect (%x)\n", + i, static_cast(dst[i]), static_cast(char_table[i])); + abort(); + } + } +} +#endif /* !NDEBUG */ + +// Helper class for decompression +class SnappyDecompressor { + private: + Source* reader_; // Underlying source of bytes to decompress + const char* ip_; // Points to next buffered byte + const char* ip_limit_; // Points just past buffered bytes + uint32 peeked_; // Bytes peeked from reader (need to skip) + bool eof_; // Hit end of input without an error? + char scratch_[5]; // Temporary buffer for PeekFast() boundaries + + // Ensure that all of the tag metadata for the next tag is available + // in [ip_..ip_limit_-1]. Also ensures that [ip,ip+4] is readable even + // if (ip_limit_ - ip_ < 5). + // + // Returns true on success, false on error or end of input. + bool RefillTag(); + + public: + explicit SnappyDecompressor(Source* reader) + : reader_(reader), + ip_(NULL), + ip_limit_(NULL), + peeked_(0), + eof_(false) { + } + + ~SnappyDecompressor() { + // Advance past any bytes we peeked at from the reader + reader_->Skip(peeked_); + } + + // Returns true iff we have hit the end of the input without an error. + bool eof() const { + return eof_; + } + + // Read the uncompressed length stored at the start of the compressed data. + // On succcess, stores the length in *result and returns true. + // On failure, returns false. + bool ReadUncompressedLength(uint32* result) { + assert(ip_ == NULL); // Must not have read anything yet + // Length is encoded in 1..5 bytes + *result = 0; + uint32 shift = 0; + while (true) { + if (shift >= 32) return false; + size_t n; + const char* ip = reader_->Peek(&n); + if (n == 0) return false; + const unsigned char c = *(reinterpret_cast(ip)); + reader_->Skip(1); + *result |= static_cast(c & 0x7f) << shift; + if (c < 128) { + break; + } + shift += 7; + } + return true; + } + + // Process the next item found in the input. + // Returns true if successful, false on error or end of input. + template + void DecompressAllTags(Writer* writer) { + const char* ip = ip_; + + // We could have put this refill fragment only at the beginning of the loop. + // However, duplicating it at the end of each branch gives the compiler more + // scope to optimize the expression based on the local + // context, which overall increases speed. + #define MAYBE_REFILL() \ + if (ip_limit_ - ip < 5) { \ + ip_ = ip; \ + if (!RefillTag()) return; \ + ip = ip_; \ + } + + MAYBE_REFILL(); + for ( ;; ) { + const unsigned char c = *(reinterpret_cast(ip++)); + + if ((c & 0x3) == LITERAL) { + size_t literal_length = (c >> 2) + 1u; + if (writer->TryFastAppend(ip, ip_limit_ - ip, literal_length)) { + assert(literal_length < 61); + ip += literal_length; + MAYBE_REFILL(); + continue; + } + if (PREDICT_FALSE(literal_length >= 61)) { + // Long literal. + const size_t literal_length_length = literal_length - 60; + literal_length = + (LittleEndian::Load32(ip) & wordmask[literal_length_length]) + 1; + ip += literal_length_length; + } + + size_t avail = ip_limit_ - ip; + while (avail < literal_length) { + if (!writer->Append(ip, avail)) return; + literal_length -= avail; + reader_->Skip(peeked_); + size_t n; + ip = reader_->Peek(&n); + avail = n; + peeked_ = avail; + if (avail == 0) return; // Premature end of input + ip_limit_ = ip + avail; + } + if (!writer->Append(ip, literal_length)) { + return; + } + ip += literal_length; + MAYBE_REFILL(); + } else { + const uint32 entry = char_table[c]; + const uint32 trailer = LittleEndian::Load32(ip) & wordmask[entry >> 11]; + const uint32 length = entry & 0xff; + ip += entry >> 11; + + // copy_offset/256 is encoded in bits 8..10. By just fetching + // those bits, we get copy_offset (since the bit-field starts at + // bit 8). + const uint32 copy_offset = entry & 0x700; + if (!writer->AppendFromSelf(copy_offset + trailer, length)) { + return; + } + MAYBE_REFILL(); + } + } + +#undef MAYBE_REFILL + } +}; + +bool SnappyDecompressor::RefillTag() { + const char* ip = ip_; + if (ip == ip_limit_) { + // Fetch a new fragment from the reader + reader_->Skip(peeked_); // All peeked bytes are used up + size_t n; + ip = reader_->Peek(&n); + peeked_ = n; + if (n == 0) { + eof_ = true; + return false; + } + ip_limit_ = ip + n; + } + + // Read the tag character + assert(ip < ip_limit_); + const unsigned char c = *(reinterpret_cast(ip)); + const uint32 entry = char_table[c]; + const uint32 needed = (entry >> 11) + 1; // +1 byte for 'c' + assert(needed <= sizeof(scratch_)); + + // Read more bytes from reader if needed + uint32 nbuf = ip_limit_ - ip; + if (nbuf < needed) { + // Stitch together bytes from ip and reader to form the word + // contents. We store the needed bytes in "scratch_". They + // will be consumed immediately by the caller since we do not + // read more than we need. + memmove(scratch_, ip, nbuf); + reader_->Skip(peeked_); // All peeked bytes are used up + peeked_ = 0; + while (nbuf < needed) { + size_t length; + const char* src = reader_->Peek(&length); + if (length == 0) return false; + uint32 to_add = min(needed - nbuf, length); + memcpy(scratch_ + nbuf, src, to_add); + nbuf += to_add; + reader_->Skip(to_add); + } + assert(nbuf == needed); + ip_ = scratch_; + ip_limit_ = scratch_ + needed; + } else if (nbuf < 5) { + // Have enough bytes, but move into scratch_ so that we do not + // read past end of input + memmove(scratch_, ip, nbuf); + reader_->Skip(peeked_); // All peeked bytes are used up + peeked_ = 0; + ip_ = scratch_; + ip_limit_ = scratch_ + nbuf; + } else { + // Pass pointer to buffer returned by reader_. + ip_ = ip; + } + return true; +} + +template +static bool InternalUncompress(Source* r, + Writer* writer, + uint32 max_len) { + // Read the uncompressed length from the front of the compressed input + SnappyDecompressor decompressor(r); + uint32 uncompressed_len = 0; + if (!decompressor.ReadUncompressedLength(&uncompressed_len)) return false; + return InternalUncompressAllTags( + &decompressor, writer, uncompressed_len, max_len); +} + +template +static bool InternalUncompressAllTags(SnappyDecompressor* decompressor, + Writer* writer, + uint32 uncompressed_len, + uint32 max_len) { + // Protect against possible DoS attack + if (static_cast(uncompressed_len) > max_len) { + return false; + } + + writer->SetExpectedLength(uncompressed_len); + + // Process the entire input + decompressor->DecompressAllTags(writer); + return (decompressor->eof() && writer->CheckLength()); +} + +bool GetUncompressedLength(Source* source, uint32* result) { + SnappyDecompressor decompressor(source); + return decompressor.ReadUncompressedLength(result); +} + +size_t Compress(Source* reader, Sink* writer) { + size_t written = 0; + size_t N = reader->Available(); + char ulength[Varint::kMax32]; + char* p = Varint::Encode32(ulength, N); + writer->Append(ulength, p-ulength); + written += (p - ulength); + + internal::WorkingMemory wmem; + char* scratch = NULL; + char* scratch_output = NULL; + + while (N > 0) { + // Get next block to compress (without copying if possible) + size_t fragment_size; + const char* fragment = reader->Peek(&fragment_size); + assert(fragment_size != 0); // premature end of input + const size_t num_to_read = min(N, kBlockSize); + size_t bytes_read = fragment_size; + + size_t pending_advance = 0; + if (bytes_read >= num_to_read) { + // Buffer returned by reader is large enough + pending_advance = num_to_read; + fragment_size = num_to_read; + } else { + // Read into scratch buffer + if (scratch == NULL) { + // If this is the last iteration, we want to allocate N bytes + // of space, otherwise the max possible kBlockSize space. + // num_to_read contains exactly the correct value + scratch = new char[num_to_read]; + } + memcpy(scratch, fragment, bytes_read); + reader->Skip(bytes_read); + + while (bytes_read < num_to_read) { + fragment = reader->Peek(&fragment_size); + size_t n = min(fragment_size, num_to_read - bytes_read); + memcpy(scratch + bytes_read, fragment, n); + bytes_read += n; + reader->Skip(n); + } + assert(bytes_read == num_to_read); + fragment = scratch; + fragment_size = num_to_read; + } + assert(fragment_size == num_to_read); + + // Get encoding table for compression + int table_size; + uint16* table = wmem.GetHashTable(num_to_read, &table_size); + + // Compress input_fragment and append to dest + const int max_output = MaxCompressedLength(num_to_read); + + // Need a scratch buffer for the output, in case the byte sink doesn't + // have room for us directly. + if (scratch_output == NULL) { + scratch_output = new char[max_output]; + } else { + // Since we encode kBlockSize regions followed by a region + // which is <= kBlockSize in length, a previously allocated + // scratch_output[] region is big enough for this iteration. + } + char* dest = writer->GetAppendBuffer(max_output, scratch_output); + char* end = internal::CompressFragment(fragment, fragment_size, + dest, table, table_size); + writer->Append(dest, end - dest); + written += (end - dest); + + N -= num_to_read; + reader->Skip(pending_advance); + } + + delete[] scratch; + delete[] scratch_output; + + return written; +} + +// ----------------------------------------------------------------------- +// Flat array interfaces +// ----------------------------------------------------------------------- + +// A type that writes to a flat array. +// Note that this is not a "ByteSink", but a type that matches the +// Writer template argument to SnappyDecompressor::DecompressAllTags(). +class SnappyArrayWriter { + private: + char* base_; + char* op_; + char* op_limit_; + + public: + inline explicit SnappyArrayWriter(char* dst) + : base_(dst), + op_(dst) { + } + + inline void SetExpectedLength(size_t len) { + op_limit_ = op_ + len; + } + + inline bool CheckLength() const { + return op_ == op_limit_; + } + + inline bool Append(const char* ip, size_t len) { + char* op = op_; + const size_t space_left = op_limit_ - op; + if (space_left < len) { + return false; + } + memcpy(op, ip, len); + op_ = op + len; + return true; + } + + inline bool TryFastAppend(const char* ip, size_t available, size_t len) { + char* op = op_; + const size_t space_left = op_limit_ - op; + if (len <= 16 && available >= 16 && space_left >= 16) { + // Fast path, used for the majority (about 95%) of invocations. + UnalignedCopy64(ip, op); + UnalignedCopy64(ip + 8, op + 8); + op_ = op + len; + return true; + } else { + return false; + } + } + + inline bool AppendFromSelf(size_t offset, size_t len) { + char* op = op_; + const size_t space_left = op_limit_ - op; + + if (op - base_ <= offset - 1u) { // -1u catches offset==0 + return false; + } + if (len <= 16 && offset >= 8 && space_left >= 16) { + // Fast path, used for the majority (70-80%) of dynamic invocations. + UnalignedCopy64(op - offset, op); + UnalignedCopy64(op - offset + 8, op + 8); + } else { + if (space_left >= len + kMaxIncrementCopyOverflow) { + IncrementalCopyFastPath(op - offset, op, len); + } else { + if (space_left < len) { + return false; + } + IncrementalCopy(op - offset, op, len); + } + } + + op_ = op + len; + return true; + } +}; + +bool RawUncompress(const char* compressed, size_t n, char* uncompressed) { + ByteArraySource reader(compressed, n); + return RawUncompress(&reader, uncompressed); +} + +bool RawUncompress(Source* compressed, char* uncompressed) { + SnappyArrayWriter output(uncompressed); + return InternalUncompress(compressed, &output, kuint32max); +} + +bool Uncompress(const char* compressed, size_t n, string* uncompressed) { + size_t ulength; + if (!GetUncompressedLength(compressed, n, &ulength)) { + return false; + } + // Protect against possible DoS attack + if ((static_cast(ulength) + uncompressed->size()) > + uncompressed->max_size()) { + return false; + } + STLStringResizeUninitialized(uncompressed, ulength); + return RawUncompress(compressed, n, string_as_array(uncompressed)); +} + + +// A Writer that drops everything on the floor and just does validation +class SnappyDecompressionValidator { + private: + size_t expected_; + size_t produced_; + + public: + inline SnappyDecompressionValidator() : produced_(0) { } + inline void SetExpectedLength(size_t len) { + expected_ = len; + } + inline bool CheckLength() const { + return expected_ == produced_; + } + inline bool Append(const char* ip, size_t len) { + produced_ += len; + return produced_ <= expected_; + } + inline bool TryFastAppend(const char* ip, size_t available, size_t length) { + return false; + } + inline bool AppendFromSelf(size_t offset, size_t len) { + if (produced_ <= offset - 1u) return false; // -1u catches offset==0 + produced_ += len; + return produced_ <= expected_; + } +}; + +bool IsValidCompressedBuffer(const char* compressed, size_t n) { + ByteArraySource reader(compressed, n); + SnappyDecompressionValidator writer; + return InternalUncompress(&reader, &writer, kuint32max); +} + +void RawCompress(const char* input, + size_t input_length, + char* compressed, + size_t* compressed_length) { + ByteArraySource reader(input, input_length); + UncheckedByteArraySink writer(compressed); + Compress(&reader, &writer); + + // Compute how many bytes were added + *compressed_length = (writer.CurrentDestination() - compressed); +} + +size_t Compress(const char* input, size_t input_length, string* compressed) { + // Pre-grow the buffer to the max length of the compressed output + compressed->resize(MaxCompressedLength(input_length)); + + size_t compressed_length; + RawCompress(input, input_length, string_as_array(compressed), + &compressed_length); + compressed->resize(compressed_length); + return compressed_length; +} + + +} // end namespace snappy + diff --git a/ext/snappy/snappy.h b/ext/snappy/snappy.h new file mode 100644 index 0000000000..d15ffbf0a8 --- /dev/null +++ b/ext/snappy/snappy.h @@ -0,0 +1,162 @@ +// Copyright 2005 and onwards Google Inc. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// A light-weight compression algorithm. It is designed for speed of +// compression and decompression, rather than for the utmost in space +// savings. +// +// For getting better compression ratios when you are compressing data +// with long repeated sequences or compressing data that is similar to +// other data, while still compressing fast, you might look at first +// using BMDiff and then compressing the output of BMDiff with +// Snappy. + +#ifndef UTIL_SNAPPY_SNAPPY_H__ +#define UTIL_SNAPPY_SNAPPY_H__ + +#include +#include + +#include "snappy-stubs-public.h" + +namespace snappy { + class Source; + class Sink; + + // ------------------------------------------------------------------------ + // Generic compression/decompression routines. + // ------------------------------------------------------------------------ + + // Compress the bytes read from "*source" and append to "*sink". Return the + // number of bytes written. + size_t Compress(Source* source, Sink* sink); + + // Find the uncompressed length of the given stream, as given by the header. + // Note that the true length could deviate from this; the stream could e.g. + // be truncated. + // + // Also note that this leaves "*source" in a state that is unsuitable for + // further operations, such as RawUncompress(). You will need to rewind + // or recreate the source yourself before attempting any further calls. + bool GetUncompressedLength(Source* source, uint32* result); + + // ------------------------------------------------------------------------ + // Higher-level string based routines (should be sufficient for most users) + // ------------------------------------------------------------------------ + + // Sets "*output" to the compressed version of "input[0,input_length-1]". + // Original contents of *output are lost. + // + // REQUIRES: "input[]" is not an alias of "*output". + size_t Compress(const char* input, size_t input_length, string* output); + + // Decompresses "compressed[0,compressed_length-1]" to "*uncompressed". + // Original contents of "*uncompressed" are lost. + // + // REQUIRES: "compressed[]" is not an alias of "*uncompressed". + // + // returns false if the message is corrupted and could not be decompressed + bool Uncompress(const char* compressed, size_t compressed_length, + string* uncompressed); + + + // ------------------------------------------------------------------------ + // Lower-level character array based routines. May be useful for + // efficiency reasons in certain circumstances. + // ------------------------------------------------------------------------ + + // REQUIRES: "compressed" must point to an area of memory that is at + // least "MaxCompressedLength(input_length)" bytes in length. + // + // Takes the data stored in "input[0..input_length]" and stores + // it in the array pointed to by "compressed". + // + // "*compressed_length" is set to the length of the compressed output. + // + // Example: + // char* output = new char[snappy::MaxCompressedLength(input_length)]; + // size_t output_length; + // RawCompress(input, input_length, output, &output_length); + // ... Process(output, output_length) ... + // delete [] output; + void RawCompress(const char* input, + size_t input_length, + char* compressed, + size_t* compressed_length); + + // Given data in "compressed[0..compressed_length-1]" generated by + // calling the Snappy::Compress routine, this routine + // stores the uncompressed data to + // uncompressed[0..GetUncompressedLength(compressed)-1] + // returns false if the message is corrupted and could not be decrypted + bool RawUncompress(const char* compressed, size_t compressed_length, + char* uncompressed); + + // Given data from the byte source 'compressed' generated by calling + // the Snappy::Compress routine, this routine stores the uncompressed + // data to + // uncompressed[0..GetUncompressedLength(compressed,compressed_length)-1] + // returns false if the message is corrupted and could not be decrypted + bool RawUncompress(Source* compressed, char* uncompressed); + + // Returns the maximal size of the compressed representation of + // input data that is "source_bytes" bytes in length; + size_t MaxCompressedLength(size_t source_bytes); + + // REQUIRES: "compressed[]" was produced by RawCompress() or Compress() + // Returns true and stores the length of the uncompressed data in + // *result normally. Returns false on parsing error. + // This operation takes O(1) time. + bool GetUncompressedLength(const char* compressed, size_t compressed_length, + size_t* result); + + // Returns true iff the contents of "compressed[]" can be uncompressed + // successfully. Does not return the uncompressed data. Takes + // time proportional to compressed_length, but is usually at least + // a factor of four faster than actual decompression. + bool IsValidCompressedBuffer(const char* compressed, + size_t compressed_length); + + // *** DO NOT CHANGE THE VALUE OF kBlockSize *** + // + // New Compression code chops up the input into blocks of at most + // the following size. This ensures that back-references in the + // output never cross kBlockSize block boundaries. This can be + // helpful in implementing blocked decompression. However the + // decompression code should not rely on this guarantee since older + // compression code may not obey it. + static const int kBlockLog = 15; + static const size_t kBlockSize = 1 << kBlockLog; + + static const int kMaxHashTableBits = 14; + static const size_t kMaxHashTableSize = 1 << kMaxHashTableBits; + +} // end namespace snappy + + +#endif // UTIL_SNAPPY_SNAPPY_H__ diff --git a/headless/Headless.cpp b/headless/Headless.cpp index dabd2586e7..b0b3362a14 100644 --- a/headless/Headless.cpp +++ b/headless/Headless.cpp @@ -13,34 +13,10 @@ #include "Log.h" #include "LogManager.h" -// TODO: Get rid of this junk -class HeadlessHost : public Host -{ -public: - // virtual void StartThread() - virtual void UpdateUI() {} - - virtual void UpdateMemView() {} - virtual void UpdateDisassembly() {} - - virtual void SetDebugMode(bool mode) { } - - virtual void InitGL() {} - virtual void BeginFrame() {} - virtual void EndFrame() {} - virtual void ShutdownGL() {} - - virtual void InitSound(PMixer *mixer) {} - virtual void UpdateSound() {} - virtual void ShutdownSound() {} - - // this is sent from EMU thread! Make sure that Host handles it properly! - virtual void BootDone() {} - virtual void PrepareShutdown() {} - - virtual bool IsDebuggingEnabled() {return false;} - virtual bool AttemptLoadSymbolMap() {return false;} -}; +#include "StubHost.h" +#ifdef _WIN32 +#include "WindowsHeadlessHost.h" +#endif class PrintfLogger : public LogListener { @@ -50,20 +26,20 @@ public: switch (level) { case LogTypes::LDEBUG: - printf("D %s", msg); + fprintf(stderr, "D %s", msg); break; case LogTypes::LINFO: - printf("I %s", msg); + fprintf(stderr, "I %s", msg); break; case LogTypes::LERROR: - printf("E %s", msg); + fprintf(stderr, "E %s", msg); break; case LogTypes::LWARNING: - printf("W %s", msg); + fprintf(stderr, "W %s", msg); break; case LogTypes::LNOTICE: default: - printf("N %s", msg); + fprintf(stderr, "N %s", msg); break; } } @@ -79,6 +55,12 @@ void printUsage(const char *progname, const char *reason) 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"); + + HEADLESSHOST_CLASS h1; + HeadlessHost h2; + if (typeid(h1) != typeid(h2)) + fprintf(stderr, " --graphics use the full gpu backend (slower)\n"); + fprintf(stderr, " -f use the fast interpreter\n"); fprintf(stderr, " -j use jit (overrides -f)\n"); fprintf(stderr, " -c, --compare compare with output in file.expected\n"); @@ -91,6 +73,7 @@ int main(int argc, const char* argv[]) bool useJit = false; bool fastInterpreter = false; bool autoCompare = false; + bool useGraphics = false; const char *bootFilename = 0; const char *mountIso = 0; @@ -114,6 +97,8 @@ int main(int argc, const char* argv[]) fastInterpreter = true; else if (!strcmp(argv[i], "-c") || !strcmp(argv[i], "--compare")) autoCompare = true; + else if (!strcmp(argv[i], "--graphics")) + useGraphics = true; else if (bootFilename == 0) bootFilename = argv[i]; else @@ -140,7 +125,9 @@ int main(int argc, const char* argv[]) return 1; } - host = new HeadlessHost(); + HeadlessHost *headlessHost = useGraphics ? new HEADLESSHOST_CLASS() : new HeadlessHost(); + host = headlessHost; + host->InitGL(); LogManager::Init(); LogManager *logman = LogManager::GetInstance(); @@ -160,7 +147,7 @@ int main(int argc, const char* argv[]) coreParameter.mountIso = mountIso ? mountIso : ""; coreParameter.startPaused = false; coreParameter.cpuCore = useJit ? CPU_JIT : (fastInterpreter ? CPU_FASTINTERPRETER : CPU_INTERPRETER); - coreParameter.gpuCore = GPU_NULL; + coreParameter.gpuCore = headlessHost->isGLWorking() ? GPU_GLES : GPU_NULL; coreParameter.enableSound = false; coreParameter.headLess = true; coreParameter.printfEmuLog = true; @@ -177,8 +164,9 @@ int main(int argc, const char* argv[]) return 1; } - coreState = CORE_RUNNING; + host->BootDone(); + coreState = CORE_RUNNING; while (coreState == CORE_RUNNING) { // Run for a frame at a time, just because. @@ -191,10 +179,13 @@ int main(int argc, const char* argv[]) coreState = CORE_RUNNING; } - // NOTE: we won't get here until I've gotten rid of the exit(0) in sceExitProcess or whatever it's called - + host->ShutdownGL(); PSP_Shutdown(); + delete host; + host = NULL; + headlessHost = NULL; + if (autoCompare) { std::string expect_filename = std::string(bootFilename).substr(strlen(bootFilename - 4)) + ".expected"; diff --git a/headless/Headless.vcxproj b/headless/Headless.vcxproj index e4e8907ff7..bc5cb35624 100644 --- a/headless/Headless.vcxproj +++ b/headless/Headless.vcxproj @@ -81,7 +81,7 @@ Level3 Disabled WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - ../Common;..;../Core;../native/ext/glew; + ../Common;..;../Core;../native/ext/glew;../native Default @@ -96,7 +96,7 @@ Level3 Disabled WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - ../Common;..;../Core;../native/ext/glew; + ../Common;..;../Core;../native/ext/glew;../native Console @@ -112,7 +112,7 @@ true true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - ../Common;..;../Core;../native/ext/glew; + ../Common;..;../Core;../native/ext/glew;../native false StreamingSIMDExtensions2 Fast @@ -133,7 +133,7 @@ true true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - ../Common;..;../Core;../native/ext/glew; + ../Common;..;../Core;../native/ext/glew;../native Console @@ -151,6 +151,7 @@ NotUsing NotUsing + @@ -175,6 +176,10 @@ {f761046e-6c38-4428-a5f1-38391a37bb34} + + + + diff --git a/headless/Headless.vcxproj.filters b/headless/Headless.vcxproj.filters index cb596797fd..5982bf35db 100644 --- a/headless/Headless.vcxproj.filters +++ b/headless/Headless.vcxproj.filters @@ -3,8 +3,13 @@ + + + + + \ No newline at end of file diff --git a/headless/StubHost.h b/headless/StubHost.h new file mode 100644 index 0000000000..9445aee06c --- /dev/null +++ b/headless/StubHost.h @@ -0,0 +1,55 @@ +// 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 "../Core/Host.h" + +#define HEADLESSHOST_CLASS HeadlessHost + +// TODO: Get rid of this junk +class HeadlessHost : public Host +{ +public: + // virtual void StartThread() + virtual void UpdateUI() {} + + virtual void UpdateMemView() {} + virtual void UpdateDisassembly() {} + + virtual void SetDebugMode(bool mode) { } + + virtual void InitGL() {} + virtual void BeginFrame() {} + virtual void EndFrame() {} + virtual void ShutdownGL() {} + + virtual void InitSound(PMixer *mixer) {} + virtual void UpdateSound() {} + virtual void ShutdownSound() {} + + // this is sent from EMU thread! Make sure that Host handles it properly + virtual void BootDone() {} + virtual void PrepareShutdown() {} + + virtual bool IsDebuggingEnabled() {return false;} + virtual bool AttemptLoadSymbolMap() {return false;} + + virtual void SendDebugOutput(const std::string &output) { printf("%s", output.c_str()); } + + virtual bool isGLWorking() { return false; } +}; \ No newline at end of file diff --git a/headless/WindowsHeadlessHost.cpp b/headless/WindowsHeadlessHost.cpp new file mode 100644 index 0000000000..dd473a8279 --- /dev/null +++ b/headless/WindowsHeadlessHost.cpp @@ -0,0 +1,174 @@ +// 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 "WindowsHeadlessHost.h" + +#include +#include +#include + +#include "gfx_es2/gl_state.h" +#include "gfx/gl_common.h" +#include "gfx/gl_lost_manager.h" +#include "file/vfs.h" +#include "file/zip_read.h" + +const bool WINDOW_VISIBLE = false; +const int WINDOW_WIDTH = 480; +const int WINDOW_HEIGHT = 272; + +typedef BOOL (APIENTRY *PFNWGLSWAPINTERVALFARPROC)(int value); +PFNWGLSWAPINTERVALFARPROC wglSwapIntervalEXT = NULL; + +HWND CreateHiddenWindow() +{ + static WNDCLASSEX wndClass = { + sizeof(WNDCLASSEX), + CS_HREDRAW | CS_VREDRAW | CS_OWNDC, + DefWindowProc, + 0, + 0, + NULL, + NULL, + LoadCursor(NULL, IDC_ARROW), + (HBRUSH) GetStockObject(BLACK_BRUSH), + NULL, + "PPSSPPHeadless", + NULL, + }; + RegisterClassEx(&wndClass); + + DWORD style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_POPUP; + return CreateWindowEx(0, "PPSSPPHeadless", "PPSSPPHeadless", style, CW_USEDEFAULT, CW_USEDEFAULT, WINDOW_WIDTH, WINDOW_HEIGHT, NULL, NULL, NULL, NULL); +} + +void SetVSync(int value) +{ + const char *extensions = (const char *) glGetString(GL_EXTENSIONS); + + if (!strstr(extensions, "WGL_EXT_swap_control")) + return; + + wglSwapIntervalEXT = (PFNWGLSWAPINTERVALFARPROC) wglGetProcAddress("wglSwapIntervalEXT"); + if (wglSwapIntervalEXT != NULL) + wglSwapIntervalEXT(value); +} + +void WindowsHeadlessHost::LoadNativeAssets() +{ + // Native is kinda talkative, but that's annoying in our case. + out = _fdopen(_dup(_fileno(stdout)), "wt"); + freopen("NUL", "wt", stdout); + + VFSRegister("", new DirectoryAssetReader("assets/")); + VFSRegister("", new DirectoryAssetReader("")); + VFSRegister("", new DirectoryAssetReader("../")); + + gl_lost_manager_init(); + + // See SendDebugOutput() for how things get back on track. +} + +void WindowsHeadlessHost::SendDebugOutput(const std::string &output) +{ + fprintf_s(out, "%s", output.c_str()); + OutputDebugString(output.c_str()); +} + +void WindowsHeadlessHost::InitGL() +{ + glOkay = false; + hWnd = CreateHiddenWindow(); + + if (WINDOW_VISIBLE) + { + ShowWindow(hWnd, TRUE); + SetFocus(hWnd); + } + + int pixelFormat; + + static PIXELFORMATDESCRIPTOR pfd = {0}; + pfd.nSize = sizeof(pfd); + pfd.nVersion = 1; + pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; + pfd.iPixelType = PFD_TYPE_RGBA; + pfd.cColorBits = 32; + pfd.cDepthBits = 16; + pfd.iLayerType = PFD_MAIN_PLANE; + +#define ENFORCE(x, msg) { if (!(x)) { fprintf(stderr, msg); return; } } + + ENFORCE(hDC = GetDC(hWnd), "Unable to create DC."); + ENFORCE(pixelFormat = ChoosePixelFormat(hDC, &pfd), "Unable to match pixel format."); + ENFORCE(SetPixelFormat(hDC, pixelFormat, &pfd), "Unable to set pixel format."); + ENFORCE(hRC = wglCreateContext(hDC), "Unable to create GL context."); + ENFORCE(wglMakeCurrent(hDC, hRC), "Unable to activate GL context."); + + SetVSync(0); + + glewInit(); + glstate.Initialize(); + + LoadNativeAssets(); + + if (ResizeGL()) + glOkay = true; +} + +void WindowsHeadlessHost::ShutdownGL() +{ + if (hRC) + { + wglMakeCurrent(NULL, NULL); + wglDeleteContext(hRC); + hRC = NULL; + } + + if (hDC) + ReleaseDC(hWnd, hDC); + hDC = NULL; + DestroyWindow(hWnd); + hWnd = NULL; +} + +bool WindowsHeadlessHost::ResizeGL() +{ + if (!hWnd) + return false; + + RECT rc; + GetWindowRect(hWnd, &rc); + + glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0.0f, WINDOW_WIDTH, WINDOW_HEIGHT, 0.0f, -1.0f, 1.0f); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + return true; +} + +void WindowsHeadlessHost::BeginFrame() +{ + +} + +void WindowsHeadlessHost::EndFrame() +{ + SwapBuffers(hDC); +} diff --git a/Core/HLE/scesupPreAcc.h b/headless/WindowsHeadlessHost.h similarity index 58% rename from Core/HLE/scesupPreAcc.h rename to headless/WindowsHeadlessHost.h index 311e679bd8..28e51ee543 100644 --- a/Core/HLE/scesupPreAcc.h +++ b/headless/WindowsHeadlessHost.h @@ -17,5 +17,32 @@ #pragma once -void Register_scesupPreAcc(); +#include "StubHost.h" +#undef HEADLESSHOST_CLASS +#define HEADLESSHOST_CLASS WindowsHeadlessHost + +#include + +// TODO: Get rid of this junk +class WindowsHeadlessHost : public HeadlessHost +{ +public: + virtual void InitGL(); + virtual void BeginFrame(); + virtual void EndFrame(); + virtual void ShutdownGL(); + virtual bool isGLWorking() { return glOkay; } + + virtual void SendDebugOutput(const std::string &output); + +private: + bool ResizeGL(); + void LoadNativeAssets(); + + bool glOkay; + HWND hWnd; + HDC hDC; + HGLRC hRC; + FILE *out; +}; \ No newline at end of file diff --git a/native b/native index 1556328129..dbda5f8037 160000 --- a/native +++ b/native @@ -1 +1 @@ -Subproject commit 15563281297adb62c8e690fc063a9bf4442fcfc7 +Subproject commit dbda5f8037e3da7fda5e5ec4cb59a8047c319e38 diff --git a/pspautotests b/pspautotests index 5e5ae52067..a35333fa37 160000 --- a/pspautotests +++ b/pspautotests @@ -1 +1 @@ -Subproject commit 5e5ae520672b816943acabed1f223ebcd7cff16f +Subproject commit a35333fa37a6439ef6623fad7b66f19115ccf724 diff --git a/test.py b/test.py index 2e1248690b..306f9e442d 100755 --- a/test.py +++ b/test.py @@ -8,7 +8,7 @@ import subprocess import threading -PPSSPP_EXECUTABLES = [ "Windows\\Release\\PPSSPPHeadless.exe", "SDL/build/PPSSPPHeadless" ] +PPSSPP_EXECUTABLES = [ "Windows\\Release\\PPSSPPHeadless.exe", "build/PPSSPPHeadless" ] PPSSPP_EXE = None TEST_ROOT = "pspautotests/tests/" teamcity_mode = False @@ -60,6 +60,7 @@ tests_good = [ "misc/testgp", "string/string", "gpu/callbacks/ge_callbacks", + "gpu/displaylist/state", "threads/alarm/alarm", "threads/alarm/cancel/cancel", "threads/alarm/refer/refer", @@ -73,6 +74,7 @@ tests_good = [ "threads/events/refer/refer", "threads/events/set/set", "threads/events/wait/wait", + "threads/k0/k0", "threads/lwmutex/create/create", "threads/lwmutex/delete/delete", "threads/lwmutex/lock/lock",