a75c4a11 [nfde] Address CMake warning, remove unwanted logging 347a1657 [sample_project] Update type descriptors fd64bfae [keel] Fix a use after free, cleanup aaeec20a [nostalgia/player] Fix build 37030f9c [keel] Cleanup pack tool 462f2bca [nostalgia,olympic] Change macro names to comply with broader conventions dc72500b [glutils] Change macro names to comply with broader conventions 962fe8bc [ox] Change macro names to comply with broader conventions 305eb626 [studio] Fix build 4754359a [ox/std] Cleanup Vec2 dc07f3d5 [studio] Change FilePicker consturctor to take StringParams fcdcfd10 [ox/std] Run liccor b74f6a7a [studio,turbine] Run liccor ac7e5be1 [ox] Remove OxException ed910c0b [nostalgia/core/studio/tilesheeteditor] Fix access overflow on out of bounds Fill command 345fb038 [ox] Remove OxError 9881253f [glutils] Cleanup OxError 96d27eec [nostalgia,olympic] Cleanup 28ebe93b [ox/std] Make source_location::current only init if valid e849e7a3 [ox/std] Add source_location e6777b0a [cityhash] Add install rule c488c336 [turbine/glfw] Fix mandatoryRefreshPeriodEnd tracking 003f9720 [turbine/glfw] Move MandatoryRefreshPeriod to config.hpp d85a10af [nostalgia/core/studio] Cleanup ff05d860 [turbine/glfw] Replace uninterruptedRefreshes with mandatoryRefreshPeriodEnd 76794037 [turbine] Add init wrapper that takes FS path c51a45e1 [olympic] Cleanup a6e24ff2 [ox/std] Add CString type alias e0ec9e0c [nostalgia,olympic] Move olympic::run to global namespace 9a42a9b9 [nfde] Fix Windows warnings 03a05c51 Merge commit '4ccdfc3a6e5bd501968903a01f7d8141b6f88375' bd91137d [nostalgia,olympic] Fix pack tool build for Windows 2b7d1294 [nostalgia/core/studio] Fix MSVC build git-subtree-dir: deps/nostalgia git-subtree-split: a75c4a11d3c555f4d3bed1ea1f70bb29fe49e99c
166 lines
4.4 KiB
C++
166 lines
4.4 KiB
C++
/*
|
|
* Copyright 2016 - 2024 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
|
*/
|
|
|
|
#include <algorithm>
|
|
#include <filesystem>
|
|
|
|
#include <ox/std/std.hpp>
|
|
|
|
#include <keel/module.hpp>
|
|
|
|
#include <studio/project.hpp>
|
|
|
|
namespace studio {
|
|
|
|
static_assert(fileExt("main.c").value == "c");
|
|
static_assert(fileExt("a.b.c").value == "c");
|
|
static_assert(parentDir("/a/b/c") == "/a/b");
|
|
static_assert(parentDir("/a/b/c/") == "/a/b");
|
|
|
|
static void generateTypes(ox::TypeStore &ts) noexcept {
|
|
for (auto const mod : keel::modules()) {
|
|
for (auto gen : mod->types()) {
|
|
oxLogError(gen(ts));
|
|
}
|
|
}
|
|
}
|
|
|
|
Project::Project(keel::Context &ctx, ox::String path, ox::StringViewCR projectDataDir):
|
|
m_ctx(ctx),
|
|
m_path(std::move(path)),
|
|
m_projectDataDir(projectDataDir),
|
|
m_typeDescPath(ox::sfmt("/{}/type_descriptors", m_projectDataDir)),
|
|
m_typeStore(*m_ctx.rom, ox::sfmt("/{}/type_descriptors", projectDataDir)),
|
|
m_fs(*m_ctx.rom) {
|
|
oxTracef("studio", "Project: {}", m_path);
|
|
generateTypes(m_typeStore);
|
|
if (ox::defines::Debug) {
|
|
OX_THROW_ERROR(writeTypeStore());
|
|
}
|
|
buildFileIndex();
|
|
}
|
|
|
|
ox::Error Project::create() noexcept {
|
|
std::error_code ec;
|
|
std::filesystem::create_directory(m_path.toStdString(), ec);
|
|
return ox::Error(static_cast<ox::ErrorCode>(ec.value()), "PassThroughFS: mkdir failed");
|
|
}
|
|
|
|
ox::String const&Project::projectPath() const noexcept {
|
|
return m_path;
|
|
}
|
|
|
|
ox::FileSystem &Project::romFs() noexcept {
|
|
return m_fs;
|
|
}
|
|
|
|
ox::Error Project::mkdir(ox::StringViewCR path) const noexcept {
|
|
auto const [stat, err] = m_fs.stat(path);
|
|
if (err) {
|
|
OX_RETURN_ERROR(m_fs.mkdir(path, true));
|
|
fileUpdated.emit(path, {});
|
|
}
|
|
return stat.fileType == ox::FileType::Directory ?
|
|
ox::Error{} : ox::Error(1, "path exists as normal file");
|
|
}
|
|
|
|
ox::Result<ox::FileStat> Project::stat(ox::StringViewCR path) const noexcept {
|
|
return m_fs.stat(path);
|
|
}
|
|
|
|
bool Project::exists(ox::StringViewCR path) const noexcept {
|
|
return m_fs.stat(path).error == 0;
|
|
}
|
|
|
|
ox::Vector<ox::String> const&Project::fileList(ox::StringViewCR ext) noexcept {
|
|
return m_fileExtFileMap[ext];
|
|
}
|
|
|
|
ox::Error Project::writeTypeStore() noexcept {
|
|
// write all descriptors because we don't know which types T depends on
|
|
OX_RETURN_ERROR(mkdir(m_typeDescPath));
|
|
for (auto const &t: m_typeStore.typeList()) {
|
|
OX_REQUIRE(typeOut, ox::writeClaw(*t, ox::ClawFormat::Organic));
|
|
// write to FS
|
|
auto const typePath = ox::sfmt("{}/{}", m_typeDescPath, buildTypeId(*t));
|
|
OX_RETURN_ERROR(writeBuff(typePath, {typeOut.data(), typeOut.size() - 1}));
|
|
}
|
|
return {};
|
|
}
|
|
|
|
void Project::buildFileIndex() noexcept {
|
|
auto [files, err] = listFiles();
|
|
if (err) {
|
|
oxLogError(err);
|
|
return;
|
|
}
|
|
m_fileExtFileMap.clear();
|
|
std::sort(files.begin(), files.end());
|
|
for (auto const&file : files) {
|
|
if (!beginsWith(file, ox::sfmt("/.{}/", m_projectDataDir))) {
|
|
indexFile(file);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Project::indexFile(ox::StringViewCR path) noexcept {
|
|
auto const [ext, err] = fileExt(path);
|
|
if (err) {
|
|
return;
|
|
}
|
|
m_fileExtFileMap[ext].emplace_back(path);
|
|
}
|
|
|
|
ox::Error Project::writeBuff(ox::StringViewCR path, ox::BufferView const&buff) noexcept {
|
|
constexpr auto HdrSz = 40;
|
|
ox::Buffer outBuff;
|
|
outBuff.reserve(buff.size() + HdrSz);
|
|
ox::BufferWriter writer(&outBuff);
|
|
auto const [uuid, err] = pathToUuid(m_ctx, path);
|
|
if (!err) {
|
|
OX_RETURN_ERROR(keel::writeUuidHeader(writer, uuid));
|
|
}
|
|
OX_RETURN_ERROR(writer.write(buff.data(), buff.size()));
|
|
auto const newFile = m_fs.stat(path).error != 0;
|
|
OX_RETURN_ERROR(m_fs.write(path, outBuff.data(), outBuff.size(), ox::FileType::NormalFile));
|
|
if (newFile) {
|
|
fileAdded.emit(path);
|
|
indexFile(path);
|
|
} else {
|
|
fileUpdated.emit(path, uuid);
|
|
}
|
|
return {};
|
|
}
|
|
|
|
ox::Result<ox::Buffer> Project::loadBuff(ox::StringViewCR path) const noexcept {
|
|
return m_fs.read(path);
|
|
}
|
|
|
|
ox::Error Project::lsProcDir(ox::Vector<ox::String> *paths, ox::StringViewCR path) const noexcept {
|
|
OX_REQUIRE(files, m_fs.ls(path));
|
|
for (auto const&name : files) {
|
|
auto fullPath = ox::sfmt("{}/{}", path, name);
|
|
OX_REQUIRE(stat, m_fs.stat(ox::StringView(fullPath)));
|
|
switch (stat.fileType) {
|
|
case ox::FileType::NormalFile:
|
|
paths->emplace_back(std::move(fullPath));
|
|
break;
|
|
case ox::FileType::Directory:
|
|
OX_RETURN_ERROR(lsProcDir(paths, fullPath));
|
|
break;
|
|
case ox::FileType::None:
|
|
break;
|
|
}
|
|
}
|
|
return {};
|
|
}
|
|
|
|
ox::Result<ox::Vector<ox::String>> Project::listFiles(ox::StringViewCR path) const noexcept {
|
|
ox::Vector<ox::String> paths;
|
|
OX_RETURN_ERROR(lsProcDir(&paths, path));
|
|
return paths;
|
|
}
|
|
|
|
}
|