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:
Henrik Rydgård
2020-10-04 20:48:47 +02:00
committed by GitHub
parent f82754e8b4
commit 4f43cff5ca
189 changed files with 1013 additions and 1098 deletions
-178
View File
@@ -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
-24
View File
@@ -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
-367
View File
@@ -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
-43
View File
@@ -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
-4
View File
@@ -21,10 +21,6 @@ namespace Draw {
class Texture;
}
#ifdef USING_QT_UI
#include <QtGui/QFont>
#endif
struct TextStringEntry {
Draw::Texture *texture;
int width;
+2
View File
@@ -7,6 +7,8 @@
#if defined(USING_QT_UI)
#include <QtGui/QFont>
class TextDrawerQt : public TextDrawer {
public:
TextDrawerQt(Draw::DrawContext *draw);
+3 -3
View File
@@ -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"
-114
View File
@@ -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;
}
-12
View File
@@ -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
-132
View File
@@ -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;
}
-46
View File
@@ -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);
-221
View File
@@ -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;
}
-14
View File
@@ -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);
+8 -49
View File
@@ -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">
-133
View File
@@ -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
-186
View File
@@ -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
-162
View File
@@ -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
-50
View File
@@ -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
-338
View File
@@ -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