diff --git a/Core/FileLoaders/ZipFileLoader.cpp b/Core/FileLoaders/ZipFileLoader.cpp index d7b9871792..bcf05907d0 100644 --- a/Core/FileLoaders/ZipFileLoader.cpp +++ b/Core/FileLoaders/ZipFileLoader.cpp @@ -1,4 +1,8 @@ -#include +#ifdef SHARED_LIBZIP +#include +#else +#include "ext/libzip/zip.h" +#endif #include "Core/FileLoaders/LocalFileLoader.h" #include "Core/FileLoaders/ZipFileLoader.h" diff --git a/Core/FileLoaders/ZipFileLoader.cpp.orig b/Core/FileLoaders/ZipFileLoader.cpp.orig new file mode 100644 index 0000000000..d7b9871792 --- /dev/null +++ b/Core/FileLoaders/ZipFileLoader.cpp.orig @@ -0,0 +1,152 @@ +#include + +#include "Core/FileLoaders/LocalFileLoader.h" +#include "Core/FileLoaders/ZipFileLoader.h" + +ZipFileLoader::ZipFileLoader(FileLoader *sourceLoader) + : ProxiedFileLoader(sourceLoader), zipArchive_(nullptr) { + if (!backend_ || !backend_->Exists() || backend_->IsDirectory()) { + // bad + } + + zip_error_t error{}; + zip_source_t* zipSource = zip_source_function_create([](void* userdata, void* data, zip_uint64_t len, zip_source_cmd_t cmd) -> zip_int64_t { + ZipFileLoader *loader = (ZipFileLoader *)userdata; + return loader->ZipSourceCallback(data, len, cmd); + }, this, &error); + if (!zipSource) { + ERROR_LOG(Log::IO, "Failed to create ZIP source: %s", zip_error_strerror(&error)); + return; + } + + zipArchive_ = zip_open_from_source(zipSource, ZIP_RDONLY, &error); + if (!zipArchive_) { + ERROR_LOG(Log::IO, "Failed to open ZIP archive: %s", zip_error_strerror(&error)); + zip_source_free(zipSource); + } +} + +ZipFileLoader::~ZipFileLoader() { + if (dataFile_) { + zip_fclose(dataFile_); + } + if (zipArchive_) { + zip_discard(zipArchive_); + } + if (data_) { + free(data_); + } +} + +bool ZipFileLoader::Initialize(int fileIndex) { + _dbg_assert_(!data_); + + struct zip_stat zstat; + int retval = zip_stat_index(zipArchive_, fileIndex, ZIP_FL_NOCASE | ZIP_FL_UNCHANGED, &zstat); + if (retval < 0) { + return false; + } + const char *name = zip_get_name(zipArchive_, fileIndex, ZIP_FL_NOCASE | ZIP_FL_UNCHANGED); + fileExtension_ = KeepIncludingLast(name, '.'); + + _dbg_assert_(zstat.index == fileIndex); + dataFileSize_ = zstat.size; + dataFile_ = zip_fopen_index(zipArchive_, zstat.index, ZIP_FL_UNCHANGED); + data_ = (u8 *)malloc(dataFileSize_); + return data_ != nullptr; +} + +size_t ZipFileLoader::ReadAt(s64 absolutePos, size_t bytes, void *data, Flags flags) { + if (!dataFile_ || absolutePos < 0 || absolutePos >= dataFileSize_) { + return 0; + } + + if (absolutePos + (s64)bytes > dataFileSize_) { + // TODO: This could go negative.. + bytes = dataFileSize_ - absolutePos; + } + + // Decompress until the requested point, filling up data_ as we go. TODO: Do on thread. + while (dataReadPos_ < absolutePos + (s64)bytes) { + int remaining = BLOCK_SIZE; + if (dataReadPos_ + remaining > dataFileSize_) { + remaining = (int)(dataFileSize_ - dataReadPos_); + } + zip_int64_t retval = zip_fread(dataFile_, data_ + dataReadPos_, remaining); + _dbg_assert_(retval == remaining); + dataReadPos_ += retval; + } + + // Perform the read. + memcpy(data, data_ + absolutePos, bytes); + return bytes; +} + +zip_int64_t ZipFileLoader::ZipSourceCallback(void *data, zip_uint64_t len, zip_source_cmd_t cmd) { + switch (cmd) { + case ZIP_SOURCE_OPEN: + { + zipReadPos_ = 0; + return 0; + } + case ZIP_SOURCE_READ: + { + size_t readBytes = static_cast(backend_->ReadAt(zipReadPos_, len, data)); + zipReadPos_ += readBytes; + return readBytes; + } + case ZIP_SOURCE_SEEK: + { + struct SeekData { + zip_int64_t offset; + int whence; + }; + if (len < sizeof(SeekData)) { + return -1; // Invalid argument size + } + const SeekData *seekData = static_cast(data); + zip_int64_t new_offset; + switch (seekData->whence) { + case SEEK_SET: + new_offset = seekData->offset; + break; + case SEEK_CUR: + new_offset = zipReadPos_ + seekData->offset; + break; + case SEEK_END: + new_offset = backend_->FileSize() + seekData->offset; + break; + default: + return -1; // Invalid 'whence' value + } + if (new_offset < 0 || new_offset > backend_->FileSize()) { + return -1; // Offset out of bounds + } + zipReadPos_ = new_offset; + return 0; + } + case ZIP_SOURCE_TELL: + return zipReadPos_; + case ZIP_SOURCE_CLOSE: + return 0; + case ZIP_SOURCE_STAT: + { + if (len < sizeof(zip_stat_t)) { + return -1; + } + zip_stat_t* st = static_cast(data); + zip_stat_init(st); + st->valid = ZIP_STAT_SIZE; + st->size = static_cast(backend_->FileSize()); + return sizeof(zip_stat_t); + } + case ZIP_SOURCE_ERROR: + return -1; + case ZIP_SOURCE_FREE: + return 0; + case ZIP_SOURCE_SUPPORTS: + return zip_source_make_command_bitmap(ZIP_SOURCE_READ, ZIP_SOURCE_SEEK, ZIP_SOURCE_TELL, ZIP_SOURCE_OPEN, ZIP_SOURCE_CLOSE, ZIP_SOURCE_STAT); + default: + return -1; + } +} diff --git a/Core/FileLoaders/ZipFileLoader.h b/Core/FileLoaders/ZipFileLoader.h index ea2040d6eb..cc7c394fd7 100644 --- a/Core/FileLoaders/ZipFileLoader.h +++ b/Core/FileLoaders/ZipFileLoader.h @@ -2,7 +2,11 @@ #include -#include +#ifdef SHARED_LIBZIP +#include +#else +#include "ext/libzip/zip.h" +#endif #include "Common/CommonTypes.h" #include "Common/Log.h" diff --git a/Core/FileLoaders/ZipFileLoader.h.orig b/Core/FileLoaders/ZipFileLoader.h.orig new file mode 100644 index 0000000000..ea2040d6eb --- /dev/null +++ b/Core/FileLoaders/ZipFileLoader.h.orig @@ -0,0 +1,65 @@ +#pragma once + +#include + +#include + +#include "Common/CommonTypes.h" +#include "Common/Log.h" +#include "Common/File/Path.h" +#include "Common/StringUtils.h" +#include "Core/Loaders.h" + +// Exposes a single (chosen) file from a zip file as another file loader. +// Useful in a bunch of possible chains. +class ZipFileLoader : public ProxiedFileLoader { +public: + ZipFileLoader(FileLoader *sourceLoader); + ~ZipFileLoader() override; + + zip_t *GetZip() const { + return zipArchive_; + } + + bool Initialize(int fileIndex); + + bool Exists() override { + return dataFile_ != nullptr; + } + + bool IsDirectory() override { + return false; + } + + s64 FileSize() override { + return dataFileSize_; + } + + Path GetPath() const override { + return backend_->GetPath(); + } + + size_t ReadAt(s64 absolutePos, size_t bytes, size_t count, void *data, Flags flags = Flags::NONE) override { + return ReadAt(absolutePos, bytes * count, data, flags) / bytes; + } + size_t ReadAt(s64 absolutePos, size_t bytes, void *data, Flags flags = Flags::NONE) override; + + std::string GetFileExtension() const override { + return fileExtension_; + } + +private: + zip_int64_t ZipSourceCallback(void* data, zip_uint64_t len, zip_source_cmd_t cmd); + + enum { + BLOCK_SIZE = 65536, + }; + zip_t *zipArchive_ = nullptr; + s64 zipReadPos_ = 0; + + zip_file_t *dataFile_ = nullptr; + uint8_t *data_ = nullptr; // malloc/free + s64 dataReadPos_ = 0; + s64 dataFileSize_ = 0; + std::string fileExtension_; +}; diff --git a/Core/Loaders.h b/Core/Loaders.h index 0063aba1af..4015e022fe 100644 --- a/Core/Loaders.h +++ b/Core/Loaders.h @@ -20,7 +20,11 @@ #include #include +#ifdef SHARED_LIBZIP +#include +#else #include "ext/libzip/zip.h" +#endif #include "Common/CommonTypes.h" #include "Common/File/Path.h" diff --git a/Core/Loaders.h.orig b/Core/Loaders.h.orig new file mode 100644 index 0000000000..0063aba1af --- /dev/null +++ b/Core/Loaders.h.orig @@ -0,0 +1,191 @@ +// 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 +#include + +#include "ext/libzip/zip.h" +#include "Common/CommonTypes.h" +#include "Common/File/Path.h" + +enum class IdentifiedFileType { + ERROR_IDENTIFYING, + + PSP_PBP_DIRECTORY, + + PSP_PBP, + PSP_ELF, + PSP_ISO, + PSP_ISO_NP, + + PSP_DISC_DIRECTORY, + + UNKNOWN_BIN, + UNKNOWN_ELF, + UNKNOWN_ISO, + + // Try to reduce support emails... + ARCHIVE_RAR, + ARCHIVE_ZIP, + ARCHIVE_7Z, + PSP_PS1_PBP, + ISO_MODE2, + + NORMAL_DIRECTORY, + + PSP_SAVEDATA_DIRECTORY, + PPSSPP_SAVESTATE, + + PPSSPP_GE_DUMP, + + UNKNOWN, +}; + +// NB: It is a REQUIREMENT that implementations of this class are entirely thread safe! +// TOOD: actually, is it really? +class FileLoader { +public: + enum class Flags { + NONE, + // Not necessary to read from / store into cache. + HINT_UNCACHED, + }; + + virtual ~FileLoader() {} + + virtual bool IsRemote() { + return false; + } + virtual bool Exists() = 0; + virtual bool ExistsFast() { + return Exists(); + } + virtual bool IsDirectory() = 0; + virtual s64 FileSize() = 0; + virtual Path GetPath() const = 0; + virtual std::string GetFileExtension() const { + return GetPath().GetFileExtension(); + } + virtual size_t ReadAt(s64 absolutePos, size_t bytes, size_t count, void *data, Flags flags = Flags::NONE) = 0; + virtual size_t ReadAt(s64 absolutePos, size_t bytes, void *data, Flags flags = Flags::NONE) { + return ReadAt(absolutePos, 1, bytes, data, flags); + } + + // Cancel any operations that might block, if possible. + virtual void Cancel() {} + + virtual std::string LatestError() const { + return ""; + } +}; + +class ProxiedFileLoader : public FileLoader { +public: + ProxiedFileLoader(FileLoader *backend) : backend_(backend) {} + ~ProxiedFileLoader() { + // Takes ownership. + delete backend_; + } + bool IsRemote() override { + return backend_->IsRemote(); + } + bool Exists() override { + return backend_->Exists(); + } + bool ExistsFast() override { + return backend_->ExistsFast(); + } + bool IsDirectory() override { + return backend_->IsDirectory(); + } + s64 FileSize() override { + return backend_->FileSize(); + } + Path GetPath() const override { + return backend_->GetPath(); + } + void Cancel() override { + backend_->Cancel(); + } + std::string LatestError() const override { + return backend_->LatestError(); + } + size_t ReadAt(s64 absolutePos, size_t bytes, size_t count, void *data, Flags flags = Flags::NONE) override { + return backend_->ReadAt(absolutePos, bytes, count, data, flags); + } + size_t ReadAt(s64 absolutePos, size_t bytes, void *data, Flags flags = Flags::NONE) override { + return backend_->ReadAt(absolutePos, bytes, data, flags); + } + FileLoader *Steal() { + FileLoader *backend = backend_; + backend_ = nullptr; + return backend; + } + +protected: + FileLoader *backend_; +}; + +inline u32 operator & (const FileLoader::Flags &a, const FileLoader::Flags &b) { + return (u32)a & (u32)b; +} + +FileLoader *ConstructFileLoader(const Path &filename); +// Resolve to the target binary, ISO, or other file (e.g. from a directory.) +FileLoader *ResolveFileLoaderTarget(FileLoader *fileLoader); + +Path ResolvePBPDirectory(const Path &filename); +Path ResolvePBPFile(const Path &filename); + +IdentifiedFileType Identify_File(FileLoader *fileLoader, std::string *errorString); + +bool UmdReplace(const Path &filepath, FileLoader **fileLoader, std::string &error); + + +enum class ZipFileContents { + UNKNOWN, + PSP_GAME_DIR, + ISO_FILE, + TEXTURE_PACK, + SAVE_DATA, + FRAME_DUMP, +}; + +struct ZipFileInfo { + ZipFileContents contents; + int numFiles; + int stripChars; // for PSP game - how much to strip from the path. + int isoFileIndex; // for ISO + int textureIniIndex; // for textures + bool ignoreMetaFiles; + std::string gameTitle; // from PARAM.SFO if available + std::string savedataTitle; + std::string savedataDetails; + std::string savedataDir; + std::string mTime; + s64 totalFileSize; + + std::string contentName; +}; + +struct zip *ZipOpenPath(const Path &fileName); +void ZipClose(zip *z); + +bool DetectZipFileContents(const Path &fileName, ZipFileInfo *info); +void DetectZipFileContents(struct zip *z, ZipFileInfo *info); diff --git a/Core/Util/PortManager.h b/Core/Util/PortManager.h index c3788fea31..b42e3f9e08 100644 --- a/Core/Util/PortManager.h +++ b/Core/Util/PortManager.h @@ -22,9 +22,9 @@ #pragma once #ifdef USE_SYSTEM_MINIUPNPC -#include -#include -#include +#include +#include +#include #else #ifndef MINIUPNP_STATICLIB #define MINIUPNP_STATICLIB diff --git a/Core/Util/PortManager.h.orig b/Core/Util/PortManager.h.orig new file mode 100644 index 0000000000..c3788fea31 --- /dev/null +++ b/Core/Util/PortManager.h.orig @@ -0,0 +1,126 @@ +// Copyright (c) 2013- 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/. + +// Most of the code are based on https://github.com/RJ/libportfwd and updated to the latest miniupnp library +// All credit goes to him and the official miniupnp project! http://miniupnp.free.fr/ + + +#pragma once + +#ifdef USE_SYSTEM_MINIUPNPC +#include +#include +#include +#else +#ifndef MINIUPNP_STATICLIB +#define MINIUPNP_STATICLIB +#endif +#include "ext/miniupnp/miniupnpc/include/miniwget.h" +#include "ext/miniupnp/miniupnpc/include/miniupnpc.h" +#include "ext/miniupnp/miniupnpc/include/upnpcommands.h" +#endif + +#include +#include + +struct UPnPArgs { + int cmd; + std::string protocol; + unsigned short port; + unsigned short intport; +}; + +#define IP_PROTOCOL_TCP "TCP" +#define IP_PROTOCOL_UDP "UDP" +#define UPNP_INITSTATE_NONE 0 +#define UPNP_INITSTATE_BUSY 1 +#define UPNP_INITSTATE_DONE 2 + +#define UPNP_CMD_ADD 0 +#define UPNP_CMD_REMOVE 1 + +struct UPNPUrls; +struct IGDdatas; + +struct PortMap { + bool taken; + std::string protocol; + std::string extPort_str; + std::string intPort_str; + std::string lanip; + std::string remoteHost; + std::string desc; + std::string duration; + std::string enabled; +}; + +class PortManager { +public: + PortManager(); + ~PortManager(); + + // Initialize UPnP + // timeout: milliseconds to wait for a router to respond (default = 2000 ms) + bool Initialize(const unsigned int timeout = 2000); + + // Get UPnP Initialization status + int GetInitState(); + + // Add a port & protocol (TCP, UDP or vendor-defined) to map for forwarding (intport = 0 : same as [external] port) + bool Add(const char* protocol, unsigned short port, unsigned short intport = 0); + + // Remove a port mapping (external port) + bool Remove(const char* protocol, unsigned short port); + + // Call on exit. Does a full shutdown. + void Shutdown(); + +private: + // Retrieves port lists mapped by PPSSPP for current LAN IP & other's applications + bool RefreshPortList(); + + // Removes any lingering mapped ports created by PPSSPP (including from previous crashes) + bool Clear(); + + // Restore ports mapped by others that were taken by PPSSPP, better used after Clear() + bool Restore(); + + // Uninitialize/Reset the state + void Terminate(); + + struct UPNPUrls* urls = nullptr; + struct IGDdatas* datas = nullptr; + + int m_InitState = UPNP_INITSTATE_NONE; + int m_LocalPort = UPNP_LOCAL_PORT_ANY; + std::string m_lanip; + std::string m_defaultDesc; + std::string m_leaseDuration = "43200"; // range(0-604800) in seconds (0 = Indefinite/permanent). Some routers doesn't support non-zero value + std::deque> m_portList; + std::deque m_otherPortList; +}; + +extern PortManager g_PortManager; + +void __UPnPInit(const int timeout_ms); +void __UPnPShutdown(); + +// Add a port & protocol (TCP, UDP or vendor-defined) to map for forwarding (intport = 0 : same as [external] port) +void UPnP_Add(const char* protocol, unsigned short port, unsigned short intport = 0); + +// Remove a port mapping (external port) +void UPnP_Remove(const char* protocol, unsigned short port);