mirror of
https://github.com/izzy2lost/ppsspp.git
synced 2026-03-10 12:43:04 -07:00
Move fileutil, net, image loaders, ui to Common. (#13506)
* Move and rename file_util/fd_util to Common/File/FileUtil and DirListing Let's also move net while we're at it. Move the ZIM/PNG loaders over to Common. Move the UI framework into Common iOS buildfix * Buildfix * Buildfixes * Apple buildfix * This typo again.. * UWP buildfix * Fix build of PPSSPPQt, such as it is (it's not in good condition...) * Guess what? Another buildfix.
This commit is contained in:
@@ -1,178 +0,0 @@
|
||||
#include "ppsspp_config.h"
|
||||
#include "file/fd_util.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#ifndef _WIN32
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/select.h>
|
||||
#include <unistd.h>
|
||||
#else
|
||||
#include <io.h>
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#endif
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "Common/Common.h"
|
||||
#include "Common/Log.h"
|
||||
|
||||
namespace fd_util {
|
||||
|
||||
// Slow as hell and should only be used for prototyping.
|
||||
// Reads from a socket, up to an '\n'. This means that if the line ends
|
||||
// with '\r', the '\r' will be returned.
|
||||
size_t ReadLine(int fd, char *vptr, size_t buf_size) {
|
||||
char *buffer = vptr;
|
||||
size_t n;
|
||||
for (n = 1; n < buf_size; n++) {
|
||||
char c;
|
||||
size_t rc;
|
||||
if ((rc = read(fd, &c, 1)) == 1) {
|
||||
*buffer++ = c;
|
||||
if (c == '\n')
|
||||
break;
|
||||
}
|
||||
else if (rc == 0) {
|
||||
if (n == 1)
|
||||
return 0;
|
||||
else
|
||||
break;
|
||||
}
|
||||
else {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
_assert_msg_(false, "Error in Readline()");
|
||||
}
|
||||
}
|
||||
|
||||
*buffer = 0;
|
||||
return n;
|
||||
}
|
||||
|
||||
// Misnamed, it just writes raw data in a retry loop.
|
||||
size_t WriteLine(int fd, const char *vptr, size_t n) {
|
||||
const char *buffer = vptr;
|
||||
size_t nleft = n;
|
||||
|
||||
while (nleft > 0) {
|
||||
int nwritten;
|
||||
if ((nwritten = (int)write(fd, buffer, (unsigned int)nleft)) <= 0) {
|
||||
if (errno == EINTR)
|
||||
nwritten = 0;
|
||||
else
|
||||
_assert_msg_(false, "Error in Writeline()");
|
||||
}
|
||||
nleft -= nwritten;
|
||||
buffer += nwritten;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t WriteLine(int fd, const char *buffer) {
|
||||
return WriteLine(fd, buffer, strlen(buffer));
|
||||
}
|
||||
|
||||
size_t Write(int fd, const std::string &str) {
|
||||
return WriteLine(fd, str.c_str(), str.size());
|
||||
}
|
||||
|
||||
bool WaitUntilReady(int fd, double timeout, bool for_write) {
|
||||
struct timeval tv;
|
||||
tv.tv_sec = floor(timeout);
|
||||
tv.tv_usec = (timeout - floor(timeout)) * 1000000.0;
|
||||
|
||||
fd_set fds;
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(fd, &fds);
|
||||
// First argument to select is the highest socket in the set + 1.
|
||||
int rval;
|
||||
if (for_write) {
|
||||
rval = select(fd + 1, NULL, &fds, NULL, &tv);
|
||||
} else {
|
||||
rval = select(fd + 1, &fds, NULL, NULL, &tv);
|
||||
}
|
||||
if (rval < 0) {
|
||||
// Error calling select.
|
||||
return false;
|
||||
} else if (rval == 0) {
|
||||
// Timeout.
|
||||
return false;
|
||||
} else {
|
||||
// Socket is ready.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void SetNonBlocking(int sock, bool non_blocking) {
|
||||
#ifndef _WIN32
|
||||
int opts = fcntl(sock, F_GETFL);
|
||||
if (opts < 0) {
|
||||
perror("fcntl(F_GETFL)");
|
||||
ERROR_LOG(IO, "Error getting socket status while changing nonblocking status");
|
||||
}
|
||||
if (non_blocking) {
|
||||
opts = (opts | O_NONBLOCK);
|
||||
} else {
|
||||
opts = (opts & ~O_NONBLOCK);
|
||||
}
|
||||
|
||||
if (fcntl(sock, F_SETFL, opts) < 0) {
|
||||
perror("fcntl(F_SETFL)");
|
||||
ERROR_LOG(IO, "Error setting socket nonblocking status");
|
||||
}
|
||||
#else
|
||||
u_long val = non_blocking ? 1 : 0;
|
||||
if (ioctlsocket(sock, FIONBIO, &val) != 0) {
|
||||
ERROR_LOG(IO, "Error setting socket nonblocking status");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string GetLocalIP(int sock) {
|
||||
union {
|
||||
struct sockaddr sa;
|
||||
struct sockaddr_in ipv4;
|
||||
#if !PPSSPP_PLATFORM(SWITCH)
|
||||
struct sockaddr_in6 ipv6;
|
||||
#endif
|
||||
} server_addr;
|
||||
memset(&server_addr, 0, sizeof(server_addr));
|
||||
socklen_t len = sizeof(server_addr);
|
||||
if (getsockname(sock, (struct sockaddr *)&server_addr, &len) == 0) {
|
||||
char temp[64]{};
|
||||
|
||||
// We clear the port below for WSAAddressToStringA.
|
||||
void *addr = nullptr;
|
||||
#if !PPSSPP_PLATFORM(SWITCH)
|
||||
if (server_addr.sa.sa_family == AF_INET6) {
|
||||
server_addr.ipv6.sin6_port = 0;
|
||||
addr = &server_addr.ipv6.sin6_addr;
|
||||
}
|
||||
#endif
|
||||
if (addr == nullptr) {
|
||||
server_addr.ipv4.sin_port = 0;
|
||||
addr = &server_addr.ipv4.sin_addr;
|
||||
}
|
||||
#ifdef _WIN32
|
||||
DWORD len = (DWORD)sizeof(temp);
|
||||
// Windows XP doesn't support inet_ntop.
|
||||
if (WSAAddressToStringA((struct sockaddr *)&server_addr, sizeof(server_addr), nullptr, temp, &len) == 0) {
|
||||
return temp;
|
||||
}
|
||||
#else
|
||||
const char *result = inet_ntop(server_addr.sa.sa_family, addr, temp, sizeof(temp));
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
} // fd_util
|
||||
@@ -1,24 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
namespace fd_util {
|
||||
|
||||
// Slow as hell and should only be used for prototyping.
|
||||
size_t ReadLine(int fd, char *buffer, size_t buf_size);
|
||||
|
||||
// Decently fast.
|
||||
size_t WriteLine(int fd, const char *buffer, size_t buf_size);
|
||||
size_t WriteLine(int fd, const char *buffer);
|
||||
size_t Write(int fd, const std::string &str);
|
||||
|
||||
// Returns true if the fd became ready, false if it didn't or
|
||||
// if there was another error.
|
||||
bool WaitUntilReady(int fd, double timeout, bool for_write = false);
|
||||
|
||||
void SetNonBlocking(int fd, bool non_blocking);
|
||||
|
||||
std::string GetLocalIP(int sock);
|
||||
|
||||
} // fd_util
|
||||
@@ -1,367 +0,0 @@
|
||||
#include "ppsspp_config.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <Windows.h>
|
||||
#include <direct.h>
|
||||
#else
|
||||
#include <strings.h>
|
||||
#include <dirent.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <set>
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <sys/stat.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "file/file_util.h"
|
||||
#include "Common/Data/Encoding/Utf8.h"
|
||||
|
||||
#include "Common/StringUtils.h"
|
||||
|
||||
#if !defined(__linux__) && !defined(_WIN32) && !defined(__QNX__)
|
||||
#define stat64 stat
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LIBNX
|
||||
// Far from optimal, but I guess it works...
|
||||
#define fseeko fseek
|
||||
#define ftello ftell
|
||||
#define fileno
|
||||
#endif // HAVE_LIBNX
|
||||
|
||||
FILE *openCFile(const std::string &filename, const char *mode)
|
||||
{
|
||||
#if defined(_WIN32) && defined(UNICODE)
|
||||
return _wfopen(ConvertUTF8ToWString(filename).c_str(), ConvertUTF8ToWString(mode).c_str());
|
||||
#else
|
||||
return fopen(filename.c_str(), mode);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool writeStringToFile(bool text_file, const std::string &str, const char *filename)
|
||||
{
|
||||
FILE *f = openCFile(filename, text_file ? "w" : "wb");
|
||||
if (!f)
|
||||
return false;
|
||||
size_t len = str.size();
|
||||
if (len != fwrite(str.data(), 1, str.size(), f))
|
||||
{
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
fclose(f);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool writeDataToFile(bool text_file, const void* data, const unsigned int size, const char *filename)
|
||||
{
|
||||
FILE *f = openCFile(filename, text_file ? "w" : "wb");
|
||||
if (!f)
|
||||
return false;
|
||||
size_t len = size;
|
||||
if (len != fwrite(data, 1, len, f))
|
||||
{
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
fclose(f);
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t GetSize(FILE *f)
|
||||
{
|
||||
// This will only support 64-bit when large file support is available.
|
||||
// That won't be the case on some versions of Android, at least.
|
||||
#if defined(__ANDROID__) || (defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS < 64)
|
||||
int fd = fileno(f);
|
||||
|
||||
off64_t pos = lseek64(fd, 0, SEEK_CUR);
|
||||
off64_t size = lseek64(fd, 0, SEEK_END);
|
||||
if (size != pos && lseek64(fd, pos, SEEK_SET) != pos) {
|
||||
// Should error here.
|
||||
return 0;
|
||||
}
|
||||
return size;
|
||||
#else
|
||||
#ifdef _WIN32
|
||||
uint64_t pos = _ftelli64(f);
|
||||
#else
|
||||
uint64_t pos = ftello(f);
|
||||
#endif
|
||||
if (fseek(f, 0, SEEK_END) != 0) {
|
||||
return 0;
|
||||
}
|
||||
#ifdef _WIN32
|
||||
uint64_t size = _ftelli64(f);
|
||||
// Reset the seek position to where it was when we started.
|
||||
if (size != pos && _fseeki64(f, pos, SEEK_SET) != 0) {
|
||||
#else
|
||||
uint64_t size = ftello(f);
|
||||
// Reset the seek position to where it was when we started.
|
||||
if (size != pos && fseeko(f, pos, SEEK_SET) != 0) {
|
||||
#endif
|
||||
// Should error here.
|
||||
return 0;
|
||||
}
|
||||
return size;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool readFileToString(bool text_file, const char *filename, std::string &str)
|
||||
{
|
||||
FILE *f = openCFile(filename, text_file ? "r" : "rb");
|
||||
if (!f)
|
||||
return false;
|
||||
size_t len = (size_t)GetSize(f);
|
||||
char *buf = new char[len + 1];
|
||||
buf[fread(buf, 1, len, f)] = 0;
|
||||
str = std::string(buf, len);
|
||||
fclose(f);
|
||||
delete [] buf;
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t *ReadLocalFile(const char *filename, size_t *size) {
|
||||
FILE *file = openCFile(filename, "rb");
|
||||
if (!file) {
|
||||
*size = 0;
|
||||
return nullptr;
|
||||
}
|
||||
fseek(file, 0, SEEK_END);
|
||||
size_t f_size = ftell(file);
|
||||
if ((long)f_size < 0) {
|
||||
*size = 0;
|
||||
fclose(file);
|
||||
return nullptr;
|
||||
}
|
||||
fseek(file, 0, SEEK_SET);
|
||||
uint8_t *contents = new uint8_t[f_size + 1];
|
||||
if (fread(contents, 1, f_size, file) != f_size) {
|
||||
delete[] contents;
|
||||
contents = nullptr;
|
||||
*size = 0;
|
||||
} else {
|
||||
contents[f_size] = 0;
|
||||
*size = f_size;
|
||||
}
|
||||
fclose(file);
|
||||
return contents;
|
||||
}
|
||||
|
||||
|
||||
// Returns true if filename is a directory
|
||||
bool isDirectory(const std::string &filename) {
|
||||
FileInfo info;
|
||||
getFileInfo(filename.c_str(), &info);
|
||||
return info.isDirectory;
|
||||
}
|
||||
|
||||
bool getFileInfo(const char *path, FileInfo *fileInfo) {
|
||||
// TODO: Expand relative paths?
|
||||
fileInfo->fullName = path;
|
||||
|
||||
#ifdef _WIN32
|
||||
WIN32_FILE_ATTRIBUTE_DATA attrs;
|
||||
if (!GetFileAttributesExW(ConvertUTF8ToWString(path).c_str(), GetFileExInfoStandard, &attrs)) {
|
||||
fileInfo->size = 0;
|
||||
fileInfo->isDirectory = false;
|
||||
fileInfo->exists = false;
|
||||
return false;
|
||||
}
|
||||
fileInfo->size = (uint64_t)attrs.nFileSizeLow | ((uint64_t)attrs.nFileSizeHigh << 32);
|
||||
fileInfo->isDirectory = (attrs.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
|
||||
fileInfo->isWritable = (attrs.dwFileAttributes & FILE_ATTRIBUTE_READONLY) == 0;
|
||||
fileInfo->exists = true;
|
||||
#else
|
||||
|
||||
std::string copy(path);
|
||||
|
||||
#if (defined __ANDROID__) && (__ANDROID_API__ < 21)
|
||||
struct stat file_info;
|
||||
int result = stat(copy.c_str(), &file_info);
|
||||
#else
|
||||
struct stat64 file_info;
|
||||
int result = stat64(copy.c_str(), &file_info);
|
||||
#endif
|
||||
if (result < 0) {
|
||||
fileInfo->exists = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
fileInfo->isDirectory = S_ISDIR(file_info.st_mode);
|
||||
fileInfo->isWritable = false;
|
||||
fileInfo->size = file_info.st_size;
|
||||
fileInfo->exists = true;
|
||||
// HACK: approximation
|
||||
if (file_info.st_mode & 0200)
|
||||
fileInfo->isWritable = true;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string getFileExtension(const std::string &fn) {
|
||||
int pos = (int)fn.rfind(".");
|
||||
if (pos < 0) return "";
|
||||
std::string ext = fn.substr(pos+1);
|
||||
for (size_t i = 0; i < ext.size(); i++) {
|
||||
ext[i] = tolower(ext[i]);
|
||||
}
|
||||
return ext;
|
||||
}
|
||||
|
||||
bool FileInfo::operator <(const FileInfo &other) const {
|
||||
if (isDirectory && !other.isDirectory)
|
||||
return true;
|
||||
else if (!isDirectory && other.isDirectory)
|
||||
return false;
|
||||
if (strcasecmp(name.c_str(), other.name.c_str()) < 0)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t getFilesInDir(const char *directory, std::vector<FileInfo> *files, const char *filter, int flags) {
|
||||
size_t foundEntries = 0;
|
||||
std::set<std::string> filters;
|
||||
if (filter) {
|
||||
std::string tmp;
|
||||
while (*filter) {
|
||||
if (*filter == ':') {
|
||||
filters.insert(std::move(tmp));
|
||||
} else {
|
||||
tmp.push_back(*filter);
|
||||
}
|
||||
filter++;
|
||||
}
|
||||
if (!tmp.empty())
|
||||
filters.insert(std::move(tmp));
|
||||
}
|
||||
#ifdef _WIN32
|
||||
// Find the first file in the directory.
|
||||
WIN32_FIND_DATA ffd;
|
||||
HANDLE hFind = FindFirstFileEx((ConvertUTF8ToWString(directory) + L"\\*").c_str(), FindExInfoStandard, &ffd, FindExSearchNameMatch, NULL, 0);
|
||||
if (hFind == INVALID_HANDLE_VALUE) {
|
||||
return 0;
|
||||
}
|
||||
// windows loop
|
||||
do
|
||||
{
|
||||
const std::string virtualName = ConvertWStringToUTF8(ffd.cFileName);
|
||||
#else
|
||||
struct dirent *result = NULL;
|
||||
|
||||
//std::string directoryWithSlash = directory;
|
||||
//if (directoryWithSlash.back() != '/')
|
||||
// directoryWithSlash += "/";
|
||||
|
||||
DIR *dirp = opendir(directory);
|
||||
if (!dirp)
|
||||
return 0;
|
||||
// non windows loop
|
||||
while ((result = readdir(dirp)))
|
||||
{
|
||||
const std::string virtualName(result->d_name);
|
||||
#endif
|
||||
// check for "." and ".."
|
||||
if (virtualName == "." || virtualName == "..")
|
||||
continue;
|
||||
|
||||
// Remove dotfiles (optional with flag.)
|
||||
if (!(flags & GETFILES_GETHIDDEN))
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if ((ffd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0)
|
||||
continue;
|
||||
#else
|
||||
if (virtualName[0] == '.')
|
||||
continue;
|
||||
#endif
|
||||
}
|
||||
|
||||
FileInfo info;
|
||||
info.name = virtualName;
|
||||
std::string dir = directory;
|
||||
|
||||
// Only append a slash if there isn't one on the end.
|
||||
size_t lastSlash = dir.find_last_of("/");
|
||||
if (lastSlash != (dir.length() - 1))
|
||||
dir.append("/");
|
||||
|
||||
info.fullName = dir + virtualName;
|
||||
info.isDirectory = isDirectory(info.fullName);
|
||||
info.exists = true;
|
||||
info.size = 0;
|
||||
info.isWritable = false; // TODO - implement some kind of check
|
||||
if (!info.isDirectory) {
|
||||
std::string ext = getFileExtension(info.fullName);
|
||||
if (filter) {
|
||||
if (filters.find(ext) == filters.end())
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (files)
|
||||
files->push_back(std::move(info));
|
||||
foundEntries++;
|
||||
#ifdef _WIN32
|
||||
} while (FindNextFile(hFind, &ffd) != 0);
|
||||
FindClose(hFind);
|
||||
#else
|
||||
}
|
||||
closedir(dirp);
|
||||
#endif
|
||||
if (files)
|
||||
std::sort(files->begin(), files->end());
|
||||
return foundEntries;
|
||||
}
|
||||
|
||||
int64_t getDirectoryRecursiveSize(const std::string &path, const char *filter, int flags) {
|
||||
std::vector<FileInfo> fileInfo;
|
||||
getFilesInDir(path.c_str(), &fileInfo, filter, flags);
|
||||
int64_t sizeSum = 0;
|
||||
// Note: getFileInDir does not fill in fileSize properly.
|
||||
for (size_t i = 0; i < fileInfo.size(); i++) {
|
||||
FileInfo finfo;
|
||||
getFileInfo(fileInfo[i].fullName.c_str(), &finfo);
|
||||
if (!finfo.isDirectory)
|
||||
sizeSum += finfo.size;
|
||||
else
|
||||
sizeSum += getDirectoryRecursiveSize(finfo.fullName, filter, flags);
|
||||
}
|
||||
return sizeSum;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
// Returns a vector with the device names
|
||||
std::vector<std::string> getWindowsDrives()
|
||||
{
|
||||
#if PPSSPP_PLATFORM(UWP)
|
||||
return std::vector<std::string>(); // TODO UWP http://stackoverflow.com/questions/37404405/how-to-get-logical-drives-names-in-windows-10
|
||||
#else
|
||||
std::vector<std::string> drives;
|
||||
|
||||
const DWORD buffsize = GetLogicalDriveStrings(0, NULL);
|
||||
std::vector<wchar_t> buff(buffsize);
|
||||
if (GetLogicalDriveStrings(buffsize, buff.data()) == buffsize - 1)
|
||||
{
|
||||
auto drive = buff.data();
|
||||
while (*drive)
|
||||
{
|
||||
std::string str(ConvertWStringToUTF8(drive));
|
||||
str.pop_back(); // we don't want the final backslash
|
||||
str += "/";
|
||||
drives.push_back(str);
|
||||
|
||||
// advance to next drive
|
||||
while (*drive++) {}
|
||||
}
|
||||
}
|
||||
return drives;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
@@ -1,43 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
// Whole-file reading/writing
|
||||
bool writeStringToFile(bool text_file, const std::string &str, const char *filename);
|
||||
bool writeDataToFile(bool text_file, const void* data, const unsigned int size, const char *filename);
|
||||
|
||||
bool readFileToString(bool text_file, const char *filename, std::string &str);
|
||||
// Return value must be delete[]-d.
|
||||
uint8_t *ReadLocalFile(const char *filename, size_t *size);
|
||||
|
||||
// Beginnings of a directory utility system. TODO: Improve.
|
||||
|
||||
struct FileInfo {
|
||||
std::string name;
|
||||
std::string fullName;
|
||||
bool exists;
|
||||
bool isDirectory;
|
||||
bool isWritable;
|
||||
uint64_t size;
|
||||
|
||||
bool operator <(const FileInfo &other) const;
|
||||
};
|
||||
|
||||
std::string getFileExtension(const std::string &fn);
|
||||
bool getFileInfo(const char *path, FileInfo *fileInfo);
|
||||
FILE *openCFile(const std::string &filename, const char *mode);
|
||||
|
||||
enum {
|
||||
GETFILES_GETHIDDEN = 1
|
||||
};
|
||||
size_t getFilesInDir(const char *directory, std::vector<FileInfo> *files, const char *filter = nullptr, int flags = 0);
|
||||
int64_t getDirectoryRecursiveSize(const std::string &path, const char *filter = nullptr, int flags = 0);
|
||||
|
||||
#ifdef _WIN32
|
||||
std::vector<std::string> getWindowsDrives();
|
||||
#endif
|
||||
@@ -21,10 +21,6 @@ namespace Draw {
|
||||
class Texture;
|
||||
}
|
||||
|
||||
#ifdef USING_QT_UI
|
||||
#include <QtGui/QFont>
|
||||
#endif
|
||||
|
||||
struct TextStringEntry {
|
||||
Draw::Texture *texture;
|
||||
int width;
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
|
||||
#if defined(USING_QT_UI)
|
||||
|
||||
#include <QtGui/QFont>
|
||||
|
||||
class TextDrawerQt : public TextDrawer {
|
||||
public:
|
||||
TextDrawerQt(Draw::DrawContext *draw);
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#include <set>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "Common/File/VFS/VFS.h"
|
||||
//#include "file/zip_read.h"
|
||||
#include "Common/File/FileUtil.h"
|
||||
#include "glsl_program.h"
|
||||
|
||||
#include "Common/Log.h"
|
||||
|
||||
@@ -1,114 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef USING_QT_UI
|
||||
#include <QtGui/QImage>
|
||||
#else
|
||||
#include <png.h>
|
||||
#endif
|
||||
|
||||
#include "png_load.h"
|
||||
|
||||
#include "Common/Log.h"
|
||||
|
||||
// *image_data_ptr should be deleted with free()
|
||||
// return value of 1 == success.
|
||||
int pngLoad(const char *file, int *pwidth, int *pheight, unsigned char **image_data_ptr, bool flip) {
|
||||
#ifdef USING_QT_UI
|
||||
QImage image(file, "PNG");
|
||||
if (image.isNull()) {
|
||||
ERROR_LOG(IO, "pngLoad: Error loading image %s", file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (flip)
|
||||
image = image.mirrored();
|
||||
*pwidth = image.width();
|
||||
*pheight = image.height();
|
||||
image = image.convertToFormat(QImage::Format_ARGB32);
|
||||
*image_data_ptr = (unsigned char *)malloc(image.byteCount());
|
||||
uint32_t *src = (uint32_t*) image.bits();
|
||||
uint32_t *dest = (uint32_t*) *image_data_ptr;
|
||||
// Qt4 does not support RGBA
|
||||
for (size_t sz = 0; sz < (size_t)image.byteCount(); sz+=4, ++src, ++dest) {
|
||||
const uint32_t v = *src;
|
||||
*dest = (v & 0xFF00FF00) | ((v & 0xFF) << 16) | (( v >> 16 ) & 0xFF); // ARGB -> RGBA
|
||||
}
|
||||
#else
|
||||
if (flip)
|
||||
ERROR_LOG(IO, "pngLoad: flip flag not supported, image will be loaded upside down");
|
||||
png_image png;
|
||||
memset(&png, 0, sizeof(png));
|
||||
png.version = PNG_IMAGE_VERSION;
|
||||
|
||||
png_image_begin_read_from_file(&png, file);
|
||||
|
||||
if (PNG_IMAGE_FAILED(png))
|
||||
{
|
||||
ERROR_LOG(IO, "pngLoad: %s", png.message);
|
||||
return 0;
|
||||
}
|
||||
*pwidth = png.width;
|
||||
*pheight = png.height;
|
||||
png.format = PNG_FORMAT_RGBA;
|
||||
|
||||
int stride = PNG_IMAGE_ROW_STRIDE(png);
|
||||
*image_data_ptr = (unsigned char *)malloc(PNG_IMAGE_SIZE(png));
|
||||
png_image_finish_read(&png, NULL, *image_data_ptr, stride, NULL);
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int pngLoadPtr(const unsigned char *input_ptr, size_t input_len, int *pwidth, int *pheight, unsigned char **image_data_ptr,
|
||||
bool flip) {
|
||||
#ifdef USING_QT_UI
|
||||
QImage image;
|
||||
if (!image.loadFromData(input_ptr, input_len, "PNG")) {
|
||||
ERROR_LOG(IO, "pngLoad: Error loading image");
|
||||
return 0;
|
||||
}
|
||||
if (flip)
|
||||
image = image.mirrored();
|
||||
*pwidth = image.width();
|
||||
*pheight = image.height();
|
||||
image = image.convertToFormat(QImage::Format_ARGB32);
|
||||
*image_data_ptr = (unsigned char *)malloc(image.byteCount());
|
||||
uint32_t *src = (uint32_t*) image.bits();
|
||||
uint32_t *dest = (uint32_t*) *image_data_ptr;
|
||||
// Qt4 does not support RGBA
|
||||
for (size_t sz = 0; sz < (size_t)image.byteCount(); sz+=4, ++src, ++dest) {
|
||||
const uint32_t v = *src;
|
||||
*dest = (v & 0xFF00FF00) | ((v & 0xFF) << 16) | (( v >> 16 ) & 0xFF); // convert it!
|
||||
}
|
||||
#else
|
||||
if (flip)
|
||||
ERROR_LOG(IO, "pngLoad: flip flag not supported, image will be loaded upside down");
|
||||
png_image png{};
|
||||
png.version = PNG_IMAGE_VERSION;
|
||||
|
||||
png_image_begin_read_from_memory(&png, input_ptr, input_len);
|
||||
|
||||
if (PNG_IMAGE_FAILED(png)) {
|
||||
ERROR_LOG(IO, "pngLoad: %s", png.message);
|
||||
return 0;
|
||||
}
|
||||
*pwidth = png.width;
|
||||
*pheight = png.height;
|
||||
png.format = PNG_FORMAT_RGBA;
|
||||
|
||||
int stride = PNG_IMAGE_ROW_STRIDE(png);
|
||||
|
||||
size_t size = PNG_IMAGE_SIZE(png);
|
||||
if (!size) {
|
||||
ERROR_LOG(IO, "pngLoad: empty image");
|
||||
return 0;
|
||||
}
|
||||
|
||||
*image_data_ptr = (unsigned char *)malloc(size);
|
||||
png_image_finish_read(&png, NULL, *image_data_ptr, stride, NULL);
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
#ifndef _PNG_LOAD_H
|
||||
#define _PNG_LOAD_H
|
||||
|
||||
// *image_data_ptr should be deleted with free()
|
||||
// return value of 1 == success.
|
||||
int pngLoad(const char *file, int *pwidth,
|
||||
int *pheight, unsigned char **image_data_ptr, bool flip);
|
||||
|
||||
int pngLoadPtr(const unsigned char *input_ptr, size_t input_len, int *pwidth,
|
||||
int *pheight, unsigned char **image_data_ptr, bool flip);
|
||||
|
||||
#endif // _PNG_LOAD_H
|
||||
@@ -1,132 +0,0 @@
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "zlib.h"
|
||||
#include "image/zim_load.h"
|
||||
#include "Common/Math/math_util.h"
|
||||
#include "Common/File/VFS/VFS.h"
|
||||
|
||||
#include "Common/Log.h"
|
||||
|
||||
int ezuncompress(unsigned char* pDest, long* pnDestLen, const unsigned char* pSrc, long nSrcLen) {
|
||||
z_stream stream;
|
||||
stream.next_in = (Bytef*)pSrc;
|
||||
stream.avail_in = (uInt)nSrcLen;
|
||||
/* Check for source > 64K on 16-bit machine: */
|
||||
if ((uLong)stream.avail_in != (uLong)nSrcLen) return Z_BUF_ERROR;
|
||||
|
||||
uInt destlen = (uInt)*pnDestLen;
|
||||
if ((uLong)destlen != (uLong)*pnDestLen) return Z_BUF_ERROR;
|
||||
stream.zalloc = (alloc_func)0;
|
||||
stream.zfree = (free_func)0;
|
||||
|
||||
int err = inflateInit(&stream);
|
||||
if (err != Z_OK) return err;
|
||||
|
||||
int nExtraChunks = 0;
|
||||
do {
|
||||
stream.next_out = pDest;
|
||||
stream.avail_out = destlen;
|
||||
err = inflate(&stream, Z_FINISH);
|
||||
if (err == Z_STREAM_END )
|
||||
break;
|
||||
if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0))
|
||||
err = Z_DATA_ERROR;
|
||||
if (err != Z_BUF_ERROR) {
|
||||
inflateEnd(&stream);
|
||||
return err;
|
||||
}
|
||||
nExtraChunks += 1;
|
||||
} while (stream.avail_out == 0);
|
||||
|
||||
*pnDestLen = stream.total_out;
|
||||
|
||||
err = inflateEnd(&stream);
|
||||
if (err != Z_OK) return err;
|
||||
|
||||
return nExtraChunks ? Z_BUF_ERROR : Z_OK;
|
||||
}
|
||||
|
||||
int LoadZIMPtr(const uint8_t *zim, size_t datasize, int *width, int *height, int *flags, uint8_t **image) {
|
||||
if (zim[0] != 'Z' || zim[1] != 'I' || zim[2] != 'M' || zim[3] != 'G') {
|
||||
ERROR_LOG(IO, "Not a ZIM file");
|
||||
return 0;
|
||||
}
|
||||
memcpy(width, zim + 4, 4);
|
||||
memcpy(height, zim + 8, 4);
|
||||
memcpy(flags, zim + 12, 4);
|
||||
|
||||
int num_levels = 1;
|
||||
int image_data_size[ZIM_MAX_MIP_LEVELS];
|
||||
if (*flags & ZIM_HAS_MIPS) {
|
||||
num_levels = log2i(*width < *height ? *width : *height) + 1;
|
||||
}
|
||||
int total_data_size = 0;
|
||||
for (int i = 0; i < num_levels; i++) {
|
||||
if (i > 0) {
|
||||
width[i] = width[i-1] / 2;
|
||||
height[i] = height[i-1] / 2;
|
||||
}
|
||||
switch (*flags & ZIM_FORMAT_MASK) {
|
||||
case ZIM_RGBA8888:
|
||||
image_data_size[i] = width[i] * height[i] * 4;
|
||||
break;
|
||||
case ZIM_RGBA4444:
|
||||
case ZIM_RGB565:
|
||||
image_data_size[i] = width[i] * height[i] * 2;
|
||||
break;
|
||||
default:
|
||||
ERROR_LOG(IO, "Invalid ZIM format %i", *flags & ZIM_FORMAT_MASK);
|
||||
return 0;
|
||||
}
|
||||
total_data_size += image_data_size[i];
|
||||
}
|
||||
|
||||
if (total_data_size == 0) {
|
||||
ERROR_LOG(IO, "Invalid ZIM data size 0");
|
||||
return 0;
|
||||
}
|
||||
|
||||
image[0] = (uint8_t *)malloc(total_data_size);
|
||||
for (int i = 1; i < num_levels; i++) {
|
||||
image[i] = image[i-1] + image_data_size[i-1];
|
||||
}
|
||||
|
||||
if (*flags & ZIM_ZLIB_COMPRESSED) {
|
||||
long outlen = (long)total_data_size;
|
||||
int retcode = ezuncompress(*image, &outlen, (unsigned char *)(zim + 16), (long)datasize - 16);
|
||||
if (Z_OK != retcode) {
|
||||
ERROR_LOG(IO, "ZIM zlib format decompression failed: %d", retcode);
|
||||
free(*image);
|
||||
*image = 0;
|
||||
return 0;
|
||||
}
|
||||
if (outlen != total_data_size) {
|
||||
// Shouldn't happen if return value was Z_OK.
|
||||
ERROR_LOG(IO, "Wrong size data in ZIM: %i vs %i", (int)outlen, (int)total_data_size);
|
||||
}
|
||||
} else {
|
||||
memcpy(*image, zim + 16, datasize - 16);
|
||||
if (datasize - 16 != (size_t)total_data_size) {
|
||||
ERROR_LOG(IO, "Wrong size data in ZIM: %i vs %i", (int)(datasize-16), (int)total_data_size);
|
||||
}
|
||||
}
|
||||
return num_levels;
|
||||
}
|
||||
|
||||
int LoadZIM(const char *filename, int *width, int *height, int *format, uint8_t **image) {
|
||||
size_t size;
|
||||
uint8_t *buffer = VFSReadFile(filename, &size);
|
||||
if (!buffer) {
|
||||
ERROR_LOG(IO, "Couldn't read data for '%s'", buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int retval = LoadZIMPtr(buffer, (int)size, width, height, format, image);
|
||||
if (!retval) {
|
||||
ERROR_LOG(IO, "Not a valid ZIM file: %s (size: %d bytes)", filename, (int)size);
|
||||
}
|
||||
delete [] buffer;
|
||||
return retval;
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
// LoadZIM's responsibility:
|
||||
// * Parse the ZIM format
|
||||
// * Extract all mip levels so they can be uploaded to GPU
|
||||
//
|
||||
// * NOT convert formats to anything, except converting ETC1 to RGBA8888 when running on the PC
|
||||
|
||||
// ZIM format:
|
||||
// 4 byte ZIMG
|
||||
// 4 byte width
|
||||
// 4 byte height
|
||||
// 4 byte flags
|
||||
// Uncompressed or ZLibbed data. If multiple mips, zlibbed separately.
|
||||
|
||||
// Defined flags:
|
||||
|
||||
enum {
|
||||
ZIM_RGBA8888 = 0, // Assumed format if no other format is set
|
||||
ZIM_RGBA4444 = 1, // GL_UNSIGNED_SHORT_4_4_4_4
|
||||
ZIM_RGB565 = 2, // GL_UNSIGNED_SHORT_5_6_5
|
||||
// There's space for plenty more formats.
|
||||
ZIM_FORMAT_MASK = 15,
|
||||
ZIM_HAS_MIPS = 16, // If set, assumes that a full mip chain is present. Mips are zlib-compressed individually and stored in sequence. Always half sized.
|
||||
ZIM_GEN_MIPS = 32, // If set, the caller is advised to automatically generate mips. (maybe later, the ZIM lib will generate the mips for you).
|
||||
ZIM_DITHER = 64, // If set, dithers during save if color reduction is necessary.
|
||||
ZIM_CLAMP = 128, // Texture should default to clamp instead of wrap.
|
||||
ZIM_ZLIB_COMPRESSED = 256,
|
||||
ZIM_ETC1_LOW = 512,
|
||||
ZIM_ETC1_MEDIUM = 1024,
|
||||
ZIM_ETC1_HIGH = 0, // default
|
||||
ZIM_ETC1_DITHER = 2048,
|
||||
};
|
||||
|
||||
// ZIM will only ever support up to 12 levels (4096x4096 max).
|
||||
enum {
|
||||
ZIM_MAX_MIP_LEVELS = 12,
|
||||
};
|
||||
|
||||
// Delete the returned pointer using free()
|
||||
// Watch out! If the image has mipmaps, multiple values will be written
|
||||
// to width, height, and image, as if they were arrays, up to 12 (max texture size is 4096 which is 2^12).
|
||||
int LoadZIM(const char *filename, int *width, int *height, int *flags, uint8_t **image);
|
||||
int LoadZIMPtr(const uint8_t *zim, size_t datasize, int *width, int *height, int *flags, uint8_t **image);
|
||||
@@ -1,221 +0,0 @@
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <cmath>
|
||||
|
||||
#include "image/zim_load.h"
|
||||
#include "image/zim_save.h"
|
||||
#include "zlib.h"
|
||||
|
||||
#include "Common/Log.h"
|
||||
|
||||
static const char magic[5] = "ZIMG";
|
||||
|
||||
/*int num_levels = 1;
|
||||
if (flags & ZIM_HAS_MIPS) {
|
||||
num_levels = log2i(width > height ? width : height);
|
||||
}*/
|
||||
static unsigned int log2i(unsigned int val) {
|
||||
unsigned int ret = -1;
|
||||
while (val != 0) {
|
||||
val >>= 1; ret++;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int ezcompress(unsigned char* pDest, long* pnDestLen, const unsigned char* pSrc, long nSrcLen) {
|
||||
z_stream stream;
|
||||
int err;
|
||||
|
||||
int nExtraChunks;
|
||||
uInt destlen;
|
||||
|
||||
stream.next_in = (Bytef*)pSrc;
|
||||
stream.avail_in = (uInt)nSrcLen;
|
||||
#ifdef MAXSEG_64K
|
||||
/* Check for source > 64K on 16-bit machine: */
|
||||
if ((uLong)stream.avail_in != nSrcLen) return Z_BUF_ERROR;
|
||||
#endif
|
||||
destlen = (uInt)*pnDestLen;
|
||||
if ((uLong)destlen != (uLong)*pnDestLen) return Z_BUF_ERROR;
|
||||
stream.zalloc = (alloc_func)0;
|
||||
stream.zfree = (free_func)0;
|
||||
stream.opaque = (voidpf)0;
|
||||
|
||||
err = deflateInit(&stream, Z_DEFAULT_COMPRESSION);
|
||||
if (err != Z_OK) return err;
|
||||
nExtraChunks = 0;
|
||||
do {
|
||||
stream.next_out = pDest;
|
||||
stream.avail_out = destlen;
|
||||
err = deflate(&stream, Z_FINISH);
|
||||
if (err == Z_STREAM_END )
|
||||
break;
|
||||
if (err != Z_OK) {
|
||||
deflateEnd(&stream);
|
||||
return err;
|
||||
}
|
||||
nExtraChunks += 1;
|
||||
} while (stream.avail_out == 0);
|
||||
|
||||
*pnDestLen = stream.total_out;
|
||||
|
||||
err = deflateEnd(&stream);
|
||||
if (err != Z_OK) return err;
|
||||
|
||||
return nExtraChunks ? Z_BUF_ERROR : Z_OK;
|
||||
}
|
||||
|
||||
inline int clamp16(int x) { if (x < 0) return 0; if (x > 15) return 15; return x; }
|
||||
inline int clamp32(int x) { if (x < 0) return 0; if (x > 31) return 31; return x; }
|
||||
inline int clamp64(int x) { if (x < 0) return 0; if (x > 63) return 63; return x; }
|
||||
|
||||
|
||||
bool ispowerof2 (int x) {
|
||||
if (!x || (x&(x-1)))
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
void Convert(const uint8_t *image_data, int width, int height, int pitch, int flags,
|
||||
uint8_t **data, int *data_size) {
|
||||
// For 4444 and 565. Ordered dither matrix. looks really surprisingly good on cell phone screens at 4444.
|
||||
int dith[16] = {
|
||||
1, 9, 3, 11,
|
||||
13, 5, 15, 7,
|
||||
4, 12, 2, 10,
|
||||
16, 8, 14, 6
|
||||
};
|
||||
if ((flags & ZIM_DITHER) == 0) {
|
||||
for (int i = 0; i < 16; i++) { dith[i] = 8; }
|
||||
}
|
||||
switch (flags & ZIM_FORMAT_MASK) {
|
||||
case ZIM_RGBA8888:
|
||||
{
|
||||
*data_size = width * height * 4;
|
||||
*data = new uint8_t[width * height * 4];
|
||||
for (int y = 0; y < height; y++) {
|
||||
memcpy((*data) + y * width * 4, image_data + y * pitch, width * 4);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ZIM_RGBA4444:
|
||||
{
|
||||
*data_size = width * height * 2;
|
||||
*data = new uint8_t[*data_size];
|
||||
uint16_t *dst = (uint16_t *)(*data);
|
||||
int i = 0;
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
int dithval = dith[(x&3)+((y&0x3)<<2)] - 8;
|
||||
int r = clamp16((image_data[i * 4] + dithval) >> 4);
|
||||
int g = clamp16((image_data[i * 4 + 1] + dithval) >> 4);
|
||||
int b = clamp16((image_data[i * 4 + 2] + dithval) >> 4);
|
||||
int a = clamp16((image_data[i * 4 + 3] + dithval) >> 4); // really dither alpha?
|
||||
// Note: GL_UNSIGNED_SHORT_4_4_4_4, not GL_UNSIGNED_SHORT_4_4_4_4_REV
|
||||
*dst++ = (r << 12) | (g << 8) | (b << 4) | (a << 0);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ZIM_RGB565:
|
||||
{
|
||||
*data_size = width * height * 2;
|
||||
*data = new uint8_t[*data_size];
|
||||
uint16_t *dst = (uint16_t *)(*data);
|
||||
int i = 0;
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
int dithval = dith[(x&3)+((y&0x3)<<2)] - 8;
|
||||
//dithval = 0; please check this
|
||||
int r = clamp32((image_data[i * 4] + dithval/2) >> 3);
|
||||
int g = clamp64((image_data[i * 4 + 1] + dithval/4) >> 2);
|
||||
int b = clamp32((image_data[i * 4 + 2] + dithval/2) >> 3);
|
||||
// Note: GL_UNSIGNED_SHORT_5_6_5, not GL_UNSIGNED_SHORT_5_6_5_REV
|
||||
*dst++ = (r << 11) | (g << 5) | (b);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
ERROR_LOG(IO, "Unhandled ZIM format %d", flags & ZIM_FORMAT_MASK);
|
||||
*data = 0;
|
||||
*data_size = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Deletes the old buffer.
|
||||
uint8_t *DownsampleBy2(const uint8_t *image, int width, int height, int pitch) {
|
||||
uint8_t *out = new uint8_t[(width/2) * (height/2) * 4];
|
||||
|
||||
int degamma[256];
|
||||
int gamma[32768];
|
||||
for (int i =0; i < 256; i++) {
|
||||
degamma[i] = (int)(powf((float)i / 255.0f, 1.0f/2.2f) * 8191.0f);
|
||||
}
|
||||
for (int i = 0; i < 32768; i++) {
|
||||
gamma[i] = (int)(powf((float)i / 32764.0f, 2.2f) * 255.0f);
|
||||
}
|
||||
|
||||
// Really stupid mipmap downsampling - at least it does gamma though.
|
||||
for (int y = 0; y < height; y+=2) {
|
||||
for (int x = 0; x < width; x+=2) {
|
||||
const uint8_t *tl = image + pitch * y + x*4;
|
||||
const uint8_t *tr = tl + 4;
|
||||
const uint8_t *bl = tl + pitch;
|
||||
const uint8_t *br = bl + 4;
|
||||
uint8_t *d = out + ((y/2) * ((width/2)) + x / 2) * 4;
|
||||
for (int c = 0; c < 4; c++) {
|
||||
d[c] = gamma[degamma[tl[c]] + degamma[tr[c]] + degamma[bl[c]] + degamma[br[c]]];
|
||||
}
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
void SaveZIM(FILE *f, int width, int height, int pitch, int flags, const uint8_t *image_data) {
|
||||
fwrite(magic, 1, 4, f);
|
||||
fwrite(&width, 1, 4, f);
|
||||
fwrite(&height, 1, 4, f);
|
||||
fwrite(&flags, 1, 4, f);
|
||||
|
||||
int num_levels = 1;
|
||||
if (flags & ZIM_HAS_MIPS) {
|
||||
num_levels = log2i(width > height ? height : width) + 1;
|
||||
}
|
||||
for (int i = 0; i < num_levels; i++) {
|
||||
uint8_t *data = 0;
|
||||
int data_size;
|
||||
Convert(image_data, width, height, pitch, flags, &data, &data_size);
|
||||
if (flags & ZIM_ZLIB_COMPRESSED) {
|
||||
long dest_len = data_size * 2;
|
||||
uint8_t *dest = new uint8_t[dest_len];
|
||||
if (Z_OK == ezcompress(dest, &dest_len, data, data_size)) {
|
||||
fwrite(dest, 1, dest_len, f);
|
||||
} else {
|
||||
ERROR_LOG(IO, "Zlib compression failed.\n");
|
||||
}
|
||||
delete [] dest;
|
||||
} else {
|
||||
fwrite(data, 1, data_size, f);
|
||||
}
|
||||
delete [] data;
|
||||
|
||||
if (i != num_levels - 1) {
|
||||
uint8_t *smaller = DownsampleBy2(image_data, width, height, pitch);
|
||||
if (i != 0) {
|
||||
delete [] image_data;
|
||||
}
|
||||
image_data = smaller;
|
||||
width /= 2;
|
||||
height /= 2;
|
||||
pitch = width * 4;
|
||||
}
|
||||
}
|
||||
delete [] image_data;
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdint>
|
||||
|
||||
// For the type enums etc.
|
||||
#include "zim_load.h"
|
||||
|
||||
// SaveZIM's responsibility:
|
||||
// * Write the ZIM format
|
||||
// * Generate mipmaps if requested
|
||||
// * Convert images to the requested format
|
||||
// Input image is always 8888 RGBA. SaveZIM takes care of downsampling and mipmap generation.
|
||||
void SaveZIM(FILE *f, int width, int height, int pitch, int format, const uint8_t *image);
|
||||
@@ -156,7 +156,7 @@
|
||||
</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<PreprocessorDefinitions>_CRTDBG_MAP_ALLOC;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>..\..\ext;..\..;..\..\dx9sdk\Include;..\..\dx9sdk\Include\DX11;..\libpng17;..\zlib;..\ext\zlib;..\native;..\RollerballGL;..\glew;..\SDL\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>..\..\ext;..\..;..\..\dx9sdk\Include;..\..\dx9sdk\Include\DX11;..\zlib;..\ext\zlib;..\native;..\RollerballGL;..\glew;..\SDL\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
@@ -178,7 +178,7 @@
|
||||
</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<PreprocessorDefinitions>_CRTDBG_MAP_ALLOC;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>..\..\ext;..\..;..\..\dx9sdk\Include;..\..\dx9sdk\Include\DX11;..\libpng17;..\zlib;..\ext\zlib;..\native;..\RollerballGL;..\glew;..\SDL\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>..\..\ext;..\..;..\..\dx9sdk\Include;..\..\dx9sdk\Include\DX11;..\zlib;..\ext\zlib;..\native;..\RollerballGL;..\glew;..\SDL\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
@@ -201,7 +201,7 @@
|
||||
</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<PreprocessorDefinitions>_CRTDBG_MAP_ALLOC;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>..\..\ext;..\..;..\..\dx9sdk\Include;..\..\dx9sdk\Include\DX11;..\libpng17;..\zlib;..\ext\zlib;..\native;..\RollerballGL;..\glew;..\SDL\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>..\..\ext;..\..;..\..\dx9sdk\Include;..\..\dx9sdk\Include\DX11;..\zlib;..\ext\zlib;..\native;..\RollerballGL;..\glew;..\SDL\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
@@ -224,7 +224,7 @@
|
||||
</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<PreprocessorDefinitions>_CRTDBG_MAP_ALLOC;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>..\..\ext;..\..;..\..\dx9sdk\Include;..\..\dx9sdk\Include\DX11;..\libpng17;..\zlib;..\ext\zlib;..\native;..\RollerballGL;..\glew;..\SDL\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>..\..\ext;..\..;..\..\dx9sdk\Include;..\..\dx9sdk\Include\DX11;..\zlib;..\ext\zlib;..\native;..\RollerballGL;..\glew;..\SDL\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
@@ -251,7 +251,7 @@
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>..\..\ext;..\..;..\..\dx9sdk\Include;..\..\dx9sdk\Include\DX11;..\libpng17;..\zlib;..\ext\zlib;..\native;..\RollerballGL;..\glew;..\SDL\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>..\..\ext;..\..;..\..\dx9sdk\Include;..\..\dx9sdk\Include\DX11;..\zlib;..\ext\zlib;..\native;..\RollerballGL;..\glew;..\SDL\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
|
||||
<FloatingPointModel>Precise</FloatingPointModel>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
@@ -279,7 +279,7 @@
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>..\..\ext;..\..;..\..\dx9sdk\Include;..\..\dx9sdk\Include\DX11;..\libpng17;..\zlib;..\ext\zlib;..\native;..\RollerballGL;..\glew;..\SDL\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>..\..\ext;..\..;..\..\dx9sdk\Include;..\..\dx9sdk\Include\DX11;..\zlib;..\ext\zlib;..\native;..\RollerballGL;..\glew;..\SDL\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
|
||||
<FloatingPointModel>Precise</FloatingPointModel>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
@@ -309,7 +309,7 @@
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>..\..\ext;..\..;..\..\dx9sdk\Include;..\..\dx9sdk\Include\DX11;..\libpng17;..\zlib;..\ext\zlib;..\native;..\RollerballGL;..\glew;..\SDL\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>..\..\ext;..\..;..\..\dx9sdk\Include;..\..\dx9sdk\Include\DX11;..\zlib;..\ext\zlib;..\native;..\RollerballGL;..\glew;..\SDL\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<FloatingPointModel>Precise</FloatingPointModel>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
@@ -338,7 +338,7 @@
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>..\..\ext;..\..;..\..\dx9sdk\Include;..\..\dx9sdk\Include\DX11;..\libpng17;..\zlib;..\ext\zlib;..\native;..\RollerballGL;..\glew;..\SDL\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>..\..\ext;..\..;..\..\dx9sdk\Include;..\..\dx9sdk\Include\DX11;..\zlib;..\ext\zlib;..\native;..\RollerballGL;..\glew;..\SDL\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
|
||||
<FloatingPointModel>Precise</FloatingPointModel>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
@@ -360,12 +360,9 @@
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Android.mk" />
|
||||
<None Include="tools\CMakeLists.txt" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="file\fd_util.h" />
|
||||
<ClInclude Include="file\file_util.h" />
|
||||
<ClInclude Include="gfx\d3d9_shader.h" />
|
||||
<ClInclude Include="gfx\d3d9_state.h" />
|
||||
<ClInclude Include="gfx\gl_common.h" />
|
||||
@@ -375,7 +372,6 @@
|
||||
<ClInclude Include="gfx_es2\draw_text_qt.h" />
|
||||
<ClInclude Include="gfx_es2\draw_text_uwp.h" />
|
||||
<ClInclude Include="gfx_es2\draw_text_win.h" />
|
||||
<ClInclude Include="net\websocket_server.h" />
|
||||
<ClInclude Include="thin3d\d3d11_loader.h" />
|
||||
<ClInclude Include="thin3d\d3d9_d3dcompiler_loader.h" />
|
||||
<ClInclude Include="thin3d\DataFormat.h" />
|
||||
@@ -385,34 +381,15 @@
|
||||
<ClInclude Include="thin3d\thin3d_create.h" />
|
||||
<ClInclude Include="thin3d\VulkanQueueRunner.h" />
|
||||
<ClInclude Include="thin3d\VulkanRenderManager.h" />
|
||||
<ClInclude Include="ui\root.h" />
|
||||
<ClInclude Include="gfx_es2\draw_buffer.h" />
|
||||
<ClInclude Include="gfx_es2\draw_text.h" />
|
||||
<ClInclude Include="gfx_es2\gl3stub.h" />
|
||||
<ClInclude Include="gfx_es2\glsl_program.h" />
|
||||
<ClInclude Include="gfx_es2\gpu_features.h" />
|
||||
<ClInclude Include="image\png_load.h" />
|
||||
<ClInclude Include="image\zim_load.h" />
|
||||
<ClInclude Include="image\zim_save.h" />
|
||||
<ClInclude Include="net\http_client.h" />
|
||||
<ClInclude Include="net\http_headers.h" />
|
||||
<ClInclude Include="net\http_server.h" />
|
||||
<ClInclude Include="net\resolve.h" />
|
||||
<ClInclude Include="net\url.h" />
|
||||
<ClInclude Include="net\sinks.h" />
|
||||
<ClInclude Include="thin3d\d3dx9_loader.h" />
|
||||
<ClInclude Include="thin3d\thin3d.h" />
|
||||
<ClInclude Include="ui\screen.h" />
|
||||
<ClInclude Include="ui\ui.h" />
|
||||
<ClInclude Include="ui\ui_context.h" />
|
||||
<ClInclude Include="ui\ui_screen.h" />
|
||||
<ClInclude Include="ui\ui_tween.h" />
|
||||
<ClInclude Include="ui\view.h" />
|
||||
<ClInclude Include="ui\viewgroup.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="file\fd_util.cpp" />
|
||||
<ClCompile Include="file\file_util.cpp" />
|
||||
<ClCompile Include="gfx\d3d9_shader.cpp" />
|
||||
<ClCompile Include="gfx\d3d9_state.cpp" />
|
||||
<ClCompile Include="gfx\gl_debug_log.cpp" />
|
||||
@@ -421,7 +398,6 @@
|
||||
<ClCompile Include="gfx_es2\draw_text_qt.cpp" />
|
||||
<ClCompile Include="gfx_es2\draw_text_uwp.cpp" />
|
||||
<ClCompile Include="gfx_es2\draw_text_win.cpp" />
|
||||
<ClCompile Include="net\websocket_server.cpp" />
|
||||
<ClCompile Include="thin3d\d3d11_loader.cpp" />
|
||||
<ClCompile Include="thin3d\d3d9_d3dcompiler_loader.cpp" />
|
||||
<ClCompile Include="thin3d\DataFormatGL.cpp" />
|
||||
@@ -430,7 +406,6 @@
|
||||
<ClCompile Include="thin3d\thin3d_d3d11.cpp" />
|
||||
<ClCompile Include="thin3d\VulkanQueueRunner.cpp" />
|
||||
<ClCompile Include="thin3d\VulkanRenderManager.cpp" />
|
||||
<ClCompile Include="ui\root.cpp" />
|
||||
<ClCompile Include="gfx_es2\draw_buffer.cpp" />
|
||||
<ClCompile Include="gfx_es2\draw_text.cpp" />
|
||||
<ClCompile Include="gfx_es2\gl3stub.c">
|
||||
@@ -445,27 +420,11 @@
|
||||
</ClCompile>
|
||||
<ClCompile Include="gfx_es2\glsl_program.cpp" />
|
||||
<ClCompile Include="gfx_es2\gpu_features.cpp" />
|
||||
<ClCompile Include="image\png_load.cpp" />
|
||||
<ClCompile Include="image\zim_load.cpp" />
|
||||
<ClCompile Include="image\zim_save.cpp" />
|
||||
<ClCompile Include="net\http_client.cpp" />
|
||||
<ClCompile Include="net\http_headers.cpp" />
|
||||
<ClCompile Include="net\http_server.cpp" />
|
||||
<ClCompile Include="net\resolve.cpp" />
|
||||
<ClCompile Include="net\url.cpp" />
|
||||
<ClCompile Include="net\sinks.cpp" />
|
||||
<ClCompile Include="thin3d\d3dx9_loader.cpp" />
|
||||
<ClCompile Include="thin3d\thin3d.cpp" />
|
||||
<ClCompile Include="thin3d\thin3d_d3d9.cpp" />
|
||||
<ClCompile Include="thin3d\thin3d_gl.cpp" />
|
||||
<ClCompile Include="thin3d\thin3d_vulkan.cpp" />
|
||||
<ClCompile Include="ui\screen.cpp" />
|
||||
<ClCompile Include="ui\ui.cpp" />
|
||||
<ClCompile Include="ui\ui_context.cpp" />
|
||||
<ClCompile Include="ui\ui_screen.cpp" />
|
||||
<ClCompile Include="ui\ui_tween.cpp" />
|
||||
<ClCompile Include="ui\view.cpp" />
|
||||
<ClCompile Include="ui\viewgroup.cpp" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<None Include="Android.mk" />
|
||||
<None Include="tools\CMakeLists.txt">
|
||||
<Filter>tools</Filter>
|
||||
</None>
|
||||
@@ -10,15 +9,6 @@
|
||||
<ClInclude Include="gfx\gl_debug_log.h">
|
||||
<Filter>gfx</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="image\png_load.h">
|
||||
<Filter>image</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="image\zim_load.h">
|
||||
<Filter>image</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="image\zim_save.h">
|
||||
<Filter>image</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="gfx_es2\draw_buffer.h">
|
||||
<Filter>gfx</Filter>
|
||||
</ClInclude>
|
||||
@@ -28,42 +18,9 @@
|
||||
<ClInclude Include="gfx\texture_atlas.h">
|
||||
<Filter>gfx</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="file\file_util.h">
|
||||
<Filter>file</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ui\ui.h">
|
||||
<Filter>ui</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="net\http_client.h">
|
||||
<Filter>net</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="file\fd_util.h">
|
||||
<Filter>file</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="net\resolve.h">
|
||||
<Filter>net</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ui\screen.h">
|
||||
<Filter>ui</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ui\ui_context.h">
|
||||
<Filter>ui</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="gfx_es2\gpu_features.h">
|
||||
<Filter>gfx</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ui\view.h">
|
||||
<Filter>ui</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ui\viewgroup.h">
|
||||
<Filter>ui</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="net\url.h">
|
||||
<Filter>net</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ui\ui_screen.h">
|
||||
<Filter>ui</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="gfx_es2\draw_text.h">
|
||||
<Filter>gfx</Filter>
|
||||
</ClInclude>
|
||||
@@ -76,18 +33,9 @@
|
||||
<ClInclude Include="thin3d\d3dx9_loader.h">
|
||||
<Filter>thin3d</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="net\http_headers.h">
|
||||
<Filter>net</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="net\http_server.h">
|
||||
<Filter>net</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="gfx\gl_common.h">
|
||||
<Filter>gfx</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="net\sinks.h">
|
||||
<Filter>net</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="gfx\d3d9_shader.h">
|
||||
<Filter>gfx</Filter>
|
||||
</ClInclude>
|
||||
@@ -115,9 +63,6 @@
|
||||
<ClInclude Include="thin3d\DataFormat.h">
|
||||
<Filter>thin3d</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ui\ui_tween.h">
|
||||
<Filter>ui</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="thin3d\GLRenderManager.h">
|
||||
<Filter>thin3d</Filter>
|
||||
</ClInclude>
|
||||
@@ -130,15 +75,9 @@
|
||||
<ClInclude Include="thin3d\thin3d_create.h">
|
||||
<Filter>thin3d</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="net\websocket_server.h">
|
||||
<Filter>net</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="thin3d\d3d9_d3dcompiler_loader.h">
|
||||
<Filter>thin3d</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ui\root.h">
|
||||
<Filter>ui</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="gfx_es2\draw_text_uwp.h">
|
||||
<Filter>gfx</Filter>
|
||||
</ClInclude>
|
||||
@@ -147,15 +86,6 @@
|
||||
<ClCompile Include="gfx\gl_debug_log.cpp">
|
||||
<Filter>gfx</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="image\png_load.cpp">
|
||||
<Filter>image</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="image\zim_load.cpp">
|
||||
<Filter>image</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="image\zim_save.cpp">
|
||||
<Filter>image</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="gfx_es2\draw_buffer.cpp">
|
||||
<Filter>gfx</Filter>
|
||||
</ClCompile>
|
||||
@@ -165,42 +95,9 @@
|
||||
<ClCompile Include="gfx\texture_atlas.cpp">
|
||||
<Filter>gfx</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="file\file_util.cpp">
|
||||
<Filter>file</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ui\ui.cpp">
|
||||
<Filter>ui</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="net\http_client.cpp">
|
||||
<Filter>net</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="file\fd_util.cpp">
|
||||
<Filter>file</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="net\resolve.cpp">
|
||||
<Filter>net</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ui\screen.cpp">
|
||||
<Filter>ui</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ui\ui_context.cpp">
|
||||
<Filter>ui</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="gfx_es2\gpu_features.cpp">
|
||||
<Filter>gfx</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ui\view.cpp">
|
||||
<Filter>ui</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ui\viewgroup.cpp">
|
||||
<Filter>ui</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="net\url.cpp">
|
||||
<Filter>net</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ui\ui_screen.cpp">
|
||||
<Filter>ui</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="gfx_es2\draw_text.cpp">
|
||||
<Filter>gfx</Filter>
|
||||
</ClCompile>
|
||||
@@ -219,18 +116,9 @@
|
||||
<ClCompile Include="thin3d\d3dx9_loader.cpp">
|
||||
<Filter>thin3d</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="net\http_headers.cpp">
|
||||
<Filter>net</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="net\http_server.cpp">
|
||||
<Filter>net</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="thin3d\thin3d_vulkan.cpp">
|
||||
<Filter>thin3d</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="net\sinks.cpp">
|
||||
<Filter>net</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="thin3d\thin3d_d3d11.cpp">
|
||||
<Filter>thin3d</Filter>
|
||||
</ClCompile>
|
||||
@@ -258,9 +146,6 @@
|
||||
<ClCompile Include="thin3d\VulkanRenderManager.cpp">
|
||||
<Filter>thin3d</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ui\ui_tween.cpp">
|
||||
<Filter>ui</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="thin3d\GLQueueRunner.cpp">
|
||||
<Filter>thin3d</Filter>
|
||||
</ClCompile>
|
||||
@@ -270,15 +155,9 @@
|
||||
<ClCompile Include="thin3d\DataFormatGL.cpp">
|
||||
<Filter>thin3d</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="net\websocket_server.cpp">
|
||||
<Filter>net</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="thin3d\d3d9_d3dcompiler_loader.cpp">
|
||||
<Filter>thin3d</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ui\root.cpp">
|
||||
<Filter>ui</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="gfx_es2\draw_text_uwp.cpp">
|
||||
<Filter>gfx</Filter>
|
||||
</ClCompile>
|
||||
@@ -287,18 +166,6 @@
|
||||
<Filter Include="gfx">
|
||||
<UniqueIdentifier>{9da89505-72a1-40e6-86e5-705372db1608}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="image">
|
||||
<UniqueIdentifier>{828bddaf-63e5-4311-985b-bf377f86ff00}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="file">
|
||||
<UniqueIdentifier>{49afd06e-eb44-41ac-b038-e109e444a834}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="ui">
|
||||
<UniqueIdentifier>{d738c2d1-749d-4b60-b98f-f3da0bbbf40c}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="net">
|
||||
<UniqueIdentifier>{6a548b3d-3a4c-4114-aa2f-0b42bf7bf2ce}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="tools">
|
||||
<UniqueIdentifier>{4515306f-4664-46bf-a89b-abfec5520a15}</UniqueIdentifier>
|
||||
</Filter>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,186 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <cstdint>
|
||||
|
||||
#include "net/resolve.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#else
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#endif
|
||||
|
||||
#include "Common/Buffer.h"
|
||||
|
||||
namespace net {
|
||||
|
||||
class Connection {
|
||||
public:
|
||||
Connection();
|
||||
virtual ~Connection();
|
||||
|
||||
// Inits the sockaddr_in.
|
||||
bool Resolve(const char *host, int port, DNSType type = DNSType::ANY);
|
||||
|
||||
bool Connect(int maxTries = 2, double timeout = 20.0f, bool *cancelConnect = nullptr);
|
||||
void Disconnect();
|
||||
|
||||
// Only to be used for bring-up and debugging.
|
||||
uintptr_t sock() const { return sock_; }
|
||||
|
||||
protected:
|
||||
// Store the remote host here, so we can send it along through HTTP/1.1 requests.
|
||||
// TODO: Move to http::client?
|
||||
std::string host_;
|
||||
int port_;
|
||||
|
||||
addrinfo *resolved_;
|
||||
|
||||
private:
|
||||
uintptr_t sock_;
|
||||
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
|
||||
namespace http {
|
||||
|
||||
bool GetHeaderValue(const std::vector<std::string> &responseHeaders, const std::string &header, std::string *value);
|
||||
|
||||
class Client : public net::Connection {
|
||||
public:
|
||||
Client();
|
||||
~Client();
|
||||
|
||||
// Return value is the HTTP return code. 200 means OK. < 0 means some local error.
|
||||
int GET(const char *resource, Buffer *output, float *progress = nullptr, bool *cancelled = nullptr);
|
||||
int GET(const char *resource, Buffer *output, std::vector<std::string> &responseHeaders, float *progress = nullptr, bool *cancelled = nullptr);
|
||||
|
||||
// Return value is the HTTP return code.
|
||||
int POST(const char *resource, const std::string &data, const std::string &mime, Buffer *output, float *progress = nullptr);
|
||||
int POST(const char *resource, const std::string &data, Buffer *output, float *progress = nullptr);
|
||||
|
||||
// HEAD, PUT, DELETE aren't implemented yet, but can be done with SendRequest.
|
||||
|
||||
int SendRequest(const char *method, const char *resource, const char *otherHeaders = nullptr, float *progress = nullptr, bool *cancelled = nullptr);
|
||||
int SendRequestWithData(const char *method, const char *resource, const std::string &data, const char *otherHeaders = nullptr, float *progress = nullptr, bool *cancelled = nullptr);
|
||||
int ReadResponseHeaders(Buffer *readbuf, std::vector<std::string> &responseHeaders, float *progress = nullptr, bool *cancelled = nullptr);
|
||||
// If your response contains a response, you must read it.
|
||||
int ReadResponseEntity(Buffer *readbuf, const std::vector<std::string> &responseHeaders, Buffer *output, float *progress = nullptr, bool *cancelled = nullptr);
|
||||
|
||||
void SetDataTimeout(double t) {
|
||||
dataTimeout_ = t;
|
||||
}
|
||||
|
||||
protected:
|
||||
const char *userAgent_;
|
||||
const char *httpVersion_;
|
||||
double dataTimeout_ = -1.0;
|
||||
};
|
||||
|
||||
// Not particularly efficient, but hey - it's a background download, that's pretty cool :P
|
||||
class Download {
|
||||
public:
|
||||
Download(const std::string &url, const std::string &outfile);
|
||||
~Download();
|
||||
|
||||
void Start();
|
||||
|
||||
void Join();
|
||||
|
||||
// Returns 1.0 when done. That one value can be compared exactly - or just use Done().
|
||||
float Progress() const { return progress_; }
|
||||
|
||||
bool Done() const { return completed_; }
|
||||
bool Failed() const { return failed_; }
|
||||
|
||||
// NOTE! The value of ResultCode is INVALID until Done() returns true.
|
||||
int ResultCode() const { return resultCode_; }
|
||||
|
||||
std::string url() const { return url_; }
|
||||
std::string outfile() const { return outfile_; }
|
||||
|
||||
// If not downloading to a file, access this to get the result.
|
||||
Buffer &buffer() { return buffer_; }
|
||||
const Buffer &buffer() const { return buffer_; }
|
||||
|
||||
void Cancel() {
|
||||
cancelled_ = true;
|
||||
}
|
||||
|
||||
bool IsCancelled() const {
|
||||
return cancelled_;
|
||||
}
|
||||
|
||||
// NOTE: Callbacks are NOT executed until RunCallback is called. This is so that
|
||||
// the call will end up on the thread that calls g_DownloadManager.Update().
|
||||
void SetCallback(std::function<void(Download &)> callback) {
|
||||
callback_ = callback;
|
||||
}
|
||||
void RunCallback() {
|
||||
if (callback_) {
|
||||
callback_(*this);
|
||||
}
|
||||
}
|
||||
|
||||
// Just metadata. Convenient for download managers, for example, if set,
|
||||
// Downloader::GetCurrentProgress won't return it in the results.
|
||||
bool IsHidden() const { return hidden_; }
|
||||
void SetHidden(bool hidden) { hidden_ = hidden; }
|
||||
|
||||
private:
|
||||
void Do(); // Actually does the download. Runs on thread.
|
||||
int PerformGET(const std::string &url);
|
||||
std::string RedirectLocation(const std::string &baseUrl);
|
||||
void SetFailed(int code);
|
||||
float progress_ = 0.0f;
|
||||
Buffer buffer_;
|
||||
std::vector<std::string> responseHeaders_;
|
||||
std::string url_;
|
||||
std::string outfile_;
|
||||
std::thread thread_;
|
||||
int resultCode_ = 0;
|
||||
bool completed_ = false;
|
||||
bool failed_ = false;
|
||||
bool cancelled_ = false;
|
||||
bool hidden_ = false;
|
||||
bool joined_ = false;
|
||||
std::function<void(Download &)> callback_;
|
||||
};
|
||||
|
||||
using std::shared_ptr;
|
||||
|
||||
class Downloader {
|
||||
public:
|
||||
~Downloader() {
|
||||
CancelAll();
|
||||
}
|
||||
|
||||
std::shared_ptr<Download> StartDownload(const std::string &url, const std::string &outfile);
|
||||
|
||||
std::shared_ptr<Download> StartDownloadWithCallback(
|
||||
const std::string &url,
|
||||
const std::string &outfile,
|
||||
std::function<void(Download &)> callback);
|
||||
|
||||
// Drops finished downloads from the list.
|
||||
void Update();
|
||||
void CancelAll();
|
||||
|
||||
std::vector<float> GetCurrentProgress();
|
||||
|
||||
private:
|
||||
std::vector<std::shared_ptr<Download>> downloads_;
|
||||
};
|
||||
|
||||
} // http
|
||||
@@ -1,162 +0,0 @@
|
||||
#include "net/http_headers.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "file/fd_util.h"
|
||||
#include "net/sinks.h"
|
||||
|
||||
#include "Common/Log.h"
|
||||
#include "Common/StringUtils.h"
|
||||
|
||||
namespace http {
|
||||
|
||||
RequestHeader::RequestHeader()
|
||||
: status(200), referer(0), user_agent(0),
|
||||
resource(0), params(0), content_length(-1), first_header_(true) {
|
||||
}
|
||||
|
||||
RequestHeader::~RequestHeader() {
|
||||
delete [] referer;
|
||||
delete [] user_agent;
|
||||
delete [] resource;
|
||||
delete [] params;
|
||||
}
|
||||
|
||||
bool RequestHeader::GetParamValue(const char *param_name, std::string *value) const {
|
||||
if (!params)
|
||||
return false;
|
||||
std::string p(params);
|
||||
std::vector<std::string> v;
|
||||
SplitString(p, '&', v);
|
||||
for (size_t i = 0; i < v.size(); i++) {
|
||||
std::vector<std::string> parts;
|
||||
SplitString(v[i], '=', parts);
|
||||
INFO_LOG(IO, "Param: %s Value: %s", parts[0].c_str(), parts[1].c_str());
|
||||
if (parts[0] == param_name) {
|
||||
*value = parts[1];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RequestHeader::GetOther(const char *name, std::string *value) const {
|
||||
auto it = other.find(name);
|
||||
if (it != other.end()) {
|
||||
*value = it->second;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Intended to be a mad fast parser. It's not THAT fast currently, there's still
|
||||
// things to optimize, but meh.
|
||||
int RequestHeader::ParseHttpHeader(const char *buffer) {
|
||||
if (first_header_) {
|
||||
// Step 1: Method
|
||||
first_header_ = false;
|
||||
if (!memcmp(buffer, "GET ", 4)) {
|
||||
method = GET;
|
||||
buffer += 4;
|
||||
} else if (!memcmp(buffer, "HEAD ", 5)) {
|
||||
method = HEAD;
|
||||
buffer += 5;
|
||||
} else if (!memcmp(buffer, "POST ", 5)) {
|
||||
method = POST;
|
||||
buffer += 5;
|
||||
} else {
|
||||
method = UNSUPPORTED;
|
||||
status = 405;
|
||||
return -1;
|
||||
}
|
||||
SkipSpace(&buffer);
|
||||
|
||||
// Step 2: Resource, params (what's after the ?, if any)
|
||||
const char *endptr = strchr(buffer, ' ');
|
||||
const char *q_ptr = strchr(buffer, '?');
|
||||
|
||||
int resource_name_len;
|
||||
if (q_ptr)
|
||||
resource_name_len = q_ptr - buffer;
|
||||
else
|
||||
resource_name_len = endptr - buffer;
|
||||
if (!resource_name_len) {
|
||||
status = 400;
|
||||
return -1;
|
||||
}
|
||||
resource = new char[resource_name_len + 1];
|
||||
memcpy(resource, buffer, resource_name_len);
|
||||
resource[resource_name_len] = '\0';
|
||||
if (q_ptr) {
|
||||
int param_length = endptr - q_ptr - 1;
|
||||
params = new char[param_length + 1];
|
||||
memcpy(params, q_ptr + 1, param_length);
|
||||
params[param_length] = '\0';
|
||||
}
|
||||
if (strstr(buffer, "HTTP/"))
|
||||
type = FULL;
|
||||
else
|
||||
type = SIMPLE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// We have a real header to parse.
|
||||
const char *colon = strchr(buffer, ':');
|
||||
if (!colon) {
|
||||
status = 400;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// The header is formatted as key: value.
|
||||
int key_len = colon - buffer;
|
||||
const char *key = buffer;
|
||||
|
||||
// Go to after the colon to get the value.
|
||||
buffer = colon + 1;
|
||||
SkipSpace(&buffer);
|
||||
int value_len = (int)strlen(buffer);
|
||||
|
||||
if (!strncasecmp(key, "User-Agent", key_len)) {
|
||||
user_agent = new char[value_len + 1];
|
||||
memcpy(user_agent, buffer, value_len + 1);
|
||||
INFO_LOG(IO, "user-agent: %s", user_agent);
|
||||
} else if (!strncasecmp(key, "Referer", key_len)) {
|
||||
referer = new char[value_len + 1];
|
||||
memcpy(referer, buffer, value_len + 1);
|
||||
} else if (!strncasecmp(key, "Content-Length", key_len)) {
|
||||
content_length = atoi(buffer);
|
||||
INFO_LOG(IO, "Content-Length: %i", (int)content_length);
|
||||
} else {
|
||||
std::string key_str(key, key_len);
|
||||
std::transform(key_str.begin(), key_str.end(), key_str.begin(), tolower);
|
||||
other[key_str] = buffer;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void RequestHeader::ParseHeaders(net::InputSink *sink) {
|
||||
int line_count = 0;
|
||||
std::string line;
|
||||
while (sink->ReadLine(line)) {
|
||||
if (line.length() == 0) {
|
||||
// Blank line, this means end of headers.
|
||||
break;
|
||||
}
|
||||
|
||||
ParseHttpHeader(line.c_str());
|
||||
line_count++;
|
||||
if (type == SIMPLE) {
|
||||
// Done!
|
||||
INFO_LOG(IO, "Simple: Done parsing http request.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
INFO_LOG(IO, "finished parsing request.");
|
||||
ok = line_count > 1;
|
||||
}
|
||||
|
||||
} // namespace http
|
||||
@@ -1,50 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "Common/Buffer.h"
|
||||
|
||||
namespace net {
|
||||
class InputSink;
|
||||
};
|
||||
|
||||
namespace http {
|
||||
|
||||
class RequestHeader {
|
||||
public:
|
||||
RequestHeader();
|
||||
~RequestHeader();
|
||||
// Public variables since it doesn't make sense
|
||||
// to bother with accessors for all these.
|
||||
int status;
|
||||
// Intentional misspelling.
|
||||
char *referer;
|
||||
char *user_agent;
|
||||
char *resource;
|
||||
char *params;
|
||||
int content_length;
|
||||
std::unordered_map<std::string, std::string> other;
|
||||
enum RequestType {
|
||||
SIMPLE, FULL,
|
||||
};
|
||||
RequestType type;
|
||||
enum Method {
|
||||
GET,
|
||||
HEAD,
|
||||
POST,
|
||||
UNSUPPORTED,
|
||||
};
|
||||
Method method;
|
||||
bool ok;
|
||||
void ParseHeaders(net::InputSink *sink);
|
||||
bool GetParamValue(const char *param_name, std::string *value) const;
|
||||
bool GetOther(const char *name, std::string *value) const;
|
||||
private:
|
||||
int ParseHttpHeader(const char *buffer);
|
||||
bool first_header_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(RequestHeader);
|
||||
};
|
||||
|
||||
} // namespace http
|
||||
@@ -1,338 +0,0 @@
|
||||
#include "Common/TimeUtil.h"
|
||||
#include "ppsspp_config.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <io.h>
|
||||
|
||||
#else
|
||||
|
||||
#include <sys/socket.h> /* socket definitions */
|
||||
#include <sys/types.h> /* socket types */
|
||||
#include <sys/wait.h> /* for waitpid() */
|
||||
#include <netinet/in.h> /* struct sockaddr_in */
|
||||
#include <arpa/inet.h> /* inet (3) funtions */
|
||||
#include <unistd.h> /* misc. UNIX functions */
|
||||
|
||||
#define closesocket close
|
||||
|
||||
#endif
|
||||
|
||||
#if PPSSPP_PLATFORM(UWP)
|
||||
#define in6addr_any IN6ADDR_ANY_INIT
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "file/fd_util.h"
|
||||
#include "net/http_server.h"
|
||||
#include "net/sinks.h"
|
||||
#include "Common/Thread/Executor.h"
|
||||
|
||||
#include "Common/Buffer.h"
|
||||
#include "Common/Log.h"
|
||||
|
||||
namespace http {
|
||||
|
||||
// Note: charset here helps prevent XSS.
|
||||
const char *const DEFAULT_MIME_TYPE = "text/html; charset=utf-8";
|
||||
|
||||
Request::Request(int fd)
|
||||
: fd_(fd) {
|
||||
in_ = new net::InputSink(fd);
|
||||
out_ = new net::OutputSink(fd);
|
||||
header_.ParseHeaders(in_);
|
||||
|
||||
if (header_.ok) {
|
||||
INFO_LOG(IO, "The request carried with it %i bytes", (int)header_.content_length);
|
||||
} else {
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
Request::~Request() {
|
||||
Close();
|
||||
|
||||
_assert_(in_->Empty());
|
||||
delete in_;
|
||||
if (!out_->Empty()) {
|
||||
ERROR_LOG(IO, "Output not empty - connection abort?");
|
||||
}
|
||||
delete out_;
|
||||
}
|
||||
|
||||
void Request::WriteHttpResponseHeader(const char *ver, int status, int64_t size, const char *mimeType, const char *otherHeaders) const {
|
||||
const char *statusStr;
|
||||
switch (status) {
|
||||
case 200: statusStr = "OK"; break;
|
||||
case 206: statusStr = "Partial Content"; break;
|
||||
case 301: statusStr = "Moved Permanently"; break;
|
||||
case 302: statusStr = "Found"; break;
|
||||
case 304: statusStr = "Not Modified"; break;
|
||||
case 400: statusStr = "Bad Request"; break;
|
||||
case 403: statusStr = "Forbidden"; break;
|
||||
case 404: statusStr = "Not Found"; break;
|
||||
case 405: statusStr = "Method Not Allowed"; break;
|
||||
case 406: statusStr = "Not Acceptable"; break;
|
||||
case 410: statusStr = "Gone"; break;
|
||||
case 416: statusStr = "Range Not Satisfiable"; break;
|
||||
case 418: statusStr = "I'm a teapot"; break;
|
||||
case 500: statusStr = "Internal Server Error"; break;
|
||||
case 503: statusStr = "Service Unavailable"; break;
|
||||
default: statusStr = "OK"; break;
|
||||
}
|
||||
|
||||
net::OutputSink *buffer = Out();
|
||||
buffer->Printf("HTTP/%s %03d %s\r\n", ver, status, statusStr);
|
||||
buffer->Push("Server: PPSSPPServer v0.1\r\n");
|
||||
if (!mimeType || strcmp(mimeType, "websocket") != 0) {
|
||||
buffer->Printf("Content-Type: %s\r\n", mimeType ? mimeType : DEFAULT_MIME_TYPE);
|
||||
buffer->Push("Connection: close\r\n");
|
||||
}
|
||||
if (size >= 0) {
|
||||
buffer->Printf("Content-Length: %llu\r\n", size);
|
||||
}
|
||||
if (otherHeaders) {
|
||||
buffer->Push(otherHeaders, (int)strlen(otherHeaders));
|
||||
}
|
||||
buffer->Push("\r\n");
|
||||
}
|
||||
|
||||
void Request::WritePartial() const {
|
||||
_assert_(fd_);
|
||||
out_->Flush();
|
||||
}
|
||||
|
||||
void Request::Write() {
|
||||
_assert_(fd_);
|
||||
WritePartial();
|
||||
Close();
|
||||
}
|
||||
|
||||
void Request::Close() {
|
||||
if (fd_) {
|
||||
closesocket(fd_);
|
||||
fd_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Server::Server(threading::Executor *executor)
|
||||
: port_(0), executor_(executor) {
|
||||
RegisterHandler("/", std::bind(&Server::HandleListing, this, std::placeholders::_1));
|
||||
SetFallbackHandler(std::bind(&Server::Handle404, this, std::placeholders::_1));
|
||||
}
|
||||
|
||||
Server::~Server() {
|
||||
delete executor_;
|
||||
}
|
||||
|
||||
void Server::RegisterHandler(const char *url_path, UrlHandlerFunc handler) {
|
||||
handlers_[std::string(url_path)] = handler;
|
||||
}
|
||||
|
||||
void Server::SetFallbackHandler(UrlHandlerFunc handler) {
|
||||
fallback_ = handler;
|
||||
}
|
||||
|
||||
bool Server::Listen(int port, net::DNSType type) {
|
||||
bool success = false;
|
||||
if (type == net::DNSType::ANY || type == net::DNSType::IPV6) {
|
||||
success = Listen6(port, type == net::DNSType::IPV6);
|
||||
}
|
||||
if (!success && (type == net::DNSType::ANY || type == net::DNSType::IPV4)) {
|
||||
success = Listen4(port);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
bool Server::Listen4(int port) {
|
||||
listener_ = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (listener_ < 0)
|
||||
return false;
|
||||
|
||||
struct sockaddr_in server_addr;
|
||||
memset(&server_addr, 0, sizeof(server_addr));
|
||||
server_addr.sin_family = AF_INET;
|
||||
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
server_addr.sin_port = htons(port);
|
||||
|
||||
int opt = 1;
|
||||
// Enable re-binding to avoid the pain when restarting the server quickly.
|
||||
setsockopt(listener_, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof(opt));
|
||||
|
||||
if (bind(listener_, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
|
||||
closesocket(listener_);
|
||||
ERROR_LOG(IO, "Failed to bind to port %d. Bailing.", port);
|
||||
return false;
|
||||
}
|
||||
|
||||
fd_util::SetNonBlocking(listener_, true);
|
||||
|
||||
// 1024 is the max number of queued requests.
|
||||
if (listen(listener_, 1024) < 0) {
|
||||
closesocket(listener_);
|
||||
return false;
|
||||
}
|
||||
|
||||
socklen_t len = sizeof(server_addr);
|
||||
if (getsockname(listener_, (struct sockaddr *)&server_addr, &len) == 0) {
|
||||
port = ntohs(server_addr.sin_port);
|
||||
}
|
||||
|
||||
INFO_LOG(IO, "HTTP server started on port %d", port);
|
||||
port_ = port;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Server::Listen6(int port, bool ipv6_only) {
|
||||
#if !PPSSPP_PLATFORM(SWITCH)
|
||||
listener_ = socket(AF_INET6, SOCK_STREAM, 0);
|
||||
if (listener_ < 0)
|
||||
return false;
|
||||
|
||||
struct sockaddr_in6 server_addr;
|
||||
memset(&server_addr, 0, sizeof(server_addr));
|
||||
server_addr.sin6_family = AF_INET6;
|
||||
server_addr.sin6_addr = in6addr_any;
|
||||
server_addr.sin6_port = htons(port);
|
||||
|
||||
int opt = 1;
|
||||
// Enable re-binding to avoid the pain when restarting the server quickly.
|
||||
setsockopt(listener_, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof(opt));
|
||||
|
||||
// Enable listening on IPv6 and IPv4?
|
||||
opt = ipv6_only ? 1 : 0;
|
||||
setsockopt(listener_, IPPROTO_IPV6, IPV6_V6ONLY, (const char *)&opt, sizeof(opt));
|
||||
|
||||
if (bind(listener_, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
|
||||
closesocket(listener_);
|
||||
ERROR_LOG(IO, "Failed to bind to port %d. Bailing.", port);
|
||||
return false;
|
||||
}
|
||||
|
||||
fd_util::SetNonBlocking(listener_, true);
|
||||
|
||||
// 1024 is the max number of queued requests.
|
||||
if (listen(listener_, 1024) < 0) {
|
||||
closesocket(listener_);
|
||||
return false;
|
||||
}
|
||||
|
||||
socklen_t len = sizeof(server_addr);
|
||||
if (getsockname(listener_, (struct sockaddr *)&server_addr, &len) == 0) {
|
||||
port = ntohs(server_addr.sin6_port);
|
||||
}
|
||||
|
||||
INFO_LOG(IO, "HTTP server started on port %d", port);
|
||||
port_ = port;
|
||||
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Server::RunSlice(double timeout) {
|
||||
if (listener_ < 0 || port_ == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (timeout <= 0.0) {
|
||||
timeout = 86400.0;
|
||||
}
|
||||
if (!fd_util::WaitUntilReady(listener_, timeout, false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
union {
|
||||
struct sockaddr sa;
|
||||
struct sockaddr_in ipv4;
|
||||
#if !PPSSPP_PLATFORM(SWITCH)
|
||||
struct sockaddr_in6 ipv6;
|
||||
#endif
|
||||
} client_addr;
|
||||
socklen_t client_addr_size = sizeof(client_addr);
|
||||
int conn_fd = accept(listener_, &client_addr.sa, &client_addr_size);
|
||||
if (conn_fd >= 0) {
|
||||
executor_->Run(std::bind(&Server::HandleConnection, this, conn_fd));
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
ERROR_LOG(IO, "socket accept failed: %i", conn_fd);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Server::Run(int port) {
|
||||
if (!Listen(port)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
RunSlice(0.0);
|
||||
}
|
||||
|
||||
// We'll never get here. Ever.
|
||||
return true;
|
||||
}
|
||||
|
||||
void Server::Stop() {
|
||||
closesocket(listener_);
|
||||
}
|
||||
|
||||
void Server::HandleConnection(int conn_fd) {
|
||||
Request request(conn_fd);
|
||||
if (!request.IsOK()) {
|
||||
WARN_LOG(IO, "Bad request, ignoring.");
|
||||
return;
|
||||
}
|
||||
HandleRequest(request);
|
||||
|
||||
// TODO: Way to mark the content body as read, read it here if never read.
|
||||
// This allows the handler to stream if need be.
|
||||
|
||||
// TODO: Could handle keep alive here.
|
||||
request.Write();
|
||||
}
|
||||
|
||||
void Server::HandleRequest(const Request &request) {
|
||||
HandleRequestDefault(request);
|
||||
}
|
||||
|
||||
void Server::HandleRequestDefault(const Request &request) {
|
||||
// First, look through all handlers. If we got one, use it.
|
||||
auto handler = handlers_.find(request.resource());
|
||||
if (handler != handlers_.end()) {
|
||||
(handler->second)(request);
|
||||
} else {
|
||||
// Let's hit the 404 handler instead.
|
||||
fallback_(request);
|
||||
}
|
||||
}
|
||||
|
||||
void Server::Handle404(const Request &request) {
|
||||
INFO_LOG(IO, "No handler for '%s', falling back to 404.", request.resource());
|
||||
const char *payload = "<html><body>404 not found</body></html>\r\n";
|
||||
request.WriteHttpResponseHeader("1.0", 404, (int)strlen(payload));
|
||||
request.Out()->Push(payload);
|
||||
}
|
||||
|
||||
void Server::HandleListing(const Request &request) {
|
||||
request.WriteHttpResponseHeader("1.0", 200, -1, "text/plain");
|
||||
for (auto iter = handlers_.begin(); iter != handlers_.end(); ++iter) {
|
||||
request.Out()->Printf("%s\n", iter->first.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace http
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user