Compare commits

...

21 Commits

Author SHA1 Message Date
5fa614ab83 [nostalgia/sample_project] Add Studio logo source to sample_project 2023-01-03 03:40:59 -06:00
7a4fbd1160 [gbastartup] Correct enabled languages in CMake 2023-01-03 03:39:53 -06:00
bb643bce42 [nostalgia] Add implementation of single tile sprites on OpenGL 2023-01-03 03:37:51 -06:00
cf6c05f4c6 [nostalgia/tools/pack] Fix for a non-compliant implementation of fstream 2023-01-03 03:33:54 -06:00
497bbc1a17 [nostalgia/studio] Update for new String beginsWith API 2023-01-03 03:32:16 -06:00
3cd638737a [ox] Update more C strings to StringViews 2023-01-03 03:31:22 -06:00
5508dc5dc0 [ox/std] Make String::beginsWtih and endsWith functions that take StringViews 2023-01-03 03:30:33 -06:00
03378ebe43 [nostalgia/core/gba] Fix buttonDown to return false for non-gamepad buttons 2023-01-03 00:13:40 -06:00
2669aafe81 [nostalgia] Cleanup frequent allocations and const correctness in project explorer 2023-01-01 15:27:28 -06:00
679226ef73 [nostalgia] Replace C strings with ox::StringView 2022-12-31 17:14:43 -06:00
55ea405a54 [ox/std] Add toStdStringView and endsWith functions 2022-12-31 17:14:09 -06:00
6cfa8dd40d [nostalgia] Replace unnecessary ox::Strings with ox::StringViews 2022-12-31 15:48:47 -06:00
5a09918b64 [nostalgia/studio] Add logo 2022-12-31 15:28:04 -06:00
2c2fce2c5a [nostalgia] Replace C strings with StringViews 2022-12-31 15:03:04 -06:00
ca07dc6152 [ox] Replace C strings in FS with StringView 2022-12-31 14:58:35 -06:00
5cae7cbd24 [nostalgia/studio] Cleanup 2022-12-30 18:19:39 -06:00
887d3b3d13 [ox/std] Remove some unnecessary/breaking checks in BasicString::set functions 2022-12-29 02:12:11 -06:00
b08559b3f3 [ox/model] MSVC fix 2022-12-19 00:19:28 -06:00
c9f1b3aaa3 [ox/claw] MSVC fix 2022-12-18 23:27:59 -06:00
907393dfe6 [nostalgia/studio] Fix MSVC issue 2022-12-18 23:18:24 -06:00
bd192ba47a [nostalgia/studio] Fix FDFilterItem to work on Windows 2022-12-18 21:56:30 -06:00
84 changed files with 828 additions and 546 deletions

View File

@ -1,4 +1,4 @@
enable_language(C ASM) enable_language(CXX ASM)
add_library( add_library(
GbaStartup OBJECT GbaStartup OBJECT

View File

@ -37,32 +37,32 @@ ClArgs::ClArgs(int argc, const char **args) noexcept {
} }
} }
bool ClArgs::getBool(const char *arg, bool defaultValue) const noexcept { bool ClArgs::getBool(ox::CRStringView arg, bool defaultValue) const noexcept {
auto [value, err] = m_ints.at(arg); auto [value, err] = m_ints.at(arg);
return !err ? *value : defaultValue; return !err ? *value : defaultValue;
} }
String ClArgs::getString(const char *arg, const char *defaultValue) const noexcept { String ClArgs::getString(ox::CRStringView arg, const char *defaultValue) const noexcept {
auto [value, err] = m_strings.at(arg); auto [value, err] = m_strings.at(arg);
return !err ? *value : defaultValue; return !err ? *value : defaultValue;
} }
int ClArgs::getInt(const char *arg, int defaultValue) const noexcept { int ClArgs::getInt(ox::CRStringView arg, int defaultValue) const noexcept {
auto [value, err] = m_ints.at(arg); auto [value, err] = m_ints.at(arg);
return !err ? *value : defaultValue; return !err ? *value : defaultValue;
} }
Result<bool> ClArgs::getBool(const char *arg) const noexcept { Result<bool> ClArgs::getBool(ox::CRStringView arg) const noexcept {
oxRequire(out, m_bools.at(arg)); oxRequire(out, m_bools.at(arg));
return *out; return *out;
} }
Result<String> ClArgs::getString(const char *argName) const noexcept { Result<String> ClArgs::getString(ox::CRStringView argName) const noexcept {
oxRequire(out, m_strings.at(argName)); oxRequire(out, m_strings.at(argName));
return *out; return *out;
} }
Result<int> ClArgs::getInt(const char *arg) const noexcept { Result<int> ClArgs::getInt(ox::CRStringView arg) const noexcept {
oxRequire(out, m_ints.at(arg)); oxRequire(out, m_ints.at(arg));
return *out; return *out;
} }

View File

@ -22,17 +22,20 @@ class ClArgs {
public: public:
ClArgs(int argc, const char **args) noexcept; ClArgs(int argc, const char **args) noexcept;
bool getBool(const char *arg, bool defaultValue) const noexcept; [[nodiscard]]
bool getBool(ox::CRStringView arg, bool defaultValue) const noexcept;
String getString(const char *argName, const char *defaultArg) const noexcept; [[nodiscard]]
String getString(ox::CRStringView argName, const char *defaultArg) const noexcept;
int getInt(const char *arg, int defaultValue) const noexcept; [[nodiscard]]
int getInt(ox::CRStringView arg, int defaultValue) const noexcept;
Result<bool> getBool(const char *arg) const noexcept; Result<bool> getBool(ox::CRStringView arg) const noexcept;
Result<String> getString(const char *argName) const noexcept; Result<String> getString(ox::CRStringView argName) const noexcept;
Result<int> getInt(const char *arg) const noexcept; Result<int> getInt(ox::CRStringView arg) const noexcept;
}; };

View File

@ -74,7 +74,7 @@ Result<Buffer> stripClawHeader(const ox::Buffer &buff) noexcept {
Result<ModelObject> readClaw(TypeStore *ts, const Buffer &buff) noexcept { Result<ModelObject> readClaw(TypeStore *ts, const Buffer &buff) noexcept {
oxRequire(header, readClawHeader(buff)); oxRequire(header, readClawHeader(buff));
oxRequire(t, ts->template getLoad(header.typeName, header.typeVersion, header.typeParams)); oxRequire(t, ts->getLoad(header.typeName, header.typeVersion, header.typeParams));
ModelObject obj; ModelObject obj;
oxReturnError(obj.setType(t)); oxReturnError(obj.setType(t));
switch (header.fmt) { switch (header.fmt) {

View File

@ -158,7 +158,7 @@ Error Directory<FileStore, InodeId_t>::mkdir(PathIterator path, bool parents, Fi
// determine if already exists // determine if already exists
auto name = nameBuff; auto name = nameBuff;
oxReturnError(path.get(name)); oxReturnError(path.get(name));
auto childInode = find(name->c_str()); auto childInode = find(PathIterator(*name));
if (!childInode.ok()) { if (!childInode.ok()) {
// if this is not the last item in the path and parents is disabled, // if this is not the last item in the path and parents is disabled,
// return an error // return an error
@ -174,7 +174,7 @@ Error Directory<FileStore, InodeId_t>::mkdir(PathIterator path, bool parents, Fi
Directory<FileStore, InodeId_t> child(m_fs, childInode.value); Directory<FileStore, InodeId_t> child(m_fs, childInode.value);
oxReturnError(child.init()); oxReturnError(child.init());
auto err = write(name->c_str(), childInode.value); auto err = write(PathIterator(*name), childInode.value);
if (err) { if (err) {
oxLogError(err); oxLogError(err);
// could not index the directory, delete it // could not index the directory, delete it
@ -202,16 +202,16 @@ Error Directory<FileStore, InodeId_t>::write(PathIterator path, InodeId_t inode,
if (path.next().hasNext()) { // not yet at target directory, recurse to next one if (path.next().hasNext()) { // not yet at target directory, recurse to next one
oxReturnError(path.get(name)); oxReturnError(path.get(name));
oxTracef("ox::fs::Directory::write", "Attempting to write to next sub-Directory: {} of {}", oxTracef("ox::fs::Directory::write", "Attempting to write to next sub-Directory: {} of {}",
name->c_str(), path.fullPath()); *name, path.fullPath());
oxRequire(nextChild, findEntry(*name)); oxRequire(nextChild, findEntry(*name));
oxTracef("ox::fs::Directory::write", "{}: {}", name->c_str(), nextChild); oxTracef("ox::fs::Directory::write", "{}: {}", *name, nextChild);
if (nextChild) { if (nextChild) {
// reuse name because it is a rather large variable and will not be used again // reuse name because it is a rather large variable and will not be used again
// be attentive that this remains true // be attentive that this remains true
name = nullptr; name = nullptr;
return Directory(m_fs, nextChild).write(path.next(), inode, nameBuff); return Directory(m_fs, nextChild).write(path.next(), inode, nameBuff);
} else { } else {
oxTracef("ox::fs::Directory::write", "{} not found and not allowed to create it.", name->c_str()); oxTracef("ox::fs::Directory::write", "{} not found and not allowed to create it.", *name);
return OxError(1, "File not found and not allowed to create it."); return OxError(1, "File not found and not allowed to create it.");
} }
} else { } else {
@ -262,14 +262,14 @@ Error Directory<FileStore, InodeId_t>::remove(PathIterator path, FileName *nameB
auto &name = *nameBuff; auto &name = *nameBuff;
oxReturnError(path.get(&name)); oxReturnError(path.get(&name));
oxTrace("ox::fs::Directory::remove", name.c_str()); oxTrace("ox::fs::Directory::remove", name);
auto buff = m_fs.read(m_inodeId).template to<Buffer>(); auto buff = m_fs.read(m_inodeId).template to<Buffer>();
if (buff.valid()) { if (buff.valid()) {
oxTrace("ox::fs::Directory::remove", "Found directory buffer."); oxTrace("ox::fs::Directory::remove", "Found directory buffer.");
for (auto i = buff->iterator(); i.valid(); i.next()) { for (auto i = buff->iterator(); i.valid(); i.next()) {
auto data = i->data(); auto data = i->data();
if (data.valid()) { if (data.valid()) {
if (ox_strncmp(data->name, name.c_str(), name.len()) == 0) { if (data->name == name) {
oxReturnError(buff->free(i)); oxReturnError(buff->free(i));
} }
} else { } else {
@ -308,7 +308,7 @@ Error Directory<FileStore, InodeId_t>::ls(F cb) noexcept {
template<typename FileStore, typename InodeId_t> template<typename FileStore, typename InodeId_t>
Result<typename FileStore::InodeId_t> Directory<FileStore, InodeId_t>::findEntry(const FileName &name) const noexcept { Result<typename FileStore::InodeId_t> Directory<FileStore, InodeId_t>::findEntry(const FileName &name) const noexcept {
oxTrace("ox::fs::Directory::findEntry", name.c_str()); oxTrace("ox::fs::Directory::findEntry", name);
auto buff = m_fs.read(m_inodeId).template to<Buffer>(); auto buff = m_fs.read(m_inodeId).template to<Buffer>();
if (!buff.valid()) { if (!buff.valid()) {
oxTrace("ox::fs::Directory::findEntry::fail", "Could not findEntry directory buffer"); oxTrace("ox::fs::Directory::findEntry::fail", "Could not findEntry directory buffer");
@ -318,9 +318,9 @@ Result<typename FileStore::InodeId_t> Directory<FileStore, InodeId_t>::findEntry
for (auto i = buff->iterator(); i.valid(); i.next()) { for (auto i = buff->iterator(); i.valid(); i.next()) {
auto data = i->data(); auto data = i->data();
if (data.valid()) { if (data.valid()) {
oxTracef("ox::fs::Directory::findEntry", "Comparing \"{}\" to \"{}\"", name.c_str(), data->name); oxTracef("ox::fs::Directory::findEntry", "Comparing \"{}\" to \"{}\"", name, data->name);
if (ox_strncmp(data->name, name.c_str(), MaxFileNameLength) == 0) { if (data->name == name) {
oxTracef("ox::fs::Directory::findEntry", "\"{}\" match found.", name.c_str()); oxTracef("ox::fs::Directory::findEntry", "\"{}\" match found.", name);
return static_cast<InodeId_t>(data->inode); return static_cast<InodeId_t>(data->inode);
} }
} else { } else {
@ -342,7 +342,7 @@ Result<typename FileStore::InodeId_t> Directory<FileStore, InodeId_t>::find(Path
auto name = nameBuff; auto name = nameBuff;
oxReturnError(path.get(name)); oxReturnError(path.get(name));
oxRequire(v, findEntry(name->c_str())); oxRequire(v, findEntry(*name));
// recurse if not at end of path // recurse if not at end of path
if (auto p = path.next(); p.valid()) { if (auto p = path.next(); p.valid()) {
Directory dir(m_fs, v); Directory dir(m_fs, v);

View File

@ -43,15 +43,6 @@ FileAddress::FileAddress(char *path) noexcept {
m_type = FileAddressType::Path; m_type = FileAddressType::Path;
} }
FileAddress::FileAddress(const char *path) noexcept {
m_data.constPath = path;
m_type = FileAddressType::ConstPath;
}
FileAddress::~FileAddress() noexcept {
cleanup();
}
FileAddress &FileAddress::operator=(const FileAddress &other) noexcept { FileAddress &FileAddress::operator=(const FileAddress &other) noexcept {
if (this == &other) { if (this == &other) {
return *this; return *this;
@ -107,16 +98,4 @@ bool FileAddress::operator==(CRStringView path) const noexcept {
return path == p; return path == p;
} }
void FileAddress::cleanup() noexcept {
if (m_type == FileAddressType::Path) {
safeDeleteArray(m_data.path);
clear();
}
}
void FileAddress::clear() noexcept {
m_data.path = nullptr;
m_type = FileAddressType::None;
}
} }

View File

@ -57,17 +57,17 @@ class FileAddress {
FileAddress(uint64_t inode) noexcept; FileAddress(uint64_t inode) noexcept;
FileAddress(CRStringView path) noexcept; explicit FileAddress(CRStringView path) noexcept;
template<std::size_t SmallStrSz> template<std::size_t SmallStrSz>
FileAddress(const ox::BasicString<SmallStrSz> &path) noexcept: FileAddress(StringView(path)) { explicit FileAddress(const ox::BasicString<SmallStrSz> &path) noexcept: FileAddress(StringView(path)) {
} }
FileAddress(char *path) noexcept; explicit FileAddress(char *path) noexcept;
FileAddress(const char *path) noexcept; explicit constexpr FileAddress(const char *path) noexcept;
~FileAddress() noexcept; constexpr ~FileAddress() noexcept;
FileAddress &operator=(const FileAddress &other) noexcept; FileAddress &operator=(const FileAddress &other) noexcept;
@ -115,15 +115,24 @@ class FileAddress {
/** /**
* Cleanup memory allocations. * Cleanup memory allocations.
*/ */
void cleanup() noexcept; constexpr void cleanup() noexcept;
/** /**
* Clears fields, but does not delete allocations. * Clears fields, but does not delete allocations.
*/ */
void clear() noexcept; constexpr void clear() noexcept;
}; };
constexpr FileAddress::FileAddress(const char *path) noexcept {
m_data.constPath = path;
m_type = FileAddressType::ConstPath;
}
constexpr FileAddress::~FileAddress() noexcept {
cleanup();
}
template<> template<>
constexpr const char *getModelTypeName<FileAddress::Data>() noexcept { constexpr const char *getModelTypeName<FileAddress::Data>() noexcept {
return FileAddress::Data::TypeName; return FileAddress::Data::TypeName;
@ -163,4 +172,16 @@ constexpr Error model(T *io, CommonPtrWith<FileAddress> auto *fa) noexcept {
return OxError(0); return OxError(0);
} }
constexpr void FileAddress::cleanup() noexcept {
if (m_type == FileAddressType::Path) {
safeDeleteArray(m_data.path);
clear();
}
}
constexpr void FileAddress::clear() noexcept {
m_data.path = nullptr;
m_type = FileAddressType::None;
}
} }

View File

@ -19,7 +19,7 @@ Result<const char*> FileSystem::directAccess(const FileAddress &addr) noexcept {
return directAccess(addr.getInode().value); return directAccess(addr.getInode().value);
case FileAddressType::ConstPath: case FileAddressType::ConstPath:
case FileAddressType::Path: case FileAddressType::Path:
return directAccess(addr.getPath().value); return directAccess(StringView(addr.getPath().value));
default: default:
return OxError(1); return OxError(1);
} }
@ -31,7 +31,7 @@ Error FileSystem::read(const FileAddress &addr, void *buffer, std::size_t size)
return read(addr.getInode().value, buffer, size); return read(addr.getInode().value, buffer, size);
case FileAddressType::ConstPath: case FileAddressType::ConstPath:
case FileAddressType::Path: case FileAddressType::Path:
return read(addr.getPath().value, buffer, size); return read(StringView(addr.getPath().value), buffer, size);
default: default:
return OxError(1); return OxError(1);
} }
@ -44,29 +44,32 @@ Result<Buffer> FileSystem::read(const FileAddress &addr) noexcept {
return buff; return buff;
} }
Result<Buffer> FileSystem::read(CRStringView path) noexcept {
oxRequire(s, stat(path));
Buffer buff(s.size);
oxReturnError(read(path, buff.data(), buff.size()));
return buff;
}
Error FileSystem::read(const FileAddress &addr, std::size_t readStart, std::size_t readSize, void *buffer, std::size_t *size) noexcept { Error FileSystem::read(const FileAddress &addr, std::size_t readStart, std::size_t readSize, void *buffer, std::size_t *size) noexcept {
switch (addr.type()) { switch (addr.type()) {
case FileAddressType::Inode: case FileAddressType::Inode:
return read(addr.getInode().value, readStart, readSize, buffer, size); return read(addr.getInode().value, readStart, readSize, buffer, size);
case FileAddressType::ConstPath: case FileAddressType::ConstPath:
case FileAddressType::Path: case FileAddressType::Path:
return read(addr.getPath().value, readStart, readSize, buffer, size); return OxError(2, "Unsupported for path lookups");
default: default:
return OxError(1); return OxError(1);
} }
} }
Result<Vector<String>> FileSystem::ls(const String &dir) const noexcept {
return ls(dir.c_str());
}
Error FileSystem::remove(const FileAddress &addr, bool recursive) noexcept { Error FileSystem::remove(const FileAddress &addr, bool recursive) noexcept {
switch (addr.type()) { switch (addr.type()) {
case FileAddressType::Inode: case FileAddressType::Inode:
return remove(addr.getInode().value, recursive); return remove(addr.getInode().value, recursive);
case FileAddressType::ConstPath: case FileAddressType::ConstPath:
case FileAddressType::Path: case FileAddressType::Path:
return remove(addr.getPath().value, recursive); return remove(StringView(addr.getPath().value), recursive);
default: default:
return OxError(1); return OxError(1);
} }
@ -78,7 +81,7 @@ Error FileSystem::write(const FileAddress &addr, const void *buffer, uint64_t si
return write(addr.getInode().value, buffer, size, fileType); return write(addr.getInode().value, buffer, size, fileType);
case FileAddressType::ConstPath: case FileAddressType::ConstPath:
case FileAddressType::Path: case FileAddressType::Path:
return write(addr.getPath().value, buffer, size, fileType); return write(StringView(addr.getPath().value), buffer, size, fileType);
default: default:
return OxError(1); return OxError(1);
} }
@ -90,7 +93,7 @@ Result<FileStat> FileSystem::stat(const FileAddress &addr) const noexcept {
return stat(addr.getInode().value); return stat(addr.getInode().value);
case FileAddressType::ConstPath: case FileAddressType::ConstPath:
case FileAddressType::Path: case FileAddressType::Path:
return stat(addr.getPath().value); return stat(StringView(addr.getPath().value));
default: default:
return OxError(1); return OxError(1);
} }

View File

@ -29,18 +29,18 @@ class FileSystem {
public: public:
virtual ~FileSystem() noexcept = default; virtual ~FileSystem() noexcept = default;
virtual Error mkdir(const char *path, bool recursive) noexcept = 0; virtual Error mkdir(CRStringView path, bool recursive) noexcept = 0;
/** /**
* Moves an entry from one directory to another. * Moves an entry from one directory to another.
* @param src the path to the file * @param src the path to the file
* @param dest the path of the destination directory * @param dest the path of the destination directory
*/ */
virtual Error move(const char *src, const char *dest) noexcept = 0; virtual Error move(CRStringView src, CRStringView dest) noexcept = 0;
virtual Error read(const char *path, void *buffer, std::size_t buffSize) noexcept = 0; virtual Error read(CRStringView path, void *buffer, std::size_t buffSize) noexcept = 0;
virtual Result<const char*> directAccess(const char *path) noexcept = 0; virtual Result<const char*> directAccess(CRStringView path) noexcept = 0;
virtual Error read(uint64_t inode, void *buffer, std::size_t size) noexcept = 0; virtual Error read(uint64_t inode, void *buffer, std::size_t size) noexcept = 0;
@ -52,38 +52,48 @@ class FileSystem {
Result<Buffer> read(const FileAddress &addr) noexcept; Result<Buffer> read(const FileAddress &addr) noexcept;
Result<Buffer> read(CRStringView path) noexcept;
Error read(const FileAddress &addr, std::size_t readStart, std::size_t readSize, void *buffer, std::size_t *size) noexcept; Error read(const FileAddress &addr, std::size_t readStart, std::size_t readSize, void *buffer, std::size_t *size) noexcept;
[[maybe_unused]] [[maybe_unused]]
Result<const char*> directAccess(const FileAddress &addr) noexcept; Result<const char*> directAccess(const FileAddress &addr) noexcept;
Result<Vector<String>> ls(const String &dir) const noexcept; virtual Result<Vector<String>> ls(CRStringView dir) const noexcept = 0;
virtual Result<Vector<String>> ls(const char *dir) const noexcept = 0; virtual Error remove(CRStringView path, bool recursive) noexcept = 0;
virtual Error remove(const char *path, bool recursive) noexcept = 0;
Error remove(const FileAddress &addr, bool recursive = false) noexcept; Error remove(const FileAddress &addr, bool recursive = false) noexcept;
virtual Error resize(uint64_t size, void *buffer) noexcept = 0; virtual Error resize(uint64_t size, void *buffer) noexcept = 0;
virtual Error write(const char *path, const void *buffer, uint64_t size, FileType fileType) noexcept = 0; virtual Error write(CRStringView path, const void *buffer, uint64_t size, FileType fileType) noexcept = 0;
virtual Error write(uint64_t inode, const void *buffer, uint64_t size, FileType fileType) noexcept = 0; virtual Error write(uint64_t inode, const void *buffer, uint64_t size, FileType fileType) noexcept = 0;
Error write(CRStringView path, const void *buffer, uint64_t size) noexcept {
return write(path, buffer, size, FileType::NormalFile);
}
Error write(uint64_t inode, const void *buffer, uint64_t size) noexcept {
return write(inode, buffer, size, FileType::NormalFile);
}
Error write(const FileAddress &addr, const void *buffer, uint64_t size, FileType fileType = FileType::NormalFile) noexcept; Error write(const FileAddress &addr, const void *buffer, uint64_t size, FileType fileType = FileType::NormalFile) noexcept;
virtual Result<FileStat> stat(uint64_t inode) const noexcept = 0; virtual Result<FileStat> stat(uint64_t inode) const noexcept = 0;
virtual Result<FileStat> stat(const char *path) const noexcept = 0; virtual Result<FileStat> stat(CRStringView path) const noexcept = 0;
Result<FileStat> stat(const FileAddress &addr) const noexcept; Result<FileStat> stat(const FileAddress &addr) const noexcept;
[[nodiscard]] [[nodiscard]]
virtual uint64_t spaceNeeded(uint64_t size) const noexcept = 0; virtual uint64_t spaceNeeded(uint64_t size) const noexcept = 0;
[[nodiscard]]
virtual Result<uint64_t> available() const noexcept = 0; virtual Result<uint64_t> available() const noexcept = 0;
[[nodiscard]]
virtual Result<uint64_t> size() const noexcept = 0; virtual Result<uint64_t> size() const noexcept = 0;
[[nodiscard]] [[nodiscard]]
@ -125,13 +135,13 @@ class FileSystemTemplate: public FileSystem {
static Error format(void *buff, uint64_t buffSize) noexcept; static Error format(void *buff, uint64_t buffSize) noexcept;
Error mkdir(const char *path, bool recursive) noexcept override; Error mkdir(CRStringView path, bool recursive) noexcept override;
Error move(const char *src, const char *dest) noexcept override; Error move(CRStringView src, CRStringView dest) noexcept override;
Error read(const char *path, void *buffer, std::size_t buffSize) noexcept override; Error read(CRStringView path, void *buffer, std::size_t buffSize) noexcept override;
Result<const char*> directAccess(const char*) noexcept override; Result<const char*> directAccess(CRStringView) noexcept override;
Error read(uint64_t inode, void *buffer, std::size_t size) noexcept override; Error read(uint64_t inode, void *buffer, std::size_t size) noexcept override;
@ -139,12 +149,12 @@ class FileSystemTemplate: public FileSystem {
Result<const char*> directAccess(uint64_t) noexcept override; Result<const char*> directAccess(uint64_t) noexcept override;
Result<Vector<String>> ls(const char *path) const noexcept override; Result<Vector<String>> ls(CRStringView dir) const noexcept override;
template<typename F> template<typename F>
Error ls(const char *path, F cb) const; Error ls(CRStringView path, F cb) const;
Error remove(const char *path, bool recursive) noexcept override; Error remove(CRStringView path, bool recursive) noexcept override;
/** /**
* Resizes FileSystem to minimum possible size. * Resizes FileSystem to minimum possible size.
@ -153,13 +163,13 @@ class FileSystemTemplate: public FileSystem {
Error resize(uint64_t size, void *buffer) noexcept override; Error resize(uint64_t size, void *buffer) noexcept override;
Error write(const char *path, const void *buffer, uint64_t size, FileType fileType) noexcept override; Error write(CRStringView path, const void *buffer, uint64_t size, FileType fileType) noexcept override;
Error write(uint64_t inode, const void *buffer, uint64_t size, FileType fileType) noexcept override; Error write(uint64_t inode, const void *buffer, uint64_t size, FileType fileType) noexcept override;
Result<FileStat> stat(uint64_t inode) const noexcept override; Result<FileStat> stat(uint64_t inode) const noexcept override;
Result<FileStat> stat(const char *path) const noexcept override; Result<FileStat> stat(CRStringView path) const noexcept override;
uint64_t spaceNeeded(uint64_t size) const noexcept override; uint64_t spaceNeeded(uint64_t size) const noexcept override;
@ -180,7 +190,7 @@ class FileSystemTemplate: public FileSystem {
/** /**
* Finds the inode ID at the given path. * Finds the inode ID at the given path.
*/ */
Result<uint64_t> find(const char *path) const noexcept; Result<uint64_t> find(CRStringView path) const noexcept;
Result<Directory> rootDir() const noexcept; Result<Directory> rootDir() const noexcept;
@ -227,14 +237,14 @@ Error FileSystemTemplate<FileStore, Directory>::format(void *buff, uint64_t buff
} }
template<typename FileStore, typename Directory> template<typename FileStore, typename Directory>
Error FileSystemTemplate<FileStore, Directory>::mkdir(const char *path, bool recursive) noexcept { Error FileSystemTemplate<FileStore, Directory>::mkdir(CRStringView path, bool recursive) noexcept {
oxTracef("ox::fs::FileSystemTemplate::mkdir", "path: {}, recursive: {}", path, recursive); oxTracef("ox::fs::FileSystemTemplate::mkdir", "path: {}, recursive: {}", path, recursive);
oxRequireM(rootDir, this->rootDir()); oxRequireM(rootDir, this->rootDir());
return rootDir.mkdir(path, recursive); return rootDir.mkdir(path, recursive);
} }
template<typename FileStore, typename Directory> template<typename FileStore, typename Directory>
Error FileSystemTemplate<FileStore, Directory>::move(const char *src, const char *dest) noexcept { Error FileSystemTemplate<FileStore, Directory>::move(CRStringView src, CRStringView dest) noexcept {
oxRequire(fd, fileSystemData()); oxRequire(fd, fileSystemData());
Directory rootDir(m_fs, fd.rootDirInode); Directory rootDir(m_fs, fd.rootDirInode);
oxRequireM(inode, rootDir.find(src)); oxRequireM(inode, rootDir.find(src));
@ -244,7 +254,7 @@ Error FileSystemTemplate<FileStore, Directory>::move(const char *src, const char
} }
template<typename FileStore, typename Directory> template<typename FileStore, typename Directory>
Error FileSystemTemplate<FileStore, Directory>::read(const char *path, void *buffer, std::size_t buffSize) noexcept { Error FileSystemTemplate<FileStore, Directory>::read(CRStringView path, void *buffer, std::size_t buffSize) noexcept {
oxRequire(fd, fileSystemData()); oxRequire(fd, fileSystemData());
Directory rootDir(m_fs, fd.rootDirInode); Directory rootDir(m_fs, fd.rootDirInode);
oxRequire(inode, rootDir.find(path)); oxRequire(inode, rootDir.find(path));
@ -252,7 +262,7 @@ Error FileSystemTemplate<FileStore, Directory>::read(const char *path, void *buf
} }
template<typename FileStore, typename Directory> template<typename FileStore, typename Directory>
Result<const char*> FileSystemTemplate<FileStore, Directory>::directAccess(const char *path) noexcept { Result<const char*> FileSystemTemplate<FileStore, Directory>::directAccess(CRStringView path) noexcept {
oxRequire(fd, fileSystemData()); oxRequire(fd, fileSystemData());
Directory rootDir(m_fs, fd.rootDirInode); Directory rootDir(m_fs, fd.rootDirInode);
oxRequire(inode, rootDir.find(path)); oxRequire(inode, rootDir.find(path));
@ -273,15 +283,15 @@ template<typename FileStore, typename Directory>
Result<const char*> FileSystemTemplate<FileStore, Directory>::directAccess(uint64_t inode) noexcept { Result<const char*> FileSystemTemplate<FileStore, Directory>::directAccess(uint64_t inode) noexcept {
auto data = m_fs.read(inode); auto data = m_fs.read(inode);
if (!data.valid()) { if (!data.valid()) {
return OxError(1); return OxError(1, "Data not valid");
} }
return reinterpret_cast<char*>(data.get()); return reinterpret_cast<char*>(data.get());
} }
template<typename FileStore, typename Directory> template<typename FileStore, typename Directory>
Result<Vector<String>> FileSystemTemplate<FileStore, Directory>::ls(const char *path) const noexcept { Result<Vector<String>> FileSystemTemplate<FileStore, Directory>::ls(CRStringView path) const noexcept {
Vector<String> out; Vector<String> out;
oxReturnError(ls(path, [&out](const char *name, typename FileStore::InodeId_t) { oxReturnError(ls(path, [&out](CRStringView name, typename FileStore::InodeId_t) {
out.emplace_back(name); out.emplace_back(name);
return OxError(0); return OxError(0);
})); }));
@ -290,7 +300,7 @@ Result<Vector<String>> FileSystemTemplate<FileStore, Directory>::ls(const char *
template<typename FileStore, typename Directory> template<typename FileStore, typename Directory>
template<typename F> template<typename F>
Error FileSystemTemplate<FileStore, Directory>::ls(const char *path, F cb) const { Error FileSystemTemplate<FileStore, Directory>::ls(CRStringView path, F cb) const {
oxTracef("ox::fs::FileSystemTemplate::ls", "path: {}", path); oxTracef("ox::fs::FileSystemTemplate::ls", "path: {}", path);
oxRequire(s, stat(path)); oxRequire(s, stat(path));
Directory dir(m_fs, s.inode); Directory dir(m_fs, s.inode);
@ -298,7 +308,7 @@ Error FileSystemTemplate<FileStore, Directory>::ls(const char *path, F cb) const
} }
template<typename FileStore, typename Directory> template<typename FileStore, typename Directory>
Error FileSystemTemplate<FileStore, Directory>::remove(const char *path, bool recursive) noexcept { Error FileSystemTemplate<FileStore, Directory>::remove(CRStringView path, bool recursive) noexcept {
oxRequire(fd, fileSystemData()); oxRequire(fd, fileSystemData());
Directory rootDir(m_fs, fd.rootDirInode); Directory rootDir(m_fs, fd.rootDirInode);
oxRequire(inode, rootDir.find(path)); oxRequire(inode, rootDir.find(path));
@ -328,7 +338,7 @@ Error FileSystemTemplate<FileStore, Directory>::resize(uint64_t size, void *buff
} }
template<typename FileStore, typename Directory> template<typename FileStore, typename Directory>
Error FileSystemTemplate<FileStore, Directory>::write(const char *path, const void *buffer, uint64_t size, FileType fileType) noexcept { Error FileSystemTemplate<FileStore, Directory>::write(CRStringView path, const void *buffer, uint64_t size, FileType fileType) noexcept {
auto [inode, err] = find(path); auto [inode, err] = find(path);
if (err) { if (err) {
oxRequire(generatedId, m_fs.generateInodeId()); oxRequire(generatedId, m_fs.generateInodeId());
@ -357,7 +367,7 @@ Result<FileStat> FileSystemTemplate<FileStore, Directory>::stat(uint64_t inode)
} }
template<typename FileStore, typename Directory> template<typename FileStore, typename Directory>
Result<FileStat> FileSystemTemplate<FileStore, Directory>::stat(const char *path) const noexcept { Result<FileStat> FileSystemTemplate<FileStore, Directory>::stat(CRStringView path) const noexcept {
oxRequire(inode, find(path)); oxRequire(inode, find(path));
return stat(inode); return stat(inode);
} }
@ -400,10 +410,10 @@ Result<typename FileSystemTemplate<FileStore, Directory>::FileSystemData> FileSy
} }
template<typename FileStore, typename Directory> template<typename FileStore, typename Directory>
Result<uint64_t> FileSystemTemplate<FileStore, Directory>::find(const char *path) const noexcept { Result<uint64_t> FileSystemTemplate<FileStore, Directory>::find(CRStringView path) const noexcept {
oxRequire(fd, fileSystemData()); oxRequire(fd, fileSystemData());
// return root as a special case // return root as a special case
if (ox_strcmp(path, "/") == 0) { if (path == "/") {
return static_cast<uint64_t>(fd.rootDirInode); return static_cast<uint64_t>(fd.rootDirInode);
} }
Directory rootDir(m_fs, fd.rootDirInode); Directory rootDir(m_fs, fd.rootDirInode);

View File

@ -13,25 +13,25 @@
#if defined(OX_HAS_PASSTHROUGHFS) #if defined(OX_HAS_PASSTHROUGHFS)
#include <fstream> #include <fstream>
#include <string_view>
namespace ox { namespace ox {
PassThroughFS::PassThroughFS(const char *dirPath) { PassThroughFS::PassThroughFS(CRStringView dirPath) {
m_path = dirPath; m_path = std::string_view(dirPath.data(), dirPath.bytes());
} }
PassThroughFS::~PassThroughFS() noexcept { PassThroughFS::~PassThroughFS() noexcept = default;
}
String PassThroughFS::basePath() const noexcept { String PassThroughFS::basePath() const noexcept {
return m_path.string().c_str(); return m_path.string().c_str();
} }
Error PassThroughFS::mkdir(const char *path, bool recursive) noexcept { Error PassThroughFS::mkdir(CRStringView path, bool recursive) noexcept {
bool success = false; bool success = false;
const auto p = m_path / stripSlash(path); const auto p = m_path / stripSlash(path);
const auto u8p = p.u8string(); const auto u8p = p.u8string();
oxTrace("ox::fs::PassThroughFS::mkdir", u8p.c_str()); oxTrace("ox::fs::PassThroughFS::mkdir", std::bit_cast<const char*>(u8p.c_str()));
if (recursive) { if (recursive) {
std::error_code ec; std::error_code ec;
const auto isDir = std::filesystem::is_directory(p, ec); const auto isDir = std::filesystem::is_directory(p, ec);
@ -47,7 +47,6 @@ Error PassThroughFS::mkdir(const char *path, bool recursive) noexcept {
if (isDir) { if (isDir) {
success = true; success = true;
} else { } else {
std::error_code ec;
success = std::filesystem::create_directory(p, ec); success = std::filesystem::create_directory(p, ec);
oxReturnError(OxError(ec.value(), "PassThroughFS: mkdir failed")); oxReturnError(OxError(ec.value(), "PassThroughFS: mkdir failed"));
} }
@ -55,7 +54,7 @@ Error PassThroughFS::mkdir(const char *path, bool recursive) noexcept {
return OxError(success ? 0 : 1); return OxError(success ? 0 : 1);
} }
Error PassThroughFS::move(const char *src, const char *dest) noexcept { Error PassThroughFS::move(CRStringView src, CRStringView dest) noexcept {
std::error_code ec; std::error_code ec;
std::filesystem::rename(m_path / stripSlash(src), m_path / stripSlash(dest), ec); std::filesystem::rename(m_path / stripSlash(src), m_path / stripSlash(dest), ec);
if (ec.value()) { if (ec.value()) {
@ -64,7 +63,7 @@ Error PassThroughFS::move(const char *src, const char *dest) noexcept {
return OxError(0); return OxError(0);
} }
Error PassThroughFS::read(const char *path, void *buffer, std::size_t buffSize) noexcept { Error PassThroughFS::read(CRStringView path, void *buffer, std::size_t buffSize) noexcept {
try { try {
std::ifstream file((m_path / stripSlash(path)), std::ios::binary | std::ios::ate); std::ifstream file((m_path / stripSlash(path)), std::ios::binary | std::ios::ate);
const std::size_t size = file.tellg(); const std::size_t size = file.tellg();
@ -73,7 +72,7 @@ Error PassThroughFS::read(const char *path, void *buffer, std::size_t buffSize)
oxTracef("ox::fs::PassThroughFS::read::error", "Read failed: Buffer too small: {}", path); oxTracef("ox::fs::PassThroughFS::read::error", "Read failed: Buffer too small: {}", path);
return OxError(1); return OxError(1);
} }
file.read(static_cast<char*>(buffer), buffSize); file.read(static_cast<char*>(buffer), static_cast<std::streamsize>(buffSize));
} catch (const std::fstream::failure &f) { } catch (const std::fstream::failure &f) {
oxTracef("ox::fs::PassThroughFS::read::error", "Read of {} failed: {}", path, f.what()); oxTracef("ox::fs::PassThroughFS::read::error", "Read of {} failed: {}", path, f.what());
return OxError(2); return OxError(2);
@ -81,7 +80,7 @@ Error PassThroughFS::read(const char *path, void *buffer, std::size_t buffSize)
return OxError(0); return OxError(0);
} }
Result<const char*> PassThroughFS::directAccess(const char*) noexcept { Result<const char*> PassThroughFS::directAccess(CRStringView) noexcept {
return OxError(1, "PassThroughFS::directAccess not supported"); return OxError(1, "PassThroughFS::directAccess not supported");
} }
@ -99,7 +98,7 @@ Result<const char*> PassThroughFS::directAccess(uint64_t) noexcept {
return OxError(1, "PassThroughFS::directAccess not supported"); return OxError(1, "PassThroughFS::directAccess not supported");
} }
Result<Vector<String>> PassThroughFS::ls(const char *dir) const noexcept { Result<Vector<String>> PassThroughFS::ls(CRStringView dir) const noexcept {
Vector<String> out; Vector<String> out;
std::error_code ec; std::error_code ec;
const auto di = std::filesystem::directory_iterator(m_path / stripSlash(dir), ec); const auto di = std::filesystem::directory_iterator(m_path / stripSlash(dir), ec);
@ -111,7 +110,7 @@ Result<Vector<String>> PassThroughFS::ls(const char *dir) const noexcept {
return out; return out;
} }
Error PassThroughFS::remove(const char *path, bool recursive) noexcept { Error PassThroughFS::remove(CRStringView path, bool recursive) noexcept {
if (recursive) { if (recursive) {
return OxError(std::filesystem::remove_all(m_path / stripSlash(path)) != 0); return OxError(std::filesystem::remove_all(m_path / stripSlash(path)) != 0);
} else { } else {
@ -124,7 +123,7 @@ Error PassThroughFS::resize(uint64_t, void*) noexcept {
return OxError(1, "resize is not supported by PassThroughFS"); return OxError(1, "resize is not supported by PassThroughFS");
} }
Error PassThroughFS::write(const char *path, const void *buffer, uint64_t size, FileType) noexcept { Error PassThroughFS::write(CRStringView path, const void *buffer, uint64_t size, FileType) noexcept {
const auto p = (m_path / stripSlash(path)); const auto p = (m_path / stripSlash(path));
try { try {
std::ofstream f(p, std::ios::binary); std::ofstream f(p, std::ios::binary);
@ -146,7 +145,7 @@ Result<FileStat> PassThroughFS::stat(uint64_t) const noexcept {
return OxError(1, "stat(uint64_t) is not supported by PassThroughFS"); return OxError(1, "stat(uint64_t) is not supported by PassThroughFS");
} }
Result<FileStat> PassThroughFS::stat(const char *path) const noexcept { Result<FileStat> PassThroughFS::stat(CRStringView path) const noexcept {
std::error_code ec; std::error_code ec;
const auto p = m_path / stripSlash(path); const auto p = m_path / stripSlash(path);
const FileType type = std::filesystem::is_directory(p, ec) ? const FileType type = std::filesystem::is_directory(p, ec) ?
@ -194,12 +193,12 @@ bool PassThroughFS::valid() const noexcept {
return false; return false;
} }
const char *PassThroughFS::stripSlash(const char *path) noexcept { std::string_view PassThroughFS::stripSlash(StringView path) noexcept {
const auto pathLen = ox_strlen(path); const auto pathLen = ox_strlen(path);
for (auto i = 0u; i < pathLen && path[0] == '/'; i++) { for (auto i = 0u; i < pathLen && path[0] == '/'; i++) {
path++; path = path.substr(1);
} }
return path; return std::string_view(path.data(), path.bytes());
} }
} }

View File

@ -30,20 +30,20 @@ class PassThroughFS: public FileSystem {
std::filesystem::path m_path; std::filesystem::path m_path;
public: public:
explicit PassThroughFS(const char *dirPath); explicit PassThroughFS(CRStringView dirPath);
~PassThroughFS() noexcept override; ~PassThroughFS() noexcept override;
[[nodiscard]] [[nodiscard]]
String basePath() const noexcept; String basePath() const noexcept;
Error mkdir(const char *path, bool recursive = false) noexcept override; Error mkdir(CRStringView path, bool recursive = false) noexcept override;
Error move(const char *src, const char *dest) noexcept override; Error move(CRStringView src, CRStringView dest) noexcept override;
Error read(const char *path, void *buffer, std::size_t buffSize) noexcept override; Error read(CRStringView path, void *buffer, std::size_t buffSize) noexcept override;
Result<const char*> directAccess(const char*) noexcept override; Result<const char*> directAccess(CRStringView) noexcept override;
Error read(uint64_t inode, void *buffer, std::size_t size) noexcept override; Error read(uint64_t inode, void *buffer, std::size_t size) noexcept override;
@ -51,29 +51,31 @@ class PassThroughFS: public FileSystem {
Result<const char*> directAccess(uint64_t) noexcept override; Result<const char*> directAccess(uint64_t) noexcept override;
Result<Vector<String>> ls(const char *dir) const noexcept override; Result<Vector<String>> ls(CRStringView dir) const noexcept override;
template<typename F> template<typename F>
Error ls(const char *dir, F cb) const noexcept; Error ls(CRStringView dir, F cb) const noexcept;
Error remove(const char *path, bool recursive = false) noexcept override; Error remove(CRStringView path, bool recursive = false) noexcept override;
Error resize(uint64_t size, void *buffer = nullptr) noexcept override; Error resize(uint64_t size, void *buffer = nullptr) noexcept override;
Error write(const char *path, const void *buffer, uint64_t size, FileType fileType = FileType::NormalFile) noexcept override; Error write(CRStringView path, const void *buffer, uint64_t size, FileType fileType = FileType::NormalFile) noexcept override;
Error write(uint64_t inode, const void *buffer, uint64_t size, FileType fileType = FileType::NormalFile) noexcept override; Error write(uint64_t inode, const void *buffer, uint64_t size, FileType fileType = FileType::NormalFile) noexcept override;
Result<FileStat> stat(uint64_t inode) const noexcept override; Result<FileStat> stat(uint64_t inode) const noexcept override;
Result<FileStat> stat(const char *path) const noexcept override; Result<FileStat> stat(CRStringView path) const noexcept override;
[[nodiscard]]
uint64_t spaceNeeded(uint64_t size) const noexcept override; uint64_t spaceNeeded(uint64_t size) const noexcept override;
Result<uint64_t> available() const noexcept override; Result<uint64_t> available() const noexcept override;
Result<uint64_t> size() const noexcept override; Result<uint64_t> size() const noexcept override;
[[nodiscard]]
char *buff() noexcept override; char *buff() noexcept override;
Error walk(Error(*cb)(uint8_t, uint64_t, uint64_t)) noexcept override; Error walk(Error(*cb)(uint8_t, uint64_t, uint64_t)) noexcept override;
@ -85,12 +87,13 @@ class PassThroughFS: public FileSystem {
/** /**
* Strips the leading slashes from a string. * Strips the leading slashes from a string.
*/ */
static const char *stripSlash(const char *path) noexcept; [[nodiscard]]
static std::string_view stripSlash(StringView path) noexcept;
}; };
template<typename F> template<typename F>
Error PassThroughFS::ls(const char *dir, F cb) const noexcept { Error PassThroughFS::ls(CRStringView dir, F cb) const noexcept {
std::error_code ec; std::error_code ec;
const auto di = std::filesystem::directory_iterator(m_path / stripSlash(dir), ec); const auto di = std::filesystem::directory_iterator(m_path / stripSlash(dir), ec);
oxReturnError(OxError(ec.value(), "PassThroughFS: ls failed")); oxReturnError(OxError(ec.value(), "PassThroughFS: ls failed"));

View File

@ -22,6 +22,9 @@ PathIterator::PathIterator(const char *path, std::size_t maxSize, std::size_t it
PathIterator::PathIterator(const char *path): PathIterator(path, ox_strlen(path)) { PathIterator::PathIterator(const char *path): PathIterator(path, ox_strlen(path)) {
} }
PathIterator::PathIterator(CRStringView path): PathIterator(path.data(), path.bytes()) {
}
/** /**
* @return 0 if no error * @return 0 if no error
*/ */

View File

@ -26,6 +26,8 @@ class PathIterator {
PathIterator(const char *path); PathIterator(const char *path);
PathIterator(CRStringView path);
/** /**
* @return 0 if no error * @return 0 if no error
*/ */

View File

@ -56,7 +56,7 @@ static ox::Error runRead(ox::FileSystem *fs, int argc, const char **argv) noexce
oxErr("Must provide a path to a file to read\n"); oxErr("Must provide a path to a file to read\n");
return OxError(1); return OxError(1);
} }
oxRequire(buff, fs->read(argv[1])); oxRequire(buff, fs->read(ox::StringView(argv[1])));
fwrite(buff.data(), sizeof(decltype(buff)::value_type), buff.size(), stdout); fwrite(buff.data(), sizeof(decltype(buff)::value_type), buff.size(), stdout);
return OxError(0); return OxError(0);
} }

View File

@ -331,8 +331,7 @@ constexpr Error MetalClawReaderTemplate<HandlerMaker>::field(const char*, BasicS
if (m_buffIt + size > m_buffLen) { if (m_buffIt + size > m_buffLen) {
return OxError(MC_BUFFENDED); return OxError(MC_BUFFENDED);
} }
memcpy(data, &m_buff[m_buffIt], size); ox_strncpy(data, &m_buff[m_buffIt], size);
data[size] = 0;
m_buffIt += size; m_buffIt += size;
} else { } else {
*val = ""; *val = "";

View File

@ -57,14 +57,21 @@ class FieldCounter {
} }
namespace detail {
template<typename T> template<typename T>
constexpr auto ModelFieldCount_v = [] { [[nodiscard]]
consteval auto modelFieldCount() noexcept {
auto a = std::allocator<T>(); auto a = std::allocator<T>();
auto t = a.allocate(1); auto t = a.allocate(1);
detail::FieldCounter<T> c; detail::FieldCounter<T> c;
oxAssert(model(&c, t), "Count failed"); const auto err = model(&c, t);
oxAssert(err, "Count failed");
a.deallocate(t, 1); a.deallocate(t, 1);
return c.fields; return c.fields;
}(); }
}
template<typename T>
constexpr auto ModelFieldCount_v = detail::modelFieldCount<T>();
} }

View File

@ -33,6 +33,7 @@ add_library(
substitutes.cpp substitutes.cpp
stacktrace.cpp stacktrace.cpp
string.cpp string.cpp
stringview.cpp
strops.cpp strops.cpp
trace.cpp trace.cpp
typetraits.cpp typetraits.cpp

View File

@ -26,6 +26,8 @@ void panic([[maybe_unused]]const char *file, [[maybe_unused]]int line, [[maybe_u
printStackTrace(2); printStackTrace(2);
oxTrace("panic").del("") << "Panic: " << panicMsg << " (" << file << ":" << line << ")"; oxTrace("panic").del("") << "Panic: " << panicMsg << " (" << file << ":" << line << ")";
std::abort(); std::abort();
#else
while (1);
#endif #endif
} }

View File

@ -36,12 +36,14 @@ constexpr void assertFunc(const char *file, int line, bool pass, [[maybe_unused]
if (!pass) { if (!pass) {
if (!std::is_constant_evaluated()) { if (!std::is_constant_evaluated()) {
#ifdef OX_USE_STDLIB #ifdef OX_USE_STDLIB
oxErrf("\n\033[31;1;1mASSERT FAILURE:\033[0m [{}:{}]: {}\n", file, line, msg); oxErrf("\n\033[31;1;1mASSERT FAILURE:\033[0m [{}:{}]: {}\n", file, line, msg);
printStackTrace(2); printStackTrace(2);
oxTracef("assert", "Failed assert: {} ({}) [{}:{}]", msg, assertTxt, file, line); oxTracef("assert", "Failed assert: {} ({}) [{}:{}]", msg, assertTxt, file, line);
std::abort(); std::abort();
#else #else
constexprPanic(file, line, msg); oxErrf("\n\033[31;1;1mASSERT FAILURE:\033[0m [{}:{}]: {}\n", file, line, msg);
oxTracef("assert", "Failed assert: {} ({}) [{}:{}]", msg, assertTxt, file, line);
constexprPanic(file, line, msg);
#endif #endif
} else { } else {
while (true); while (true);

View File

@ -143,6 +143,7 @@ template<std::size_t size>
constexpr BString<size> &BString<size>::operator+=(StringView s) noexcept { constexpr BString<size> &BString<size>::operator+=(StringView s) noexcept {
std::size_t strLen = s.bytes(); std::size_t strLen = s.bytes();
oxIgnoreError(append(s.data(), strLen)); oxIgnoreError(append(s.data(), strLen));
return *this;
} }
template<std::size_t size> template<std::size_t size>

View File

@ -178,18 +178,6 @@ class BasicString {
[[nodiscard]] [[nodiscard]]
constexpr BasicString substr(std::size_t begin, std::size_t end) const noexcept; constexpr BasicString substr(std::size_t begin, std::size_t end) const noexcept;
[[nodiscard]]
constexpr bool beginsWith(const char *ending) const noexcept;
[[nodiscard]]
constexpr bool beginsWith(const BasicString &ending) const noexcept;
[[nodiscard]]
constexpr bool endsWith(const char *ending) const noexcept;
[[nodiscard]]
constexpr bool endsWith(const BasicString &ending) const noexcept;
[[nodiscard]] [[nodiscard]]
constexpr const char *data() const noexcept { constexpr const char *data() const noexcept {
return m_buff.data(); return m_buff.data();
@ -238,7 +226,7 @@ class BasicString {
template<std::size_t OtherSize> template<std::size_t OtherSize>
constexpr void set(const BasicString<OtherSize> &src) noexcept; constexpr void set(const BasicString<OtherSize> &src) noexcept;
constexpr void set(StringView str) noexcept; constexpr void set(CRStringView str) noexcept;
constexpr void set(const char *str) noexcept; constexpr void set(const char *str) noexcept;
@ -291,10 +279,10 @@ constexpr BasicString<SmallStringSize_v>::BasicString(const char *str, std::size
template<std::size_t SmallStringSize_v> template<std::size_t SmallStringSize_v>
constexpr BasicString<SmallStringSize_v>::BasicString(CRStringView str) noexcept { constexpr BasicString<SmallStringSize_v>::BasicString(CRStringView str) noexcept {
if (m_buff.size()) { if (m_buff.empty()) {
m_buff[0] = 0;
} else {
m_buff.push_back(0); m_buff.push_back(0);
} else {
m_buff[0] = 0;
} }
set(str); set(str);
} }
@ -520,30 +508,6 @@ constexpr BasicString<SmallStringSize_v> BasicString<SmallStringSize_v>::substr(
return out; return out;
} }
template<std::size_t SmallStringSize_v>
constexpr bool BasicString<SmallStringSize_v>::beginsWith(const char *beginning) const noexcept {
const auto beginningLen = ox::min(ox_strlen(beginning), len());
return ox_strncmp(data(), beginning, beginningLen) == 0;
}
template<std::size_t SmallStringSize_v>
constexpr bool BasicString<SmallStringSize_v>::beginsWith(const BasicString &beginning) const noexcept {
const auto sz = ox::min(beginning.len(), len());;
return ox_strncmp(data(), beginning.c_str(), sz) == 0;
}
template<std::size_t SmallStringSize_v>
constexpr bool BasicString<SmallStringSize_v>::endsWith(const char *ending) const noexcept {
const auto endingLen = ox_strlen(ending);
return len() >= endingLen && ox_strcmp(data() + (len() - endingLen), ending) == 0;
}
template<std::size_t SmallStringSize_v>
constexpr bool BasicString<SmallStringSize_v>::endsWith(const BasicString &ending) const noexcept {
const auto endingLen = ending.len();
return len() >= endingLen && ox_strcmp(data() + (len() - endingLen), ending.c_str()) == 0;
}
template<std::size_t SmallStringSize_v> template<std::size_t SmallStringSize_v>
constexpr std::size_t BasicString<SmallStringSize_v>::bytes() const noexcept { constexpr std::size_t BasicString<SmallStringSize_v>::bytes() const noexcept {
std::size_t i; std::size_t i;
@ -555,11 +519,10 @@ template<std::size_t SmallStringSize_v>
constexpr std::size_t BasicString<SmallStringSize_v>::len() const noexcept { constexpr std::size_t BasicString<SmallStringSize_v>::len() const noexcept {
std::size_t length = 0; std::size_t length = 0;
for (const auto c : m_buff) { for (const auto c : m_buff) {
auto b = static_cast<uint8_t>(c); const auto b = static_cast<uint8_t>(c);
if (b) { if (b) {
if ((b & 128) == 0) { // normal ASCII character // normal ASCII character or start of UTF-8 character
++length; if ((b & 128) == 0 || (b & (256 << 6)) == (256 << 6)) {
} else if ((b & (256 << 6)) == (256 << 6)) { // start of UTF-8 character
++length; ++length;
} }
} else { } else {
@ -573,41 +536,33 @@ template<std::size_t SmallStringSize_v>
template<std::size_t OtherSize> template<std::size_t OtherSize>
constexpr void BasicString<SmallStringSize_v>::set(const BasicString<OtherSize> &src) noexcept { constexpr void BasicString<SmallStringSize_v>::set(const BasicString<OtherSize> &src) noexcept {
std::size_t strBytes = src.bytes(); std::size_t strBytes = src.bytes();
if (strBytes > 1) { m_buff.resize(strBytes);
m_buff.resize(strBytes); copy_n(src.begin(), strBytes, m_buff.data());
copy_n(src.begin(), strBytes, m_buff.data()); m_buff.back().value = 0;
m_buff.back().value = 0;
}
} }
template<std::size_t SmallStringSize_v> template<std::size_t SmallStringSize_v>
constexpr void BasicString<SmallStringSize_v>::set(StringView str) noexcept { constexpr void BasicString<SmallStringSize_v>::set(CRStringView str) noexcept {
std::size_t strBytes = str.bytes(); std::size_t strBytes = str.bytes();
if (strBytes > 1) { m_buff.resize(strBytes + 1);
m_buff.resize(strBytes + 1); copy_n(str.data(), strBytes, m_buff.data());
copy_n(str.data(), strBytes, m_buff.data()); m_buff.back().value = 0;
m_buff.back().value = 0;
}
} }
template<std::size_t SmallStringSize_v> template<std::size_t SmallStringSize_v>
constexpr void BasicString<SmallStringSize_v>::set(const char *str) noexcept { constexpr void BasicString<SmallStringSize_v>::set(const char *str) noexcept {
std::size_t strBytes = ox_strlen(str) + 1; std::size_t strBytes = ox_strlen(str) + 1;
if (strBytes > 1) { m_buff.resize(strBytes);
m_buff.resize(strBytes); copy_n(str, strBytes, m_buff.data());
copy_n(str, strBytes, m_buff.data()); m_buff.back().value = 0;
m_buff.back().value = 0;
}
} }
template<std::size_t SmallStringSize_v> template<std::size_t SmallStringSize_v>
constexpr void BasicString<SmallStringSize_v>::set(const char8_t *str) noexcept { constexpr void BasicString<SmallStringSize_v>::set(const char8_t *str) noexcept {
std::size_t strBytes = ox_strlen(str) + 1; std::size_t strBytes = ox_strlen(str) + 1;
if (strBytes > 1) { m_buff.resize(strBytes);
m_buff.resize(strBytes); memcpy(m_buff.data(), str, strBytes);
memcpy(m_buff.data(), str, strBytes); m_buff.back().value = 0;
m_buff.back().value = 0;
}
} }
extern template class BasicString<8>; extern template class BasicString<8>;

20
deps/ox/src/ox/std/stringview.cpp vendored Normal file
View File

@ -0,0 +1,20 @@
/*
* Copyright 2015 - 2022 gary@drinkingtea.net
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#include "stringview.hpp"
static_assert(ox::StringView("Read").bytes() == 4);
static_assert(ox::StringView("Read") == ox::StringView("Read"));
static_assert(ox::StringView("Read") != ox::StringView("Write"));
static_assert(ox::StringView("Write") != ox::StringView("Read"));
static_assert(ox::StringView("Write") != ox::StringView(""));
static_assert(ox::StringView("") != ox::StringView("Read"));
static_assert(ox::StringView("") == ox::StringView(""));
static_assert(ox::StringView("") == "");
static_assert("Read" == ox::StringView("Read"));
static_assert(ox::StringView("Read") == ox::StringView("Read"));

View File

@ -8,6 +8,10 @@
#pragma once #pragma once
#ifdef OX_USE_STDLIB
#include <string_view>
#endif
#include "iterator.hpp" #include "iterator.hpp"
#include "strops.hpp" #include "strops.hpp"
#include "types.hpp" #include "types.hpp"
@ -133,8 +137,11 @@ class StringView {
public: public:
constexpr StringView() noexcept = default; constexpr StringView() noexcept = default;
constexpr StringView(const StringView &sv) noexcept: m_str(sv.m_str), m_len(sv.m_len) { constexpr StringView(const StringView &sv) noexcept = default;
}
#ifdef OX_USE_STDLIB
constexpr StringView(const std::string_view &sv) noexcept: m_str(sv.data()), m_len(sv.size()) {}
#endif
template<std::size_t SmallStrSz> template<std::size_t SmallStrSz>
constexpr StringView(const BasicString<SmallStrSz> &str) noexcept: m_str(str.c_str()), m_len(str.len()) {} constexpr StringView(const BasicString<SmallStrSz> &str) noexcept: m_str(str.c_str()), m_len(str.len()) {}
@ -259,16 +266,22 @@ class StringView {
using CRStringView = const StringView&; using CRStringView = const StringView&;
static_assert(StringView("Read").bytes() == 4); [[nodiscard]]
static_assert(StringView("Read") == StringView("Read")); constexpr bool beginsWith(CRStringView base, CRStringView beginning) noexcept {
static_assert(StringView("Read") != StringView("Write")); const auto beginningLen = ox::min(beginning.len(), base.len());
static_assert(StringView("Write") != StringView("Read")); return ox_strncmp(base.data(), beginning, beginningLen) == 0;
static_assert(StringView("Write") != StringView(""));
static_assert(StringView("") != StringView("Read"));
static_assert(StringView("") == StringView(""));
static_assert(StringView("") == "");
static_assert("Read" == StringView("Read"));
static_assert(StringView("Read") == StringView("Read"));
} }
[[nodiscard]]
constexpr bool endsWith(CRStringView base, CRStringView ending) noexcept {
const auto endingLen = ending.len();
return base.len() >= endingLen && ox_strcmp(base.data() + (base.len() - endingLen), ending) == 0;
}
#ifdef OX_USE_STDLIB
constexpr auto toStdStringView(CRStringView sv) noexcept {
return std::string_view(sv.data(), sv.bytes());
}
#endif
}

View File

@ -15,9 +15,10 @@
template<typename T1, typename T2> template<typename T1, typename T2>
constexpr char *ox_strcpy(T1 dest, T2 src) noexcept { constexpr char *ox_strcpy(T1 dest, T2 src) noexcept {
using T1Type = typename ox::remove_reference<decltype(dest[0])>::type;
std::size_t i = 0; std::size_t i = 0;
while (src[i]) { while (src[i]) {
dest[i] = src[i]; dest[i] = static_cast<T1Type>(src[i]);
++i; ++i;
} }
// set null terminator // set null terminator
@ -27,9 +28,10 @@ constexpr char *ox_strcpy(T1 dest, T2 src) noexcept {
template<typename T1, typename T2> template<typename T1, typename T2>
constexpr char *ox_strncpy(T1 dest, T2 src, std::size_t maxLen) noexcept { constexpr char *ox_strncpy(T1 dest, T2 src, std::size_t maxLen) noexcept {
using T1Type = typename ox::remove_reference<decltype(dest[0])>::type;
std::size_t i = 0; std::size_t i = 0;
while (i < maxLen && src[i]) { while (i < maxLen && src[i]) {
dest[i] = src[i]; dest[i] = static_cast<T1Type>(src[i]);
++i; ++i;
} }
// set null terminator // set null terminator
@ -113,18 +115,7 @@ constexpr char *ox_strchr(char *str, int character, std::size_t maxLen = 0xFFFFF
} }
[[nodiscard]] [[nodiscard]]
constexpr int ox_lastIndexOf(const char *str, int character, std::size_t maxLen = 0xFFFFFFFF) noexcept { constexpr int ox_lastIndexOf(const auto &str, int character, std::size_t maxLen = 0xFFFFFFFF) noexcept {
int retval = -1;
for (std::size_t i = 0; i < maxLen && str[i]; i++) {
if (str[i] == character) {
retval = static_cast<int>(i);
}
}
return retval;
}
[[nodiscard]]
constexpr int ox_lastIndexOf(char *str, int character, std::size_t maxLen = 0xFFFFFFFF) noexcept {
int retval = -1; int retval = -1;
for (std::size_t i = 0; i < maxLen && str[i]; i++) { for (std::size_t i = 0; i < maxLen && str[i]; i++) {
if (str[i] == character) { if (str[i] == character) {

View File

@ -75,17 +75,17 @@ static std::map<ox::String, ox::Error(*)()> tests = {
oxAssert(s == "asdf", "String assign broken"); oxAssert(s == "asdf", "String assign broken");
s += "aoeu"; s += "aoeu";
oxAssert(s == "asdfaoeu", "String append broken"); oxAssert(s == "asdfaoeu", "String append broken");
ox::String ending = "asdf"; const ox::StringView str = "asdf";
oxAssert(ending.beginsWith("as"), "String::beginsWith is broken"); oxAssert(beginsWith(str, "as"), "String beginsWith is broken");
oxAssert(ending.beginsWith("asd"), "String::beginsWith is broken"); oxAssert(beginsWith(str, "asd"), "String beginsWith is broken");
oxAssert(ending.beginsWith("asdf"), "String::beginsWith is broken"); oxAssert(beginsWith(str, "asdf"), "String beginsWith is broken");
oxAssert(!ending.beginsWith("aa"), "String::beginsWith is broken"); oxAssert(!beginsWith(str, "aa"), "String beginsWith is broken");
oxAssert(!ending.beginsWith("aaaaaaa"), "String::beginsWith is broken"); oxAssert(!beginsWith(str, "aaaaaaa"), "String beginsWith is broken");
oxAssert(!ending.beginsWith("li"), "String::beginsWith is broken"); oxAssert(!beginsWith(str, "li"), "String beginsWith is broken");
oxAssert(!ending.beginsWith("afoif"), "String::beginsWith is broken"); oxAssert(!beginsWith(str, "afoif"), "String beginsWith is broken");
oxAssert(ending.endsWith("df"), "String::endsWith is broken"); oxAssert(endsWith(str, "df"), "String endsWith is broken");
oxAssert(!ending.endsWith("awefawe"), "String::endsWith is broken"); oxAssert(!endsWith(str, "awefawe"), "String endsWith is broken");
oxAssert(!ending.endsWith("eu"), "String::endsWith is broken"); oxAssert(!endsWith(str, "eu"), "String endsWith is broken");
return OxError(0); return OxError(0);
} }
}, },

View File

@ -59,11 +59,11 @@ class OutStream {
TraceMsg m_msg; TraceMsg m_msg;
public: public:
constexpr OutStream(const char *file, int line, const char *ch, const String &msg) noexcept { constexpr OutStream(const char *file, int line, const char *ch, const StringView &msg) noexcept {
m_msg.file = file; m_msg.file = file;
m_msg.line = line; m_msg.line = line;
m_msg.ch = ch; m_msg.ch = ch;
m_msg.msg = msg.c_str(); m_msg.msg = msg;
} }
constexpr OutStream(const char *file, int line, const char *ch, const char *msg = "") noexcept { constexpr OutStream(const char *file, int line, const char *ch, const char *msg = "") noexcept {
@ -182,7 +182,7 @@ constexpr OutStream &OutStream::operator<<(Integer_c auto v) noexcept {
class NullStream { class NullStream {
public: public:
constexpr NullStream(const char*, int, const char*, const String&) noexcept { constexpr NullStream(const char*, int, const char*, const StringView&) noexcept {
} }
constexpr NullStream(const char*, int, const char*, const char* = "") noexcept { constexpr NullStream(const char*, int, const char*, const char* = "") noexcept {

View File

@ -43,7 +43,7 @@ void initConsole() {
if (REG_MGBA_DEBUG_ENABLE == 0x1DEA) { if (REG_MGBA_DEBUG_ENABLE == 0x1DEA) {
infoLog = log<LogChan::Info>; infoLog = log<LogChan::Info>;
debugLog = log<LogChan::Info>; // use INFO because mGBA disables DEBUG on start debugLog = log<LogChan::Info>; // use INFO because mGBA disables DEBUG on start
errorLog = log<LogChan::Error>; errorLog = log<LogChan::Info>;
} }
} }
@ -71,13 +71,14 @@ void oxTraceHook([[maybe_unused]] const char *file, [[maybe_unused]] int line,
std::cout << std::setw(65) << std::left << msg << '|'; std::cout << std::setw(65) << std::left << msg << '|';
std::cout << " " << file << ':' << line << "\n"; std::cout << " " << file << ':' << line << "\n";
} else if (ox_strcmp(ch, "debug") == 0 || ox_strcmp(ch, "info") == 0) { } else if (ox_strcmp(ch, "debug") == 0 || ox_strcmp(ch, "info") == 0) {
std::cout << msg << '\n'; printf("%s\n", msg);
} else if (ox_strcmp(ch, "stdout") == 0) { } else if (ox_strcmp(ch, "stdout") == 0) {
std::cout << msg; printf("%s", msg);
} else if (ox_strcmp(ch, "stderr") == 0) { } else if (ox_strcmp(ch, "stderr") == 0) {
std::cerr << msg; printf("%s", msg);
} else if (ox_strcmp(ch, "error") == 0) { } else if (ox_strcmp(ch, "error") == 0) {
std::cerr << "\033[31;1;1mERROR:\033[0m (" << file << ':' << line << "): " << msg << '\n'; //std::cerr << "\033[31;1;1mERROR:\033[0m (" << file << ':' << line << "): " << msg << '\n';
fprintf(stderr, "\033[31;1;1mERROR:\033[0m (%s:%d): %s\n", file, line, msg);
} }
#else #else
if (ox_strcmp(ch, "info") == 0) { if (ox_strcmp(ch, "info") == 0) {

Binary file not shown.

View File

@ -0,0 +1 @@
M2;net.drinkingtea.nostalgia.core.Palette;1;PÛ{³ÖCˆ

Binary file not shown.

Binary file not shown.

View File

@ -14,7 +14,7 @@ namespace nostalgia::core {
ox::String getClipboardText(class Context *ctx) noexcept; ox::String getClipboardText(class Context *ctx) noexcept;
void setClipboardText(class Context *ctx, const ox::String &text) noexcept; void setClipboardText(class Context *ctx, ox::CRStringView text) noexcept;
template<typename T> template<typename T>
void setClipboardObject([[maybe_unused]] class Context *ctx, [[maybe_unused]] ox::UniquePtr<T> obj) noexcept { void setClipboardObject([[maybe_unused]] class Context *ctx, [[maybe_unused]] ox::UniquePtr<T> obj) noexcept {

View File

@ -61,9 +61,14 @@ class Context {
unsigned cbb, unsigned cbb,
const ox::FileAddress &tilesheetPath, const ox::FileAddress &tilesheetPath,
const ox::FileAddress &palettePath) noexcept; const ox::FileAddress &palettePath) noexcept;
friend ox::Error loadSpriteTileSheet(Context *ctx,
const ox::FileAddress &tilesheetAddr,
const ox::FileAddress &paletteAddr) noexcept;
friend ox::Result<struct TileSheetData> loadTileSheet(Context *ctx,
const struct CompactTileSheet &tilesheetAddr) noexcept;
friend ox::Error run(Context *ctx) noexcept; friend ox::Error run(Context *ctx) noexcept;
friend void shutdown(Context *ctx) noexcept; friend void shutdown(Context *ctx) noexcept;
friend ox::Result<ox::UniquePtr<Context>> init(ox::UniquePtr<ox::FileSystem> fs, const char *appName) noexcept; friend ox::Result<ox::UniquePtr<Context>> init(ox::UniquePtr<ox::FileSystem> fs, ox::CRStringView appName) noexcept;
friend ox::String getClipboardText(Context *ctx) noexcept; friend ox::String getClipboardText(Context *ctx) noexcept;
friend uint64_t ticksMs(Context *ctx) noexcept; friend uint64_t ticksMs(Context *ctx) noexcept;
friend uint8_t bgStatus(Context *ctx) noexcept; friend uint8_t bgStatus(Context *ctx) noexcept;
@ -73,17 +78,24 @@ class Context {
friend void setBgCbb(Context *ctx, unsigned bgIdx, unsigned cbb) noexcept; friend void setBgCbb(Context *ctx, unsigned bgIdx, unsigned cbb) noexcept;
friend void setBgStatus(Context *ctx, uint32_t status) noexcept; friend void setBgStatus(Context *ctx, uint32_t status) noexcept;
friend void setBgStatus(Context *ctx, unsigned bg, bool status) noexcept; friend void setBgStatus(Context *ctx, unsigned bg, bool status) noexcept;
friend void setClipboardText(Context *ctx, const ox::String &text) noexcept;
friend void setUpdateHandler(Context *ctx, UpdateHandler h) noexcept; friend void setUpdateHandler(Context *ctx, UpdateHandler h) noexcept;
friend constexpr void setKeyEventHandler(Context *ctx, KeyEventHandler h) noexcept; friend constexpr void setKeyEventHandler(Context *ctx, KeyEventHandler h) noexcept;
friend constexpr KeyEventHandler keyEventHandler(Context *ctx) noexcept; friend constexpr KeyEventHandler keyEventHandler(Context *ctx) noexcept;
friend void setTile(Context *ctx, unsigned bgIdx, int column, int row, uint8_t tile) noexcept; friend void setTile(Context *ctx, unsigned bgIdx, int column, int row, uint8_t tile) noexcept;
friend void setWindowTitle(Context *ctx, const char *title) noexcept; friend void setSprite(Context *ctx,
unsigned idx,
int x,
int y,
unsigned tileIdx,
unsigned spriteShape,
unsigned spriteSize,
unsigned flipX) noexcept;
friend void hideSprite(Context *ctx, unsigned idx) noexcept;
public: public:
ox::UniquePtr<ox::FileSystem> rom; ox::UniquePtr<ox::FileSystem> rom;
ox::Vector<Drawer*, 5> drawers; ox::Vector<Drawer*, 5> drawers;
const char *appName = "Nostalgia"; ox::StringView appName = "Nostalgia";
#ifndef OX_BARE_METAL #ifndef OX_BARE_METAL
AssetManager assetManager; AssetManager assetManager;

View File

@ -17,7 +17,7 @@
namespace nostalgia::core { namespace nostalgia::core {
ox::Result<ox::UniquePtr<Context>> init(ox::UniquePtr<ox::FileSystem> fs, const char *appName = "Nostalgia") noexcept; ox::Result<ox::UniquePtr<Context>> init(ox::UniquePtr<ox::FileSystem> fs, ox::CRStringView appName = "Nostalgia") noexcept;
ox::Error run(Context *ctx) noexcept; ox::Error run(Context *ctx) noexcept;

View File

@ -54,7 +54,7 @@ static ox::Result<std::size_t> findPreloadSection() noexcept {
return OxError(1); return OxError(1);
} }
ox::Result<ox::UniquePtr<Context>> init(ox::UniquePtr<ox::FileSystem> fs, const char *appName) noexcept { ox::Result<ox::UniquePtr<Context>> init(ox::UniquePtr<ox::FileSystem> fs, ox::CRStringView appName) noexcept {
auto ctx = ox::make_unique<Context>(); auto ctx = ox::make_unique<Context>();
ctx->rom = std::move(fs); ctx->rom = std::move(fs);
ctx->appName = appName; ctx->appName = appName;
@ -74,7 +74,7 @@ uint64_t ticksMs(Context*) noexcept {
} }
bool buttonDown(Context*, Key k) noexcept { bool buttonDown(Context*, Key k) noexcept {
return !(REG_GAMEPAD & (1 << static_cast<int>(k))); return k <= Key::GamePad_L && !(REG_GAMEPAD & (1 << static_cast<int>(k)));
} }
void shutdown(Context *ctx) noexcept { void shutdown(Context *ctx) noexcept {

View File

@ -85,7 +85,7 @@ ox::Error shutdownGfx(Context*) noexcept {
return {}; return {};
} }
void setWindowTitle(Context*, const char*) noexcept { void setWindowTitle(Context*, ox::CRStringView) noexcept {
} }
void focusWindow(Context*) noexcept { void focusWindow(Context*) noexcept {
@ -124,8 +124,8 @@ void setBgStatus(Context*, unsigned bg, bool status) noexcept {
// Do NOT rely on Context in the GBA version of this function. // Do NOT rely on Context in the GBA version of this function.
ox::Error initConsole(Context *ctx) noexcept { ox::Error initConsole(Context *ctx) noexcept {
constexpr auto TilesheetAddr = "/TileSheets/Charset.ng"; constexpr ox::FileAddress TilesheetAddr("/TileSheets/Charset.ng");
constexpr auto PaletteAddr = "/Palettes/Charset.npal"; constexpr ox::FileAddress PaletteAddr("/Palettes/Charset.npal");
setBgStatus(ctx, 0b0001); setBgStatus(ctx, 0b0001);
if (!ctx) { if (!ctx) {
ctx = new (ox_alloca(sizeof(Context))) Context(); ctx = new (ox_alloca(sizeof(Context))) Context();
@ -174,14 +174,13 @@ ox::Error loadBgTileSheet(Context *ctx,
} }
ox::Error loadSpriteTileSheet(Context *ctx, ox::Error loadSpriteTileSheet(Context *ctx,
unsigned cbb,
const ox::FileAddress &tilesheetAddr, const ox::FileAddress &tilesheetAddr,
const ox::FileAddress &paletteAddr) noexcept { const ox::FileAddress &paletteAddr) noexcept {
oxRequire(tsStat, ctx->rom->stat(tilesheetAddr)); oxRequire(tsStat, ctx->rom->stat(tilesheetAddr));
oxRequire(ts, ctx->rom->directAccess(tilesheetAddr)); oxRequire(ts, ctx->rom->directAccess(tilesheetAddr));
GbaTileMapTarget target; GbaTileMapTarget target;
target.pal.palette = &MEM_SPRITE_PALETTE[cbb]; target.pal.palette = MEM_SPRITE_PALETTE;
target.tileMap = &reinterpret_cast<uint16_t*>(MEM_SPRITE_TILES)[cbb * 512]; target.tileMap = MEM_SPRITE_TILES;
oxReturnError(ox::readMC(ts, tsStat.size, &target)); oxReturnError(ox::readMC(ts, tsStat.size, &target));
// load external palette if available // load external palette if available
if (paletteAddr) { if (paletteAddr) {
@ -211,10 +210,11 @@ ox::Error loadSpritePalette(Context *ctx, unsigned cbb, const ox::FileAddress &p
} }
// Do NOT use Context in the GBA version of this function. // Do NOT use Context in the GBA version of this function.
void puts(Context *ctx, int column, int row, const char *str) noexcept { void puts(Context *ctx, int column, int row, ox::CRStringView str) noexcept {
for (int i = 0; str[i]; i++) { const auto col = static_cast<unsigned>(column);
for (auto i = 0u; i < str.bytes(); i++) {
const auto c = charMap[static_cast<unsigned>(str[i])]; const auto c = charMap[static_cast<unsigned>(str[i])];
setTile(ctx, 0, column + i, row, static_cast<uint8_t>(c)); setTile(ctx, 0, static_cast<int>(col + i), row, static_cast<uint8_t>(c));
} }
} }
@ -253,15 +253,15 @@ void hideSprite(Context*, unsigned idx) noexcept {
void setSprite(Context*, void setSprite(Context*,
unsigned idx, unsigned idx,
unsigned x, int x,
unsigned y, int y,
unsigned tileIdx, unsigned tileIdx,
unsigned spriteShape, unsigned spriteShape,
unsigned spriteSize, unsigned spriteSize,
unsigned flipX) noexcept { unsigned flipX) noexcept {
oxAssert(g_spriteUpdates < config::GbaSpriteBufferLen, "Sprite update buffer overflow"); oxAssert(g_spriteUpdates < config::GbaSpriteBufferLen, "Sprite update buffer overflow");
GbaSpriteAttrUpdate oa; GbaSpriteAttrUpdate oa;
oa.attr0 = static_cast<uint16_t>(y & ox::onMask<uint8_t>(7)) oa.attr0 = static_cast<uint16_t>(y & ox::onMask<uint8_t>(0b111'1111))
| (static_cast<uint16_t>(1) << 10) // enable alpha | (static_cast<uint16_t>(1) << 10) // enable alpha
| (static_cast<uint16_t>(spriteShape) << 14); | (static_cast<uint16_t>(spriteShape) << 14);
oa.attr1 = (static_cast<uint16_t>(x) & ox::onMask<uint8_t>(8)) oa.attr1 = (static_cast<uint16_t>(x) & ox::onMask<uint8_t>(8))

View File

@ -11,7 +11,7 @@
namespace nostalgia::core { namespace nostalgia::core {
ox::Result<char*> loadRom(const char*) noexcept { ox::Result<char*> loadRom(ox::CRStringView) noexcept {
// put the header in the wrong order to prevent mistaking this code for the // put the header in the wrong order to prevent mistaking this code for the
// media section // media section
constexpr auto headerP2 = "HEADER__________"; constexpr auto headerP2 = "HEADER__________";
@ -31,6 +31,14 @@ ox::Result<char*> loadRom(const char*) noexcept {
void unloadRom(char*) noexcept { void unloadRom(char*) noexcept {
} }
ox::Result<std::size_t> getPreloadAddr(Context *ctx, ox::CRStringView path) noexcept {
oxRequire(stat, ctx->rom->stat(path));
oxRequire(buff, ctx->rom->directAccess(path));
PreloadPtr p;
oxReturnError(ox::readMC(buff, stat.size, &p));
return p.preloadAddr + ctx->preloadSectionOffset;
}
ox::Result<std::size_t> getPreloadAddr(Context *ctx, const ox::FileAddress &file) noexcept { ox::Result<std::size_t> getPreloadAddr(Context *ctx, const ox::FileAddress &file) noexcept {
oxRequire(stat, ctx->rom->stat(file)); oxRequire(stat, ctx->rom->stat(file));
oxRequire(buff, ctx->rom->directAccess(file)); oxRequire(buff, ctx->rom->directAccess(file));

View File

@ -27,7 +27,7 @@ void panic(const char*, int, const char *msg, const ox::Error &err) noexcept {
puts(nullptr, 32 + 1, 4, "UNEXPECTED STATE:"); puts(nullptr, 32 + 1, 4, "UNEXPECTED STATE:");
puts(nullptr, 32 + 2, 6, msg); puts(nullptr, 32 + 2, 6, msg);
if (err) { if (err) {
puts(nullptr, 32 + 2, 8, serr.c_str()); puts(nullptr, 32 + 2, 8, serr);
} }
puts(nullptr, 32 + 1, 15, "PLEASE RESTART THE SYSTEM"); puts(nullptr, 32 + 1, 15, "PLEASE RESTART THE SYSTEM");
// disable all interrupt handling and IntrWait on no interrupts // disable all interrupt handling and IntrWait on no interrupts

View File

@ -93,11 +93,11 @@ struct TileSheet {
other.columns = 0; other.columns = 0;
other.rows = 0; other.rows = 0;
} }
constexpr SubSheet(const char *pName, int pColumns, int pRows, int bpp) noexcept: constexpr SubSheet(ox::CRStringView pName, int pColumns, int pRows, int bpp) noexcept:
name(pName), columns(pColumns), rows(pRows), name(pName), columns(pColumns), rows(pRows),
pixels(static_cast<std::size_t>(columns * rows * PixelsPerTile) / (bpp == 4 ? 2u : 1u)) { pixels(static_cast<std::size_t>(columns * rows * PixelsPerTile) / (bpp == 4 ? 2u : 1u)) {
} }
constexpr SubSheet(const char *pName, int pColumns, int pRows, ox::Vector<uint8_t> pPixels) noexcept: constexpr SubSheet(ox::CRStringView pName, int pColumns, int pRows, ox::Vector<uint8_t> pPixels) noexcept:
name(pName), columns(pColumns), rows(pRows), pixels(std::move(pPixels)) { name(pName), columns(pColumns), rows(pRows), pixels(std::move(pPixels)) {
} }
@ -120,7 +120,7 @@ struct TileSheet {
} }
[[nodiscard]] [[nodiscard]]
auto idx(const geo::Point &pt) const noexcept { constexpr auto idx(const geo::Point &pt) const noexcept {
return ptToIdx(pt, columns); return ptToIdx(pt, columns);
} }
@ -275,6 +275,7 @@ struct TileSheet {
* @param pBpp bits per pixel, need for knowing how to count the pixels * @param pBpp bits per pixel, need for knowing how to count the pixels
* @return a count of the pixels in this sheet * @return a count of the pixels in this sheet
*/ */
[[nodiscard]]
constexpr auto pixelCnt(int8_t pBpp) const noexcept { constexpr auto pixelCnt(int8_t pBpp) const noexcept {
return pBpp == 4 ? pixels.size() * 2 : pixels.size(); return pBpp == 4 ? pixels.size() * 2 : pixels.size();
} }
@ -287,13 +288,9 @@ struct TileSheet {
SubSheet subsheet{"Root", 1, 1, bpp}; SubSheet subsheet{"Root", 1, 1, bpp};
constexpr TileSheet() noexcept = default; constexpr TileSheet() noexcept = default;
inline TileSheet(const TileSheet &other) noexcept: TileSheet(const TileSheet &other) noexcept = default;
bpp(other.bpp),
defaultPalette(other.defaultPalette),
subsheet(other.subsheet) {
}
inline TileSheet(TileSheet &&other) noexcept: inline TileSheet(TileSheet &&other) noexcept:
bpp(std::move(other.bpp)), bpp(other.bpp),
defaultPalette(std::move(other.defaultPalette)), defaultPalette(std::move(other.defaultPalette)),
subsheet(std::move(other.subsheet)) { subsheet(std::move(other.subsheet)) {
} }
@ -375,7 +372,7 @@ struct TileSheet {
constexpr ox::Error addSubSheet(const SubSheetIdx &idx) noexcept { constexpr ox::Error addSubSheet(const SubSheetIdx &idx) noexcept {
auto &parent = getSubSheet(idx); auto &parent = getSubSheet(idx);
if (parent.subsheets.size() < 2) { if (parent.subsheets.size() < 2) {
parent.subsheets.emplace_back(ox::sfmt("Subsheet {}", parent.subsheets.size()).c_str(), 1, 1, bpp); parent.subsheets.emplace_back(ox::sfmt("Subsheet {}", parent.subsheets.size()), 1, 1, bpp);
} else { } else {
parent.subsheets.emplace_back("Subsheet 0", parent.columns, parent.rows, bpp); parent.subsheets.emplace_back("Subsheet 0", parent.columns, parent.rows, bpp);
parent.subsheets.emplace_back("Subsheet 1", 1, 1, bpp); parent.subsheets.emplace_back("Subsheet 1", 1, 1, bpp);
@ -468,21 +465,31 @@ oxModelEnd()
struct Sprite { struct Sprite {
unsigned idx = 0; unsigned idx = 0;
unsigned x = 0; int x = 0;
unsigned y = 0; int y = 0;
unsigned tileIdx = 0; unsigned tileIdx = 0;
unsigned spriteShape = 0; unsigned spriteShape = 0;
unsigned spriteSize = 0; unsigned spriteSize = 0;
unsigned flipX = 0; unsigned flipX = 0;
}; };
oxModelBegin(Sprite)
oxModelField(idx)
oxModelField(x)
oxModelField(y)
oxModelField(tileIdx)
oxModelField(spriteShape)
oxModelField(spriteSize)
oxModelField(flipX)
oxModelEnd()
ox::Error initGfx(Context *ctx) noexcept; ox::Error initGfx(Context *ctx) noexcept;
void addCustomDrawer(Context *ctx, Drawer *cd) noexcept; void addCustomDrawer(Context *ctx, Drawer *cd) noexcept;
void removeCustomDrawer(Context *ctx, Drawer *cd) noexcept; void removeCustomDrawer(Context *ctx, Drawer *cd) noexcept;
void setWindowTitle(Context *ctx, const char *title) noexcept; void setWindowTitle(Context *ctx, ox::CRStringView title) noexcept;
void focusWindow(Context *ctx) noexcept; void focusWindow(Context *ctx) noexcept;
@ -515,11 +522,10 @@ ox::Error loadBgTileSheet(Context *ctx, unsigned cbb, const ox::FileAddress &til
const ox::FileAddress &palette = nullptr) noexcept; const ox::FileAddress &palette = nullptr) noexcept;
ox::Error loadSpriteTileSheet(Context *ctx, ox::Error loadSpriteTileSheet(Context *ctx,
unsigned section,
const ox::FileAddress &tilesheetAddr, const ox::FileAddress &tilesheetAddr,
const ox::FileAddress &paletteAddr) noexcept; const ox::FileAddress &paletteAddr) noexcept;
void puts(Context *ctx, int column, int row, const char *str) noexcept; void puts(Context *ctx, int column, int row, ox::CRStringView str) noexcept;
void setTile(Context *ctx, unsigned bgIdx, int column, int row, uint8_t tile) noexcept; void setTile(Context *ctx, unsigned bgIdx, int column, int row, uint8_t tile) noexcept;
@ -527,7 +533,7 @@ void clearTileLayer(Context *ctx, unsigned bgIdx) noexcept;
void hideSprite(Context *ctx, unsigned) noexcept; void hideSprite(Context *ctx, unsigned) noexcept;
void setSprite(Context *ctx, unsigned idx, unsigned x, unsigned y, unsigned tileIdx, void setSprite(Context *ctx, unsigned idx, int x, int y, unsigned tileIdx,
unsigned spriteShape = 0, unsigned spriteSize = 0, unsigned flipX = 0) noexcept; unsigned spriteShape = 0, unsigned spriteSize = 0, unsigned flipX = 0) noexcept;
void setSprite(Context *ctx, const Sprite &s) noexcept; void setSprite(Context *ctx, const Sprite &s) noexcept;

View File

@ -17,9 +17,11 @@ ox::String getClipboardText(Context *ctx) noexcept {
return glfwGetClipboardString(id->window); return glfwGetClipboardString(id->window);
} }
void setClipboardText(Context *ctx, const ox::String &text) noexcept { void setClipboardText(Context *ctx, ox::CRStringView text) noexcept {
const auto id = ctx->windowerData<GlfwImplData>(); const auto id = ctx->windowerData<GlfwImplData>();
glfwSetClipboardString(id->window, text.c_str()); auto cstr = ox_malloca(text.bytes() + 1, char);
ox_strncpy(cstr.get(), text.data(), text.bytes());
glfwSetClipboardString(id->window, cstr);
} }
} }

View File

@ -13,7 +13,7 @@
namespace nostalgia::core { namespace nostalgia::core {
ox::Result<ox::UniquePtr<Context>> init(ox::UniquePtr<ox::FileSystem> fs, const char *appName) noexcept { ox::Result<ox::UniquePtr<Context>> init(ox::UniquePtr<ox::FileSystem> fs, ox::CRStringView appName) noexcept {
auto ctx = ox::make_unique<Context>(); auto ctx = ox::make_unique<Context>();
ctx->rom = std::move(fs); ctx->rom = std::move(fs);
ctx->appName = appName; ctx->appName = appName;
@ -32,15 +32,13 @@ ox::Error run(Context *ctx) noexcept {
while (!glfwWindowShouldClose(id->window)) { while (!glfwWindowShouldClose(id->window)) {
glfwPollEvents(); glfwPollEvents();
const auto ticks = ticksMs(ctx); const auto ticks = ticksMs(ctx);
if (id->eventHandler) { if (id->wakeupTime <= ticks) {
if (id->wakeupTime <= ticks) { sleepTime = id->eventHandler(ctx);
sleepTime = id->eventHandler(ctx); if (sleepTime >= 0) {
if (sleepTime >= 0) { id->wakeupTime = ticks + static_cast<unsigned>(sleepTime);
id->wakeupTime = ticks + static_cast<unsigned>(sleepTime); } else {
} else { id->wakeupTime = ~uint64_t(0);
id->wakeupTime = ~uint64_t(0); }
}
}
} else { } else {
sleepTime = 10; sleepTime = 10;
} }

View File

@ -11,7 +11,7 @@ namespace nostalgia::core {
struct GlfwImplData { struct GlfwImplData {
struct GLFWwindow *window = nullptr; struct GLFWwindow *window = nullptr;
int64_t startTime = 0; int64_t startTime = 0;
UpdateHandler eventHandler = nullptr; UpdateHandler eventHandler = [](Context*) -> int {return 0;};
KeyEventHandler keyEventHandler = nullptr; KeyEventHandler keyEventHandler = nullptr;
uint64_t wakeupTime = 0; uint64_t wakeupTime = 0;
uint64_t keysDown = 0; uint64_t keysDown = 0;

View File

@ -192,7 +192,9 @@ ox::Error initGfx(Context *ctx) noexcept {
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE); glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
} }
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
id->window = glfwCreateWindow(240 * Scale, 160 * Scale, ctx->appName, nullptr, nullptr); auto cstr = ox_malloca(ctx->appName.bytes() + 1, char);
ox_strncpy(cstr.get(), ctx->appName.data(), ctx->appName.bytes());
id->window = glfwCreateWindow(240 * Scale, 160 * Scale, cstr, nullptr, nullptr);
if (id->window == nullptr) { if (id->window == nullptr) {
return OxError(1, "Could not open GLFW window"); return OxError(1, "Could not open GLFW window");
} }
@ -220,9 +222,11 @@ ox::Error initGfx(Context *ctx) noexcept {
return OxError(0); return OxError(0);
} }
void setWindowTitle(Context *ctx, const char *title) noexcept { void setWindowTitle(Context *ctx, ox::CRStringView title) noexcept {
const auto id = ctx->windowerData<GlfwImplData>(); const auto id = ctx->windowerData<GlfwImplData>();
glfwSetWindowTitle(id->window, title); auto cstr = ox_malloca(title.bytes() + 1, char);
ox_strncpy(cstr.get(), title.data(), title.bytes());
glfwSetWindowTitle(id->window, cstr);
} }
void focusWindow(Context *ctx) noexcept { void focusWindow(Context *ctx) noexcept {

View File

@ -7,7 +7,7 @@
namespace nostalgia::core { namespace nostalgia::core {
ox::Result<ox::UniquePtr<Context>> init(ox::UniquePtr<ox::FileSystem>, const char*) noexcept { ox::Result<ox::UniquePtr<Context>> init(ox::UniquePtr<ox::FileSystem>, ox::CRStringView) noexcept {
return OxError(1); return OxError(1);
} }

View File

@ -15,7 +15,7 @@ ox::Error shutdownGfx(Context*) noexcept {
return OxError(0); return OxError(0);
} }
void setWindowTitle(Context*, const char*) noexcept { void setWindowTitle(Context*, ox::CRStringView) noexcept {
} }
void focusWindow(Context*) noexcept { void focusWindow(Context*) noexcept {
@ -60,7 +60,6 @@ ox::Error loadBgTileSheet(Context*,
} }
ox::Error loadSpriteTileSheet(Context*, ox::Error loadSpriteTileSheet(Context*,
int,
const ox::FileAddress&, const ox::FileAddress&,
const ox::FileAddress&) noexcept { const ox::FileAddress&) noexcept {
return OxError(0); return OxError(0);
@ -75,7 +74,7 @@ ox::Error loadSpritePalette(Context*, int, const ox::FileAddress&) noexcept {
} }
// Do NOT use Context in the GBA version of this function. // Do NOT use Context in the GBA version of this function.
void puts(Context*, int, int, const char*) noexcept { void puts(Context*, int, int, ox::CRStringView) noexcept {
} }
void setTile(Context*, int, int, int, uint8_t) noexcept { void setTile(Context*, int, int, int, uint8_t) noexcept {
@ -91,8 +90,8 @@ void hideSprite(Context*, unsigned) noexcept {
void setSprite(Context*, void setSprite(Context*,
unsigned, unsigned,
unsigned, int,
unsigned, int,
unsigned, unsigned,
unsigned, unsigned,
unsigned, unsigned,

View File

@ -8,7 +8,7 @@
namespace nostalgia::core { namespace nostalgia::core {
ox::Result<char*> loadRom(const char*) noexcept { ox::Result<char*> loadRom(ox::CRStringView) noexcept {
return OxError(1); return OxError(1);
} }

View File

@ -6,9 +6,9 @@
namespace nostalgia::core { namespace nostalgia::core {
ox::Result<ox::UniquePtr<ox::FileSystem>> loadRomFs(const char *path) noexcept { ox::Result<ox::UniquePtr<ox::FileSystem>> loadRomFs(ox::CRStringView path) noexcept {
const auto lastDot = ox_lastIndexOf(path, '.'); const auto lastDot = ox_lastIndexOf(path, '.');
const auto fsExt = lastDot != -1 ? path + lastDot : ""; const auto fsExt = lastDot != -1 ? path.substr(static_cast<std::size_t>(lastDot)) : "";
if (ox_strcmp(fsExt, ".oxfs") == 0) { if (ox_strcmp(fsExt, ".oxfs") == 0) {
oxRequire(rom, core::loadRom(path)); oxRequire(rom, core::loadRom(path));
return {ox::make_unique<ox::FileSystem32>(rom, 32 * ox::units::MB, unloadRom)}; return {ox::make_unique<ox::FileSystem32>(rom, 32 * ox::units::MB, unloadRom)};

View File

@ -26,9 +26,10 @@ oxModelBegin(PreloadPtr)
oxModelEnd() oxModelEnd()
ox::Result<std::size_t> getPreloadAddr(Context *ctx, const ox::FileAddress &file) noexcept; ox::Result<std::size_t> getPreloadAddr(Context *ctx, const ox::FileAddress &file) noexcept;
ox::Result<std::size_t> getPreloadAddr(Context *ctx, ox::CRStringView file) noexcept;
template<typename T> template<typename T>
ox::Result<AssetRef<T>> readObj(Context *ctx, const ox::FileAddress &file, ox::Result<AssetRef<T>> readObj([[maybe_unused]] Context *ctx, [[maybe_unused]] ox::CRStringView path,
[[maybe_unused]] bool forceLoad = false) noexcept { [[maybe_unused]] bool forceLoad = false) noexcept {
#ifndef OX_BARE_METAL #ifndef OX_BARE_METAL
constexpr auto readConvert = [](const ox::Buffer &buff) -> ox::Result<T> { constexpr auto readConvert = [](const ox::Buffer &buff) -> ox::Result<T> {
@ -41,21 +42,36 @@ ox::Result<AssetRef<T>> readObj(Context *ctx, const ox::FileAddress &file,
} }
return std::move(obj); return std::move(obj);
}; };
oxRequire(path, file.getPath());
if (forceLoad) { if (forceLoad) {
oxRequire(buff, ctx->rom->read(file)); oxRequire(buff, ctx->rom->read(path));
oxRequire(obj, readConvert(buff)); oxRequire(obj, readConvert(buff));
oxRequire(cached, ctx->assetManager.setAsset(path, obj)); oxRequire(cached, ctx->assetManager.setAsset(path, obj));
return std::move(cached); return std::move(cached);
} else { } else {
auto [cached, err] = ctx->assetManager.getAsset<T>(path); auto [cached, err] = ctx->assetManager.getAsset<T>(path);
if (err) { if (err) {
oxRequire(buff, ctx->rom->read(file)); oxRequire(buff, ctx->rom->read(path));
oxRequire(obj, readConvert(buff)); oxRequire(obj, readConvert(buff));
oxReturnError(ctx->assetManager.setAsset(path, obj).moveTo(&cached)); oxReturnError(ctx->assetManager.setAsset(path, obj).moveTo(&cached));
} }
return std::move(cached); return std::move(cached);
} }
#else
if constexpr(ox::preloadable<T>::value) {
oxRequire(addr, getPreloadAddr(ctx, path));
return AssetRef<T>(reinterpret_cast<const T*>(addr));
} else {
return OxError(1);
}
#endif
}
template<typename T>
ox::Result<AssetRef<T>> readObj(Context *ctx, const ox::FileAddress &file,
[[maybe_unused]] bool forceLoad = false) noexcept {
#ifndef OX_BARE_METAL
oxRequire(path, file.getPath());
return readObj<T>(ctx, ox::StringView(path), forceLoad);
#else #else
if constexpr(ox::preloadable<T>::value) { if constexpr(ox::preloadable<T>::value) {
oxRequire(addr, getPreloadAddr(ctx, file)); oxRequire(addr, getPreloadAddr(ctx, file));
@ -73,9 +89,9 @@ ox::Error writeObj(Context *ctx, const ox::FileAddress &file, const T &obj,
return ctx->rom->write(file, objBuff.data(), objBuff.size()); return ctx->rom->write(file, objBuff.data(), objBuff.size());
} }
ox::Result<ox::UniquePtr<ox::FileSystem>> loadRomFs(const char *path) noexcept; ox::Result<ox::UniquePtr<ox::FileSystem>> loadRomFs(ox::CRStringView path) noexcept;
ox::Result<char*> loadRom(const char *path = "") noexcept; ox::Result<char*> loadRom(ox::CRStringView path = "") noexcept;
void unloadRom(char*) noexcept; void unloadRom(char*) noexcept;

View File

@ -2,7 +2,8 @@
* Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved. * Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/ */
#include "ox/std/memory.hpp" #include <ox/std/memory.hpp>
#include "paletteeditor-imgui.hpp" #include "paletteeditor-imgui.hpp"
#include "tilesheeteditor-imgui.hpp" #include "tilesheeteditor-imgui.hpp"
@ -14,7 +15,7 @@ ox::Vector<studio::EditorMaker> Module::editors(core::Context *ctx) noexcept {
return { return {
{ {
{"ng"}, {"ng"},
[ctx](const ox::String &path) -> ox::Result<studio::BaseEditor*> { [ctx](ox::CRStringView path) -> ox::Result<studio::BaseEditor*> {
try { try {
return ox::make<TileSheetEditorImGui>(ctx, path); return ox::make<TileSheetEditorImGui>(ctx, path);
} catch (const ox::Exception &ex) { } catch (const ox::Exception &ex) {
@ -24,7 +25,7 @@ ox::Vector<studio::EditorMaker> Module::editors(core::Context *ctx) noexcept {
}, },
{ {
{"npal"}, {"npal"},
[ctx](const ox::String &path) -> ox::Result<studio::BaseEditor*> { [ctx](ox::CRStringView path) -> ox::Result<studio::BaseEditor*> {
return PaletteEditorImGui::make(ctx, path); return PaletteEditorImGui::make(ctx, path);
} }
} }

View File

@ -13,13 +13,13 @@
namespace nostalgia::core { namespace nostalgia::core {
ox::Result<PaletteEditorImGui*> PaletteEditorImGui::make(Context *ctx, const ox::String &path) noexcept { ox::Result<PaletteEditorImGui*> PaletteEditorImGui::make(Context *ctx, ox::CRStringView path) noexcept {
auto out = ox::UniquePtr<PaletteEditorImGui>(new PaletteEditorImGui); auto out = ox::UniquePtr<PaletteEditorImGui>(new PaletteEditorImGui);
out->m_ctx = ctx; out->m_ctx = ctx;
out->m_itemPath = path; out->m_itemPath = path;
const auto lastSlash = std::find(out->m_itemPath.rbegin(), out->m_itemPath.rend(), '/').offset(); const auto lastSlash = std::find(out->m_itemPath.rbegin(), out->m_itemPath.rend(), '/').offset();
out->m_itemName = out->m_itemPath.substr(lastSlash + 1); out->m_itemName = out->m_itemPath.substr(lastSlash + 1);
oxRequire(pal, core::readObj<Palette>(out->m_ctx, out->m_itemPath)); oxRequire(pal, core::readObj<Palette>(out->m_ctx, ox::FileAddress(out->m_itemPath.c_str())));
out->m_pal = *pal; out->m_pal = *pal;
return out.release(); return out.release();
} }

View File

@ -21,7 +21,7 @@ class PaletteEditorImGui: public studio::Editor {
PaletteEditorImGui() noexcept = default; PaletteEditorImGui() noexcept = default;
public: public:
static ox::Result<PaletteEditorImGui*> make(Context *ctx, const ox::String &path) noexcept; static ox::Result<PaletteEditorImGui*> make(Context *ctx, ox::CRStringView path) noexcept;
/** /**
* Returns the name of item being edited. * Returns the name of item being edited.

View File

@ -38,7 +38,7 @@ ox::Error toPngFile(const ox::String &path, const TileSheet::SubSheet &s, const
return OxError(lodepng_encode_file(path.c_str(), outData.data(), width, height, fmt, 8)); return OxError(lodepng_encode_file(path.c_str(), outData.data(), width, height, fmt, 8));
} }
TileSheetEditorImGui::TileSheetEditorImGui(Context *ctx, const ox::String &path): m_tileSheetEditor(ctx, path) { TileSheetEditorImGui::TileSheetEditorImGui(Context *ctx, ox::CRStringView path): m_tileSheetEditor(ctx, path) {
m_ctx = ctx; m_ctx = ctx;
m_itemPath = path; m_itemPath = path;
const auto lastSlash = ox::find(m_itemPath.rbegin(), m_itemPath.rend(), '/').offset(); const auto lastSlash = ox::find(m_itemPath.rbegin(), m_itemPath.rend(), '/').offset();
@ -416,7 +416,7 @@ void TileSheetEditorImGui::SubSheetEditor::draw() noexcept {
if (ImGui::Button("OK")) { if (ImGui::Button("OK")) {
ImGui::CloseCurrentPopup(); ImGui::CloseCurrentPopup();
m_show = false; m_show = false;
inputSubmitted.emit(m_name.c_str(), m_cols, m_rows); inputSubmitted.emit(m_name, m_cols, m_rows);
} }
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Button("Cancel")) { if (ImGui::Button("Cancel")) {

View File

@ -32,7 +32,7 @@ class TileSheetEditorImGui: public studio::BaseEditor {
int m_rows = 0; int m_rows = 0;
bool m_show = false; bool m_show = false;
public: public:
ox::Signal<ox::Error(const ox::String &name, int cols, int rows)> inputSubmitted; ox::Signal<ox::Error(const ox::StringView &name, int cols, int rows)> inputSubmitted;
constexpr void show(const ox::String &name, int cols, int rows) noexcept { constexpr void show(const ox::String &name, int cols, int rows) noexcept {
m_show = true; m_show = true;
m_name = name.c_str(); m_name = name.c_str();
@ -54,7 +54,7 @@ class TileSheetEditorImGui: public studio::BaseEditor {
Tool m_tool = Tool::Draw; Tool m_tool = Tool::Draw;
public: public:
TileSheetEditorImGui(Context *ctx, const ox::String &path); TileSheetEditorImGui(Context *ctx, ox::CRStringView path);
~TileSheetEditorImGui() override = default; ~TileSheetEditorImGui() override = default;

View File

@ -256,7 +256,7 @@ class AddSubSheetCommand: public TileSheetCommand {
auto &parent = m_img->getSubSheet(m_parentIdx); auto &parent = m_img->getSubSheet(m_parentIdx);
if (m_addedSheets.size() < 2) { if (m_addedSheets.size() < 2) {
auto i = parent.subsheets.size(); auto i = parent.subsheets.size();
parent.subsheets.emplace_back(ox::sfmt("Subsheet {}", i).c_str(), 1, 1, m_img->bpp); parent.subsheets.emplace_back(ox::sfmt("Subsheet {}", i), 1, 1, m_img->bpp);
} else { } else {
parent.subsheets.emplace_back("Subsheet 0", parent.columns, parent.rows, std::move(parent.pixels)); parent.subsheets.emplace_back("Subsheet 0", parent.columns, parent.rows, std::move(parent.pixels));
parent.rows = 0; parent.rows = 0;
@ -511,7 +511,7 @@ class PaletteChangeCommand: public TileSheetCommand {
m_idx = idx; m_idx = idx;
m_img = img; m_img = img;
m_oldPalette = m_img->defaultPalette; m_oldPalette = m_img->defaultPalette;
m_newPalette = newPalette; m_newPalette = ox::FileAddress(newPalette);
} }
void redo() noexcept final { void redo() noexcept final {
@ -538,7 +538,7 @@ class PaletteChangeCommand: public TileSheetCommand {
TileSheetEditorModel::TileSheetEditorModel(Context *ctx, ox::String path) { TileSheetEditorModel::TileSheetEditorModel(Context *ctx, ox::String path) {
m_ctx = ctx; m_ctx = ctx;
m_path = std::move(path); m_path = std::move(path);
oxRequireT(img, readObj<TileSheet>(ctx, m_path.c_str())); oxRequireT(img, readObj<TileSheet>(ctx, m_path));
m_img = *img; m_img = *img;
if (m_img.defaultPalette) { if (m_img.defaultPalette) {
oxThrowError(readObj<Palette>(ctx, m_img.defaultPalette).moveTo(&m_pal)); oxThrowError(readObj<Palette>(ctx, m_img.defaultPalette).moveTo(&m_pal));
@ -709,7 +709,7 @@ ox::Error TileSheetEditorModel::markUpdatedCmdId(const studio::UndoCommand *cmd)
m_updated = true; m_updated = true;
const auto cmdId = cmd->commandId(); const auto cmdId = cmd->commandId();
if (static_cast<CommandId>(cmdId) == CommandId::PaletteChange) { if (static_cast<CommandId>(cmdId) == CommandId::PaletteChange) {
oxReturnError(readObj<Palette>(m_ctx, m_img.defaultPalette.getPath().value).moveTo(&m_pal)); oxReturnError(readObj<Palette>(m_ctx, ox::StringView(m_img.defaultPalette.getPath().value)).moveTo(&m_pal));
} }
auto tsCmd = dynamic_cast<const TileSheetCommand*>(cmd); auto tsCmd = dynamic_cast<const TileSheetCommand*>(cmd);
auto idx = m_img.validateSubSheetIdx(tsCmd->subsheetIdx()); auto idx = m_img.validateSubSheetIdx(tsCmd->subsheetIdx());

View File

@ -10,7 +10,7 @@
namespace nostalgia::core { namespace nostalgia::core {
TileSheetEditorView::TileSheetEditorView(Context *ctx, const ox::String &path): m_model(ctx, path), m_pixelsDrawer(&m_model) { TileSheetEditorView::TileSheetEditorView(Context *ctx, ox::CRStringView path): m_model(ctx, path), m_pixelsDrawer(&m_model) {
// build shaders // build shaders
oxThrowError(m_pixelsDrawer.buildShader()); oxThrowError(m_pixelsDrawer.buildShader());
oxThrowError(m_pixelGridDrawer.buildShader()); oxThrowError(m_pixelGridDrawer.buildShader());

View File

@ -49,7 +49,7 @@ class TileSheetEditorView: public ox::SignalHandler {
std::size_t m_palIdx = 0; std::size_t m_palIdx = 0;
public: public:
TileSheetEditorView(Context *ctx, const ox::String &path); TileSheetEditorView(Context *ctx, ox::CRStringView path);
~TileSheetEditorView() override = default; ~TileSheetEditorView() override = default;

View File

@ -49,8 +49,8 @@ static const auto converters = [] {
}(); }();
[[nodiscard]] [[nodiscard]]
static auto findConverter(const char *srcTypeName, int srcTypeVersion, static auto findConverter(ox::CRStringView srcTypeName, int srcTypeVersion,
const char *dstTypeName, int dstTypeVersion) noexcept -> ox::Result<BaseConverter*> { ox::CRStringView dstTypeName, int dstTypeVersion) noexcept -> ox::Result<BaseConverter*> {
for (auto &c : converters) { for (auto &c : converters) {
if (c->matches(srcTypeName, srcTypeVersion, dstTypeName, dstTypeVersion)) { if (c->matches(srcTypeName, srcTypeVersion, dstTypeName, dstTypeVersion)) {
return c.get(); return c.get();
@ -60,8 +60,8 @@ static auto findConverter(const char *srcTypeName, int srcTypeVersion,
}; };
static ox::Result<ox::UniquePtr<Wrap>> convert(const ox::Buffer &srcBuffer, static ox::Result<ox::UniquePtr<Wrap>> convert(const ox::Buffer &srcBuffer,
const char *srcTypeName, int srcTypeVersion, ox::CRStringView srcTypeName, int srcTypeVersion,
const char *dstTypeName, int dstTypeVersion) noexcept { ox::CRStringView dstTypeName, int dstTypeVersion) noexcept {
// look for direct converter // look for direct converter
auto [c, err] = findConverter(srcTypeName, srcTypeVersion, dstTypeName, dstTypeVersion); auto [c, err] = findConverter(srcTypeName, srcTypeVersion, dstTypeName, dstTypeVersion);
if (!err) { if (!err) {
@ -82,9 +82,9 @@ static ox::Result<ox::UniquePtr<Wrap>> convert(const ox::Buffer &srcBuffer,
return OxError(1, "Could not convert between types"); return OxError(1, "Could not convert between types");
} }
ox::Result<ox::UniquePtr<Wrap>> convert(const ox::Buffer &srcBuffer, const char *dstTypeName, int dstTypeVersion) noexcept { ox::Result<ox::UniquePtr<Wrap>> convert(const ox::Buffer &srcBuffer, ox::CRStringView dstTypeName, int dstTypeVersion) noexcept {
oxRequire(hdr, ox::readClawHeader(srcBuffer)); oxRequire(hdr, ox::readClawHeader(srcBuffer));
return convert(srcBuffer, hdr.typeName.c_str(), hdr.typeVersion, dstTypeName, dstTypeVersion); return convert(srcBuffer, hdr.typeName, hdr.typeVersion, dstTypeName, dstTypeVersion);
} }
#endif #endif

View File

@ -51,24 +51,24 @@ struct BaseConverter {
virtual ~BaseConverter() noexcept = default; virtual ~BaseConverter() noexcept = default;
[[nodiscard]] [[nodiscard]]
virtual const char *srcTypeName() noexcept = 0; virtual ox::StringView srcTypeName() noexcept = 0;
[[nodiscard]] [[nodiscard]]
virtual int srcTypeVersion() noexcept = 0; virtual int srcTypeVersion() noexcept = 0;
[[nodiscard]] [[nodiscard]]
virtual bool srcMatches(const char *srcTypeName, int srcTypeVersion) const noexcept = 0; virtual bool srcMatches(ox::CRStringView srcTypeName, int srcTypeVersion) const noexcept = 0;
[[nodiscard]] [[nodiscard]]
virtual bool dstMatches(const char *dstTypeName, int dstTypeVersion) const noexcept = 0; virtual bool dstMatches(ox::CRStringView dstTypeName, int dstTypeVersion) const noexcept = 0;
virtual ox::Result<ox::UniquePtr<Wrap>> convertPtrToPtr(Wrap *src) noexcept = 0; virtual ox::Result<ox::UniquePtr<Wrap>> convertPtrToPtr(Wrap *src) noexcept = 0;
virtual ox::Result<ox::UniquePtr<Wrap>> convertBuffToPtr(const ox::Buffer &srcBuff) noexcept = 0; virtual ox::Result<ox::UniquePtr<Wrap>> convertBuffToPtr(const ox::Buffer &srcBuff) noexcept = 0;
[[nodiscard]] [[nodiscard]]
inline bool matches(const char *srcTypeName, int srcTypeVersion, inline bool matches(ox::CRStringView srcTypeName, int srcTypeVersion,
const char *dstTypeName, int dstTypeVersion) const noexcept { ox::CRStringView dstTypeName, int dstTypeVersion) const noexcept {
return srcMatches(srcTypeName, srcTypeVersion) return srcMatches(srcTypeName, srcTypeVersion)
&& dstMatches(dstTypeName, dstTypeVersion); && dstMatches(dstTypeName, dstTypeVersion);
} }
@ -81,7 +81,7 @@ struct Converter: public BaseConverter {
virtual ox::Error convert(SrcType*, DstType*) noexcept = 0; virtual ox::Error convert(SrcType*, DstType*) noexcept = 0;
[[nodiscard]] [[nodiscard]]
const char *srcTypeName() noexcept final { ox::StringView srcTypeName() noexcept final {
return ox::requireModelTypeName<SrcType>(); return ox::requireModelTypeName<SrcType>();
} }
@ -91,7 +91,7 @@ struct Converter: public BaseConverter {
} }
[[nodiscard]] [[nodiscard]]
bool srcMatches(const char *srcTypeName, int srcTypeVersion) const noexcept final { bool srcMatches(ox::CRStringView srcTypeName, int srcTypeVersion) const noexcept final {
static constexpr auto SrcTypeName = ox::requireModelTypeName<SrcType>(); static constexpr auto SrcTypeName = ox::requireModelTypeName<SrcType>();
static constexpr auto SrcTypeVersion = ox::requireModelTypeVersion<SrcType>(); static constexpr auto SrcTypeVersion = ox::requireModelTypeVersion<SrcType>();
return ox_strcmp(srcTypeName, SrcTypeName) == 0 return ox_strcmp(srcTypeName, SrcTypeName) == 0
@ -99,7 +99,7 @@ struct Converter: public BaseConverter {
} }
[[nodiscard]] [[nodiscard]]
bool dstMatches(const char *dstTypeName, int dstTypeVersion) const noexcept final { bool dstMatches(ox::CRStringView dstTypeName, int dstTypeVersion) const noexcept final {
static constexpr auto DstTypeName = ox::requireModelTypeName<DstType>(); static constexpr auto DstTypeName = ox::requireModelTypeName<DstType>();
static constexpr auto DstTypeVersion = ox::requireModelTypeVersion<DstType>(); static constexpr auto DstTypeVersion = ox::requireModelTypeVersion<DstType>();
return ox_strcmp(dstTypeName, DstTypeName) == 0 return ox_strcmp(dstTypeName, DstTypeName) == 0
@ -122,7 +122,7 @@ struct Converter: public BaseConverter {
}; };
ox::Result<ox::UniquePtr<Wrap>> convert(const ox::Buffer &srcBuffer, ox::Result<ox::UniquePtr<Wrap>> convert(const ox::Buffer &srcBuffer,
const char *dstTypeName, int dstTypeVersion) noexcept; ox::CRStringView dstTypeName, int dstTypeVersion) noexcept;
template<typename DstType> template<typename DstType>
ox::Result<DstType> convert(const ox::Buffer &srcBuffer) noexcept { ox::Result<DstType> convert(const ox::Buffer &srcBuffer) noexcept {

View File

@ -10,52 +10,71 @@
namespace nostalgia::core { namespace nostalgia::core {
ox::Error initConsole(Context *ctx) noexcept { ox::Error initConsole(Context *ctx) noexcept {
constexpr auto TilesheetAddr = "/TileSheets/Charset.ng"; constexpr ox::FileAddress TilesheetAddr("/TileSheets/Charset.ng");
constexpr auto PaletteAddr = "/Palettes/Charset.npal"; constexpr ox::FileAddress PaletteAddr("/Palettes/Charset.npal");
setBgStatus(ctx, 0b0001); setBgStatus(ctx, 0b0001);
setBgCbb(ctx, 0, 0); setBgCbb(ctx, 0, 0);
return loadBgTileSheet(ctx, 0, TilesheetAddr, PaletteAddr); return loadBgTileSheet(ctx, 0, TilesheetAddr, PaletteAddr);
} }
ox::Error loadSpriteTileSheet(Context*, struct TileSheetData {
unsigned, ox::Vector<uint32_t> pixels;
const ox::FileAddress&, int width = 0;
const ox::FileAddress&) noexcept { int height = 0;
return OxError(0); };
}
ox::Error loadBgTileSheet(Context *ctx, ox::Result<TileSheetData> loadTileSheet(Context *ctx, const CompactTileSheet &tilesheet) noexcept {
unsigned cbb, const unsigned bytesPerTile = tilesheet.bpp == 8 ? 64 : 32;
const ox::FileAddress &tilesheetPath, const auto tiles = tilesheet.pixels.size() / bytesPerTile;
const ox::FileAddress &palettePath) noexcept {
oxRequire(tilesheet, readObj<CompactTileSheet>(ctx, tilesheetPath));
oxRequire(palette, readObj<Palette>(ctx, palettePath ? palettePath : tilesheet->defaultPalette));
const unsigned bytesPerTile = tilesheet->bpp == 8 ? 64 : 32;
const auto tiles = tilesheet->pixels.size() / bytesPerTile;
constexpr int width = 8; constexpr int width = 8;
const int height = 8 * static_cast<int>(tiles); const int height = 8 * static_cast<int>(tiles);
ox::Vector<uint32_t> pixels; ox::Vector<uint32_t> pixels;
if (bytesPerTile == 64) { // 8 BPP if (bytesPerTile == 64) { // 8 BPP
pixels.resize(tilesheet->pixels.size()); pixels.resize(tilesheet.pixels.size());
for (std::size_t i = 0; i < tilesheet->pixels.size(); ++i) { for (std::size_t i = 0; i < tilesheet.pixels.size(); ++i) {
pixels[i] = tilesheet->pixels[i]; pixels[i] = tilesheet.pixels[i];
} }
} else { // 4 BPP } else { // 4 BPP
pixels.resize(tilesheet->pixels.size() * 2); pixels.resize(tilesheet.pixels.size() * 2);
for (std::size_t i = 0; i < tilesheet->pixels.size(); ++i) { for (std::size_t i = 0; i < tilesheet.pixels.size(); ++i) {
pixels[i * 2 + 0] = tilesheet->pixels[i] & 0xF; pixels[i * 2 + 0] = tilesheet.pixels[i] & 0xF;
pixels[i * 2 + 1] = tilesheet->pixels[i] >> 4; pixels[i * 2 + 1] = tilesheet.pixels[i] >> 4;
} }
} }
const auto rd = ctx->rendererData<void>(); const auto rd = ctx->rendererData<void>();
renderer::loadBgTexture(rd, cbb, pixels.data(), width, height); renderer::loadSpriteTexture(rd, pixels.data(), width, height);
renderer::loadBgPalette(rd, *palette); return TileSheetData{std::move(pixels), width, height};
return OxError(0);
} }
void puts(Context *ctx, int column, int row, const char *str) noexcept { ox::Error loadBgTileSheet(Context *ctx,
for (int i = 0; str[i]; ++i) { unsigned cbb,
setTile(ctx, 0, column + i, row, static_cast<uint8_t>(charMap[static_cast<uint8_t>(str[i])])); const ox::FileAddress &tilesheetAddr,
const ox::FileAddress &paletteAddr) noexcept {
oxRequire(tilesheet, readObj<CompactTileSheet>(ctx, tilesheetAddr));
oxRequire(palette, readObj<Palette>(ctx, paletteAddr ? paletteAddr : tilesheet->defaultPalette));
oxRequire(tsd, loadTileSheet(ctx, *tilesheet));
const auto rd = ctx->rendererData<void>();
renderer::loadBgTexture(rd, cbb, tsd.pixels.data(), tsd.width, tsd.height);
renderer::loadBgPalette(rd, *palette);
return {};
}
ox::Error loadSpriteTileSheet(Context *ctx,
const ox::FileAddress &tilesheetAddr,
const ox::FileAddress &paletteAddr) noexcept {
oxRequire(tilesheet, readObj<CompactTileSheet>(ctx, tilesheetAddr));
oxRequire(palette, readObj<Palette>(ctx, paletteAddr ? paletteAddr : tilesheet->defaultPalette));
oxRequire(tsd, loadTileSheet(ctx, *tilesheet));
const auto rd = ctx->rendererData<void>();
renderer::loadSpriteTexture(rd, tsd.pixels.data(), tsd.width, tsd.height);
renderer::loadSpritePalette(rd, *palette);
return {};
}
void puts(Context *ctx, int column, int row, ox::CRStringView str) noexcept {
const auto col = static_cast<unsigned>(column);
for (auto i = 0u; i < str.bytes(); ++i) {
setTile(ctx, 0, static_cast<int>(col + i), row, static_cast<uint8_t>(charMap[static_cast<uint8_t>(str[i])]));
} }
} }

View File

@ -20,6 +20,10 @@ void shutdown(Context *ctx, void *rendererData) noexcept;
void loadBgPalette(void *rendererData, const Palette &pal) noexcept; void loadBgPalette(void *rendererData, const Palette &pal) noexcept;
void loadBgTexture(void *rendererData, unsigned cbb, void *pixels, int w, int h) noexcept; void loadBgTexture(void *rendererData, unsigned cbb, const void *pixels, int w, int h) noexcept;
void loadSpritePalette(void *rendererData, const Palette &pal) noexcept;
void loadSpriteTexture(void *rendererData, const void *pixels, int w, int h) noexcept;
} }

View File

@ -22,55 +22,96 @@ namespace renderer {
constexpr uint64_t TileRows = 128; constexpr uint64_t TileRows = 128;
constexpr uint64_t TileColumns = 128; constexpr uint64_t TileColumns = 128;
constexpr uint64_t TileCount = TileRows * TileColumns; constexpr uint64_t TileCount = TileRows * TileColumns;
constexpr uint64_t SpriteCount = 128;
constexpr uint64_t BgVertexVboRows = 4; constexpr uint64_t BgVertexVboRows = 4;
constexpr uint64_t BgVertexVboRowLength = 4; constexpr uint64_t BgVertexVboRowLength = 4;
constexpr uint64_t BgVertexVboLength = BgVertexVboRows * BgVertexVboRowLength; constexpr uint64_t BgVertexVboLength = BgVertexVboRows * BgVertexVboRowLength;
constexpr uint64_t BgVertexEboLength = 6; constexpr uint64_t BgVertexEboLength = 6;
constexpr uint64_t SpriteVertexVboRows = 4;
constexpr uint64_t SpriteVertexVboRowLength = 5;
constexpr uint64_t SpriteVertexVboLength = SpriteVertexVboRows * SpriteVertexVboRowLength;
constexpr uint64_t SpriteVertexEboLength = 6;
struct CBB: public glutils::BufferSet { struct CBB: public glutils::BufferSet {
bool updated = false; bool updated = false;
constexpr CBB() noexcept {
CBB() noexcept {
vertices.resize(TileCount * BgVertexVboLength); vertices.resize(TileCount * BgVertexVboLength);
elements.resize(TileCount * BgVertexEboLength); elements.resize(TileCount * BgVertexEboLength);
} }
}; };
struct SpriteBlockset: public glutils::BufferSet {
bool updated = false;
constexpr SpriteBlockset() noexcept {
vertices.resize(SpriteCount * SpriteVertexVboLength);
elements.resize(SpriteCount * SpriteVertexEboLength);
}
};
struct Background { struct Background {
bool enabled = false; bool enabled = false;
unsigned cbbIdx = 0; unsigned cbbIdx = 0;
}; };
struct Sprite {
bool enabled = false;
};
struct GlImplData { struct GlImplData {
glutils::GLProgram bgShader; glutils::GLProgram bgShader;
glutils::GLProgram spriteShader;
int64_t prevFpsCheckTime = 0; int64_t prevFpsCheckTime = 0;
uint64_t draws = 0; uint64_t draws = 0;
ox::Array<CBB, 4> cbbs; ox::Array<CBB, 4> cbbs;
SpriteBlockset spriteBlocks;
ox::Array<Sprite, 128> spriteStates;
ox::Array<Background, 4> backgrounds; ox::Array<Background, 4> backgrounds;
static constexpr std::size_t ColorCnt = 256;
ox::Array<GLfloat, ColorCnt * 3> palette{};
}; };
constexpr const GLchar *bgvshad = R"( constexpr ox::StringView bgvshadTmpl = R"(
{} {}
in vec2 vTexCoord; in vec2 vTexCoord;
in vec2 vPosition; in vec2 vPosition;
out vec2 fTexCoord; out vec2 fTexCoord;
uniform float vTileHeight; uniform float vTileHeight;
void main() { void main() {
gl_Position = vec4(vPosition, 0.0, 1.0); gl_Position = vec4(vPosition, 0.0, 1.0);
fTexCoord = vTexCoord * vec2(1, vTileHeight); fTexCoord = vTexCoord * vec2(1, vTileHeight);
})"; })";
constexpr const GLchar *bgfshad = R"( constexpr ox::StringView bgfshadTmpl = R"(
{} {}
out vec4 outColor; out vec4 outColor;
in vec2 fTexCoord; in vec2 fTexCoord;
uniform sampler2D image; uniform sampler2D image;
uniform vec3 fPalette[256]; uniform vec4 fPalette[256];
void main() { void main() {
int idx = int(texture(image, fTexCoord).rgb.r * 256); int idx = int(texture(image, fTexCoord).rgb.r * 256);
outColor = vec4(fPalette[idx], 1.0); outColor = fPalette[idx];
//outColor = vec4(0.0, 0.7, 1.0, 1.0);
})";
constexpr ox::StringView spritevshadTmpl = R"(
{}
in float vEnabled;
in vec2 vTexCoord;
in vec2 vPosition;
out vec2 fTexCoord;
uniform float vTileHeight;
void main() {
gl_Position = vec4(vPosition, 0.0, 1.0);
fTexCoord = vTexCoord * vec2(1, vTileHeight) * vec2(vEnabled, vEnabled);
})";
constexpr ox::StringView spritefshadTmpl = R"(
{}
out vec4 outColor;
in vec2 fTexCoord;
uniform sampler2D image;
uniform vec4 fPalette[256];
void main() {
int idx = int(texture(image, fTexCoord).rgb.r * 256);
outColor = fPalette[idx];
//outColor = vec4(0.0, 0.7, 1.0, 1.0); //outColor = vec4(0.0, 0.7, 1.0, 1.0);
})"; })";
@ -79,8 +120,46 @@ static constexpr auto bgVertexRow(unsigned x, unsigned y) noexcept {
return y * TileRows + x; return y * TileRows + x;
} }
static void static void setSpriteBufferObject(Context *ctx,
setTileBufferObject(Context *ctx, unsigned vi, float x, float y, int textureRow, float *vbo, GLuint *ebo) noexcept { unsigned vi,
float enabled,
float x, float y,
unsigned textureRow,
unsigned flipX,
float *vbo,
GLuint *ebo) noexcept {
// don't worry, this memcpy gets optimized to something much more ideal
const auto [sw, sh] = getScreenSize(ctx);
constexpr float ymod = 0.1f;
const auto xmod = ymod * static_cast<float>(sh) / static_cast<float>(sw);
x *= xmod;
y *= -ymod;
x -= 1.f;
y += 1.f - ymod;
const auto textureRowf = static_cast<float>(textureRow);
const float L = flipX ? 1 : 0;
const float R = flipX ? 0 : 1;
const ox::Array<float, SpriteVertexVboLength> vertices {
enabled, x, y, L, textureRowf + 1, // bottom left
enabled, x + xmod, y, R, textureRowf + 1, // bottom right
enabled, x + xmod, y + ymod, R, textureRowf + 0, // top right
enabled, x, y + ymod, L, textureRowf + 0, // top left
};
memcpy(vbo, vertices.data(), sizeof(vertices));
const ox::Array<GLuint, SpriteVertexEboLength> elms {
vi + 0, vi + 1, vi + 2,
vi + 2, vi + 3, vi + 0,
};
memcpy(ebo, elms.data(), sizeof(elms));
}
static void setTileBufferObject(Context *ctx,
unsigned vi,
float x,
float y,
unsigned textureRow,
float *vbo,
GLuint *ebo) noexcept {
// don't worry, this memcpy gets optimized to something much more ideal // don't worry, this memcpy gets optimized to something much more ideal
const auto [sw, sh] = getScreenSize(ctx); const auto [sw, sh] = getScreenSize(ctx);
constexpr float ymod = 2.0f / 20.0f; constexpr float ymod = 2.0f / 20.0f;
@ -89,11 +168,12 @@ setTileBufferObject(Context *ctx, unsigned vi, float x, float y, int textureRow,
y *= -ymod; y *= -ymod;
x -= 1.0f; x -= 1.0f;
y += 1.0f - ymod; y += 1.0f - ymod;
const auto textureRowf = static_cast<float>(textureRow);
const ox::Array<float, BgVertexVboLength> vertices { const ox::Array<float, BgVertexVboLength> vertices {
x, y, 0, static_cast<float>(textureRow + 1), // bottom left x, y, 0, textureRowf + 1, // bottom left
x + xmod, y, 1, static_cast<float>(textureRow + 1), // bottom right x + xmod, y, 1, textureRowf + 1, // bottom right
x + xmod, y + ymod, 1, static_cast<float>(textureRow + 0), // top right x + xmod, y + ymod, 1, textureRowf + 0, // top right
x, y + ymod, 0, static_cast<float>(textureRow + 0), // top left x, y + ymod, 0, textureRowf + 0, // top left
}; };
memcpy(vbo, vertices.data(), sizeof(vertices)); memcpy(vbo, vertices.data(), sizeof(vertices));
const ox::Array<GLuint, BgVertexEboLength> elms { const ox::Array<GLuint, BgVertexEboLength> elms {
@ -103,6 +183,14 @@ setTileBufferObject(Context *ctx, unsigned vi, float x, float y, int textureRow,
memcpy(ebo, elms.data(), sizeof(elms)); memcpy(ebo, elms.data(), sizeof(elms));
} }
static void initSpriteBufferObjects(Context *ctx, glutils::BufferSet *bs) noexcept {
for (auto i = 0u; i < SpriteCount; ++i) {
auto vbo = &bs->vertices[i * static_cast<std::size_t>(SpriteVertexVboLength)];
auto ebo = &bs->elements[i * static_cast<std::size_t>(SpriteVertexEboLength)];
setSpriteBufferObject(ctx, i * SpriteVertexVboRows, 0, 0, 0, 0, false, vbo, ebo);
}
}
static void initBackgroundBufferObjects(Context *ctx, glutils::BufferSet *bg) noexcept { static void initBackgroundBufferObjects(Context *ctx, glutils::BufferSet *bg) noexcept {
for (auto x = 0u; x < TileColumns; ++x) { for (auto x = 0u; x < TileColumns; ++x) {
for (auto y = 0u; y < TileRows; ++y) { for (auto y = 0u; y < TileRows; ++y) {
@ -114,6 +202,30 @@ static void initBackgroundBufferObjects(Context *ctx, glutils::BufferSet *bg) no
} }
} }
static void initSpritesBufferset(Context *ctx, GLuint shader, glutils::BufferSet *bs) noexcept {
// vao
bs->vao = glutils::generateVertexArrayObject();
glBindVertexArray(bs->vao);
// vbo & ebo
bs->vbo = glutils::generateBuffer();
bs->ebo = glutils::generateBuffer();
initSpriteBufferObjects(ctx, bs);
glutils::sendVbo(*bs);
glutils::sendEbo(*bs);
// vbo layout
auto enabledAttr = static_cast<GLuint>(glGetAttribLocation(shader, "vEnabled"));
glEnableVertexAttribArray(enabledAttr);
glVertexAttribPointer(enabledAttr, 1, GL_FLOAT, GL_FALSE, SpriteVertexVboRowLength * sizeof(float), nullptr);
auto posAttr = static_cast<GLuint>(glGetAttribLocation(shader, "vPosition"));
glEnableVertexAttribArray(posAttr);
glVertexAttribPointer(posAttr, 2, GL_FLOAT, GL_FALSE, SpriteVertexVboRowLength * sizeof(float),
reinterpret_cast<void*>(1 * sizeof(float)));
auto texCoordAttr = static_cast<GLuint>(glGetAttribLocation(shader, "vTexCoord"));
glEnableVertexAttribArray(texCoordAttr);
glVertexAttribPointer(texCoordAttr, 2, GL_FLOAT, GL_FALSE, SpriteVertexVboRowLength * sizeof(float),
reinterpret_cast<void*>(3 * sizeof(float)));
}
static void initBackgroundBufferset(Context *ctx, GLuint shader, glutils::BufferSet *bg) noexcept { static void initBackgroundBufferset(Context *ctx, GLuint shader, glutils::BufferSet *bg) noexcept {
// vao // vao
bg->vao = glutils::generateVertexArrayObject(); bg->vao = glutils::generateVertexArrayObject();
@ -134,7 +246,7 @@ static void initBackgroundBufferset(Context *ctx, GLuint shader, glutils::Buffer
reinterpret_cast<void*>(2 * sizeof(float))); reinterpret_cast<void*>(2 * sizeof(float)));
} }
static glutils::GLTexture loadTexture(GLsizei w, GLsizei h, void *pixels) noexcept { static glutils::GLTexture loadTexture(GLsizei w, GLsizei h, const void *pixels) noexcept {
GLuint texId = 0; GLuint texId = 0;
glGenTextures(1, &texId); glGenTextures(1, &texId);
glutils::GLTexture tex(texId); glutils::GLTexture tex(texId);
@ -179,10 +291,9 @@ static void drawBackground(CBB *cbb) noexcept {
static void drawBackgrounds(GlImplData *id) noexcept { static void drawBackgrounds(GlImplData *id) noexcept {
// load background shader and its uniforms // load background shader and its uniforms
glUseProgram(id->bgShader); glUseProgram(id->bgShader);
const auto uniformPalette = static_cast<GLint>(glGetUniformLocation(id->bgShader, "fPalette"));
const auto uniformTileHeight = static_cast<GLint>(glGetUniformLocation(id->bgShader, "vTileHeight")); const auto uniformTileHeight = static_cast<GLint>(glGetUniformLocation(id->bgShader, "vTileHeight"));
glUniform3fv(uniformPalette, GlImplData::ColorCnt, id->palette.data()); //glUniform3fv(uniformPalette, GlImplData::ColorCnt, id->palette.data());
for (auto &bg : id->backgrounds) { for (const auto &bg : id->backgrounds) {
if (bg.enabled) { if (bg.enabled) {
auto &cbb = id->cbbs[bg.cbbIdx]; auto &cbb = id->cbbs[bg.cbbIdx];
const auto tileRows = cbb.tex.height / TileHeight; const auto tileRows = cbb.tex.height / TileHeight;
@ -192,16 +303,39 @@ static void drawBackgrounds(GlImplData *id) noexcept {
} }
} }
static void drawSprites(GlImplData *id) noexcept {
glUseProgram(id->spriteShader);
auto &sb = id->spriteBlocks;
const auto uniformTileHeight = static_cast<GLint>(glGetUniformLocation(id->spriteShader, "vTileHeight"));
// update vbo
glBindVertexArray(sb.vao);
if (sb.updated) {
sb.updated = false;
glutils::sendVbo(sb);
}
// set vTileHeight uniform
const auto tileRows = sb.tex.height / TileHeight;
glUniform1f(uniformTileHeight, 1.0f / static_cast<float>(tileRows));
// draw
glBindTexture(GL_TEXTURE_2D, sb.tex);
glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(sb.elements.size()), GL_UNSIGNED_INT, nullptr);
}
ox::Error init(Context *ctx, void **rendererData) noexcept { ox::Error init(Context *ctx, void **rendererData) noexcept {
const auto vshad = ox::sfmt(bgvshad, glutils::GlslVersion); glEnable(GL_BLEND);
const auto fshad = ox::sfmt(bgfshad, glutils::GlslVersion); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
oxRequireM(bgShader, glutils::buildShaderProgram(vshad.c_str(), fshad.c_str())); const auto bgVshad = ox::sfmt(bgvshadTmpl, glutils::GlslVersion);
const auto id = new GlImplData; const auto bgFshad = ox::sfmt(bgfshadTmpl, glutils::GlslVersion);
const auto spriteVshad = ox::sfmt(spritevshadTmpl, glutils::GlslVersion);
const auto spriteFshad = ox::sfmt(spritefshadTmpl, glutils::GlslVersion);
const auto id = ox::make<GlImplData>();
*rendererData = id; *rendererData = id;
id->bgShader = std::move(bgShader); oxReturnError(glutils::buildShaderProgram(bgVshad.c_str(), bgFshad.c_str()).moveTo(&id->bgShader));
oxReturnError(glutils::buildShaderProgram(spriteVshad.c_str(), spriteFshad.c_str()).moveTo(&id->spriteShader));
for (auto &bg : id->cbbs) { for (auto &bg : id->cbbs) {
initBackgroundBufferset(ctx, id->bgShader, &bg); initBackgroundBufferset(ctx, id->bgShader, &bg);
} }
initSpritesBufferset(ctx, id->spriteShader, &id->spriteBlocks);
ImGui_ImplOpenGL3_Init(glutils::GlslVersion); ImGui_ImplOpenGL3_Init(glutils::GlslVersion);
return OxError(0); return OxError(0);
} }
@ -211,20 +345,43 @@ void shutdown(Context*, void *rendererData) noexcept {
ox::safeDelete(id); ox::safeDelete(id);
} }
void loadBgPalette(void *rendererData, const Palette &pal) noexcept { static void loadPalette(GLuint shaderPgrm, const Palette &pal, bool firstIsTransparent = false) noexcept {
const auto id = static_cast<GlImplData*>(rendererData); static constexpr std::size_t ColorCnt = 256;
ox::Array<GLfloat, ColorCnt * 4> palette{};
for (auto i = 0u; const auto c : pal.colors) { for (auto i = 0u; const auto c : pal.colors) {
id->palette[i++] = redf(c); palette[i++] = redf(c);
id->palette[i++] = greenf(c); palette[i++] = greenf(c);
id->palette[i++] = bluef(c); palette[i++] = bluef(c);
palette[i++] = 255;
} }
if (firstIsTransparent) {
palette[3] = 0;
}
glUseProgram(shaderPgrm);
const auto uniformPalette = static_cast<GLint>(glGetUniformLocation(shaderPgrm, "fPalette"));
glUniform4fv(uniformPalette, ColorCnt, palette.data());
} }
void loadBgTexture(void *rendererData, unsigned cbbIdx, void *pixels, int w, int h) noexcept { void loadBgPalette(void *rendererData, const Palette &pal) noexcept {
const auto id = static_cast<GlImplData*>(rendererData);
loadPalette(id->bgShader, pal);
}
void loadSpritePalette(void *rendererData, const Palette &pal) noexcept {
const auto id = static_cast<GlImplData*>(rendererData);
loadPalette(id->spriteShader, pal, true);
}
void loadBgTexture(void *rendererData, unsigned cbbIdx, const void *pixels, int w, int h) noexcept {
oxTracef("nostalgia::core::gfx::gl", "loadBgTexture: { cbbIdx: {}, w: {}, h: {} }", cbbIdx, w, h); oxTracef("nostalgia::core::gfx::gl", "loadBgTexture: { cbbIdx: {}, w: {}, h: {} }", cbbIdx, w, h);
const auto id = static_cast<GlImplData*>(rendererData); const auto id = static_cast<GlImplData*>(rendererData);
auto &tex = id->cbbs[cbbIdx].tex; id->cbbs[cbbIdx].tex = loadTexture(w, h, pixels);
tex = loadTexture(w, h, pixels); }
void loadSpriteTexture(void *rendererData, const void *pixels, int w, int h) noexcept {
oxTracef("nostalgia::core::gfx::gl", "loadSpriteTexture: { w: {}, h: {} }", w, h);
const auto id = static_cast<GlImplData*>(rendererData);
id->spriteBlocks.tex = loadTexture(w, h, pixels);
} }
} }
@ -275,6 +432,7 @@ void draw(Context *ctx) noexcept {
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
// render // render
renderer::drawBackgrounds(id); renderer::drawBackgrounds(id);
renderer::drawSprites(id);
for (const auto cd : ctx->drawers) { for (const auto cd : ctx->drawers) {
cd->draw(ctx); cd->draw(ctx);
} }
@ -291,17 +449,31 @@ void clearTileLayer(Context *ctx, unsigned bgIdx) noexcept {
bg.updated = true; bg.updated = true;
} }
void hideSprite(Context*, unsigned) noexcept { void hideSprite(Context *ctx, unsigned idx) noexcept {
auto &id = *ctx->rendererData<renderer::GlImplData>();
auto vbo = &id.spriteBlocks.vertices[idx * renderer::SpriteVertexVboLength];
auto ebo = &id.spriteBlocks.elements[idx * renderer::SpriteVertexEboLength];
renderer::setSpriteBufferObject(ctx, idx * renderer::SpriteVertexVboRows, 0,
0, 0, 0, false, vbo, ebo);
id.spriteBlocks.updated = true;
} }
void setSprite(Context*, void setSprite(Context *ctx,
unsigned, unsigned idx,
unsigned, int x,
unsigned, int y,
unsigned, unsigned tileIdx,
unsigned, [[maybe_unused]] unsigned spriteShape,
unsigned, [[maybe_unused]] unsigned spriteSize,
unsigned) noexcept { unsigned flipX) noexcept {
const auto uX = static_cast<unsigned>(x) % 255;
const auto uY = static_cast<unsigned>(y) % 127;
auto &id = *ctx->rendererData<renderer::GlImplData>();
auto vbo = &id.spriteBlocks.vertices[idx * renderer::SpriteVertexVboLength];
auto ebo = &id.spriteBlocks.elements[idx * renderer::SpriteVertexEboLength];
renderer::setSpriteBufferObject(ctx, idx * renderer::SpriteVertexVboRows, 1,
static_cast<float>(uX) / 8, static_cast<float>(uY) / 8, tileIdx, flipX, vbo, ebo);
id.spriteBlocks.updated = true;
} }
void setTile(Context *ctx, unsigned bgIdx, int column, int row, uint8_t tile) noexcept { void setTile(Context *ctx, unsigned bgIdx, int column, int row, uint8_t tile) noexcept {

View File

@ -10,8 +10,8 @@
namespace nostalgia::core { namespace nostalgia::core {
ox::Result<char*> loadRom(const char *path) noexcept { ox::Result<char*> loadRom(ox::CRStringView path) noexcept {
std::ifstream file(path, std::ios::binary | std::ios::ate); std::ifstream file(std::string(toStdStringView(path)), std::ios::binary | std::ios::ate);
if (!file.good()) { if (!file.good()) {
oxErrorf("Could not find ROM file: {}", path); oxErrorf("Could not find ROM file: {}", path);
return OxError(1, "Could not find ROM file"); return OxError(1, "Could not find ROM file");

View File

@ -47,7 +47,7 @@ template struct GLObject<deleteProgram>;
template struct GLObject<deleteShader>; template struct GLObject<deleteShader>;
[[nodiscard]] [[nodiscard]]
static ox::Result<GLShader> buildShader(GLuint shaderType, const GLchar *src, const char *shaderName) noexcept { static ox::Result<GLShader> buildShader(GLuint shaderType, const GLchar *src, ox::CRStringView shaderName) noexcept {
GLShader shader(glCreateShader(shaderType)); GLShader shader(glCreateShader(shaderType));
glShaderSource(shader, 1, &src, nullptr); glShaderSource(shader, 1, &src, nullptr);
glCompileShader(shader); glCompileShader(shader);

View File

@ -12,14 +12,14 @@ static int spriteY = 64;
static int updateHandler(core::Context *ctx) noexcept { static int updateHandler(core::Context *ctx) noexcept {
int xmod = 0; int xmod = 0;
int ymod = 0; int ymod = 0;
if (core::buttonDown(ctx, core::GamePad_Right)) { if (core::buttonDown(ctx, core::Alpha_D) || core::buttonDown(ctx, core::GamePad_Right)) {
xmod = 2; xmod = 2;
} else if (core::buttonDown(ctx, core::GamePad_Left)) { } else if (core::buttonDown(ctx, core::Alpha_A) || core::buttonDown(ctx, core::GamePad_Left)) {
xmod = -2; xmod = -2;
} }
if (core::buttonDown(ctx, core::GamePad_Down)) { if (core::buttonDown(ctx, core::Alpha_S) || core::buttonDown(ctx, core::GamePad_Down)) {
ymod = 2; ymod = 2;
} else if (core::buttonDown(ctx, core::GamePad_Up)) { } else if (core::buttonDown(ctx, core::Alpha_W) || core::buttonDown(ctx, core::GamePad_Up)) {
ymod = -2; ymod = -2;
} }
if (!xmod && !ymod) { if (!xmod && !ymod) {
@ -27,10 +27,10 @@ static int updateHandler(core::Context *ctx) noexcept {
} }
spriteX += xmod; spriteX += xmod;
spriteY += ymod; spriteY += ymod;
constexpr auto s = "nostalgia"; constexpr ox::StringView s = "nostalgia";
for (unsigned i = 0; s[i]; ++i) { for (unsigned i = 0; s[i]; ++i) {
const auto c = static_cast<unsigned>(s[i] - ('a' - 1)); const auto c = static_cast<unsigned>(s[i] - ('a' - 1));
core::setSprite(ctx, i, static_cast<unsigned>(spriteX) + 8 * (i + 1), static_cast<unsigned>(spriteY), c); core::setSprite(ctx, i, spriteX + 8 * (static_cast<int>(i) + 1), spriteY, c);
} }
return 16; return 16;
} }
@ -42,15 +42,15 @@ static void keyEventHandler(core::Context *ctx, core::Key key, bool down) noexce
} }
ox::Error run(ox::UniquePtr<ox::FileSystem> fs) noexcept { ox::Error run(ox::UniquePtr<ox::FileSystem> fs) noexcept {
oxTraceInitHook();
oxRequireM(ctx, core::init(std::move(fs))); oxRequireM(ctx, core::init(std::move(fs)));
constexpr auto TileSheetAddr = "/TileSheets/Charset.ng"; constexpr ox::FileAddress TileSheetAddr("/TileSheets/Charset.ng");
constexpr auto PaletteAddr = "/Palettes/Charset.npal"; constexpr ox::FileAddress PaletteAddr("/Palettes/Charset.npal");
oxRequire(tsStat, ctx->rom->stat(PaletteAddr)); oxRequire(tsStat, ctx->rom->stat(PaletteAddr));
oxReturnError(core::loadSpriteTileSheet(ctx.get(), 0, TileSheetAddr, PaletteAddr)); oxReturnError(core::loadSpriteTileSheet(ctx.get(), TileSheetAddr, PaletteAddr));
oxReturnError(core::initConsole(ctx.get())); oxReturnError(core::initConsole(ctx.get()));
core::puts(ctx.get(), 10, 9, "DOPENESS!!!"); core::puts(ctx.get(), 10, 9, "DOPENESS!!!");
core::setUpdateHandler(ctx.get(), updateHandler); core::setUpdateHandler(ctx.get(), updateHandler);
core::setKeyEventHandler(ctx.get(), keyEventHandler); core::setKeyEventHandler(ctx.get(), keyEventHandler);
return core::run(ctx.get()); return core::run(ctx.get());
} }

View File

@ -35,6 +35,13 @@ install(
${NOSTALGIA_DIST_RESOURCES} ${NOSTALGIA_DIST_RESOURCES}
) )
install(
FILES
ns_logo128.png
DESTINATION
${NOSTALGIA_DIST_RESOURCES}/icons
)
install( install(
TARGETS TARGETS
nostalgia-studio nostalgia-studio

View File

@ -38,7 +38,7 @@ constexpr auto ConfigDir = [] {
}(); }();
template<typename T> template<typename T>
ox::Result<T> readConfig(core::Context *ctx, const char *name) noexcept { ox::Result<T> readConfig(core::Context *ctx, ox::CRStringView name) noexcept {
oxAssert(ox_strcmp(name, ""), "Config type has no TypeName"); oxAssert(ox_strcmp(name, ""), "Config type has no TypeName");
const auto homeDir = std::getenv(ox::defines::OS == ox::OS::Windows ? "USERPROFILE" : "HOME"); const auto homeDir = std::getenv(ox::defines::OS == ox::OS::Windows ? "USERPROFILE" : "HOME");
const auto configPath = ox::sfmt(ConfigDir, homeDir, ctx->appName).toStdString(); const auto configPath = ox::sfmt(ConfigDir, homeDir, ctx->appName).toStdString();
@ -70,16 +70,16 @@ template<typename T>
ox::Error writeConfig(core::Context *ctx, const auto &name, T *data) noexcept { ox::Error writeConfig(core::Context *ctx, const auto &name, T *data) noexcept {
oxAssert(ox_strcmp(name, ""), "Config type has no TypeName"); oxAssert(ox_strcmp(name, ""), "Config type has no TypeName");
const auto homeDir = std::getenv(ox::defines::OS == ox::OS::Windows ? "USERPROFILE" : "HOME"); const auto homeDir = std::getenv(ox::defines::OS == ox::OS::Windows ? "USERPROFILE" : "HOME");
const auto configPath = ox::sfmt(ConfigDir, homeDir, ctx->appName).toStdString(); const auto configPath = ox::sfmt(ConfigDir, homeDir, ctx->appName);
const auto path = ox::sfmt("{}.json", name).toStdString(); const auto path = ox::sfmt("{}.json", name);
ox::PassThroughFS fs(configPath.c_str()); ox::PassThroughFS fs(configPath);
if (auto err = fs.mkdir("/", true)) { if (auto err = fs.mkdir("/", true)) {
oxErrf("Could not create config directory: {}\n", toStr(err)); oxErrf("Could not create config directory: {}\n", toStr(err));
return err; return err;
} }
oxRequireM(buff, ox::writeOC(data)); oxRequireM(buff, ox::writeOC(data));
buff.back().value = '\n'; buff.back().value = '\n';
if (auto err = fs.write(path.c_str(), buff.data(), buff.size())) { if (auto err = fs.write(path, buff.data(), buff.size())) {
oxErrf("Could not read config file: {}\n", toStr(err)); oxErrf("Could not read config file: {}\n", toStr(err));
return OxError(2, "Could not read config file"); return OxError(2, "Could not read config file");
} }

View File

@ -2,6 +2,8 @@
* Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved. * Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/ */
#include <algorithm>
#include <ox/std/string.hpp> #include <ox/std/string.hpp>
#include "editor.hpp" #include "editor.hpp"
@ -93,6 +95,11 @@ ox::Error BaseEditor::saveItem() noexcept {
return OxError(0); return OxError(0);
} }
ox::StringView BaseEditor::pathToItemName(ox::CRStringView path) noexcept {
const auto lastSlash = std::find(path.rbegin(), path.rend(), '/').offset();
return path.substr(lastSlash + 1);
}
Editor::Editor() noexcept { Editor::Editor() noexcept {
m_undoStack.changeTriggered.connect(this, &Editor::markUnsavedChanges); m_undoStack.changeTriggered.connect(this, &Editor::markUnsavedChanges);
} }

View File

@ -100,10 +100,7 @@ class NOSTALGIASTUDIO_EXPORT BaseEditor: public Widget {
return nullptr; return nullptr;
} }
static constexpr auto pathToItemName(ox::CRStringView path) noexcept { static ox::StringView pathToItemName(ox::CRStringView path) noexcept;
const auto lastSlash = std::find(path.rbegin(), path.rend(), '/').offset();
return path.substr(lastSlash + 1);
}
// signals // signals
public: public:

View File

@ -10,12 +10,14 @@ namespace nostalgia::studio {
struct FDFilterItem { struct FDFilterItem {
#ifdef OX_OS_Windows #ifdef OX_OS_Windows
using String = ox::BasicString<8, wchar_t>; using String = ox::Vector<wchar_t>;
#else #else
using String = ox::String; using String = ox::Vector<char>;
#endif #endif
String name; String name{};
String spec; String spec{};
constexpr FDFilterItem() noexcept = default;
FDFilterItem(ox::CRStringView pName, ox::CRStringView pSpec) noexcept;
}; };
ox::Result<ox::String> saveFile(const ox::Vector<FDFilterItem> &exts) noexcept; ox::Result<ox::String> saveFile(const ox::Vector<FDFilterItem> &exts) noexcept;

View File

@ -6,12 +6,18 @@
#include <ox/std/error.hpp> #include <ox/std/error.hpp>
#include <ox/std/string.hpp> #include <ox/std/string.hpp>
#include <ox/std/trace.hpp>
#include "filedialog.hpp" #include "filedialog.hpp"
namespace nostalgia::studio { namespace nostalgia::studio {
FDFilterItem::FDFilterItem(ox::CRStringView pName, ox::CRStringView pSpec) noexcept {
name.resize(pName.len() + 1);
ox_strncpy(name.data(), pName.data(), pName.len());
spec.resize(pSpec.len() + 1);
ox_strncpy(spec.data(), pSpec.data(), pSpec.len());
}
static ox::Result<ox::String> toResult(nfdresult_t r, const NFD::UniquePathN &path) noexcept { static ox::Result<ox::String> toResult(nfdresult_t r, const NFD::UniquePathN &path) noexcept {
switch (r) { switch (r) {
case NFD_OKAY: { case NFD_OKAY: {
@ -30,19 +36,19 @@ static ox::Result<ox::String> toResult(nfdresult_t r, const NFD::UniquePathN &pa
} }
ox::Result<ox::String> saveFile(const ox::Vector<FDFilterItem> &filters) noexcept { ox::Result<ox::String> saveFile(const ox::Vector<FDFilterItem> &filters) noexcept {
NFD::Guard guard; const NFD::Guard guard;
NFD::UniquePathN path; NFD::UniquePathN path;
ox::Vector<nfdnfilteritem_t, 5> filterItems(filters.size()); ox::Vector<nfdnfilteritem_t, 5> filterItems(filters.size());
for (auto i = 0u; const auto &f : filters) { for (auto i = 0u; const auto &f : filters) {
filterItems[i].name = f.name.c_str(); filterItems[i].name = f.name.data();
filterItems[i].spec = f.spec.c_str(); filterItems[i].spec = f.spec.data();
++i; ++i;
} }
return toResult(NFD::SaveDialog(path, filterItems.data(), filterItems.size()), path); return toResult(NFD::SaveDialog(path, filterItems.data(), filterItems.size()), path);
} }
ox::Result<ox::String> chooseDirectory() noexcept { ox::Result<ox::String> chooseDirectory() noexcept {
NFD::Guard guard; const NFD::Guard guard;
NFD::UniquePathN path; NFD::UniquePathN path;
return toResult(NFD::PickFolder(path), path); return toResult(NFD::PickFolder(path), path);
} }

View File

@ -21,7 +21,7 @@ class ItemMaker {
fileExt(std::move(pFileExt)) { fileExt(std::move(pFileExt)) {
} }
virtual ~ItemMaker() noexcept = default; virtual ~ItemMaker() noexcept = default;
virtual ox::Error write(core::Context *ctx, const char *pName) const noexcept = 0; virtual ox::Error write(core::Context *ctx, ox::CRStringView pName) const noexcept = 0;
}; };
template<typename T> template<typename T>
@ -44,7 +44,7 @@ class ItemMakerT: public ItemMaker {
item(std::forward(pItem)), item(std::forward(pItem)),
fmt(pFmt) { fmt(pFmt) {
} }
ox::Error write(core::Context *ctx, const char *pName) const noexcept override { ox::Error write(core::Context *ctx, ox::CRStringView pName) const noexcept override {
const auto path = ox::sfmt("{}/{}.{}", parentDir, pName, fileExt); const auto path = ox::sfmt("{}/{}.{}", parentDir, pName, fileExt);
auto sctx = core::applicationData<studio::StudioContext>(ctx); auto sctx = core::applicationData<studio::StudioContext>(ctx);
return sctx->project->writeObj(path, &item, fmt); return sctx->project->writeObj(path, &item, fmt);

View File

@ -17,7 +17,7 @@ namespace nostalgia::studio {
class ItemMaker; class ItemMaker;
struct EditorMaker { struct EditorMaker {
using Func = std::function<ox::Result<class BaseEditor*>(const ox::String&)>; using Func = std::function<ox::Result<class BaseEditor*>(ox::CRStringView)>;
ox::Vector<ox::String> fileTypes; ox::Vector<ox::String> fileTypes;
Func make; Func make;
}; };

View File

@ -26,21 +26,21 @@ ox::FileSystem *Project::romFs() noexcept {
return m_fs; return m_fs;
} }
ox::Error Project::mkdir(const ox::String &path) const noexcept { ox::Error Project::mkdir(ox::CRStringView path) const noexcept {
oxReturnError(m_fs->mkdir(path.c_str(), true)); oxReturnError(m_fs->mkdir(path, true));
fileUpdated.emit(path); fileUpdated.emit(path);
return OxError(0); return OxError(0);
} }
ox::Result<ox::FileStat> Project::stat(const ox::String &path) const noexcept { ox::Result<ox::FileStat> Project::stat(ox::CRStringView path) const noexcept {
return m_fs->stat(path.c_str()); return m_fs->stat(path);
} }
bool Project::exists(const ox::String &path) const noexcept { bool Project::exists(ox::CRStringView path) const noexcept {
return m_fs->stat(path.c_str()).error == 0; return m_fs->stat(path).error == 0;
} }
const ox::Vector<ox::String> &Project::fileList(const char *ext) noexcept { const ox::Vector<ox::String> &Project::fileList(ox::CRStringView ext) noexcept {
return m_fileExtFileMap[ext]; return m_fileExtFileMap[ext];
} }
@ -53,13 +53,13 @@ void Project::buildFileIndex() noexcept {
m_fileExtFileMap.clear(); m_fileExtFileMap.clear();
std::sort(files.begin(), files.end()); std::sort(files.begin(), files.end());
for (const auto &file : files) { for (const auto &file : files) {
if (!file.beginsWith("/.nostalgia/")) { if (!beginsWith(file, "/.nostalgia/")) {
indexFile(file); indexFile(file);
} }
} }
} }
void Project::indexFile(const ox::String &path) noexcept { void Project::indexFile(ox::CRStringView path) noexcept {
const auto [ext, err] = fileExt(path); const auto [ext, err] = fileExt(path);
if (err) { if (err) {
return; return;
@ -67,9 +67,9 @@ void Project::indexFile(const ox::String &path) noexcept {
m_fileExtFileMap[ext].emplace_back(path); m_fileExtFileMap[ext].emplace_back(path);
} }
ox::Error Project::writeBuff(const ox::String &path, const ox::Buffer &buff) noexcept { ox::Error Project::writeBuff(const ox::StringView &path, const ox::Buffer &buff) noexcept {
const auto newFile = m_fs->stat(path.c_str()).error != 0; const auto newFile = m_fs->stat(path).error != 0;
oxReturnError(m_fs->write(path.c_str(), buff.data(), buff.size())); oxReturnError(m_fs->write(path, buff.data(), buff.size(), ox::FileType::NormalFile));
if (newFile) { if (newFile) {
fileAdded.emit(path); fileAdded.emit(path);
indexFile(path); indexFile(path);
@ -80,17 +80,17 @@ ox::Error Project::writeBuff(const ox::String &path, const ox::Buffer &buff) noe
} }
ox::Result<ox::Buffer> Project::loadBuff(const ox::String &path) const noexcept { ox::Result<ox::Buffer> Project::loadBuff(const ox::String &path) const noexcept {
return m_fs->read(path.c_str()); return m_fs->read(path);
} }
ox::Error Project::lsProcDir(ox::Vector<ox::String> *paths, const ox::String &path) const noexcept { ox::Error Project::lsProcDir(ox::Vector<ox::String> *paths, ox::CRStringView path) const noexcept {
oxRequire(files, m_fs->ls(path.c_str())); oxRequire(files, m_fs->ls(path));
for (const auto &name : files) { for (const auto &name : files) {
const auto fullPath = path + "/" + name.c_str(); auto fullPath = ox::sfmt("{}/{}", path, name);
oxRequire(stat, m_fs->stat(fullPath.c_str())); oxRequire(stat, m_fs->stat(ox::StringView(fullPath)));
switch (stat.fileType) { switch (stat.fileType) {
case ox::FileType::NormalFile: case ox::FileType::NormalFile:
paths->push_back(fullPath); paths->emplace_back(std::move(fullPath));
break; break;
case ox::FileType::Directory: case ox::FileType::Directory:
oxReturnError(lsProcDir(paths, fullPath)); oxReturnError(lsProcDir(paths, fullPath));
@ -102,7 +102,7 @@ ox::Error Project::lsProcDir(ox::Vector<ox::String> *paths, const ox::String &pa
return OxError(0); return OxError(0);
} }
ox::Result<ox::Vector<ox::String>> Project::listFiles(const ox::String &path) const noexcept { ox::Result<ox::Vector<ox::String>> Project::listFiles(ox::CRStringView path) const noexcept {
ox::Vector<ox::String> paths; ox::Vector<ox::String> paths;
oxReturnError(lsProcDir(&paths, path)); oxReturnError(lsProcDir(&paths, path));
return paths; return paths;

View File

@ -29,7 +29,7 @@ enum class ProjectEvent {
}; };
[[nodiscard]] [[nodiscard]]
constexpr ox::Result<ox::String> fileExt(const ox::String &path) noexcept { constexpr ox::Result<ox::StringView> fileExt(ox::CRStringView path) noexcept {
const auto extStart = ox::find(path.crbegin(), path.crend(), '.').offset(); const auto extStart = ox::find(path.crbegin(), path.crend(), '.').offset();
if (!extStart) { if (!extStart) {
return OxError(1, "Cannot open a file without valid extension."); return OxError(1, "Cannot open a file without valid extension.");
@ -52,7 +52,7 @@ class NOSTALGIASTUDIO_EXPORT Project {
[[nodiscard]] [[nodiscard]]
ox::FileSystem *romFs() noexcept; ox::FileSystem *romFs() noexcept;
ox::Error mkdir(const ox::String &path) const noexcept; ox::Error mkdir(ox::CRStringView path) const noexcept;
/** /**
* Writes a MetalClaw object to the project at the given path. * Writes a MetalClaw object to the project at the given path.
@ -63,40 +63,40 @@ class NOSTALGIASTUDIO_EXPORT Project {
template<typename T> template<typename T>
ox::Result<T> loadObj(const ox::String &path) const noexcept; ox::Result<T> loadObj(const ox::String &path) const noexcept;
ox::Result<ox::FileStat> stat(const ox::String &path) const noexcept; ox::Result<ox::FileStat> stat(ox::CRStringView path) const noexcept;
[[nodiscard]] [[nodiscard]]
bool exists(const ox::String& path) const noexcept; bool exists(ox::CRStringView path) const noexcept;
template<typename Functor> template<typename Functor>
ox::Error subscribe(ProjectEvent e, ox::SignalHandler *tgt, Functor &&slot) const noexcept; ox::Error subscribe(ProjectEvent e, ox::SignalHandler *tgt, Functor &&slot) const noexcept;
[[nodiscard]] [[nodiscard]]
const ox::Vector<ox::String> &fileList(const char *ng) noexcept; const ox::Vector<ox::String> &fileList(ox::CRStringView ext) noexcept;
private: private:
void buildFileIndex() noexcept; void buildFileIndex() noexcept;
void indexFile(const ox::String &path) noexcept; void indexFile(ox::CRStringView path) noexcept;
ox::Error writeBuff(const ox::String &path, const ox::Buffer &buff) noexcept; ox::Error writeBuff(const ox::StringView &path, const ox::Buffer &buff) noexcept;
ox::Result<ox::Buffer> loadBuff(const ox::String &path) const noexcept; ox::Result<ox::Buffer> loadBuff(const ox::String &path) const noexcept;
ox::Error lsProcDir(ox::Vector<ox::String> *paths, const ox::String &path) const noexcept; ox::Error lsProcDir(ox::Vector<ox::String> *paths, ox::CRStringView path) const noexcept;
ox::Result<ox::Vector<ox::String>> listFiles(const ox::String &path = "") const noexcept; ox::Result<ox::Vector<ox::String>> listFiles(ox::CRStringView path = "") const noexcept;
// signals // signals
public: public:
ox::Signal<ox::Error(ProjectEvent, const ox::String&)> fileEvent; ox::Signal<ox::Error(ProjectEvent, const ox::String&)> fileEvent;
ox::Signal<ox::Error(const ox::String&)> fileAdded; ox::Signal<ox::Error(ox::CRStringView)> fileAdded;
// FileRecognized is triggered for all matching files upon a new // FileRecognized is triggered for all matching files upon a new
// subscription to a section of the project and upon the addition of a // subscription to a section of the project and upon the addition of a
// file. // file.
ox::Signal<ox::Error(const ox::String&)> fileRecognized; ox::Signal<ox::Error(ox::StringView)> fileRecognized;
ox::Signal<ox::Error(const ox::String&)> fileDeleted; ox::Signal<ox::Error(ox::StringView)> fileDeleted;
ox::Signal<ox::Error(const ox::String&)> fileUpdated; ox::Signal<ox::Error(ox::StringView)> fileUpdated;
}; };

View File

@ -108,7 +108,7 @@ void NewMenu::drawLastPageButtons(core::Context *ctx) noexcept {
} }
void NewMenu::finish(core::Context *ctx) noexcept { void NewMenu::finish(core::Context *ctx) noexcept {
const auto err = m_types[static_cast<std::size_t>(m_selectedType)]->write(ctx, m_itemName.c_str()); const auto err = m_types[static_cast<std::size_t>(m_selectedType)]->write(ctx, m_itemName);
if (err) { if (err) {
oxLogError(err); oxLogError(err);
return; return;

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

View File

@ -13,9 +13,9 @@
namespace nostalgia { namespace nostalgia {
static ox::Result<ox::UniquePtr<ProjectTreeModel>> static ox::Result<ox::UniquePtr<ProjectTreeModel>>
buildProjectTreeModel(ProjectExplorer *explorer, const ox::String &name, const ox::String &path, ProjectTreeModel *parent) noexcept { buildProjectTreeModel(ProjectExplorer *explorer, ox::CRStringView name, ox::CRStringView path, ProjectTreeModel *parent) noexcept {
const auto fs = explorer->romFs(); const auto fs = explorer->romFs();
oxRequire(stat, fs->stat(path.c_str())); oxRequire(stat, fs->stat(path));
auto out = ox::make_unique<ProjectTreeModel>(explorer, name, parent); auto out = ox::make_unique<ProjectTreeModel>(explorer, name, parent);
if (stat.fileType == ox::FileType::Directory) { if (stat.fileType == ox::FileType::Directory) {
oxRequireM(children, fs->ls(path)); oxRequireM(children, fs->ls(path));
@ -58,7 +58,7 @@ void ProjectExplorer::setModel(ox::UniquePtr<ProjectTreeModel> model) noexcept {
m_treeModel = std::move(model); m_treeModel = std::move(model);
} }
ox::Error ProjectExplorer::refreshProjectTreeModel(const ox::String&) noexcept { ox::Error ProjectExplorer::refreshProjectTreeModel(ox::CRStringView) noexcept {
oxRequireM(model, buildProjectTreeModel(this, "Project", "/", nullptr)); oxRequireM(model, buildProjectTreeModel(this, "Project", "/", nullptr));
setModel(std::move(model)); setModel(std::move(model));
return OxError(0); return OxError(0);

View File

@ -23,7 +23,7 @@ class ProjectExplorer: public studio::Widget {
void setModel(ox::UniquePtr<ProjectTreeModel> model) noexcept; void setModel(ox::UniquePtr<ProjectTreeModel> model) noexcept;
ox::Error refreshProjectTreeModel(const ox::String& = {}) noexcept; ox::Error refreshProjectTreeModel(ox::CRStringView = {}) noexcept;
[[nodiscard]] [[nodiscard]]
constexpr ox::FileSystem *romFs() noexcept { constexpr ox::FileSystem *romFs() noexcept {
@ -32,7 +32,7 @@ class ProjectExplorer: public studio::Widget {
// slots // slots
public: public:
ox::Signal<ox::Error(const ox::String&)> fileChosen; ox::Signal<ox::Error(const ox::StringView&)> fileChosen;
}; };
} }

View File

@ -9,32 +9,38 @@
namespace nostalgia { namespace nostalgia {
ProjectTreeModel::ProjectTreeModel(ProjectExplorer *explorer, const ox::String &name, ProjectTreeModel::ProjectTreeModel(ProjectExplorer *explorer, ox::String name,
ProjectTreeModel *parent) noexcept { ProjectTreeModel *parent) noexcept:
m_explorer = explorer; m_explorer(explorer),
m_name = name; m_parent(parent),
m_parent = parent; m_name(std::move(name)) {
} }
ProjectTreeModel::ProjectTreeModel(ProjectTreeModel &&other) noexcept { ProjectTreeModel::ProjectTreeModel(ProjectTreeModel &&other) noexcept:
m_name = std::move(other.m_name); m_explorer(other.m_explorer),
m_children = std::move(other.m_children); m_parent(other.m_parent),
m_name(std::move(other.m_name)),
m_children(std::move(other.m_children)) {
} }
void ProjectTreeModel::draw(core::Context *ctx) noexcept { void ProjectTreeModel::draw(core::Context *ctx) const noexcept {
constexpr auto dirFlags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick; constexpr auto dirFlags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick;
if (m_children.size()) { if (!m_children.empty()) {
if (ImGui::TreeNodeEx(m_name.c_str(), dirFlags)) { if (ImGui::TreeNodeEx(m_name.c_str(), dirFlags)) {
for (auto &child : m_children) { for (const auto &child : m_children) {
child->draw(ctx); child->draw(ctx);
} }
ImGui::TreePop(); ImGui::TreePop();
} }
} else if (auto path = fullPath(); ImGui::TreeNodeEx(ox::sfmt("{}##{}", m_name, path).c_str(), ImGuiTreeNodeFlags_Leaf)) { } else {
if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(0)) { const auto path = fullPath();
m_explorer->fileChosen.emit(path); const auto name = ox::sfmt<ox::BasicString<255>>("{}##{}", m_name, path);
if (ImGui::TreeNodeEx(name.c_str(), ImGuiTreeNodeFlags_Leaf)) {
if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(0)) {
m_explorer->fileChosen.emit(path);
}
ImGui::TreePop();
} }
ImGui::TreePop();
} }
} }
@ -42,9 +48,9 @@ void ProjectTreeModel::setChildren(ox::Vector<ox::UniquePtr<ProjectTreeModel>> c
m_children = std::move(children); m_children = std::move(children);
} }
ox::String ProjectTreeModel::fullPath() noexcept { ox::BasicString<255> ProjectTreeModel::fullPath() const noexcept {
if (m_parent) { if (m_parent) {
return m_parent->fullPath() + "/" + m_name; return m_parent->fullPath() + "/" + ox::StringView(m_name);
} }
return ""; return "";
} }

View File

@ -18,18 +18,18 @@ class ProjectTreeModel {
ox::String m_name; ox::String m_name;
ox::Vector<ox::UniquePtr<ProjectTreeModel>> m_children; ox::Vector<ox::UniquePtr<ProjectTreeModel>> m_children;
public: public:
explicit ProjectTreeModel(class ProjectExplorer *explorer, const ox::String &name, explicit ProjectTreeModel(class ProjectExplorer *explorer, ox::String name,
ProjectTreeModel *parent = nullptr) noexcept; ProjectTreeModel *parent = nullptr) noexcept;
ProjectTreeModel(ProjectTreeModel &&other) noexcept; ProjectTreeModel(ProjectTreeModel &&other) noexcept;
void draw(core::Context *ctx) noexcept; void draw(core::Context *ctx) const noexcept;
void setChildren(ox::Vector<ox::UniquePtr<ProjectTreeModel>> children) noexcept; void setChildren(ox::Vector<ox::UniquePtr<ProjectTreeModel>> children) noexcept;
private: private:
[[nodiscard]] [[nodiscard]]
ox::String fullPath() noexcept; ox::BasicString<255> fullPath() const noexcept;
}; };
} }

View File

@ -283,10 +283,10 @@ void StudioUI::save() noexcept {
} }
} }
ox::Error StudioUI::openProject(const ox::String &path) noexcept { ox::Error StudioUI::openProject(ox::CRStringView path) noexcept {
oxRequireM(fs, core::loadRomFs(path.c_str())); oxRequireM(fs, core::loadRomFs(path));
m_ctx->rom = std::move(fs); m_ctx->rom = std::move(fs);
core::setWindowTitle(m_ctx, ox::sfmt("Nostalgia Studio - {}", path).c_str()); core::setWindowTitle(m_ctx, ox::sfmt("Nostalgia Studio - {}", path));
m_project = ox::make_unique<studio::Project>(m_ctx->rom.get(), path); m_project = ox::make_unique<studio::Project>(m_ctx->rom.get(), path);
auto sctx = applicationData<studio::StudioContext>(m_ctx); auto sctx = applicationData<studio::StudioContext>(m_ctx);
sctx->project = m_project.get(); sctx->project = m_project.get();

View File

@ -77,7 +77,7 @@ class StudioUI: public ox::SignalHandler {
void save() noexcept; void save() noexcept;
ox::Error openProject(const ox::String &path) noexcept; ox::Error openProject(ox::CRStringView path) noexcept;
ox::Error openFile(ox::CRStringView path) noexcept; ox::Error openFile(ox::CRStringView path) noexcept;

View File

@ -13,9 +13,9 @@
using namespace nostalgia; using namespace nostalgia;
static ox::Error writeFileBuff(ox::CRString path, const ox::Buffer &buff) noexcept { static ox::Error writeFileBuff(ox::CRStringView path, const ox::Buffer &buff) noexcept {
try { try {
std::ofstream f(path.c_str(), std::ios::binary); std::ofstream f(std::string(toStdStringView(path)), std::ios::binary);
f.write(buff.data(), static_cast<intptr_t>(buff.size())); f.write(buff.data(), static_cast<intptr_t>(buff.size()));
} catch (const std::fstream::failure&) { } catch (const std::fstream::failure&) {
return OxError(2, "failed to write file"); return OxError(2, "failed to write file");
@ -23,8 +23,8 @@ static ox::Error writeFileBuff(ox::CRString path, const ox::Buffer &buff) noexce
return {}; return {};
} }
static ox::Result<ox::Buffer> readFileBuff(const char *path) noexcept { static ox::Result<ox::Buffer> readFileBuff(ox::CRStringView path) noexcept {
std::ifstream file(path, std::ios::binary | std::ios::ate); std::ifstream file(std::string(toStdStringView(path)), std::ios::binary | std::ios::ate);
if (!file.good()) { if (!file.good()) {
oxErrorf("Could not find OxFS file: {}", path); oxErrorf("Could not find OxFS file: {}", path);
return OxError(1, "Could not find OxFS file"); return OxError(1, "Could not find OxFS file");
@ -55,7 +55,7 @@ static ox::Error run(const ox::ClArgs &args) noexcept {
} }
ox::Buffer dstBuff(32 * ox::units::MB); ox::Buffer dstBuff(32 * ox::units::MB);
oxReturnError(ox::FileSystem32::format(dstBuff.data(), dstBuff.size())); oxReturnError(ox::FileSystem32::format(dstBuff.data(), dstBuff.size()));
ox::PassThroughFS src(argSrc.c_str()); ox::PassThroughFS src(argSrc);
ox::FileSystem32 dst(ox::FileStore32(dstBuff.data(), dstBuff.size())); ox::FileSystem32 dst(ox::FileStore32(dstBuff.data(), dstBuff.size()));
core::TypeStore ts(&src); core::TypeStore ts(&src);
oxReturnError(pack(&ts, &src, &dst)); oxReturnError(pack(&ts, &src, &dst));
@ -66,7 +66,7 @@ static ox::Error run(const ox::ClArgs &args) noexcept {
oxRequire(dstSize, dst.size()); oxRequire(dstSize, dst.size());
dstBuff.resize(dstSize); dstBuff.resize(dstSize);
oxRequireM(romBuff, readFileBuff(argRomBin.c_str())); oxRequireM(romBuff, readFileBuff(argRomBin));
oxReturnError(appendBinary(&romBuff, &dstBuff, pl.get())); oxReturnError(appendBinary(&romBuff, &dstBuff, pl.get()));
oxOutf("Dest FS size: {} bytes\n", dstSize); oxOutf("Dest FS size: {} bytes\n", dstSize);

View File

@ -32,7 +32,7 @@ static ox::Error pathToInode(ox::FileSystem *dest, ox::ModelObject *obj) noexcep
case ox::FileAddressType::None: case ox::FileAddressType::None:
return {}; return {};
} }
oxRequire(s, dest->stat(path.c_str())); oxRequire(s, dest->stat(ox::StringView(path)));
oxReturnError(o["type"].set(static_cast<int8_t>(ox::FileAddressType::Inode))); oxReturnError(o["type"].set(static_cast<int8_t>(ox::FileAddressType::Inode)));
return data.set(2, s.inode); return data.set(2, s.inode);
} }
@ -58,12 +58,12 @@ static ox::Error transformObj(ox::FileSystem *dest, ox::ModelObject *obj) noexce
return {}; return {};
} }
static ox::Error doTransformations(core::TypeStore *ts, ox::FileSystem *dest, ox::CRString filePath) noexcept { static ox::Error doTransformations(core::TypeStore *ts, ox::FileSystem *dest, ox::CRStringView filePath) noexcept {
if (filePath.endsWith(".ng") || filePath.endsWith(".npal")) { if (endsWith(filePath, ".ng") || endsWith(filePath, ".npal")) {
// load file // load file
oxRequire(s, dest->stat(filePath.c_str())); oxRequire(s, dest->stat(filePath));
oxRequireM(buff, dest->read(s.inode)); oxRequireM(buff, dest->read(s.inode));
if (filePath.endsWith(".ng")) { if (endsWith(filePath, ".ng")) {
oxReturnError(core::convertBuffToBuff<core::CompactTileSheet>(buff, ox::ClawFormat::Metal).moveTo(&buff)); oxReturnError(core::convertBuffToBuff<core::CompactTileSheet>(buff, ox::ClawFormat::Metal).moveTo(&buff));
} }
oxRequireM(obj, ox::readClaw(ts, buff)); oxRequireM(obj, ox::readClaw(ts, buff));
@ -78,15 +78,15 @@ static ox::Error doTransformations(core::TypeStore *ts, ox::FileSystem *dest, ox
// claw file transformations are broken out because path to inode // claw file transformations are broken out because path to inode
// transformations need to be done after the copy to the new FS is complete // transformations need to be done after the copy to the new FS is complete
static ox::Error transformClaw(core::TypeStore *ts, ox::FileSystem *dest, ox::CRString path) noexcept { static ox::Error transformClaw(core::TypeStore *ts, ox::FileSystem *dest, ox::CRStringView path) noexcept {
// copy // copy
oxTracef("pack::transformClaw", "path: {}", path); oxTracef("pack::transformClaw", "path: {}", path);
oxRequire(fileList, dest->ls(path)); oxRequire(fileList, dest->ls(path));
for (const auto &name : fileList) { for (const auto &name : fileList) {
const auto filePath = path + name; const auto filePath = ox::sfmt("{}{}", path, name);
oxRequire(stat, dest->stat(filePath.c_str())); oxRequire(stat, dest->stat(filePath));
if (stat.fileType == ox::FileType::Directory) { if (stat.fileType == ox::FileType::Directory) {
const auto dir = path + name + '/'; const auto dir = ox::sfmt("{}{}/", path, name);
oxReturnError(transformClaw(ts, dest, dir)); oxReturnError(transformClaw(ts, dest, dir));
} else { } else {
oxReturnError(doTransformations(ts, dest, filePath)); oxReturnError(doTransformations(ts, dest, filePath));
@ -95,9 +95,9 @@ static ox::Error transformClaw(core::TypeStore *ts, ox::FileSystem *dest, ox::CR
return {}; return {};
} }
static ox::Error verifyFile(ox::FileSystem *fs, ox::CRString path, const ox::Buffer &expected) noexcept { static ox::Error verifyFile(ox::FileSystem *fs, ox::CRStringView path, const ox::Buffer &expected) noexcept {
ox::Buffer buff(expected.size()); ox::Buffer buff(expected.size());
oxReturnError(fs->read(path.c_str(), buff.data(), buff.size())); oxReturnError(fs->read(path, buff.data(), buff.size()));
return OxError(buff == expected ? 0 : 1); return OxError(buff == expected ? 0 : 1);
} }
@ -110,7 +110,7 @@ struct VerificationPair {
} }
}; };
static ox::Error copy(ox::FileSystem *src, ox::FileSystem *dest, ox::CRString path) noexcept { static ox::Error copy(ox::FileSystem *src, ox::FileSystem *dest, ox::CRStringView path) noexcept {
oxOutf("copying directory: {}\n", path); oxOutf("copying directory: {}\n", path);
ox::Vector<VerificationPair> verificationPairs; ox::Vector<VerificationPair> verificationPairs;
// copy // copy
@ -121,16 +121,16 @@ static ox::Error copy(ox::FileSystem *src, ox::FileSystem *dest, ox::CRString pa
continue; continue;
} }
oxOutf("reading {}\n", currentFile); oxOutf("reading {}\n", currentFile);
oxRequire(stat, src->stat(currentFile.c_str())); oxRequire(stat, src->stat(ox::StringView(currentFile)));
if (stat.fileType == ox::FileType::Directory) { if (stat.fileType == ox::FileType::Directory) {
oxReturnError(dest->mkdir(currentFile.c_str(), true)); oxReturnError(dest->mkdir(currentFile, true));
oxReturnError(copy(src, dest, currentFile + '/')); oxReturnError(copy(src, dest, currentFile + '/'));
} else { } else {
// load file // load file
oxRequireM(buff, src->read(currentFile.c_str())); oxRequireM(buff, src->read(currentFile));
// write file to dest // write file to dest
oxOutf("writing {}\n", currentFile); oxOutf("writing {}\n", currentFile);
oxReturnError(dest->write(currentFile.c_str(), buff.data(), buff.size())); oxReturnError(dest->write(currentFile, buff.data(), buff.size()));
oxReturnError(verifyFile(dest, currentFile, buff)); oxReturnError(verifyFile(dest, currentFile, buff));
verificationPairs.emplace_back(std::move(currentFile), std::move(buff)); verificationPairs.emplace_back(std::move(currentFile), std::move(buff));
} }
@ -143,9 +143,9 @@ static ox::Error copy(ox::FileSystem *src, ox::FileSystem *dest, ox::CRString pa
} }
// transformations need to be done after the copy to the new FS is complete // transformations need to be done after the copy to the new FS is complete
static ox::Error preloadObj(core::TypeStore *ts, ox::FileSystem *romFs, GbaPreloader *pl, ox::CRString path) noexcept { static ox::Error preloadObj(core::TypeStore *ts, ox::FileSystem *romFs, GbaPreloader *pl, ox::CRStringView path) noexcept {
// load file // load file
oxRequireM(buff, romFs->read(path.c_str())); oxRequireM(buff, romFs->read(path));
oxRequireM(obj, ox::readClaw(ts, buff)); oxRequireM(obj, ox::readClaw(ts, buff));
if (obj.type()->preloadable) { if (obj.type()->preloadable) {
// preload // preload
@ -159,21 +159,21 @@ static ox::Error preloadObj(core::TypeStore *ts, ox::FileSystem *romFs, GbaPrelo
// strip the Claw header (it is not needed after preloading) and write back out to dest fs // strip the Claw header (it is not needed after preloading) and write back out to dest fs
oxReturnError(ox::writeMC(&obj).moveTo(&buff)); oxReturnError(ox::writeMC(&obj).moveTo(&buff));
} }
oxReturnError(romFs->write(path.c_str(), buff.data(), buff.size())); oxReturnError(romFs->write(path, buff.data(), buff.size()));
return {}; return {};
} }
// claw file transformations are broken out because path to inode // claw file transformations are broken out because path to inode
// transformations need to be done after the copy to the new FS is complete // transformations need to be done after the copy to the new FS is complete
static ox::Error preloadDir(core::TypeStore *ts, ox::FileSystem *romFs, GbaPreloader *pl, ox::CRString path) noexcept { static ox::Error preloadDir(core::TypeStore *ts, ox::FileSystem *romFs, GbaPreloader *pl, ox::CRStringView path) noexcept {
// copy // copy
oxTracef("pack::preload", "path: {}", path); oxTracef("pack::preload", "path: {}", path);
oxRequire(fileList, romFs->ls(path)); oxRequire(fileList, romFs->ls(path));
for (const auto &name : fileList) { for (const auto &name : fileList) {
const auto filePath = path + name; const auto filePath = ox::sfmt("{}{}", path, name);
oxRequire(stat, romFs->stat(filePath.c_str())); oxRequire(stat, romFs->stat(ox::StringView(filePath)));
if (stat.fileType == ox::FileType::Directory) { if (stat.fileType == ox::FileType::Directory) {
const auto dir = path + name + '/'; const auto dir = ox::sfmt("{}{}/", path, name);
oxReturnError(preloadDir(ts, romFs, pl, dir)); oxReturnError(preloadDir(ts, romFs, pl, dir));
} else { } else {
oxReturnError(preloadObj(ts, romFs, pl, filePath)); oxReturnError(preloadObj(ts, romFs, pl, filePath));