mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-07-11 01:25:07 +02:00
Fixed build with system libzip and miniupnpc
This commit is contained in:
@@ -1,4 +1,8 @@
|
||||
#include <ext/libzip/zip.h>
|
||||
#ifdef SHARED_LIBZIP
|
||||
#include <zip.h>
|
||||
#else
|
||||
#include "ext/libzip/zip.h"
|
||||
#endif
|
||||
|
||||
#include "Core/FileLoaders/LocalFileLoader.h"
|
||||
#include "Core/FileLoaders/ZipFileLoader.h"
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
#include <ext/libzip/zip.h>
|
||||
|
||||
#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<zip_int64_t>(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<const SeekData*>(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<zip_stat_t*>(data);
|
||||
zip_stat_init(st);
|
||||
st->valid = ZIP_STAT_SIZE;
|
||||
st->size = static_cast<zip_uint64_t>(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;
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,11 @@
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include <ext/libzip/zip.h>
|
||||
#ifdef SHARED_LIBZIP
|
||||
#include <zip.h>
|
||||
#else
|
||||
#include "ext/libzip/zip.h"
|
||||
#endif
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/Log.h"
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include <ext/libzip/zip.h>
|
||||
|
||||
#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_;
|
||||
};
|
||||
@@ -20,7 +20,11 @@
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
#ifdef SHARED_LIBZIP
|
||||
#include <zip.h>
|
||||
#else
|
||||
#include "ext/libzip/zip.h"
|
||||
#endif
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/File/Path.h"
|
||||
|
||||
|
||||
@@ -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 <string>
|
||||
#include <memory>
|
||||
|
||||
#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);
|
||||
@@ -22,9 +22,9 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_SYSTEM_MINIUPNPC
|
||||
#include <miniupnpc/include/miniwget.h>
|
||||
#include <miniupnpc/include/miniupnpc.h>
|
||||
#include <miniupnpc/include/upnpcommands.h>
|
||||
#include <miniupnpc/miniwget.h>
|
||||
#include <miniupnpc/miniupnpc.h>
|
||||
#include <miniupnpc/upnpcommands.h>
|
||||
#else
|
||||
#ifndef MINIUPNP_STATICLIB
|
||||
#define MINIUPNP_STATICLIB
|
||||
|
||||
@@ -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 <miniupnpc/include/miniwget.h>
|
||||
#include <miniupnpc/include/miniupnpc.h>
|
||||
#include <miniupnpc/include/upnpcommands.h>
|
||||
#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 <string>
|
||||
#include <deque>
|
||||
|
||||
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<std::pair<std::string, std::string>> m_portList;
|
||||
std::deque<PortMap> 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);
|
||||
Reference in New Issue
Block a user