Moved C++ tools out

This commit is contained in:
2023-10-31 21:15:03 -05:00
parent f8a715ec78
commit 343f75433e
98 changed files with 5 additions and 16 deletions

View File

@ -0,0 +1,19 @@
# Copyright (c) 2023 Dominic Msters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
set(D ${CMAKE_CURRENT_LIST_DIR})
set(
DAWN_TOOL_SOURCES
${DAWN_TOOL_SOURCES}
${D}/DawnTool.cpp
${D}/File.cpp
${D}/Language.cpp
${D}/CodeGen.cpp
${D}/Directory.cpp
CACHE INTERNAL
${DAWN_CACHE_TARGET}
)

View File

@ -0,0 +1,100 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "CodeGen.hpp"
using namespace Dawn;
void CodeGen::line(
std::vector<std::string> *out,
std::string contents,
std::string tabs
) {
out->push_back(tabs + contents);
}
void CodeGen::lines(
std::vector<std::string> *out,
std::vector<std::string> lines,
std::string tabs
) {
auto itLine = lines.begin();
while(itLine != lines.end()) {
line(out, *itLine, tabs);
++itLine;
}
}
void CodeGen::classGen(
std::vector<std::string> *out,
struct ClassGenInfo info
) {
std::vector<std::string> buffer;
line(out, "#pragma once", "");
line(out, "", "");
// Includes
if(info.includes.size() > 0) {
// iterate over info.includes
std::vector<std::string> included;
auto itInclude = info.includes.begin();
while(itInclude != info.includes.end()) {
// skip if already included
if(std::find(included.begin(), included.end(), *itInclude) != included.end()) {
++itInclude;
continue;
}
if(itInclude->find("#include") == std::string::npos) {
line(out, "#include \"" + *itInclude + "\"", "");
} else {
line(out, *itInclude, "");
}
included.push_back(*itInclude);
++itInclude;
}
line(out, "", "");
}
line(out, "namespace Dawn {", "");
auto itTemplates = info.classTemplates.begin();
while(itTemplates != info.classTemplates.end()) {
line(out, "template<" + itTemplates->second + " " + itTemplates->first + ">", " ");
++itTemplates;
}
line(out, "class " + info.clazz + (info.extend.size() == 0 ? "{" : " : public " + info.extend + " {" ), " ");
if(info.protectedCode.size() > 0) {
line(out, "protected:", " ");
lines(out, info.protectedProperties, " ");
line(out, "", " ");
lines(out, info.protectedCode, " ");
}
if(info.publicCode.size() > 0 || info.constructorArgs.size() > 0 || info.constructorCode.size() > 0) {
line(out, "public:", " ");
lines(out, info.publicProperties, " ");
line(out, "", " ");
line(out, info.clazz + "(" + info.constructorArgs + ")" + (info.extend.size() > 0 ? " : " + info.extend + "(" + info.extendArgs + ")" : "") + " {", " ");
lines(out, info.constructorCode, " ");
line(out, "}", " ");
if(info.publicCode.size() > 0) {
line(out, "", " ");
lines(out, info.publicCode, " ");
}
}
line(out, "};", " ");
line(out, "}", "");
}
void CodeGen::methodGen(
std::vector<std::string> *out,
struct MethodGenInfo info
) {
line(out, (info.isStatic ? "static " : "") + info.type + " " + info.name + "(" + info.args + ") " + ( info.isOverride ? "override " : "" ) + "{", "");
lines(out, info.body, " ");
line(out, "}", "");
}

View File

@ -0,0 +1,87 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "dawnsharedlibs.hpp"
namespace Dawn {
struct ClassGenInfo {
std::vector<std::string> includes;
std::string clazz = "Unknown";
std::string extend = "";
std::string constructorArgs = "";
std::string extendArgs = "";
std::map<std::string, std::string> classTemplates;
std::vector<std::string> constructorCode;
std::vector<std::string> protectedCode;
std::vector<std::string> protectedProperties;
std::vector<std::string> publicCode;
std::vector<std::string> publicProperties;
};
struct MethodGenInfo {
std::string name;
std::string type = "void";
std::vector<std::string> body;
std::string args = "";
bool_t isStatic = false;
bool_t isOverride = false;
};
class CodeGen {
protected:
/**
* Append a line of code to the output buffer.
*
* @param out Output buffer to append to.
* @param contents Line of code to append to the buffer.
* @param tabs Prepended string of spaces/tabs.
*/
static void line(
std::vector<std::string> *out,
std::string contents,
std::string tabs
);
/**
* Append an array of lines to the output buffer.
*
* @param out Output buffer to append to.
* @param lines List of lines of code to append to the buffer.
* @param tabs Prepended string of tabs/spaces to prepend to EACH line.
*/
static void lines(
std::vector<std::string> *out,
std::vector<std::string> lines,
std::string tabs
);
/**
* Generate code of a class
*
* @param out Output buffer to write the class to.
* @param info Information about how the class should be generated.
*/
static void classGen(
std::vector<std::string> *out,
struct ClassGenInfo info
);
/**
* Generate code of a method/function.
*
* @param out Output buffer to write the method to.
* @param info Information about how the method should be generated.
*/
static void methodGen(
std::vector<std::string> *out,
struct MethodGenInfo info
);
};
}

View File

@ -0,0 +1,81 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "DawnTool.hpp"
#include "util/memory.hpp"
#if !defined(DAWN_TOOL_INSTANCE)
#error Dawn Tool Instance, e.g. LanguageTool has not been defined
#endif
#if !defined(DAWN_TOOL_HEADER)
#error Dawn Tool Include, e.g. LanguageTool.hpp has not been defined
#endif
#include DAWN_TOOL_HEADER
using namespace Dawn;
std::vector<std::string> DawnTool::getRequiredFlags() {
return std::vector<std::string>();
}
std::map<std::string, std::string> DawnTool::getOptionalFlags() {
return std::map<std::string, std::string>();
}
int32_t DawnTool::exec(const int32_t argc, const char *argv[]) {
// Set up flags
flags = this->getOptionalFlags();
// Parse args
for(int32_t i = 0; i < argc; i++) {
std::string a(argv[i]);
this->args.push_back(a);
// First arg is the path, we ignore it.
if(i == 0) continue;
// Is this a flag-like arg, as in has "--[name]=[value]"
if(a.size() < 5) continue;
if(a[0] != '-' || a[1] != '-') continue;
// Remove --
auto flag = a.erase(0, 2);
// Ensure = is present
auto equalPos = flag.find('=');
if(equalPos == std::string::npos) continue;
// Get prefix and val and store
auto prefix = flag.substr(0, equalPos);
auto val = flag.substr(equalPos + 1);
if(val.size() == 0) continue;
flags[prefix] = val;
}
// Now validate flags
auto required = this->getRequiredFlags();
auto itReq = required.begin();
while(itReq != required.end()) {
auto match = flags.find(*itReq);
if(match == flags.end()) {
std::cout << "Missing required flag \"" + *itReq + "\"!" << std::endl;
return 1;
}
++itReq;
}
return this->start();
}
int main(const int32_t argc, const char *argv[]) {
memoryInit();
DAWN_TOOL_INSTANCE *self = new DAWN_TOOL_INSTANCE();
int32_t ret = self->exec(argc, argv);
delete self;
if(ret == 0) memoryDispose();
return ret;
}

View File

@ -0,0 +1,57 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "dawnsharedlibs.hpp"
#include <iostream>
#include <map>
#include <vector>
#include <algorithm>
#include <iterator>
namespace Dawn {
class DawnTool {
protected:
std::vector<std::string> args;
std::map<std::string, std::string> flags;
/**
* Get the required flags for this tool. These flags will be checked in
* the passed args to the CLI.
*
* @return Array of required flags.
*/
virtual std::vector<std::string> getRequiredFlags();
/**
* Get the optional flags for this tool. Mapped by key value pair where
* the value is some default value you want for your tool.
*
* @return Array of optional flags.
*/
virtual std::map<std::string, std::string> getOptionalFlags();
public:
int32_t *test;
/**
* Execute the Dawn Tool from the CLI, functionally identically to main()
*
* @param argc Count of arguments in the array.
* @param argv Array of arguments.
* @return int32_t
*/
int32_t exec(const int32_t argc, const char *argv[]);
/**
* Overridable start method after the tool has been set up.
*
* @return 0 for success, otherwise for failure.
*/
virtual int32_t start() = 0;
};
}
int main(const int32_t argc, const char *argv[]);

View File

@ -0,0 +1,58 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "Directory.hpp"
using namespace Dawn;
Directory::Directory(std::string dirname) {
this->filename = File::normalizeSlashes(dirname);
}
bool_t Directory::open() {
if(!this->exists()) return false;
if (handle != std::filesystem::end(handle)) return true;
handle = std::filesystem::directory_iterator(filename);
return handle != std::filesystem::end(handle);
}
bool_t Directory::isOpen() {
return handle != std::filesystem::end(handle);
}
void Directory::close() {
if(this->isOpen()) {
handle = std::filesystem::directory_iterator();
}
}
bool_t Directory::exists() {
return std::filesystem::exists(filename);
}
void Directory::mkdirp() {
File::mkdirp(this->filename);
}
std::map<std::string, enum DirectoryChildType> Directory::readDirectory() {
this->close();
if(!this->open()) return {};
std::map<std::string, enum DirectoryChildType> children;
for(const auto& entry : handle) {
if (entry.is_directory()) {
children[entry.path().filename().string()] = DIRECTORY_CHILD_TYPE_DIRECTORY;
} else if (entry.is_regular_file()) {
children[entry.path().filename().string()] = DIRECTORY_CHILD_TYPE_FILE;
}
}
this->close();
return children;
}
Directory::~Directory() {
this->close();
}

View File

@ -0,0 +1,60 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "util/File.hpp"
#include <filesystem>
namespace Dawn {
enum DirectoryChildType {
DIRECTORY_CHILD_TYPE_FILE,
DIRECTORY_CHILD_TYPE_DIRECTORY
};
class Directory {
public:
std::string filename;
std::filesystem::directory_iterator handle;
Directory(std::string dir);
/**
* Opens a connection to the directory.
* @return True if success, otherwise for failure.
*/
bool_t open();
/**
* Returns if the directory is open.
* @return True if open, otherwise false.
*/
bool_t isOpen();
/**
* Closes the directory.
*/
void close();
/**
* Returns if the directory exists.
* @return True if exists, otherwise false.
*/
bool_t exists();
/**
* Creates the directory and all parent directories.
*/
void mkdirp();
/**
* Reads the directory and returns a list of files.
*
* @return List of filenames/directories within this directory.
*/
std::map<std::string, enum DirectoryChildType> readDirectory();
~Directory();
};
}

View File

@ -0,0 +1,264 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "File.hpp"
using namespace Dawn;
std::string File::normalizeSlashes(std::string str) {
size_t i = 0;
while(i < str.size()) {
auto c = str[i];
if(c == '\\' || c == '/') str[i] = FILE_PATH_SEP;
++i;
}
return str;
}
void File::mkdirp(std::string path) {
std::string buffer;
char c;
size_t i = 0;
bool_t inFile;
bool_t hasMore;
inFile = false;
hasMore = false;
while(c = path[i]) {
if((c == '\\' || c == '/') && i > 0) {
fileMkdir(buffer.c_str(), 0755);
inFile = false;
hasMore = false;
buffer += FILE_PATH_SEP;
i++;
continue;
}
if(c == '.') inFile = true;
hasMore = true;
buffer += c;
i++;
}
if(!inFile && hasMore) {
fileMkdir(buffer.c_str(), 0755);
}
}
//
File::File(std::string filename) {
this->filename = File::normalizeSlashes(filename);
}
bool_t File::open(enum FileMode mode) {
assertNull(this->file, "File is already open");
this->mode = mode;
this->file = fopen(
this->filename.c_str(),
mode == FILE_MODE_READ ? "rb" : "wb"
);
if(this->file == NULL) return false;
if(mode == FILE_MODE_READ) {
fseek(this->file, 0, SEEK_END);
this->length = ftell(this->file);
fseek(this->file, 0, SEEK_SET);
if(this->length <= 0) {
this->close();
return false;
}
} else {
this->length = 0;
}
return true;
}
bool_t File::isOpen() {
return this->file != nullptr;
}
bool_t File::exists() {
if(this->file != nullptr) return true;
FILE *f = fopen(this->filename.c_str(), "rb");
if(f == NULL || f == nullptr) return false;
fclose(f);
return true;
}
void File::close() {
assertNotNull(this->file, "File::close: File is not open");
fclose(this->file);
this->file = nullptr;
}
bool_t File::mkdirp() {
File::mkdirp(this->filename);
return true;
}
bool_t File::readString(std::string *out) {
assertNotNull(out, "File::readString: Out cannot be null");
if(!this->isOpen()) {
if(!this->open(FILE_MODE_READ)) return false;
}
assertTrue(this->mode == FILE_MODE_READ, "File::readString: File must be open in read mode");
out->clear();
size_t i = 0;
char buffer[FILE_BUFFER_SIZE + 1];// +1 for null term
while(i != this->length) {
size_t amt = mathMin<size_t>(FILE_BUFFER_SIZE, (this->length - i));
auto amtRead = fread(buffer, sizeof(char), amt, this->file);
if(amtRead != amt) return false;
i += amtRead;
buffer[amtRead] = '\0';
out->append(buffer);
}
return true;
}
size_t File::readAhead(char *buffer, size_t max, char needle) {
assertNotNull(buffer, "File::readAhead: Buffer cannot be null");
assertTrue(max > 0, "File::readAhead: Max must be greater than 0");
if(!this->isOpen()) {
if(!this->open(FILE_MODE_READ)) return 0;
}
assertTrue(this->mode == FILE_MODE_READ, "File::readAhead: File must be open in read mode");
// Buffer
size_t pos = ftell(this->file);
size_t amountLeftToRead = mathMin<size_t>(max, this->length - pos);
char temporary[FILE_BUFFER_SIZE];
size_t n = 0;
while(amountLeftToRead > 0) {
size_t toRead = mathMin<size_t>(amountLeftToRead, FILE_BUFFER_SIZE);
amountLeftToRead -= toRead;
// Read bytes
size_t read = fread(temporary, sizeof(char), toRead, this->file);
// Read error?
if(toRead != read) return 0;
// Did we read the needle?
size_t i = 0;
while(i < read) {
char c = temporary[i++];
if(c == needle) {
return n;
} else {
buffer[n++] = c;
}
}
}
// Needle was not found.
return -1;
}
size_t File::readToBuffer(char **buffer) {
if(!this->isOpen()) {
if(!this->open(FILE_MODE_READ)) return 0;
}
assertTrue(this->mode == FILE_MODE_READ, "File::readToBuffer: File must be open in read mode");
if((*buffer) == nullptr) *buffer = (char*)malloc(this->length);
fseek(this->file, 0, SEEK_SET);
auto l = fread((*buffer), sizeof(char), this->length, this->file);
return l;
}
size_t File::readRaw(char *buffer, size_t max) {
assertNotNull(buffer, "File::readRaw: Buffer cannot be null");
assertTrue(max > 0, "File::readRaw: Max must be greater than 0");
if(!this->isOpen()) {
if(!this->open(FILE_MODE_READ)) return 0;
}
assertTrue(this->mode == FILE_MODE_READ, "File::readRaw: File must be open in read mode");
return fread(buffer, sizeof(char), max, this->file);
}
bool_t File::writeString(std::string in) {
if(!this->isOpen() && !this->open(FILE_MODE_WRITE)) return false;
assertTrue(this->mode == FILE_MODE_WRITE, "File::writeString: File must be open in write mode");
return this->writeRaw((char *)in.c_str(), in.size()) && this->length == in.size();
}
bool_t File::writeRaw(char *data, size_t len) {
if(!this->isOpen() && !this->open(FILE_MODE_WRITE)) return false;
assertTrue(this->mode == FILE_MODE_WRITE, "File::writeRaw: File must be open in write mode");
assertTrue(len > 0, "File::writeRaw: Length must be greater than 0");
this->length = fwrite(data, sizeof(char_t), len, this->file);
return true;
}
bool_t File::copyRaw(File *otherFile, size_t length) {
assertTrue(length > 0, "File::copyRaw: Length must be greater than 0");
assertTrue(otherFile->isOpen(), "File::copyRaw: Other file must be open");
char buffer[FILE_BUFFER_SIZE];
size_t amountLeftToRead = length;
size_t read = 0;
while(amountLeftToRead > 0) {
auto iRead = otherFile->readRaw(buffer, mathMin<size_t>(FILE_BUFFER_SIZE, amountLeftToRead));
if(iRead == 0) return false;
this->writeRaw(buffer, iRead);
amountLeftToRead -= iRead;
read += iRead;
}
assertTrue(read == length, "File::copyRaw: Read length does not match expected length");
return true;
}
void File::setPosition(size_t n) {
fseek(this->file, 0, SEEK_SET);
fseek(this->file, n, SEEK_CUR);
}
std::string File::getFileName(bool_t withExt) {
// Remove all but last slash
std::string basename;
size_t lastSlash = this->filename.find_last_of('/');
if(lastSlash == std::string::npos) {
basename = this->filename;
} else {
basename = this->filename.substr(lastSlash + 1);
}
// Do we need to remove ext?
if(withExt) return basename;
size_t lastDot = basename.find_last_of('.');
if(lastDot == std::string::npos) return basename;
return basename.substr(0, lastDot);
}
std::string File::getExtension() {
// Remove all but last slash
std::string basename;
size_t lastSlash = this->filename.find_last_of('/');
if(lastSlash == std::string::npos) {
basename = this->filename;
} else {
basename = this->filename.substr(lastSlash + 1);
}
size_t lastDot = basename.find_last_of('.');
if(lastDot == std::string::npos) return "";
return basename.substr(lastDot + 1);
}
File::~File() {
if(this->file != nullptr) this->close();
}

View File

@ -0,0 +1,183 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "assert/assert.hpp"
#include "util/mathutils.hpp"
#if defined(_MSC_VER)
#include <direct.h>
#include <windows.h>
#define getcwd _getcwd
#define FILE_PATH_SEP '\\'
#define fileMkdir(path, perms) _mkdir(path)
#elif defined(__GNUC__)
#include <unistd.h>
#include <dirent.h>
#include <sys/stat.h>
#define FILE_PATH_SEP '/'
#define fileMkdir(path, perms) mkdir(path, perms)
#endif
#include <errno.h>
#define FILE_BUFFER_SIZE 512
namespace Dawn {
enum FileMode {
FILE_MODE_READ,
FILE_MODE_WRITE
};
class File {
private:
enum FileMode mode;
public:
static std::string normalizeSlashes(std::string str);
static void mkdirp(std::string path);
std::string filename;
size_t length;
FILE *file = nullptr;
/**
* Constructs a new File interface class.
*
* @param filename Filename that you want to interface with.
*/
File(std::string filename);
/**
* Opens a connection to the file.
*
* @param mode File mode to use for this interface.
* @return True if success, otherwise for failure.
*/
bool_t open(enum FileMode mode);
/**
* Returns whether or not the file connection is currently opened.
*
* @return True if the connection is open, otherwise if it's not.
*/
bool_t isOpen();
/**
* Returns whether or not the file exists. Will open the connection if it
* does exist.
*
* @return True if exists, otherwsie if it doesn't.
*/
bool_t exists();
/**
* Closes the currently open interface to the file. Done automatically
* when this object is disposed.
*/
void close();
/**
* Makes all directories above this file's filename.
*
* @return True if successful, otherwise false.
*/
bool_t mkdirp();
/**
* Reads the entire contents of a file to the given output string buffer.
* This is a bit dangerous since the length of the file isn't checked
* against the memory to be consumed.
*
* @param out Pointer to a string where the output data will be stored.
* @return True if the read was successful, otherwise false.
*/
bool_t readString(std::string *out);
/**
* Reads ahead from the current position to a specific needle (character).
*
* @param buffer Buffer to output read chars to.
* @param max Max length of the buffer / amount of chars to read ahead.
* @param needle The character (needle) to look for.
* @return Amount of chars read, or <= 0 on error.
*/
size_t readAhead(
char *buffer,
size_t max,
char needle
);
/**
* Reads the contents of this file into a given buffer. If buffer is null
* then the buffer will be allocated and returned.
*
* @param buffer Pointer to buffer to read to.
* @return The size of the read data.
*/
size_t readToBuffer(char **buffer);
/**
* Reads the contents of this file into a given buffer.
*
* @param buffer Pointer to buffer to read to.
* @param max Max length of the buffer.
* @return Amount of bytes read.
*/
size_t readRaw(char *buffer, size_t max);
/**
* Writes the entire contents of a string to a file.
*
* @param in String to write to this file.
* @return True if written successfully, otherwise false.
*/
bool_t writeString(std::string in);
/**
* Write raw bytes to the file.
*
* @param data Data to write.
* @param size Size of the data to write.
* @return True if written successfully, otherwise false.
*/
bool_t writeRaw(char *data, size_t size);
/**
* Write raw bytes to the file from another file.
*
* @param otherFile Other file to read from.
* @param length Length of the data to read.
* @return True if written successfully, otherwise false.
*/
bool_t copyRaw(File *otherFile, size_t length);
/**
* Set the position of the cursor of the file reader.
*
* @param pos Position to set.
*/
void setPosition(size_t pos);
/**
* Get the file name of this file, optionally with the extension.
*
* @param withExtension If true, the extension will be included.
* @return The file name.
*/
std::string getFileName(bool_t withExtension = true);
/**
* Returns the extension of this file.
*
* @return The extension of this file.
*/
std::string getExtension();
/**
* Destruct the File manager.
*/
~File();
};
}

View File

@ -0,0 +1,55 @@
/**
* Copyright (c) 2021 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "Image.hpp"
#ifndef STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
#endif
#ifndef STB_IMAGE_RESIZE_IMPLEMENTATION
#define STB_IMAGE_RESIZE_IMPLEMENTATION
#include <stb_image_resize.h>
#endif
#ifndef STB_IMAGE_WRITE_IMPLEMENTATION
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include <stb_image_write.h>
#endif
void imageCopy(
uint8_t *source, int32_t sourceWidth, int32_t sourceHeight,
uint8_t *dest, int32_t destWidth, int32_t destHeight,
int32_t cropX, int32_t cropY, int32_t cropWidth, int32_t cropHeight,
int32_t pasteX, int32_t pasteY,
int32_t channels
) {
int32_t x, y, c;
int32_t absX, absY;
int32_t sourceIndex, targetIndex;
if(cropX == -1) cropX = 0;
if(cropY == -1) cropY = 0;
if(cropWidth == -1) cropWidth = sourceWidth;
if(cropHeight == -1) cropHeight = sourceHeight;
if(pasteX == -1) pasteX = 0;
if(pasteY == -1) pasteY = 0;
for(x = cropX; x < cropX + cropWidth; x++) {
for(y = cropY; y < cropY + cropHeight; y++) {
absX = x - cropX + pasteX;
absY = y - cropY + pasteY;
if(absX >= destWidth || absY >= destHeight || absX < 0 || absY < 0)continue;
targetIndex = absY * destWidth + absX;
sourceIndex = y * sourceWidth + x;
for(c = 0; c < channels; c++) {
dest[(targetIndex*channels) + c] = source[(sourceIndex*channels) + c];
}
}
}
}

View File

@ -0,0 +1,38 @@
/**
* Copyright (c) 2021 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "File.hpp"
#include <stb_image.h>
#include <stb_image_resize.h>
#include <stb_image_write.h>
/**
* Unused currently.
*
* @param source
* @param sourceWidth
* @param sourceHeight
* @param dest
* @param destWidth
* @param destHeight
* @param cropX
* @param cropY
* @param cropWidth
* @param cropHeight
* @param pasteX
* @param pasteY
* @param channels
*/
void imageCopy(
uint8_t *source, int32_t sourceWidth, int32_t sourceHeight,
uint8_t *dest, int32_t destWidth, int32_t destHeight,
int32_t cropX, int32_t cropY, int32_t cropWidth, int32_t cropHeight,
int32_t pasteX, int32_t pasteY,
int32_t channels
);

View File

@ -0,0 +1,36 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "Language.hpp"
using namespace Dawn;
int32_t languageSaveStrings(
std::string languagesDir,
std::vector<struct LanguageString> strings
) {
if(languagesDir.size() <= 0) {
std::cout << "Languages dir is not defined" << std::endl;
return 1;
}
File file(languagesDir);
if(!file.mkdirp()) {
std::cout << "Failed to create languages string output dir " << file.filename << std::endl;
return 1;
}
// Now convert to xml
std::string buffer;
auto it = strings.begin();
while(it != strings.end()) {
auto s = *it;
buffer += s.lang + "|" + s.key + "|" + s.text + "|";
++it;
}
file.writeString(buffer);
return 0;
}

View File

@ -0,0 +1,26 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "util/Xml.hpp"
#include "util/File.hpp"
struct LanguageString {
std::string key;
std::string lang;
std::string text;
};
/**
* Shorthand method to save a list of languages to a file.
*
* @param languagesDir Directory where the language(s) are to be stored.
* @param strings List of strings to store.
* @return 0 for success, otherwise for failure.
*/
static int32_t languageSaveStrings(
std::string languagesDir,
std::vector<struct LanguageString> strings
);

View File

@ -0,0 +1,113 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "util/Xml.hpp"
namespace Dawn {
template<typename T>
class XmlParser {
protected:
/**
* Get the required attributes for this Xml Node to have.
*
* @return Vector of strings of required attribute keys.
*/
virtual std::vector<std::string> getRequiredAttributes() = 0;
/**
* Return optional attributes with defaults for this node to have.
*
* @return Key-Value Pair of optional attributes and defaults.
*/
virtual std::map<std::string, std::string> getOptionalAttributes() = 0;
/**
* Callback to be invoked upon successful parse of this node.
*
* @param node Node that was parsed.
* @param values KVP of values from the required and optional attrs.
* @param output Templated output of the parse from this method.
* @param error Pointer to a string to write-out any errors.
* @return Non 0 for error, 0 for success.
*/
virtual int32_t onParse(
Xml *node,
std::map<std::string, std::string> values,
T *output,
std::string *error
) = 0;
public:
/**
* Common parse method to parse a duration from a value string.
*
* @param duration Duration string to parse.
* @return The parsed duration.
*/
static std::string parseDuration(std::string duration) {
std::string dur = duration;
if(dur.find('.') == std::string::npos) dur += ".0";
return dur + "f";
}
/**
* Common parse method to parse an easing from a value string.
*
* @param e Easing string to parse.
* @return The parsed ease.
*/
static std::string parseEase(std::string e) {
if(e == "out-quad") return "&easeOutQuad";
if(e == "linear") return "&easeLinear";
return "";
}
/**
* Handles parsing of an Xml node.
*
* @param xml Xml node to parse.
* @param output Output struct to put the output data.
* @param error Pointer to the string for errors to be stored.
* @return 0 for success, otherwise for error.
*/
int32_t parse(Xml *xml, T *output, std::string *error) {
std::map<std::string, std::string> values;
// First get the required attributes
auto required = this->getRequiredAttributes();
auto itRequired = required.begin();
while(itRequired != required.end()) {
auto s = *itRequired;
auto attr = xml->attributes.find(s);
if(attr == xml->attributes.end()) {
std::cout << "Missing required attribute \"" << s << "\" on node \"" << xml->node << "\"" << std::endl;
return 1;
}
values[s] = attr->second;
++itRequired;
}
// Now get the optional attributes
auto optional = this->getOptionalAttributes();
auto itOptional = optional.begin();
while(itOptional != optional.end()) {
auto key = itOptional->first;
auto defaultValue = itOptional->second;
auto attr = xml->attributes.find(key);
if(attr == xml->attributes.end()) {
values[key] = defaultValue;
} else {
values[key] = attr->second;
}
++itOptional;
}
// Now send to parser
return this->onParse(xml, values, output, error);
}
};
}

View File

@ -0,0 +1,19 @@
# Copyright (c) 2023 Dominic Msters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
set(D ${CMAKE_CURRENT_LIST_DIR})
set(
DAWN_TOOL_SOURCES
${DAWN_TOOL_SOURCES}
${D}/SceneItemGenerator.cpp
${D}/SceneAssetGenerator.cpp
${D}/SceneCodeGenerator.cpp
${D}/SceneGenerator.cpp
${D}/SceneItemComponentGenerator.cpp
CACHE INTERNAL
${DAWN_CACHE_TARGET}
)

View File

@ -0,0 +1,53 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "SceneAssetGenerator.hpp"
using namespace Dawn;
void SceneAssetGenerator::generate(
std::map<std::string, std::string> &assetMap,
int32_t &assetNumber,
std::vector<std::string> *publicProperties,
std::vector<std::string> *initBody,
std::vector<std::string> *assetsBody,
struct SceneAsset *asset,
std::string tabs
) {
std::string assetType = "";
bool_t componentInit = true;
if(asset->ref.empty()) {
asset->usageName = "asset" + std::to_string(assetNumber++);
} else {
asset->usageName = asset->ref;
}
switch(asset->type) {
case SCENE_ASSET_TYPE_TEXTURE:
assetType = "TextureAsset";
assetMap[asset->fileName] = "&" + asset->usageName + "->texture";
assetMap[asset->usageName] = "&" + asset->usageName + "->texture";
break;
case SCENE_ASSET_TYPE_TRUETYPE_FONT:
assetType = "TrueTypeAsset";
assetMap[asset->fileName] = asset->usageName;
assetMap[asset->usageName] = asset->usageName;
break;
default:
assertUnreachable("SceneAssetGenerator::generate: Unknown asset type");
}
if(!asset->ref.empty()) {
line(publicProperties, assetType + " *" + asset->usageName + " = nullptr;", "");
line(initBody, asset->usageName + " = man->get<" + assetType + ">(\"" + asset->fileName + "\");", "");
} else {
line(initBody, "auto " + asset->usageName + " = man->get<" + assetType + ">(\"" + asset->fileName + "\");", "");
}
line(assetsBody, "assets.push_back(man->get<" + assetType + ">(\"" + asset->fileName + "\"));", "");
}

View File

@ -0,0 +1,23 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "util/parser/SceneAssetParser.hpp"
#include "util/CodeGen.hpp"
namespace Dawn {
class SceneAssetGenerator : public CodeGen {
public:
static void generate(
std::map<std::string, std::string> &assetMap,
int32_t &assetNumber,
std::vector<std::string> *publicProperties,
std::vector<std::string> *initBody,
std::vector<std::string> *assetsBody,
struct SceneAsset *asset,
std::string tabs
);
};
}

View File

@ -0,0 +1,33 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "SceneCodeGenerator.hpp"
using namespace Dawn;
void SceneCodeGenerator::generate(
std::vector<std::string> *publicProperties,
std::vector<std::string> *initBody,
std::vector<std::string> *includes,
struct SceneCode *code,
std::string tabs
) {
switch(code->codeType) {
case SCENE_CODE_TYPE_PROPERTIES:
line(publicProperties, code->code, "");
break;
case SCENE_CODE_TYPE_INIT:
line(initBody, code->code, "");
break;
case SCENE_CODE_TYPE_INCLUDE:
line(includes, code->code, "");
break;
default:
break;
}
}

View File

@ -0,0 +1,21 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "util/parser/SceneCodeParser.hpp"
#include "util/CodeGen.hpp"
namespace Dawn {
class SceneCodeGenerator : public CodeGen {
public:
static void generate(
std::vector<std::string> *publicProperties,
std::vector<std::string> *initBody,
std::vector<std::string> *includes,
struct SceneCode *code,
std::string tabs
);
};
}

View File

@ -0,0 +1,103 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "SceneGenerator.hpp"
using namespace Dawn;
void SceneGenerator::generate(
struct Scene *scene,
struct ClassGenInfo &classInfo,
struct MethodGenInfo &methodAssets,
struct MethodGenInfo &methodInit
) {
assertNotNull(scene, "SceneGenerator::generate: Scene cannot be null");
std::map<std::string, std::string> assetMap;
int32_t assetNumber = 0;
std::string baseClassName = "Scene";
classInfo.clazz = scene->name;
classInfo.constructorArgs = "DawnGame *game";
classInfo.extendArgs = "game";
// Determine extends
if(scene->extend.empty()) {
classInfo.includes.push_back("scene/Scene.hpp");
} else {
classInfo.includes.push_back(scene->extend + ".hpp");
// Get last slash of scene->extend
auto lastSlash = scene->extend.find_last_of('/');
baseClassName = scene->extend;
if(lastSlash != std::string::npos) {
baseClassName = scene->extend.substr(lastSlash + 1);
}
}
classInfo.extend = baseClassName;
methodAssets.name = "getRequiredAssets";
methodAssets.isOverride = true;
methodAssets.type = "std::vector<Asset*>";
line(&methodAssets.body, "auto man = &this->game->assetManager;", "");
methodInit.name = "stage";
methodInit.isOverride = true;
if(scene->extend.empty()) {
line(&methodAssets.body, "std::vector<Asset*> assets;", "");
} else {
line(&methodAssets.body, "std::vector<Asset*> assets = " + baseClassName + "::getRequiredAssets();", "");
line(&methodInit.body, baseClassName + "::stage();", "");
}
// Scene assets
line(&methodInit.body, "auto man = &this->game->assetManager;", "");
auto itAssets = scene->assets.begin();
while(itAssets != scene->assets.end()) {
SceneAssetGenerator::generate(
assetMap,
assetNumber,
&classInfo.publicProperties,
&methodInit.body,
&methodAssets.body,
&(*itAssets),
""
);
++itAssets;
}
// Generate
int32_t childNumber = 0;
int32_t componentNumber = 0;
std::vector<struct SceneItemComponent> componentsUnused;
auto itDeps = scene->dependencies.begin();
while(itDeps != scene->dependencies.end()) {
auto dep = *itDeps;
SceneItemGenerator::generateDependency(
assetNumber,
componentNumber,
childNumber,
assetMap,
classInfo.includes,
&classInfo.publicProperties,
&methodInit.body,
&methodAssets.body,
"",
"this",
componentsUnused,
scene->items,
scene->code,
dep
);
++itDeps;
}
// Seal methods
line(&methodAssets.body, "return assets;", "");
}

View File

@ -0,0 +1,21 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "util/parser/SceneParser.hpp"
#include "SceneItemGenerator.hpp"
#include "util/CodeGen.hpp"
namespace Dawn {
class SceneGenerator : public CodeGen {
public:
static void generate(
struct Scene *scene,
struct ClassGenInfo &classInfo,
struct MethodGenInfo &methodAssets,
struct MethodGenInfo &methodInit
);
};
}

View File

@ -0,0 +1,43 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "SceneItemComponentGenerator.hpp"
using namespace Dawn;
void SceneItemComponentGenerator::generate(
std::map<std::string, std::string> &assetMap,
int32_t &componentNumber,
std::vector<std::string> &includes,
std::string itemRef,
std::vector<std::string> *publicProperties,
std::vector<std::string> *initBody,
struct SceneItemComponent *component,
std::string tabs
) {
auto componentName = "cmp" + std::to_string(componentNumber++);
bool_t componentInit = true;
if(component->ref.size() > 0) {
componentInit = false;
componentName = component->ref;
line(publicProperties, component->type + " *" + component->ref + " = nullptr;", "");
}
// Initialize
line(initBody, (componentInit ? "auto " : "") + componentName + " = " + itemRef + "->addComponent<" + component->type + ">();", "");
// Now set each property
auto itValues = component->values.begin();
while(itValues != component->values.end()) {
auto value = itValues->second;
if(assetMap.find(value) != assetMap.end()) {
value = assetMap[value];
}
line(initBody, componentName + "->" + itValues->first + " = " + value + ";", "");
++itValues;
}
includes.push_back(component->include);
}

View File

@ -0,0 +1,24 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "util/parser/SceneItemComponentParser.hpp"
#include "util/CodeGen.hpp"
namespace Dawn {
class SceneItemComponentGenerator : public CodeGen {
public:
static void generate(
std::map<std::string, std::string> &assetMap,
int32_t &componentNumber,
std::vector<std::string> &includes,
std::string itemRef,
std::vector<std::string> *publicProperties,
std::vector<std::string> *initBody,
struct SceneItemComponent *component,
std::string tabs
);
};
}

View File

@ -0,0 +1,213 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "SceneItemGenerator.hpp"
using namespace Dawn;
void SceneItemGenerator::generateDependency(
int32_t &assetNumber,
int32_t &componentNumber,
int32_t &childNumber,
std::map<std::string, std::string> &assetMap,
std::vector<std::string> &includes,
std::vector<std::string> *publicProperties,
std::vector<std::string> *initBody,
std::vector<std::string> *assetBody,
std::string name,
std::string sceneRef,
std::vector<struct SceneItemComponent> &components,
std::vector<struct SceneItem> &children,
std::vector<struct SceneCode> &code,
struct SceneItemDependency dep
) {
switch(dep.type) {
case SCENE_ITEM_DEPENDENCY_TYPE_CODE: {
auto i = &code[dep.position];
SceneCodeGenerator::generate(
publicProperties,
initBody,
&includes,
i,
""
);
line(initBody, "", "");
break;
}
case SCENE_ITEM_DEPENDENCY_TYPE_COMPONENT: {
auto i = &components[dep.position];
SceneItemComponentGenerator::generate(
assetMap,
componentNumber,
includes,
name,
publicProperties,
initBody,
i,
""
);
line(initBody, "", "");
break;
}
case SCENE_ITEM_DEPENDENCY_TYPE_ITEM: {
auto i = &children[dep.position];
SceneItemGenerator::generate(
assetNumber,
componentNumber,
childNumber,
assetMap,
includes,
publicProperties,
initBody,
assetBody,
name,
sceneRef,
i,
""
);
line(initBody, "", "");
break;
}
default:
assertUnreachable("SceneItemGenerator::generateDependency: Unknown dependency type");
}
}
void SceneItemGenerator::generate(
int32_t &assetNumber,
int32_t &componentNumber,
int32_t &childNumber,
std::map<std::string, std::string> &assetMap,
std::vector<std::string> &includes,
std::vector<std::string> *publicProperties,
std::vector<std::string> *initBody,
std::vector<std::string> *assetBody,
std::string parentRef,
std::string sceneRef,
struct SceneItem *item,
std::string tabs
) {
assertNotNull(publicProperties, "SceneItemGenerator::generate: publicProperties cannot be null");
assertNotNull(initBody, "SceneItemGenerator::generate: initBody cannot be null");
assertNotNull(assetBody, "SceneItemGenerator::generate: assetBody cannot be null");
assertNotNull(item, "SceneItemGenerator::generate: item cannot be null");
// Determine interface
std::string name = "itm" + std::to_string(childNumber++);
std::string itemType = "SceneItem";
if(item->ref == "this") {
name = item->ref;
} else {
// Determine name
if(!item->ref.empty()) name = item->ref;
// Initialize, either prefab or created.
if(!item->prefab.empty()) {
// Determine prefab name, type and include
std::string prefabName = item->prefab;
if(prefabName.find("/") != std::string::npos) {
prefabName = prefabName.substr(prefabName.find_last_of("/") + 1);
}
itemType = prefabName;
// Create prefab
includes.push_back(item->prefab + ".hpp");
line(initBody, (item->ref.empty() ? "auto " : "") + name + " = " + prefabName + "::create(" + sceneRef + ");", "");
line(assetBody, "vectorAppend(&assets, " + prefabName + "::getRequiredAssets(man));", "");
} else {
// Not a prefab, init empty child.
line(initBody, (item->ref.empty() ? "auto " : "") + name + " = " + sceneRef + "->createSceneItem();", "");
}
// Property/Ref defintion
if(!item->ref.empty() && item->ref != "this") {
line(publicProperties, itemType + " *" + name + " = nullptr;", "");
}
}
// Process extra properties
if(item->position.size() > 0) {
line(initBody, name + "->transform.setLocalPosition(" + item->position + ");", "");
}
if(item->alignment.size() > 0) {
line(initBody, name + "->uiItem->alignment = " + item->alignment + ";", "");
}
if(item->alignX.size() > 0) {
line(initBody, name + "->uiItem->alignX = " + item->alignX + ";", "");
}
if(item->alignY.size() > 0) {
line(initBody, name + "->uiItem->alignY = " + item->alignY + ";", "");
}
if(item->menuX.size() > 0) {
line(initBody, name + "->menuItem->menuX = " + item->menuX + ";", "");
}
if(item->menuY.size() > 0) {
line(initBody, name + "->menuItem->menuY = " + item->menuY + ";", "");
}
if(item->scale.size() > 0) {
line(initBody, name + "->transform.setLocalScale(" + item->scale + ");", "");
}
if(item->lookAtPosition.size() > 0) {
line(initBody, name + "->transform.lookAt(" + item->lookAtPosition + ", " + item->lookAtTarget + ");", "");
}
if(!item->label.empty()) {
line(initBody, name + "->label->text = " + item->label + ";", "");
}
// Add assets
auto itAssets = item->assets.begin();
while(itAssets != item->assets.end()) {
SceneAssetGenerator::generate(
assetMap,
assetNumber,
publicProperties,
initBody,
assetBody,
&(*itAssets),
""
);
++itAssets;
}
// Add the dependencies
auto itDeps = item->dependencies.begin();
while(itDeps != item->dependencies.end()) {
auto dep = *itDeps;
SceneItemGenerator::generateDependency(
assetNumber,
componentNumber,
childNumber,
assetMap,
includes,
publicProperties,
initBody,
assetBody,
name,
sceneRef,
item->components,
item->children,
item->code,
dep
);
++itDeps;
}
// Set parent
if(!parentRef.empty()) {
line(initBody, name + "->transform.setParent(&"+parentRef+"->transform);", "");
}
}

View File

@ -0,0 +1,47 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "SceneItemComponentGenerator.hpp"
#include "SceneAssetGenerator.hpp"
#include "util/parser/SceneItemParser.hpp"
#include "util/generator/SceneCodeGenerator.hpp"
namespace Dawn {
class SceneItemGenerator : public CodeGen {
public:
static void generateDependency(
int32_t &assetNumber,
int32_t &componentNumber,
int32_t &childNumber,
std::map<std::string, std::string> &assetMap,
std::vector<std::string> &includes,
std::vector<std::string> *publicProperties,
std::vector<std::string> *initBody,
std::vector<std::string> *assetBody,
std::string name,
std::string sceneRef,
std::vector<struct SceneItemComponent> &components,
std::vector<struct SceneItem> &children,
std::vector<struct SceneCode> &code,
struct SceneItemDependency dep
);
static void generate(
int32_t &assetNumber,
int32_t &componentNumber,
int32_t &childNumber,
std::map<std::string, std::string> &assetMap,
std::vector<std::string> &includes,
std::vector<std::string> *publicProperties,
std::vector<std::string> *initBody,
std::vector<std::string> *assetBody,
std::string parentRef,
std::string sceneRef,
struct SceneItem *item,
std::string tabs
);
};
}

View File

@ -0,0 +1,20 @@
# Copyright (c) 2023 Dominic Msters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
set(D ${CMAKE_CURRENT_LIST_DIR})
set(
DAWN_TOOL_SOURCES
${DAWN_TOOL_SOURCES}
${D}/SceneParser.cpp
${D}/SceneItemParser.cpp
${D}/SceneAssetParser.cpp
${D}/SceneCodeParser.cpp
${D}/SceneItemComponentParser.cpp
${D}/SceneItemComponentRegistry.cpp
CACHE INTERNAL
${DAWN_CACHE_TARGET}
)

View File

@ -0,0 +1,40 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "SceneAssetParser.hpp"
using namespace Dawn;
std::vector<std::string> SceneAssetParser::getRequiredAttributes() {
return { "type", "name" };
}
std::map<std::string, std::string> SceneAssetParser::getOptionalAttributes() {
return {
{ "ref", "" }
};
}
int32_t SceneAssetParser::onParse(
Xml *node,
std::map<std::string, std::string> values,
struct SceneAsset *out,
std::string *error
) {
out->fileName = values["name"];
if(values["type"] == "texture") {
out->type = SCENE_ASSET_TYPE_TEXTURE;
} else if(values["type"] == "truetype") {
out->type = SCENE_ASSET_TYPE_TRUETYPE_FONT;
} else {
*error = "Unknown asset type '" + values["type"] + "'";
return 1;
}
out->ref = values["ref"];
return 0;
}

View File

@ -0,0 +1,34 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "util/XmlParser.hpp"
namespace Dawn {
enum SceneAssetType {
SCENE_ASSET_TYPE_TEXTURE,
SCENE_ASSET_TYPE_TRUETYPE_FONT
};
struct SceneAsset {
SceneAssetType type;
std::string fileName;
std::string usageName;
std::string ref;
};
class SceneAssetParser : public XmlParser<SceneAsset> {
public:
virtual std::vector<std::string> getRequiredAttributes();
virtual std::map<std::string, std::string> getOptionalAttributes();
virtual int32_t onParse(
Xml *node,
std::map<std::string, std::string> values,
struct SceneAsset *out,
std::string *error
);
};
}

View File

@ -0,0 +1,41 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "SceneCodeParser.hpp"
using namespace Dawn;
std::vector<std::string> SceneCodeParser::getRequiredAttributes() {
return { "type" };
}
std::map<std::string, std::string> SceneCodeParser::getOptionalAttributes() {
return {};
}
int32_t SceneCodeParser::onParse(
Xml *node,
std::map<std::string, std::string> values,
struct SceneCode *out,
std::string *error
) {
// Get the type
std::string type = values["type"];
if(type == "properties") {
out->codeType = SCENE_CODE_TYPE_PROPERTIES;
} else if(type == "init") {
out->codeType = SCENE_CODE_TYPE_INIT;
} else if(type == "include") {
out->codeType = SCENE_CODE_TYPE_INCLUDE;
} else {
*error = "Invalid type '" + type + "' for SceneCode.";
return -1;
}
// Get the code
out->code = node->textContent;
return 0;
}

View File

@ -0,0 +1,33 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "util/XmlParser.hpp"
namespace Dawn {
enum SceneCodeType {
SCENE_CODE_TYPE_PROPERTIES,
SCENE_CODE_TYPE_INIT,
SCENE_CODE_TYPE_INCLUDE
};
struct SceneCode {
enum SceneCodeType codeType;
std::string code;
};
class SceneCodeParser : public XmlParser<struct SceneCode> {
public:
virtual std::vector<std::string> getRequiredAttributes();
virtual std::map<std::string, std::string> getOptionalAttributes();
virtual int32_t onParse(
Xml *node,
std::map<std::string, std::string> values,
struct SceneCode *out,
std::string *error
);
};
}

View File

@ -0,0 +1,80 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "SceneItemComponentParser.hpp"
using namespace Dawn;
std::vector<std::string> SceneItemComponentParser::getRequiredAttributes() {
return { };
}
std::map<std::string, std::string> SceneItemComponentParser::getOptionalAttributes() {
return { { "ref", "" } };
}
int32_t SceneItemComponentParser::onParse(
Xml *node,
std::map<std::string, std::string> values,
struct SceneItemComponent *out,
std::string *error
) {
// Check if values contains key "ref" and has a string of size more than 0
if(values.find("ref") != values.end() && values["ref"].size() > 0) {
out->ref = values["ref"];
}
// Get the ruleset.
auto ruleset = out->registry->getRuleset(node->node);
if(ruleset.name.size() == 0) {
*error = "Unknown prefab node type: " + node->node;
return 1;
}
out->include = ruleset.include;
out->type = node->node;
// Define parser types here.
// Iterate all the optional attributes and store within the out if present
auto itOptional = ruleset.optional.begin();
while(itOptional != ruleset.optional.end()) {
// Determine the parser to use
auto name = itOptional->first;
auto type = itOptional->second;
std::function<std::string(std::string, std::string*)> parser = rawParser;
try {
parser = parserFromTypeName(type);
} catch(std::string err) {
if(name[0] == '*') {
name = name.substr(1, name.size());
parser = rawParser;
} else {
*error = "Unknown parser for type: " + type + "::" + name;
return 1;
}
}
if(node->attributes.find(name) != node->attributes.end()) {
auto raw = node->attributes[name];
out->values[name] = parser(raw, error);
if(error->size() != 0) return 1;
}
++itOptional;
}
auto itInnerXml = ruleset.innerXml.begin();
while(itInnerXml != ruleset.innerXml.end()) {
auto name = itInnerXml->first;
auto type = itInnerXml->second;
out->values[name] = stringParser(node->innerXml, error);
if(error->size() != 0) return 1;
++itInnerXml;
}
return 0;
}

View File

@ -0,0 +1,30 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "util/parser/SceneItemComponentRegistry.hpp"
#include "util/parser/TypeParsers.hpp"
namespace Dawn {
struct SceneItemComponent {
struct SceneItemComponentRegistry *registry;
std::string include;
std::string type;
std::map<std::string, std::string> values;
std::string ref;
};
class SceneItemComponentParser : public XmlParser<struct SceneItemComponent> {
protected:
std::vector<std::string> getRequiredAttributes() override;
std::map<std::string, std::string> getOptionalAttributes() override;
int32_t onParse(
Xml *node,
std::map<std::string, std::string> values,
struct SceneItemComponent *out,
std::string *error
) override;
};
}

View File

@ -0,0 +1,203 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "SceneItemComponentRegistry.hpp"
using namespace Dawn;
struct SceneItemComponentRuleset SceneItemComponentRegistry::parseFile(
File *file,
std::string clazz
) {
assertNotNull(file, "SceneItemCOmponentRegistry::parseFile: File is null");
std::string data;
if(!file->readString(&data)) {
std::cout << "Failed to read ruleset file!" << std::endl;
return { .name = "" };
}
// Begin scanning contentsr
// std::string include = this->sources;
auto toRemove = File::normalizeSlashes(this->sources + FILE_PATH_SEP);
auto includePath = file->filename.substr(toRemove.size(), file->filename.size() - toRemove.size());
// Now locate the first subdir since we don't want to include the root path (e.g. dawn, dawnrose, etc)
auto firstSlash = includePath.find(FILE_PATH_SEP);
if(firstSlash != std::string::npos) {
includePath = includePath.substr(firstSlash + 1, includePath.size() - firstSlash - 1);
}
struct SceneItemComponentRuleset ruleset;
// Replace all file seps with slashes
size_t pos = 0;
while ((pos = includePath.find(FILE_PATH_SEP, pos)) != std::string::npos) {
includePath.replace(pos, 1, "/");
pos += 1;
}
ruleset.include = includePath;
ruleset.name = clazz;
std::regex_constants::syntax_option_type regexFlags;
#if defined(__GNUC__)
regexFlags = std::regex_constants::ECMAScript | std::regex_constants::multiline;
#else
regexFlags = std::regex_constants::ECMAScript;
#endif
// First, let's look for what class(es) this class extends
std::regex regexClassName("class\\s+\\w+\\s+:\\s+", regexFlags);
std::smatch match;
if(std::regex_search(data, match, regexClassName)) {
// Now find the next "{"
auto matchStart = match.position() + match.length();
auto openBracePos = data.find("{", matchStart);
if(openBracePos == std::string::npos) {
std::cout << "Failed to find open brace for class!" << std::endl;
return { .name = "" };
}
// Match each of the class names
std::regex regexClass("(public\\s+(\\w+))[,\\s{]+", regexFlags);
std::sregex_iterator itClass(data.begin() + matchStart, data.begin() + openBracePos, regexClass);
std::sregex_iterator endClass;
while(itClass != endClass) {
// Get the class name
auto className = itClass->str(2);
itClass++;
// We don't parse interface classes
if(className[0] == 'I') continue;
ruleset.extends.push_back(className);
}
}
// Find each instance of "@optional" when it's used within a comment
// e.g. // @optional or /* @optional */ in the string data.1
std::regex regex("^\\s*(?:\\/\\/|\\/\\*){1}\\s*\\@(optional|innerXml)\\s*(?:\\*\\/)?\\s*$", regexFlags);
std::sregex_iterator it(data.begin(), data.end(), regex);
std::sregex_iterator end;
while(it != end) {
// Extract the kind of parameter this is
std::smatch match;
if(!std::regex_search(data, match, regex)) {
std::cout << "Failed to determine parameter type!" << std::endl;
return { .name = "" };
}
std::string paramType = match[1].str();
// Find the next ";"
auto endPos = data.find(";", it->position() + it->length());
if(endPos == std::string::npos) {
std::cout << "Failed to find end of line for attribute!" << std::endl;
return { .name = "" };
}
// Go backwards see if there's an equals sign after the match but before endPos
auto equalsPos = data.rfind("=", endPos);
// If there's an equals sign, we search backwards from there,
// otherwise we search backwards from the ;
auto varStart = it->position() + it->length() + 1;
size_t lastSearchPos = (
(equalsPos == std::string::npos || equalsPos <= varStart) ?
endPos :
equalsPos
);
// Now we have our string
auto varLength = lastSearchPos - varStart;
auto variableString = data.substr(varStart, varLength);
// Now (should) be able to extract the type;
std::regex regex2("^\\s*(?:[\\S]+<)?([\\w*:_\\s]+)(?:[\\S]+)? (\\**[\\w]+)\\s*$", regexFlags);
std::smatch match2;
if(!std::regex_search(variableString, match2, regex2)) {
std::cout << "Failed to extract type and name from variable string! " << variableString << std::endl;
return { .name = "" };
}
// Now we have our type and name
auto type = match2[1].str();
auto name = match2[2].str();
// Now store depending on the type
if(paramType == "optional") {
ruleset.selfOptional[name] = type;
} else if(paramType == "innerXml") {
ruleset.innerXml[name] = type;
} else {
assertUnreachable("SceneItemComponentRegistry::parseFile: Unknown parameter type");
}
++it;
}
return ruleset;
}
struct SceneItemComponentRuleset SceneItemComponentRegistry::getRuleset(std::string type) {
if(this->rulesets.find(type) != this->rulesets.end()) {
return this->rulesets[type];
}
std::vector<std::string> pathsToScan;
pathsToScan.push_back(this->sources);
auto it = pathsToScan.begin();
while(it != pathsToScan.end()) {
auto str = *it;
pathsToScan.erase(it);
Directory dir = Directory(str);
auto children = dir.readDirectory();
auto itChildren = children.begin();
while(itChildren != children.end()) {
if(itChildren->second == DIRECTORY_CHILD_TYPE_DIRECTORY) {
pathsToScan.push_back(str + "/" + itChildren->first);
++itChildren;
continue;
}
if(itChildren->first != type+".hpp") {
++itChildren;
continue;
}
// Load ruleset
File file(str + "/" + itChildren->first);
auto parsed = SceneItemComponentRegistry::parseFile(&file, type);
if(parsed.name == "") {
std::cout << "Parsing error occured on " << type << std::endl;
return parsed;
}
// Update optionals
parsed.optional.insert(parsed.selfOptional.begin(), parsed.selfOptional.end());
// Recursively parse children
auto itExtends = parsed.extends.begin();
while(itExtends != parsed.extends.end()) {
auto ruleset = this->getRuleset(*itExtends);
if(ruleset.name == "") {
++itExtends;
continue;
}
// Merge ruleset
parsed.optional.insert(ruleset.optional.begin(), ruleset.optional.end());
++itExtends;
}
this->rulesets[type] = parsed;
return parsed;
}
it = pathsToScan.begin();
};
return { .name = "" };
}

View File

@ -0,0 +1,41 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "util/DawnTool.hpp"
#include "util/Directory.hpp"
#include "util/XmlParser.hpp"
#include "util/CodeGen.hpp"
#include "util/string.hpp"
#include <regex>
namespace Dawn {
struct SceneItemComponentRuleset {
std::string name;
std::string include;
std::map<std::string, std::string> selfOptional;
std::map<std::string, std::string> innerXml;
std::map<std::string, std::string> optional;
std::vector<std::string> extends;
};
struct SceneItemComponentRegistry {
std::string sources;
std::map<std::string, struct SceneItemComponentRuleset> rulesets;
struct SceneItemComponentRuleset parseFile(
File *file,
std::string clazz
);
/**
* Gets a prefab component ruleset for a specific scene item component type.
*
* @param type Scene Item Component Type to get ruleset for.
* @return The found ruleset, or an empty ruleset if not found.
*/
struct SceneItemComponentRuleset getRuleset(std::string type);
};
}

View File

@ -0,0 +1,152 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "SceneItemParser.hpp"
using namespace Dawn;
std::vector<std::string> SceneItemParser::getRequiredAttributes() {
return std::vector<std::string>();
}
std::map<std::string, std::string> SceneItemParser::getOptionalAttributes() {
return {
{ "ref", "" },
{ "position", "" },
{ "lookAt", "" },
{ "scale", "" },
{ "prefab", "" },
{ "alignment", "" },
{ "alignX", "" },
{ "aignY", "" },
{ "menuX", "" },
{ "menuY", "" },
{ "label", "" }
};
}
int32_t SceneItemParser::onParse(
Xml *node,
std::map<std::string, std::string> values,
struct SceneItem *out,
std::string *error
) {
out->ref = values["ref"];
if(values["position"].size() > 0) {
out->position = vec3Parser(values["position"], error);
if(error->size() > 0) return 1;
}
if(values["alignment"].size() > 0) {
out->alignment = vec4Parser(values["alignment"], error);
if(error->size() > 0) return 1;
}
if(values["alignX"].size() > 0) {
out->alignX = uiComponentAlignParser(values["alignX"], error);
if(error->size() > 0) return 1;
}
if(values["alignY"].size() > 0) {
out->alignY = uiComponentAlignParser(values["alignY"], error);
if(error->size() > 0) return 1;
}
if(values["menuX"].size() > 0) {
out->menuX = intParser(values["menuX"], error);
if(error->size() > 0) return 1;
}
if(values["menuY"].size() > 0) {
out->menuY = intParser(values["menuY"], error);
if(error->size() > 0) return 1;
}
if(values["scale"].size() > 0) {
out->scale = vec3Parser(values["scale"], error);
if(error->size() > 0) return 1;
}
if(values["label"].size() > 0) {
out->label = stringParser(values["label"], error);
if(error->size() > 0) return 1;
}
if(values["lookAt"].size() > 0) {
auto lookAtSplit = stringSplit(values["lookAt"], ",");
if(lookAtSplit.size() != 6) {
*error = "Invalid lookAt value: " + values["lookAt"];
return 1;
}
out->lookAtPosition = vec3Parser(lookAtSplit[0] + "," + lookAtSplit[1] + "," + lookAtSplit[2], error);
if(error->size() > 0) return 1;
out->lookAtTarget = vec3Parser(lookAtSplit[3] + "," + lookAtSplit[4] + "," + lookAtSplit[5], error);
if(error->size() > 0) return 1;
}
out->prefab = values["prefab"];
struct SceneItemDependency dep;
auto itChildren = node->childNodes.begin();
while(itChildren != node->childNodes.end()) {
if(itChildren->nodeType != XML_NODE_TYPE_ELEMENT) {
++itChildren;
continue;
}
// Parse child nodes, they may be components or not
auto c = itChildren->child;
if(c->node == "child" || c->node == "item") {
struct SceneItem child;
child.registry = out->registry;
auto ret = (SceneItemParser()).parse(c, &child, error);
if(ret != 0) return ret;
out->children.push_back(child);
// Add a dependency. This solves the reference order problem.
struct SceneItemDependency dep;
dep.type = SCENE_ITEM_DEPENDENCY_TYPE_ITEM;
dep.position = out->children.size() - 1;
out->dependencies.push_back(dep);
} else if(c->node == "asset") {
struct SceneAsset asset;
auto ret = (SceneAssetParser()).parse(c, &asset, error);
if(ret != 0) return ret;
out->assets.push_back(asset);
} else if(c->node == "code") {
struct SceneCode code;
auto ret = (SceneCodeParser()).parse(c, &code, error);
if(ret != 0) return ret;
out->code.push_back(code);
// Add Dep
dep.type = SCENE_ITEM_DEPENDENCY_TYPE_CODE;
dep.position = out->code.size() - 1;
out->dependencies.push_back(dep);
} else {
struct SceneItemComponent component;
component.registry = out->registry;
auto ret = (SceneItemComponentParser()).parse(c, &component, error);
if(ret != 0) return ret;
out->components.push_back(component);
// Add dep
struct SceneItemDependency dep;
dep.type = SCENE_ITEM_DEPENDENCY_TYPE_COMPONENT;
dep.position = out->components.size() - 1;
out->dependencies.push_back(dep);
}
++itChildren;
}
return 0;
}

View File

@ -0,0 +1,55 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "util/parser/SceneItemComponentParser.hpp"
#include "util/parser/SceneAssetParser.hpp"
#include "util/parser/SceneCodeParser.hpp"
namespace Dawn {
enum SceneItemDependencyType {
SCENE_ITEM_DEPENDENCY_TYPE_ITEM,
SCENE_ITEM_DEPENDENCY_TYPE_COMPONENT,
SCENE_ITEM_DEPENDENCY_TYPE_CODE,
};
struct SceneItemDependency {
enum SceneItemDependencyType type;
size_t position;
};
struct SceneItem {
struct SceneItemComponentRegistry *registry;
std::string ref;
std::string position;
std::string alignment;
std::string alignX;
std::string alignY;
std::string lookAtPosition;
std::string lookAtTarget;
std::string scale;
std::string prefab;
std::string menuX;
std::string menuY;
std::string label;
std::vector<struct SceneItemComponent> components;
std::vector<struct SceneItem> children;
std::vector<struct SceneAsset> assets;
std::vector<struct SceneCode> code;
std::vector<struct SceneItemDependency> dependencies;
};
class SceneItemParser : public XmlParser<struct SceneItem> {
protected:
std::vector<std::string> getRequiredAttributes() override;
std::map<std::string, std::string> getOptionalAttributes() override;
int32_t onParse(
Xml *node,
std::map<std::string, std::string> values,
struct SceneItem *out,
std::string *error
) override;
};
}

View File

@ -0,0 +1,78 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "SceneParser.hpp"
using namespace Dawn;
std::vector<std::string> SceneParser::getRequiredAttributes() {
return { "name" };
}
std::map<std::string, std::string> SceneParser::getOptionalAttributes() {
return {
{ "extend", "" }
};
}
int32_t SceneParser::onParse(
Xml *node,
std::map<std::string, std::string> values,
struct Scene *out,
std::string *error
) {
int32_t ret;
//Create the scene item
out->name = values["name"];
out->extend = values["extend"];
struct SceneItemDependency dep;
//Parse the children
auto itChildren = node->childNodes.begin();
while(itChildren != node->childNodes.end()) {
if(itChildren->nodeType != XML_NODE_TYPE_ELEMENT) {
++itChildren;
continue;
}
Xml *child = itChildren->child;
if(child->node == "asset") {
struct SceneAsset asset;
auto ret = (SceneAssetParser()).parse(child, &asset, error);
if(ret != 0) return ret;
out->assets.push_back(asset);
} else if(child->node == "item") {
struct SceneItem item;
item.registry = out->registry;
ret = (SceneItemParser()).parse(child, &item, error);
if(ret != 0) return 1;
out->items.push_back(item);
//Add the dependency
dep.type = SCENE_ITEM_DEPENDENCY_TYPE_ITEM;
dep.position = out->items.size() - 1;
out->dependencies.push_back(dep);
} else if(child->node == "code") {
struct SceneCode code;
ret = (SceneCodeParser()).parse(child, &code, error);
if(ret != 0) return ret;
out->code.push_back(code);
//Add the dependency
dep.type = SCENE_ITEM_DEPENDENCY_TYPE_CODE;
dep.position = out->code.size() - 1;
out->dependencies.push_back(dep);
}
++itChildren;
}
return 0;
}

View File

@ -0,0 +1,32 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "util/parser/SceneItemParser.hpp"
#include "util/parser/SceneCodeParser.hpp"
namespace Dawn {
struct Scene {
std::string name;
std::string extend;
std::vector<struct SceneItem> items;
std::vector<struct SceneCode> code;
std::vector<struct SceneAsset> assets;
struct SceneItemComponentRegistry *registry;
std::vector<struct SceneItemDependency> dependencies;
};
class SceneParser : public XmlParser<struct Scene> {
protected:
std::vector<std::string> getRequiredAttributes() override;
std::map<std::string, std::string> getOptionalAttributes() override;
int32_t onParse(
Xml *node,
std::map<std::string, std::string> values,
struct Scene *out,
std::string *error
) override;
};
}

View File

@ -0,0 +1,36 @@
/**
* Copyright (c) 2023 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dawnsharedlibs.hpp"
/**
* Remove all instances of a character from a C-Styled string.
*
* @param string String to remove characters from.
* @param remove Character to remove.
*/
static inline void stringRemoveAll(char *string, char remove) {
size_t len = strlen(string);
size_t i, j;
i = 0;
while(i < len) {
char c = string[i];
if(c != remove) {
i++;
continue;
}
j = i + 1;
while(j < len) {
string[j-1] = string[j];
j++;
}
len--;
}
}