This commit is contained in:
2026-06-14 19:09:18 +01:00
parent 14bd1a9271
commit 13fa90a0e9
3958 changed files with 999286 additions and 4 deletions
+167
View File
@@ -0,0 +1,167 @@
/* -*- tab-width: 4; -*- */
/* vi: set sw=2 ts=4 expandtab: */
/*
* Copyright 2017-2020 Mark Callow, github.com/MarkCallow.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @internal
* @class argparser
* @~English
*
* @brief parse a command argument list.
*
* Can't use getopt_long because it declares argv as char* const*. We
* need const char* const* when we're parsing an embedded string. Also
* Windows C library does not have getopt_long.
*
* @author Mark Callow, github.com/MarkCallow.
*/
#include <assert.h>
#include <iostream>
#include "argparser.h"
#include <regex>
using namespace std;
/*
* Construct from a string of arguments.
*/
argvector::argvector(const string& sArgs)
{
const string sep(" \t");
// std::regex does not support negative lookbehind assertions. Darn!
// RE to extract argument between string start and separator.
// - Match 0 is whole matching string including separator.
// - Match 1 is the argument.
// - Match 2 is an empty string or a backslash.
// Use negative character class as it is easiest way to handle
// utf-8 file names with non-Latin characters. $ must not be last
// in the character class or it will be taken literally not as
// end of string.
// Use raw literal to avoid excess backslashes.
regex re(R"--(^([^$\\ \t]+)(\\?)(?:[ \t]+|$))--");
size_t pos;
pos = sArgs.find_first_not_of(sep);
assert(pos != string::npos);
auto first = sArgs.begin() + pos;
auto last = sArgs.end();
bool continuation = false;
for (smatch sm; regex_search(first, last, sm, re);) {
bool needContinuation = false;
#define DEBUG_REGEX 0
#if DEBUG_REGEX
std::cout << "prefix: " << sm.prefix() << '\n';
std::cout << "suffix: " << sm.suffix() << '\n';
std::cout << "match size: " << sm.size() << '\n';
for(uint32_t i = 0; i < sm.size(); i++) {
std::cout << "match " << i << ": " << "\"" << sm.str(i) << "\"" << '\n';
}
#endif
string arg;
// All this because std::regex does not support negative
// lookbehind assertions.
arg = sm.str(1);
if (sm.str(2)[0] == '\\') {
arg += " ";
needContinuation = true;
}
if (continuation) {
this->back() += arg;
} else {
push_back(arg);
}
continuation = needContinuation;
first = sm.suffix().first;
}
}
/*
* Construct from an array of C strings
*/
argvector::argvector(int argc, const char* const* argv)
{
for (int i = 0; i < argc; i++) {
push_back(argv[i]);
}
}
/*
* Functions the same as getopt_long. See `man 3 getopt_long`.
*/
int
argparser::getopt(string* shortopts, const struct option* longopts,
int* /*longindex*/)
{
if (optind == argv.size())
return -1;
string arg;
arg = argv[optind];
if (arg[0] != '-' || (arg[0] == '-' && arg.size() == 1))
return -1;
optind++;
int retval = '?';
if (arg.compare(0, 2, "--") == 0) {
if (arg.size() == 2) return -1; // " -- " separates options and files
const struct option* opt = longopts;
while (opt->name != nullptr) {
if (arg.compare(2, string::npos, opt->name) == 0) {
retval = opt->val;
if (opt->has_arg != option::no_argument) {
if (optind >= argv.size() || (optarg = argv[optind++])[0] == '-') {
optarg.clear();
optind--;
if (opt->has_arg == option::required_argument)
retval = ':';
}
}
if (opt->flag != nullptr) {
*opt->flag = opt->val;
retval = 0;
}
break;
}
opt++;
}
} else if (shortopts != nullptr && arg.compare(0, 1, "-") == 0) {
size_t pos = shortopts->find(arg.substr(1, 1));
if (pos != string::npos) {
retval = (*shortopts)[pos];
if (pos < shortopts->length()
&& ((*shortopts)[++pos] == ':' || (*shortopts)[pos] == ';')) {
if (optind >= argv.size() || (optarg = argv[optind++])[0] == '-') {
optarg.clear();
optind--;
if ((*shortopts)[pos] == ':') // required argument
retval = ':';
}
}
}
}
return retval;
}
//================== Helper for apps' processArgs ========================
std::istream& operator >> (std::istream& stream, const skip& x)
{
std::ios_base::fmtflags f = stream.flags();
stream >> std::noskipws;
char c;
const char* text = x.text;
while (stream && *text++)
stream >> c;
stream.flags(f);
return stream;
}
+61
View File
@@ -0,0 +1,61 @@
/* -*- tab-width: 4; -*- */
/* vi: set sw=2 ts=4 expandtab: */
#ifndef ARGPARSER_H
#define ARGPARSER_H
/*
* Copyright 2017-2020 Mark Callow, github.com/MarkCallow.
* SPDX-License-Identifier: Apache-2.0
*/
#include <ios>
#include <sstream>
#include <string>
#include <vector>
class argvector : public std::vector<std::string> {
public:
argvector() { };
argvector(const std::string& argstring);
argvector(int argc, const char* const* argv);
};
class argparser {
public:
struct option {
const char* name;
enum has_arg_t {no_argument, required_argument, optional_argument} has_arg;
int* flag;
int val;
option(const char* name, has_arg_t has_arg, int* flag, int val) : name(name), has_arg(has_arg), flag(flag), val(val) {}
};
std::string optarg;
unsigned int optind;
argvector argv;
argparser(argvector& argv, unsigned int startindex = 0)
: optind(startindex), argv(argv) { }
argparser(int argc, const char* const* argv1)
: optind(1), argv(argc, argv1) { }
int getopt(std::string* shortopts, const struct option* longopts,
int* longindex = nullptr);
};
//================== Helper for apps' processArgs ========================
// skips the number of characters equal to the length of given text
// does not check whether the skipped characters are the same as it
struct skip
{
const char* text;
skip(const char* text) : text(text) {}
};
std::istream& operator >> (std::istream& stream, const skip& x);
#endif /* ARGPARSER_H */
+472
View File
@@ -0,0 +1,472 @@
// -*- tab-width: 4; -*-
// vi: set sw=2 ts=4 sts=4 expandtab:
// Copyright 2019-2020 The Khronos Group Inc.
// SPDX-License-Identifier: Apache-2.0
#include "stdafx.h"
#if defined (_WIN32)
#define _CRT_SECURE_NO_WARNINGS
#define WINDOWS_LEAN_AND_MEAN
#include <windows.h>
#endif
#include <stdarg.h>
#if (_MSVC_LANG >= 201703L || __cplusplus >= 201703L)
#include <algorithm>
#endif
#include <iostream>
#include <vector>
#include <ktx.h>
#include "argparser.h"
#include "platform_utils.h"
#define QUOTE(x) #x
#define STR(x) QUOTE(x)
// Thanks Windows!!!
#if defined(min)
#undef min
#endif
#if defined(max)
#undef max
#endif
using namespace std;
// clamp is in std:: from c++17.
#if !(_MSVC_LANG >= 201703L || __cplusplus >= 201703L)
template <typename T> inline T clamp(T value, T low, T high) {
return (value < low) ? low : ((value > high) ? high : value);
}
#endif
template<typename T>
struct clamped
{
clamped(T def_v, T min_v, T max_v) :
def(def_v),
min(min_v),
max(max_v),
value(def_v)
{
}
void clear()
{
value = def;
}
operator T() const
{
return value;
}
T operator= (T v)
{
value = clamp<T>(v, min, max);
return value;
}
T def;
T min;
T max;
T value;
};
/**
//! [ktxApp options]
<dl>
<dt>-h, \--help</dt>
<dd>Print this usage message and exit.</dd>
<dt>-v, \--version</dt>
<dd>Print the version number of this program and exit.</dd>
</dl>
//! [ktxApp options]
*/
class ktxApp {
public:
virtual int main(int argc, char* argv[]) = 0;
virtual void usage() {
cerr <<
" -h, --help Print this usage message and exit.\n"
" -v, --version Print the version number of this program and exit.\n"
#if defined(_WIN32) && defined(DEBUG)
" --ld Launch Visual Studio deugger at start up.\n"
#endif
;
};
string& getName() { return name; }
protected:
struct commandOptions {
std::vector<string> infiles;
string outfile;
int test;
int warn;
int launchDebugger;
commandOptions() : test(false), warn(1), launchDebugger(0) { }
};
ktxApp(std::string& version, std::string& defaultVersion,
commandOptions& options)
: version(version), defaultVersion(defaultVersion),
options(options) { }
void error(const char *pFmt, ...) {
va_list args;
va_start(args, pFmt);
cerr << name << ": ";
vfprintf(stderr, pFmt, args);
va_end(args);
cerr << "\n";
}
void warning(const char *pFmt, va_list args) {
if (options.warn) {
cerr << name << " warning! ";
vfprintf(stderr, pFmt, args);
cerr << endl;
}
}
void warning(const char *pFmt, ...) {
if (options.warn) {
va_list args;
va_start(args, pFmt);
warning(pFmt, args);
cerr << endl;
}
}
void warning(const string& msg) {
if (options.warn) {
cerr << name << " warning! ";
cerr << msg << endl;
}
}
/** @internal
* @~English
* @brief Open a file for writing, failing if it exists.
*
* Assumes binary mode is wanted.
*
* Works around an annoying limitation of the VS2013-era msvcrt's
* @c fopen that implements an early version of the @c fopen spec.
* that does not accept 'x' as a mode character. For some reason
* Mingw uses this ancient version. Rather than use ifdef heuristics
* to identify sufferers of the limitation, it handles the error case
* and uses an alternate way to check for file existence.
*
* @return A stdio FILE* for the created file. If the file already exists
* returns nullptr and sets errno to EEXIST.
*/
static FILE* fopen_write_if_not_exists(const string& path) {
FILE* file = ::fopenUTF8(path, "wxb");
if (!file && errno == EINVAL) {
file = ::fopenUTF8(path, "r");
if (file) {
fclose(file);
file = nullptr;
errno = EEXIST;
} else {
file = ::fopenUTF8(path, "wb");
}
}
return file;
}
int strtoi(const char* str)
{
char* endptr;
int value = (int)strtol(str, &endptr, 0);
// Some implementations set errno == EINVAL but we can't rely on it.
if (value == 0 && endptr && *endptr != '\0') {
cerr << "Argument \"" << endptr << "\" not a number." << endl;
usage();
exit(1);
}
return value;
}
enum StdinUse { eDisallowStdin, eAllowStdin };
enum OutfilePos { eNone, eFirst, eLast };
void processCommandLine(int argc, char* argv[],
StdinUse stdinStat = eAllowStdin,
OutfilePos outfilePos = eNone)
{
uint32_t i;
size_t slash, dot;
name = argv[0];
// For consistent Id, only use the stem of name;
slash = name.find_last_of('\\');
if (slash == string::npos)
slash = name.find_last_of('/');
if (slash != string::npos)
name.erase(0, slash+1); // Remove directory name.
dot = name.find_last_of('.');
if (dot != string::npos)
name.erase(dot, string::npos); // Remove extension.
argparser parser(argc, argv);
processOptions(parser);
i = parser.optind;
if (argc - i > 0) {
if (outfilePos == eFirst)
options.outfile = parser.argv[i++];
uint32_t infileCount = outfilePos == eLast ? argc - 1 : argc;
for (; i < infileCount; i++) {
if (parser.argv[i][0] == '@') {
if (!loadFileList(parser.argv[i],
parser.argv[i][1] == '@',
options.infiles)) {
exit(1);
}
} else {
options.infiles.push_back(parser.argv[i]);
}
}
if (options.infiles.size() > 1) {
std::vector<string>::const_iterator it;
for (it = options.infiles.begin(); it < options.infiles.end(); it++) {
if (it->compare("-") == 0) {
error("cannot use stdin as one among many inputs.");
usage();
exit(1);
}
}
}
if (outfilePos == eLast)
options.outfile = parser.argv[i];
}
if (options.infiles.size() == 0) {
if (stdinStat == eAllowStdin) {
options.infiles.push_back("-"); // Use stdin as 0 files.
} else {
error("need some input files.");
usage();
exit(1);
}
}
if (outfilePos != eNone && options.outfile.empty()) {
error("need an output file");
}
}
bool loadFileList(const string &f, bool relativize,
vector<string>& filenames)
{
string listName(f);
listName.erase(0, relativize ? 2 : 1);
FILE *lf = nullptr;
lf = fopenUTF8(listName, "r");
if (!lf) {
error("failed opening filename list: \"%s\": %s\n",
listName.c_str(), strerror(errno));
return false;
}
string dirname;
if (relativize) {
size_t dirnameEnd = listName.find_last_of('/');
if (dirnameEnd == string::npos) {
relativize = false;
} else {
dirname = listName.substr(0, dirnameEnd + 1);
}
}
for (;;) {
// Cross platform PATH_MAX def is too much trouble!
char buf[4096];
buf[0] = '\0';
char *p = fgets(buf, sizeof(buf), lf);
if (!p) {
if (ferror(lf)) {
error("failed reading filename list: \"%s\": %s\n",
listName.c_str(), strerror(errno));
fclose(lf);
return false;
} else
break;
}
string readFilename(p);
while (readFilename.size()) {
if (readFilename[0] == ' ')
readFilename.erase(0, 1);
else
break;
}
while (readFilename.size()) {
const char c = readFilename.back();
if ((c == ' ') || (c == '\n') || (c == '\r'))
readFilename.erase(readFilename.size() - 1, 1);
else
break;
}
if (readFilename.size()) {
if (relativize)
filenames.push_back(dirname + readFilename);
else
filenames.push_back(readFilename);
}
}
fclose(lf);
return true;
}
virtual void processOptions(argparser& parser) {
int opt;
while ((opt = parser.getopt(&short_opts, option_list.data(), NULL)) != -1) {
switch (opt) {
case 0:
break;
case 10000:
break;
case 'h':
usage();
exit(0);
case 'v':
printVersion();
exit(0);
case ':':
error("missing required option argument.");
usage();
exit(0);
default:
if (!processOption(parser, opt)) {
usage();
exit(1);
}
}
}
#if defined(_WIN32) && defined(DEBUG)
if (options.launchDebugger)
launchDebugger();
#endif
}
virtual bool processOption(argparser& parser, int opt) = 0;
void writeId(std::ostream& dst, bool chktest) {
dst << name << " ";
dst << (!chktest || !options.test ? version : defaultVersion);
}
void printVersion() {
writeId(cerr, false);
cerr << endl;
}
#if defined(_WIN32) && defined(DEBUG)
// For use when debugging stdin with Visual Studio which does not have a
// "wait for executable to be launched" choice in its debugger settings.
bool launchDebugger()
{
// Get System directory, typically c:\windows\system32
std::wstring systemDir(MAX_PATH + 1, '\0');
UINT nChars = GetSystemDirectoryW(&systemDir[0],
static_cast<UINT>(systemDir.length()));
if (nChars == 0) return false; // failed to get system directory
systemDir.resize(nChars);
// Get process ID and create the command line
DWORD pid = GetCurrentProcessId();
std::wostringstream s;
s << systemDir << L"\\vsjitdebugger.exe -p " << pid;
std::wstring cmdLine = s.str();
// Start debugger process
STARTUPINFOW si;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
PROCESS_INFORMATION pi;
ZeroMemory(&pi, sizeof(pi));
if (!CreateProcessW(NULL, &cmdLine[0], NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) return false;
// Close debugger process handles to eliminate resource leak
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
// Wait for the debugger to attach
while (!IsDebuggerPresent()) Sleep(100);
// Stop execution so the debugger can take over
DebugBreak();
return true;
}
#endif
string name;
string& version;
string& defaultVersion;
commandOptions& options;
virtual void validateOptions() { }
std::vector<argparser::option> option_list {
{ "help", argparser::option::no_argument, NULL, 'h' },
{ "version", argparser::option::no_argument, NULL, 'v' },
{ "test", argparser::option::no_argument, &options.test, 1},
#if defined(_WIN32) && defined(DEBUG)
{ "ld", argparser::option::no_argument, &options.launchDebugger, 1},
#endif
// -NSDocumentRevisionsDebugMode YES is appended to the end
// of the command by Xcode when debugging and "Allow debugging when
// using document Versions Browser" is checked in the scheme. It
// defaults to checked and is saved in a user-specific file not the
// pbxproj file so it can't be disabled in a generated project.
// Remove these from the arguments under consideration.
{ "-NSDocumentRevisionsDebugMode", argparser::option::required_argument, NULL, 10000 },
{ nullptr, argparser::option::no_argument, nullptr, 0 }
};
string short_opts = "hv";
};
extern ktxApp& theApp;
/** @internal
* @~English
* @brief Common main for all derived classes.
*
* Handles rewriting of argv to UTF-8 on Windows.
* Each app needs to initialize @c theApp to
* point to an instance of itself.
*/
int main(int argc, char* argv[])
{
InitUTF8CLI(argc, argv);
#if 0
if (!SetConsoleOutputCP(CP_UTF8)) {
cerr << theApp.getName() << "warning: failed to set UTF-8 code page for console output."
<< endl;
}
#endif
return theApp.main(argc, argv);
}
+81
View File
@@ -0,0 +1,81 @@
// Copyright 2022-2023 The Khronos Group Inc.
// Copyright 2022-2023 RasterGrid Kft.
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "stdafx.h"
#include <string>
#include <iostream>
#include <memory>
#include <vector>
#if defined(_WIN32)
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h>
#include <shellapi.h>
#endif
#if defined(_WIN32) && !defined(_UNICODE)
// For Windows, we convert the UTF-8 path to a UTF-16 path to force using
// the APIs that correctly handle unicode characters.
inline std::wstring DecodeUTF8Path(std::string path) {
std::wstring result;
int len = MultiByteToWideChar(CP_UTF8, 0, path.c_str(), static_cast<int>(path.length()), NULL, 0);
if (len > 0)
{
result.resize(len);
MultiByteToWideChar(CP_UTF8, 0, path.c_str(), static_cast<int>(path.length()), &result[0], len);
}
return result;
}
#else
// For other platforms there is no need for any conversion, they
// support UTF-8 natively.
inline std::string DecodeUTF8Path(std::string path) {
return path;
}
#endif
inline void InitUTF8CLI(int& argc, char* argv[]) {
#if defined(_WIN32)
// Windows does not support UTF-8 argv so we have to manually acquire it
static std::vector<std::unique_ptr<char[]>> utf8Argv(argc);
LPWSTR commandLine = GetCommandLineW();
LPWSTR* wideArgv = CommandLineToArgvW(commandLine, &argc);
for (int i = 0; i < argc; ++i) {
int byteSize = WideCharToMultiByte(CP_UTF8, 0, wideArgv[i], -1, nullptr, 0, nullptr, nullptr);
utf8Argv[i] = std::make_unique<char[]>(byteSize);
WideCharToMultiByte(CP_UTF8, 0, wideArgv[i], -1, utf8Argv[i].get(), byteSize, nullptr, nullptr);
argv[i] = utf8Argv[i].get();
}
#else
// Nothing to do for other platforms
(void)argc;
(void)argv;
#endif
}
inline FILE* fopenUTF8(const std::string& path, const std::string& mode) {
#if defined(_WIN32)
FILE* fp;
// Returned errno_t value is also set in the global errno.
(void)_wfopen_s(&fp, DecodeUTF8Path(path).c_str(), DecodeUTF8Path(mode).c_str());
return fp;
#else
return fopen(path.c_str(), mode.c_str());
#endif
}
inline int unlinkUTF8(const std::string& path) {
#if defined(_WIN32)
return _wunlink(DecodeUTF8Path(path).c_str());
#else
return unlink(path.c_str());
#endif
}
+225
View File
@@ -0,0 +1,225 @@
/* -*- tab-width: 4; -*- */
/* vi: set sw=2 ts=4 expandtab: */
/*
* Copyright 2021 Paolo Jovon.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @internal
* @file
* @~English
*
* @brief A ktxStream that wraps a C++ std::streambuf.
*
* Modified from Paolo's original to make it a template so that the reference to std::streambuf can
* be passed either as a `unique_ptr` or a regular pointer. As our use is to pass the streambuf from
* an istream, potentially that from std::cin, or an istringstream that is not being not allocated by `new`
* we can't use a `unique_ptr`. i.e. can't attempt to delete the stringbuf when done. Also the
* destructor now destructs the stream, if not already destructed.
*
* @author Paolo Jovon
* @author Mark Callow
*/
#include <iostream>
#include <memory>
#include <ktx.h>
static std::ostream cnull(0);
static std::ostream& logstream = cnull;
//static std::ostream logstream = std::cerr;
/// @brief Template for a ktxStream that wraps a C++ std::streambuf.
///
/// The template supports 2 ways of referencing the underlying std::streambuf:
/// as a @c unique_ptr or as a regular pointer. For the former case, the
/// @c unique_ptr std::streambuf and StreambufStream should be created
/// like this:
/// @code
/// // Can use stringbuf or any class derived from it.
/// auto filebuf = std::make_unique<std::filebuf>();
/// StreambufStream<std::unique_ptr<std::streambuf>>ktxStream(
/// std::move(filebuf),
/// ios::in);
/// @endcode
///
///
template <typename T>
class StreambufStream
{
// Doubt this will ever get triggered
static_assert(sizeof(char) == sizeof(uint8_t),
"Chars are != 1 byte in this platform");
public:
StreambufStream(T streambuf,
std::ios::openmode seek_mode = std::ios::in | std::ios::out)
: _streambuf{std::move(streambuf)}
, _seek_mode{seek_mode}
, _stream{std::make_unique<ktxStream>()}
, _destructed{false}
{
initialize_stream();
}
StreambufStream(const StreambufStream&) = delete;
StreambufStream &operator=(const StreambufStream&) = delete;
StreambufStream(StreambufStream&&) = delete;
StreambufStream &operator=(StreambufStream&&) = delete;
virtual ~StreambufStream()
{
if (!_destructed)
stream()->destruct(stream());
}
inline ktxStream* stream() const
{
return _stream.get();
}
std::streambuf* streambuf() const;
inline std::ios::openmode seek_mode() const
{
return _seek_mode;
}
inline void seek_mode(std::ios::openmode newmode)
{
_seek_mode = newmode;
}
inline bool destructed() const
{
return _destructed;
}
protected:
void initialize_stream() {
_stream->type = eStreamTypeCustom;
_stream->closeOnDestruct = false;
auto& custom_ptr = _stream->data.custom_ptr;
custom_ptr.address = this;
custom_ptr.allocatorAddress = nullptr; // N/A
custom_ptr.size = 0; // N/A
_stream->read = read;
_stream->skip = skip;
_stream->write = write;
_stream->getpos = getpos;
_stream->setpos = setpos;
_stream->getsize = getsize;
_stream->destruct = destruct;
}
// C++ streambuf overrides
// ktxStream vtable implementations
inline static StreambufStream* parent(ktxStream *str)
{
return reinterpret_cast<StreambufStream*>(str->data.custom_ptr.address);
}
static KTX_error_code read(ktxStream* str, void* dst, ktx_size_t count)
{
auto self = parent(str);
if (count == 0)
{
return KTX_SUCCESS;
}
logstream << "\t read: " << count << 'B' << std::endl;
const auto stdcount = std::streamsize(count);
const std::streamsize nread = self->_streambuf->sgetn(reinterpret_cast<char*>(dst), stdcount);
return (nread == stdcount) ? KTX_SUCCESS : KTX_FILE_UNEXPECTED_EOF;
}
static KTX_error_code skip(ktxStream* str, ktx_size_t count)
{
auto self = parent(str);
if (count == 0)
{
return KTX_SUCCESS;
}
logstream << "\t skip: " << count << 'B' << std::endl;
const std::streampos curpos = self->_streambuf->pubseekoff(0, std::ios::cur, self->_seek_mode);
const std::streampos newpos = self->_streambuf->pubseekoff(std::streamoff(count), std::ios::cur, self->_seek_mode);
return (curpos > newpos) ? KTX_SUCCESS : KTX_FILE_SEEK_ERROR;
}
static KTX_error_code write(ktxStream* str, const void* src, ktx_size_t size, ktx_size_t count)
{
auto self = parent(str);
if (size == 0 || count == 0)
{
return KTX_SUCCESS;
}
logstream << "\t write: " << count << "*" << size << "B" << std::endl;
const auto ntotal = std::streamsize(size * count);
const std::streamsize nput = self->_streambuf->sputn(reinterpret_cast<const char*>(src), ntotal);
return (nput == ntotal) ? KTX_SUCCESS : KTX_FILE_WRITE_ERROR;
}
static KTX_error_code getpos(ktxStream* str, ktx_off_t *offset)
{
auto self = parent(str);
*offset = ktx_off_t(self->_streambuf->pubseekoff(0, std::ios::cur, self->_seek_mode));
logstream << "\tgetpos: " << *offset << std::endl;
return KTX_SUCCESS;
}
static KTX_error_code setpos(ktxStream* str, ktx_off_t offset)
{
auto self = parent(str);
const auto newpos = std::streamoff(offset);
const std::streampos setpos = self->_streambuf->pubseekoff(newpos, std::ios::beg, self->_seek_mode);
logstream << "\tsetpos: " << offset << std::endl;
return (setpos == newpos) ? KTX_SUCCESS : KTX_FILE_SEEK_ERROR;
}
static KTX_error_code getsize(ktxStream* str, ktx_size_t* size)
{
auto self = parent(str);
const std::streampos oldpos = self->_streambuf->pubseekoff(0, std::ios::cur, self->_seek_mode);
*size = ktx_size_t(self->_streambuf->pubseekoff(0, std::ios::end));
const std::streampos newpos = self->_streambuf->pubseekoff(oldpos, std::ios::beg, self->_seek_mode);
logstream << "\t size: " << *size << 'B' << std::endl;
return (oldpos == newpos) ? KTX_SUCCESS : KTX_FILE_SEEK_ERROR;
}
static void destruct(ktxStream* str)
{
auto self = parent(str);
self->_destructed = true;
}
T _streambuf;
std::ios::openmode _seek_mode;
std::unique_ptr<ktxStream> _stream;
// ktxTexture?_CreateFromStream destructs the ktxStream when finished, if
// KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT was passed. This variable tracks
// if the ktxStream's destructor has been called.
bool _destructed;
};
// I have not yet found a way to do this inside the template definition.
// However `inline` should prevent any multiple definition errors.
template<>
inline std::streambuf* StreambufStream<std::streambuf*>::streambuf() const
{
return _streambuf;
}
template<>
inline std::streambuf* StreambufStream<std::unique_ptr<std::streambuf>>::streambuf() const
{
return _streambuf.get();
}
+1154
View File
File diff suppressed because it is too large Load Diff
+28
View File
@@ -0,0 +1,28 @@
// Copyright 2010-2020 The Khronos Group Inc.
// SPDX-License-Identifier: Apache-2.0
// stdafx.h
#pragma once
#if defined(_WIN32)
// _CRT_SECURE_NO_WARNINGS must be defined before <windows.h>,
// <stdio.h> and and <iostream>
#define _CRT_SECURE_NO_WARNINGS
#endif
#include <assert.h>
#include <stdio.h>
#ifdef _WIN32
#include <io.h>
#if _MSC_VER < 1900
#define snprintf _snprintf
#endif
#else
#include <unistd.h>
#define _setmode(x, y) 0
#endif
#include <fcntl.h>
#include <errno.h>
#include <string.h>
+37
View File
@@ -0,0 +1,37 @@
/* -*- tab-width: 4; -*- */
/* vi: set sw=2 ts=4 expandtab: */
/* Copyright 2019-2018 The Khronos Group Inc.
* SPDX-License-Identifier: Apache-2.0
*/
/* I'm extending this beyond the purpose implied by its name rather than creating
* a new file to hold the FALLTHROUGH declaration as this
* file is already included in most places FALLTHROUGH
* is needed.
*/
#ifndef _UNUSED_H
#define _UNUSED_H
#if (__cplusplus >= 201703L)
#define MAYBE_UNUSED [[maybe_unused]]
#elif __GNUC__ || __clang__
#define MAYBE_UNUSED __attribute__((unused))
#else
// Boohoo. VC++ has no equivalent
#define MAYBE_UNUSED
#endif
#define U_ASSERT_ONLY MAYBE_UNUSED
// For unused parameters of c functions. Portable.
#define UNUSED(x) (void)(x)
#if !__clang__ && __GNUC__ // grumble ... clang ... grumble
#define FALLTHROUGH __attribute__((fallthrough))
#else
#define FALLTHROUGH
#endif
#endif /* UNUSED_H */