diff --git a/deps/buildcore/scripts/setup-build.py b/deps/buildcore/scripts/setup-build.py index ebbad37..8a2c8aa 100755 --- a/deps/buildcore/scripts/setup-build.py +++ b/deps/buildcore/scripts/setup-build.py @@ -99,7 +99,9 @@ def main() -> int: if platform.system() == 'Windows': cmake_cmd.append('-A x64') - subprocess.run(cmake_cmd) + cmake_err = subprocess.run(cmake_cmd).returncode + if cmake_err != 0: + return cmake_err util.mkdir_p('dist') if int(args.current_build) != 0: diff --git a/deps/ox/src/ox/oc/write.hpp b/deps/ox/src/ox/oc/write.hpp index 77bb8cf..c2cf9de 100644 --- a/deps/ox/src/ox/oc/write.hpp +++ b/deps/ox/src/ox/oc/write.hpp @@ -37,7 +37,7 @@ class OrganicClawWriter { explicit OrganicClawWriter(Json::Value json, int unionIdx = -1) noexcept; Error field(const char *key, const int8_t *val) noexcept { - if (*val) { + if (targetValid() && (*val || m_json.isArray())) { value(key) = *val; } ++m_fieldIt; @@ -45,7 +45,7 @@ class OrganicClawWriter { } Error field(const char *key, const int16_t *val) noexcept { - if (*val) { + if (targetValid() && (*val || m_json.isArray())) { value(key) = *val; } ++m_fieldIt; @@ -53,7 +53,7 @@ class OrganicClawWriter { } Error field(const char *key, const int32_t *val) noexcept { - if (*val) { + if (targetValid() && (*val || m_json.isArray())) { value(key) = *val; } ++m_fieldIt; @@ -61,7 +61,7 @@ class OrganicClawWriter { } Error field(const char *key, const int64_t *val) noexcept { - if (*val) { + if (targetValid() && (*val || m_json.isArray())) { value(key) = *val; } ++m_fieldIt; @@ -70,7 +70,7 @@ class OrganicClawWriter { Error field(const char *key, const uint8_t *val) noexcept { - if (*val) { + if (targetValid() && (*val || m_json.isArray())) { value(key) = *val; } ++m_fieldIt; @@ -78,7 +78,7 @@ class OrganicClawWriter { } Error field(const char *key, const uint16_t *val) noexcept { - if (targetValid() && *val) { + if (targetValid() && (*val || m_json.isArray())) { value(key) = *val; } ++m_fieldIt; @@ -86,7 +86,7 @@ class OrganicClawWriter { } Error field(const char *key, const uint32_t *val) noexcept { - if (targetValid() && *val) { + if (targetValid() && (*val || m_json.isArray())) { value(key) = *val; } ++m_fieldIt; @@ -94,15 +94,15 @@ class OrganicClawWriter { } Error field(const char *key, const uint64_t *val) noexcept { - if (targetValid() && *val) { + if (targetValid() && (*val || m_json.isArray())) { value(key) = *val; } ++m_fieldIt; return OxError(0); } - Error field(const char *key, const bool *val) noexcept { - if (targetValid() && *val) { + Error field(char const*key, bool const*val) noexcept { + if (targetValid() && (*val || m_json.isArray())) { value(key) = *val; } ++m_fieldIt; @@ -110,10 +110,10 @@ class OrganicClawWriter { } template - Error field(const char*, UnionView val) noexcept; + Error field(char const*, UnionView val) noexcept; template - Error field(const char *key, const HashMap *val) noexcept { + Error field(char const*key, HashMap const*val) noexcept { if (targetValid()) { const auto &keys = val->keys(); OrganicClawWriter w; @@ -132,7 +132,7 @@ class OrganicClawWriter { } template - Error field(const char *key, const BString *val) noexcept { + Error field(char const*key, BString const*val) noexcept { if (targetValid() && val->len()) { value(key) = val->c_str(); } @@ -141,7 +141,7 @@ class OrganicClawWriter { } template - Error field(const char *key, const BasicString *val) noexcept { + Error field(char const*key, BasicString const*val) noexcept { if (targetValid() && val->len()) { value(key) = val->c_str(); } @@ -199,7 +199,7 @@ Error OrganicClawWriter::field(const char *key, const T *val, std::size_t len) n OrganicClawWriter w((Json::Value(Json::arrayValue))); ModelHandlerInterface handler{&w}; for (std::size_t i = 0; i < len; ++i) { - oxReturnError(handler.field("", &val[i])); + oxReturnError(handler.field({}, &val[i])); } value(key) = w.m_json; } diff --git a/sample_project/.nostalgia/type_descriptors/net.drinkingtea.nostalgia.core.TileSheet.SubSheet;4 b/sample_project/.nostalgia/type_descriptors/net.drinkingtea.nostalgia.core.TileSheet.SubSheet;4 new file mode 100644 index 0000000..185778a --- /dev/null +++ b/sample_project/.nostalgia/type_descriptors/net.drinkingtea.nostalgia.core.TileSheet.SubSheet;4 @@ -0,0 +1,46 @@ +O1;net.drinkingtea.ox.TypeDescriptor;1;{ + "fieldList" : + [ + { + "fieldName" : "id", + "typeId" : "B.int32;0" + }, + { + "fieldName" : "name", + "typeId" : "net.drinkingtea.ox.BasicString#8#;1" + }, + { + "fieldName" : "rows", + "typeId" : "B.int32;0" + }, + { + "fieldName" : "columns", + "typeId" : "B.int32;0" + }, + { + "fieldName" : "subsheets", + "subscriptLevels" : 1, + "subscriptStack" : + [ + { + "subscriptType" : 4 + } + ], + "typeId" : "net.drinkingtea.nostalgia.core.TileSheet.SubSheet;4" + }, + { + "fieldName" : "pixels", + "subscriptLevels" : 1, + "subscriptStack" : + [ + { + "subscriptType" : 4 + } + ], + "typeId" : "B.uint8;0" + } + ], + "primitiveType" : 5, + "typeName" : "net.drinkingtea.nostalgia.core.TileSheet.SubSheet", + "typeVersion" : 4 +} diff --git a/sample_project/.nostalgia/type_descriptors/net.drinkingtea.nostalgia.core.TileSheet;4 b/sample_project/.nostalgia/type_descriptors/net.drinkingtea.nostalgia.core.TileSheet;4 new file mode 100644 index 0000000..9dc2b45 --- /dev/null +++ b/sample_project/.nostalgia/type_descriptors/net.drinkingtea.nostalgia.core.TileSheet;4 @@ -0,0 +1,24 @@ +O1;net.drinkingtea.ox.TypeDescriptor;1;{ + "fieldList" : + [ + { + "fieldName" : "bpp", + "typeId" : "B.int8;0" + }, + { + "fieldName" : "idIt", + "typeId" : "B.int32;0" + }, + { + "fieldName" : "defaultPalette", + "typeId" : "net.drinkingtea.ox.FileAddress;1" + }, + { + "fieldName" : "subsheet", + "typeId" : "net.drinkingtea.nostalgia.core.TileSheet.SubSheet;4" + } + ], + "primitiveType" : 5, + "typeName" : "net.drinkingtea.nostalgia.core.TileSheet", + "typeVersion" : 4 +} diff --git a/sample_project/TileSheets/Charset.ng b/sample_project/TileSheets/Charset.ng index 0d81950..5deb280 100644 Binary files a/sample_project/TileSheets/Charset.ng and b/sample_project/TileSheets/Charset.ng differ diff --git a/sample_project/TileSheets/Chester.ng b/sample_project/TileSheets/Chester.ng index fe131dc..f641e4a 100644 Binary files a/sample_project/TileSheets/Chester.ng and b/sample_project/TileSheets/Chester.ng differ diff --git a/sample_project/TileSheets/Dirt.ng b/sample_project/TileSheets/Dirt.ng index 54f1cd1..a081913 100644 Binary files a/sample_project/TileSheets/Dirt.ng and b/sample_project/TileSheets/Dirt.ng differ diff --git a/src/nostalgia/modules/core/include/nostalgia/core/gfx.hpp b/src/nostalgia/modules/core/include/nostalgia/core/gfx.hpp index 8cc6882..3c8a45c 100644 --- a/src/nostalgia/modules/core/include/nostalgia/core/gfx.hpp +++ b/src/nostalgia/modules/core/include/nostalgia/core/gfx.hpp @@ -48,8 +48,8 @@ struct BgTile { static constexpr auto TypeVersion = 1; unsigned tileIdx = 0; unsigned palBank = 0; - unsigned horizontalFlip = false; - unsigned verticalFlip = false; + unsigned flipX = false; + unsigned flipY = false; }; oxModelBegin(BgTile) diff --git a/src/nostalgia/modules/core/include/nostalgia/core/tilesheet.hpp b/src/nostalgia/modules/core/include/nostalgia/core/tilesheet.hpp index f8509ba..e556423 100644 --- a/src/nostalgia/modules/core/include/nostalgia/core/tilesheet.hpp +++ b/src/nostalgia/modules/core/include/nostalgia/core/tilesheet.hpp @@ -61,7 +61,7 @@ struct TileSheetV2 { using SubSheetId = int32_t; -struct TileSheet { +struct TileSheetV3 { using SubSheetIdx = ox::Vector; struct SubSheet { @@ -73,119 +73,20 @@ struct TileSheet { int rows = 0; ox::Vector subsheets; ox::Vector pixels; - constexpr SubSheet() noexcept = default; - constexpr SubSheet(SubSheet const&other) noexcept = default; - SubSheet(SubSheet &&other) noexcept; - SubSheet( + inline SubSheet( SubSheetId pId, ox::CRStringView pName, int pColumns, int pRows, - int bpp) noexcept; - SubSheet( - SubSheetId pId, - ox::CRStringView pName, - int pColumns, - int pRows, - ox::Vector pPixels) noexcept; - - constexpr SubSheet &operator=(const SubSheet &other) noexcept = default; - - SubSheet &operator=(SubSheet &&other) noexcept; - - [[nodiscard]] - std::size_t idx(ox::Point const&pt) const noexcept; - - /** - * Reads all pixels of this sheet or its children into the given pixel list - * @param pixels - */ - void readPixelsTo(ox::Vector *pPixels, int8_t pBpp) const noexcept; - - /** - * Reads all pixels of this sheet or its children into the given pixel list - * @param pixels - */ - void readPixelsTo(ox::Vector *pPixels) const noexcept; - - [[nodiscard]] - constexpr std::size_t size() const noexcept { - return static_cast(columns) * static_cast(rows); + int bpp) noexcept: + id(pId), + name(pName), + columns(pColumns), + rows(pRows), + pixels(static_cast(columns * rows * PixelsPerTile) / (bpp == 4 ? 2u : 1u)) { } - [[nodiscard]] - std::size_t unusedPixels() const noexcept; - - [[nodiscard]] - uint8_t getPixel4Bpp(std::size_t idx) const noexcept; - - [[nodiscard]] - uint8_t getPixel8Bpp(std::size_t idx) const noexcept; - - [[nodiscard]] - uint8_t getPixel(int8_t pBpp, std::size_t idx) const noexcept; - - [[nodiscard]] - uint8_t getPixel4Bpp(const ox::Point &pt) const noexcept; - - [[nodiscard]] - uint8_t getPixel8Bpp(const ox::Point &pt) const noexcept; - - [[nodiscard]] - uint8_t getPixel(int8_t pBpp, const ox::Point &pt) const noexcept; - - constexpr auto walkPixels(int8_t pBpp, auto callback) const noexcept { - if (pBpp == 4) { - const auto pixelCnt = ox::min(static_cast(columns * rows * PixelsPerTile) / 2, - pixels.size()); - //oxAssert(pixels.size() == pixelCnt, "Pixel count does not match rows and columns"); - for (std::size_t i = 0; i < pixelCnt; ++i) { - const auto colorIdx1 = static_cast(pixels[i] & 0xF); - const auto colorIdx2 = static_cast(pixels[i] >> 4); - callback(i * 2 + 0, colorIdx1); - callback(i * 2 + 1, colorIdx2); - } - } else { - const auto pixelCnt = ox::min( - static_cast(columns * rows * PixelsPerTile), - pixels.size()); - for (std::size_t i = 0; i < pixelCnt; ++i) { - const auto p = pixels[i]; - callback(i, p); - } - } - } - - void setPixel(int8_t pBpp, uint64_t idx, uint8_t palIdx) noexcept; - - void setPixel(int8_t pBpp, ox::Point const&pt, uint8_t palIdx) noexcept; - - ox::Error setPixelCount(int8_t pBpp, std::size_t cnt) noexcept; - - /** - * Gets a count of the pixels in this sheet, and not that of its children. - * @param pBpp bits per pixel, need for knowing how to count the pixels - * @return a count of the pixels in this sheet - */ - [[nodiscard]] - unsigned pixelCnt(int8_t pBpp) const noexcept; - - /** - * Gets the offset in tiles of the desired subsheet. - */ - ox::Result getTileOffset( - ox::SpanView const&pNamePath, - int8_t pBpp, - std::size_t pIt = 0, - unsigned pCurrentTotal = 0) const noexcept; - - ox::Result getIdFor( - ox::SpanView const&pNamePath, - std::size_t pIt = 0) const noexcept; - - ox::Result getNameFor(SubSheetId pId) const noexcept; - }; static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.TileSheet"; @@ -195,86 +96,185 @@ struct TileSheet { ox::FileAddress defaultPalette; SubSheet subsheet{0, "Root", 1, 1, bpp}; +}; + +struct TileSheet { + using SubSheetIdx = ox::Vector; + + struct SubSheet { + static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.TileSheet.SubSheet"; + static constexpr auto TypeVersion = 4; + SubSheetId id = 0; + ox::String name; + int columns = 0; + int rows = 0; + ox::Vector subsheets; + ox::Vector pixels; + + constexpr SubSheet() noexcept = default; + inline SubSheet( + SubSheetId pId, + ox::CRStringView pName, + int pColumns, + int pRows, + int bpp) noexcept: + id(pId), + name(pName), + columns(pColumns), + rows(pRows), + pixels(static_cast(columns * rows * PixelsPerTile) / (bpp == 4 ? 2u : 1u)) { + } + inline SubSheet( + SubSheetId pId, + ox::CRStringView pName, + int pColumns, + int pRows, + ox::Vector pPixels) noexcept: + id(pId), + name(pName), + columns(pColumns), + rows(pRows), + pixels(std::move(pPixels)) { + } + + [[nodiscard]] + constexpr std::size_t size() const noexcept { + return static_cast(columns) * static_cast(rows); + } + + }; + + static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.TileSheet"; + static constexpr auto TypeVersion = 4; + int8_t bpp = 4; + SubSheetId idIt = 0; + ox::FileAddress defaultPalette; + SubSheet subsheet{0, "Root", 1, 1, bpp}; + constexpr TileSheet() noexcept = default; - TileSheet(TileSheet const&other) noexcept = default; - inline TileSheet(TileSheet &&other) noexcept: - bpp(other.bpp), - idIt(other.idIt), - defaultPalette(std::move(other.defaultPalette)), - subsheet(std::move(other.subsheet)) { - } - - TileSheet &operator=(TileSheet const&other) noexcept; - - TileSheet &operator=(TileSheet &&other) noexcept; - - [[nodiscard]] - SubSheetIdx validateSubSheetIdx( - SubSheetIdx const&pIdx, - std::size_t pIdxIt, - const SubSheet *pSubsheet) noexcept; - - /** - * validateSubSheetIdx takes a SubSheetIdx and moves the index to the - * preceding or parent sheet if the current corresponding sheet does - * not exist. - * @param idx SubSheetIdx to validate and correct - * @return a valid version of idx - */ - [[nodiscard]] - SubSheetIdx validateSubSheetIdx(const SubSheetIdx &idx) noexcept; - - [[nodiscard]] - static SubSheet const&getSubSheet( - SubSheetIdx const&idx, - std::size_t idxIt, - const SubSheet *pSubsheet) noexcept; - - [[nodiscard]] - static SubSheet &getSubSheet( - SubSheetIdx const&idx, - std::size_t idxIt, - SubSheet *pSubsheet) noexcept; - - [[nodiscard]] - const SubSheet &getSubSheet(SubSheetIdx const&idx) const noexcept; - - [[nodiscard]] - SubSheet &getSubSheet(SubSheetIdx const&idx) noexcept; - - ox::Error addSubSheet(SubSheetIdx const&idx) noexcept; - - [[nodiscard]] - static ox::Error rmSubSheet( - SubSheetIdx const&idx, - std::size_t idxIt, - SubSheet *pSubsheet) noexcept; - - [[nodiscard]] - ox::Error rmSubSheet(SubSheetIdx const&idx) noexcept; - - [[nodiscard]] - uint8_t getPixel4Bpp( - ox::Point const&pt, - SubSheetIdx const&subsheetIdx) const noexcept; - - [[nodiscard]] - uint8_t getPixel8Bpp( - ox::Point const&pt, - SubSheetIdx const&subsheetIdx) const noexcept; - - ox::Result getIdFor(ox::CRStringView path) const noexcept; - - ox::Result getTileOffset(ox::CRStringView pNamePath) const noexcept; - - ox::Result getNameFor(SubSheetId pId) const noexcept; - - [[nodiscard]] - ox::Vector pixels() const noexcept; }; -using TileSheetV3 = TileSheet; +[[nodiscard]] +std::size_t idx(TileSheet::SubSheet const&ss, ox::Point const&pt) noexcept; + +[[nodiscard]] +uint8_t getPixel4Bpp(TileSheet::SubSheet const&ss, std::size_t idx) noexcept; + +[[nodiscard]] +uint8_t getPixel8Bpp(TileSheet::SubSheet const&ss, std::size_t idx) noexcept; + +[[nodiscard]] +uint8_t getPixel(TileSheet::SubSheet const&ss, int8_t pBpp, std::size_t idx) noexcept; + +[[nodiscard]] +uint8_t getPixel4Bpp(TileSheet::SubSheet const&ss, ox::Point const&pt) noexcept; + +[[nodiscard]] +uint8_t getPixel8Bpp(TileSheet::SubSheet const&ss, ox::Point const&pt) noexcept; + +[[nodiscard]] +uint8_t getPixel(TileSheet::SubSheet const&ss, int8_t pBpp, ox::Point const&pt) noexcept; + +constexpr void walkPixels(TileSheet::SubSheet const&ss, int8_t pBpp, auto callback) noexcept { + if (pBpp == 4) { + const auto pixelCnt = ox::min( + static_cast(ss.columns * ss.rows * PixelsPerTile) / 2, + ss.pixels.size()); + //oxAssert(pixels.size() == pixelCnt, "Pixel count does not match rows and columns"); + for (std::size_t i = 0; i < pixelCnt; ++i) { + const auto colorIdx1 = static_cast(ss.pixels[i] & 0xF); + const auto colorIdx2 = static_cast(ss.pixels[i] >> 4); + callback(i * 2 + 0, colorIdx1); + callback(i * 2 + 1, colorIdx2); + } + } else { + const auto pixelCnt = ox::min( + static_cast(ss.columns * ss.rows * PixelsPerTile), + ss.pixels.size()); + for (std::size_t i = 0; i < pixelCnt; ++i) { + const auto p = ss.pixels[i]; + callback(i, p); + } + } +} + +void setPixel(TileSheet::SubSheet &ss, int8_t pBpp, uint64_t idx, uint8_t palIdx) noexcept; + +void setPixel(TileSheet::SubSheet &ss, int8_t pBpp, ox::Point const&pt, uint8_t palIdx) noexcept; + +ox::Error setPixelCount(TileSheet::SubSheet &ss, int8_t pBpp, std::size_t cnt) noexcept; + +/** + * Gets a count of the pixels in this sheet, and not that of its children. + * @param pBpp bits per pixel, need for knowing how to count the pixels + * @return a count of the pixels in this sheet + */ +[[nodiscard]] +unsigned pixelCnt(TileSheet::SubSheet const&ss, int8_t pBpp) noexcept; + +/** + * validateSubSheetIdx takes a SubSheetIdx and moves the index to the + * preceding or parent sheet if the current corresponding sheet does + * not exist. + * @param idx SubSheetIdx to validate and correct + * @return a valid version of idx + */ +[[nodiscard]] +TileSheet::SubSheetIdx validateSubSheetIdx(TileSheet const&ts, TileSheet::SubSheetIdx const&idx) noexcept; + +[[nodiscard]] +TileSheet::SubSheet const&getSubSheet( + TileSheet::SubSheetIdx const&idx, + std::size_t idxIt, + TileSheet::SubSheet const&pSubsheet) noexcept; + +[[nodiscard]] +TileSheet::SubSheet &getSubSheet( + TileSheet::SubSheetIdx const&idx, + std::size_t idxIt, + TileSheet::SubSheet &pSubsheet) noexcept; + +[[nodiscard]] +TileSheet::SubSheet const&getSubSheet(TileSheet const&ts, TileSheet::SubSheetIdx const&idx) noexcept; + +[[nodiscard]] +TileSheet::SubSheet &getSubSheet(TileSheet &ts, TileSheet::SubSheetIdx const&idx) noexcept; + +ox::Error addSubSheet(TileSheet &ts, TileSheet::SubSheetIdx const&idx) noexcept; + +ox::Error rmSubSheet( + TileSheet &ts, + TileSheet::SubSheetIdx const&idx, + std::size_t idxIt, + TileSheet::SubSheet &pSubsheet) noexcept; + +ox::Error rmSubSheet(TileSheet &ts, TileSheet::SubSheetIdx const&idx) noexcept; + +[[nodiscard]] +uint8_t getPixel4Bpp( + TileSheet const&ts, + ox::Point const&pt, + TileSheet::SubSheetIdx const&subsheetIdx) noexcept; + +[[nodiscard]] +uint8_t getPixel8Bpp( + TileSheet const&ts, + ox::Point const&pt, + TileSheet::SubSheetIdx const&subsheetIdx) noexcept; + +ox::Result getIdFor(TileSheet const&ts, ox::CRStringView path) noexcept; + +ox::Result getTileOffset(TileSheet const&ts, ox::CRStringView pNamePath) noexcept; + +ox::Result getNameFor(TileSheet::SubSheet const&ss, SubSheetId pId) noexcept; + +ox::Result getNameFor(TileSheet const&ss, SubSheetId pId) noexcept; + +[[nodiscard]] +ox::Vector pixels(TileSheet &ts) noexcept; + +using TileSheetV4 = TileSheet; struct CompactTileSheet { static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.CompactTileSheet"; @@ -294,9 +294,9 @@ oxModelBegin(TileSheetV1) oxModelEnd() oxModelBegin(TileSheetV2::SubSheet) - oxModelField(name); - oxModelField(rows); - oxModelField(columns); + oxModelField(name) + oxModelField(rows) + oxModelField(columns) oxModelField(subsheets) oxModelField(pixels) oxModelEnd() @@ -308,9 +308,9 @@ oxModelBegin(TileSheetV2) oxModelEnd() oxModelBegin(TileSheetV3::SubSheet) - oxModelField(name); - oxModelField(rows); - oxModelField(columns); + oxModelField(name) + oxModelField(rows) + oxModelField(columns) oxModelField(subsheets) oxModelField(pixels) oxModelEnd() @@ -322,6 +322,22 @@ oxModelBegin(TileSheetV3) oxModelField(subsheet) oxModelEnd() +oxModelBegin(TileSheetV4::SubSheet) + oxModelField(id) + oxModelField(name) + oxModelField(rows) + oxModelField(columns) + oxModelField(subsheets) + oxModelField(pixels) +oxModelEnd() + +oxModelBegin(TileSheetV4) + oxModelField(bpp) + oxModelField(idIt) + oxModelField(defaultPalette) + oxModelField(subsheet) +oxModelEnd() + oxModelBegin(CompactTileSheet) oxModelField(bpp) oxModelField(defaultPalette) diff --git a/src/nostalgia/modules/core/src/gba/gfx.cpp b/src/nostalgia/modules/core/src/gba/gfx.cpp index dcd33ba..ec0153e 100644 --- a/src/nostalgia/modules/core/src/gba/gfx.cpp +++ b/src/nostalgia/modules/core/src/gba/gfx.cpp @@ -289,8 +289,8 @@ void setBgTile(Context&, uint_t bgIdx, int column, int row, BgTile const&tile) n // see Tonc 9.3 MEM_BG_MAP[bgIdx][tileIdx] = static_cast(tile.tileIdx & 0b1'1111'1111) | - static_cast(tile.horizontalFlip << 0xa) | - static_cast(tile.verticalFlip << 0xb) | + static_cast(tile.flipX << 0xa) | + static_cast(tile.flipY << 0xb) | static_cast(tile.palBank << 0xc); } diff --git a/src/nostalgia/modules/core/src/keel/keelmodule.cpp b/src/nostalgia/modules/core/src/keel/keelmodule.cpp index 57dd903..a91d0e5 100644 --- a/src/nostalgia/modules/core/src/keel/keelmodule.cpp +++ b/src/nostalgia/modules/core/src/keel/keelmodule.cpp @@ -18,7 +18,8 @@ class KeelModule: public keel::Module { private: NostalgiaPaletteToPaletteConverter m_nostalgiaPaletteToPaletteConverter; TileSheetV1ToTileSheetV2Converter m_tileSheetV1ToTileSheetV2Converter; - TileSheetV2ToTileSheetV3Converter m_tileSheetV2ToTileSheetConverter; + TileSheetV2ToTileSheetV3Converter m_tileSheetV2ToTileSheetV3Converter; + TileSheetV3ToTileSheetV4Converter m_tileSheetV3ToTileSheetV4Converter; TileSheetToCompactTileSheetConverter m_tileSheetToCompactTileSheetConverter; public: @@ -32,18 +33,20 @@ class KeelModule: public keel::Module { return { keel::generateTypeDesc, keel::generateTypeDesc, - keel::generateTypeDesc, + keel::generateTypeDesc, + keel::generateTypeDesc, keel::generateTypeDesc, keel::generateTypeDesc, }; } [[nodiscard]] - ox::Vector converters() const noexcept final { + ox::Vector converters() const noexcept final { return { &m_nostalgiaPaletteToPaletteConverter, &m_tileSheetV1ToTileSheetV2Converter, - &m_tileSheetV2ToTileSheetConverter, + &m_tileSheetV2ToTileSheetV3Converter, + &m_tileSheetV3ToTileSheetV4Converter, &m_tileSheetToCompactTileSheetConverter, }; } @@ -54,11 +57,12 @@ class KeelModule: public keel::Module { // convert tilesheets to CompactTileSheets [](keel::Context &ctx, ox::Buffer &buff) -> ox::Error { oxRequire(hdr, keel::readAssetHeader(buff)); - const auto typeId = ox::buildTypeId( + auto const typeId = ox::buildTypeId( hdr.clawHdr.typeName, hdr.clawHdr.typeVersion, hdr.clawHdr.typeParams); if (typeId == ox::buildTypeId() || - typeId == ox::buildTypeId() || - typeId == ox::buildTypeId()) { + typeId == ox::buildTypeId() || + typeId == ox::buildTypeId() || + typeId == ox::buildTypeId()) { oxReturnError(keel::convertBuffToBuff( ctx, buff, ox::ClawFormat::Metal).moveTo(buff)); } diff --git a/src/nostalgia/modules/core/src/keel/typeconv.cpp b/src/nostalgia/modules/core/src/keel/typeconv.cpp index 7a3662e..9f4aefe 100644 --- a/src/nostalgia/modules/core/src/keel/typeconv.cpp +++ b/src/nostalgia/modules/core/src/keel/typeconv.cpp @@ -53,13 +53,41 @@ ox::Error TileSheetV2ToTileSheetV3Converter::convert( return {}; } + +void TileSheetV3ToTileSheetV4Converter::convertSubsheet( + TileSheetV3::SubSheet &src, + TileSheetV4::SubSheet &dst, + SubSheetId &idIt) noexcept { + dst.id = idIt; + dst.name = std::move(src.name); + dst.columns = src.columns; + dst.rows = src.rows; + dst.pixels = std::move(src.pixels); + ++idIt; + dst.subsheets.resize(src.subsheets.size()); + for (auto i = 0u; i < src.subsheets.size(); ++i) { + convertSubsheet(src.subsheets[i], dst.subsheets[i], idIt); + } +} + +ox::Error TileSheetV3ToTileSheetV4Converter::convert( + keel::Context&, + TileSheetV3 &src, + TileSheetV4 &dst) const noexcept { + dst.bpp = src.bpp; + dst.idIt = src.idIt; + dst.defaultPalette = std::move(src.defaultPalette); + convertSubsheet(src.subsheet, dst.subsheet, dst.idIt); + return {}; +} + ox::Error TileSheetToCompactTileSheetConverter::convert( keel::Context&, TileSheet &src, CompactTileSheet &dst) const noexcept { dst.bpp = src.bpp; dst.defaultPalette = std::move(src.defaultPalette); - dst.pixels = src.pixels(); + dst.pixels = pixels(src); return {}; } diff --git a/src/nostalgia/modules/core/src/keel/typeconv.hpp b/src/nostalgia/modules/core/src/keel/typeconv.hpp index 012ff7f..d47e25f 100644 --- a/src/nostalgia/modules/core/src/keel/typeconv.hpp +++ b/src/nostalgia/modules/core/src/keel/typeconv.hpp @@ -25,11 +25,19 @@ class TileSheetV1ToTileSheetV2Converter: public keel::Converter { + static void convertSubsheet( + TileSheetV2::SubSheet &src, + TileSheetV3::SubSheet &dst, + SubSheetId &idIt) noexcept; + ox::Error convert(keel::Context&, TileSheetV2 &src, TileSheetV3 &dst) const noexcept final; +}; + +class TileSheetV3ToTileSheetV4Converter: public keel::Converter { static void convertSubsheet( - TileSheetV2::SubSheet &src, - TileSheetV3::SubSheet &dst, + TileSheetV3::SubSheet &src, + TileSheetV4::SubSheet &dst, SubSheetId &idIt) noexcept; - ox::Error convert(keel::Context&, TileSheetV2 &src, TileSheetV3 &dst) const noexcept final; + ox::Error convert(keel::Context&, TileSheetV3 &src, TileSheetV4 &dst) const noexcept final; }; class TileSheetToCompactTileSheetConverter: public keel::Converter { diff --git a/src/nostalgia/modules/core/src/opengl/gfx.cpp b/src/nostalgia/modules/core/src/opengl/gfx.cpp index 3dd9dc8..1900517 100644 --- a/src/nostalgia/modules/core/src/opengl/gfx.cpp +++ b/src/nostalgia/modules/core/src/opengl/gfx.cpp @@ -152,6 +152,8 @@ static void setTileBufferObject( float textureTileIdx, float priority, float palOffset, + bool flipX, + bool flipY, float *vbo, GLuint *ebo) noexcept { // don't worry, this memcpy gets optimized to something much more ideal @@ -162,11 +164,15 @@ static void setTileBufferObject( x -= 1.0f; y += 1.0f - ymod; auto const prif = priority * PriorityScale; + float const L = flipX ? 1 : 0; + float const R = flipX ? 0 : 1; + float const T = flipY ? 1 : 0; + float const B = flipY ? 0 : 1; ox::Array const vertices { - x, y, prif, 0, 1, textureTileIdx, palOffset, // bottom left - x + xmod, y, prif, 1, 1, textureTileIdx, palOffset, // bottom right - x + xmod, y + ymod, prif, 1, 0, textureTileIdx, palOffset, // top right - x, y + ymod, prif, 0, 0, textureTileIdx, palOffset, // top left + x, y, prif, L, B, textureTileIdx, palOffset, // bottom left + x + xmod, y, prif, R, B, textureTileIdx, palOffset, // bottom right + x + xmod, y + ymod, prif, R, T, textureTileIdx, palOffset, // top right + x, y + ymod, prif, L, T, textureTileIdx, palOffset, // top left }; memcpy(vbo, vertices.data(), sizeof(vertices)); ox::Array const elms { @@ -197,6 +203,8 @@ static void initBackgroundBufferObjects(glutils::BufferSet &bs) noexcept { 0, 0, 0, + false, + false, vbo, ebo); } @@ -612,6 +620,8 @@ void setBgTile( static_cast(tile.tileIdx), bg.priority, static_cast(tile.palBank * 16), + tile.flipX, + tile.flipY, vbo, ebo); cbb.updated = true; diff --git a/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/addsubsheetcommand.cpp b/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/addsubsheetcommand.cpp index 8a31bfb..fdaa777 100644 --- a/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/addsubsheetcommand.cpp +++ b/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/addsubsheetcommand.cpp @@ -10,7 +10,7 @@ AddSubSheetCommand::AddSubSheetCommand( TileSheet &img, TileSheet::SubSheetIdx parentIdx) noexcept: m_img(img), m_parentIdx(std::move(parentIdx)) { - auto &parent = m_img.getSubSheet(m_parentIdx); + auto &parent = getSubSheet(m_img, m_parentIdx); if (!parent.subsheets.empty()) { auto idx = m_parentIdx; idx.emplace_back(parent.subsheets.size()); @@ -25,7 +25,7 @@ AddSubSheetCommand::AddSubSheetCommand( } void AddSubSheetCommand::redo() noexcept { - auto &parent = m_img.getSubSheet(m_parentIdx); + auto &parent = getSubSheet(m_img, m_parentIdx); if (m_addedSheets.size() < 2) { auto i = parent.subsheets.size(); parent.subsheets.emplace_back(m_img.idIt++, ox::sfmt("Subsheet {}", i), 1, 1, m_img.bpp); @@ -38,7 +38,7 @@ void AddSubSheetCommand::redo() noexcept { } void AddSubSheetCommand::undo() noexcept { - auto &parent = m_img.getSubSheet(m_parentIdx); + auto &parent = getSubSheet(m_img, m_parentIdx); if (parent.subsheets.size() == 2) { auto s = parent.subsheets[0]; parent.rows = s.rows; @@ -47,7 +47,7 @@ void AddSubSheetCommand::undo() noexcept { parent.subsheets.clear(); } else { for (auto idx = m_addedSheets.rbegin(); idx != m_addedSheets.rend(); ++idx) { - oxLogError(m_img.rmSubSheet(*idx)); + oxLogError(rmSubSheet(m_img, *idx)); } } } diff --git a/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/cutpastecommand.cpp b/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/cutpastecommand.cpp index c011c63..cee911b 100644 --- a/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/cutpastecommand.cpp +++ b/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/cutpastecommand.cpp @@ -31,27 +31,27 @@ CutPasteCommand::CutPasteCommand( m_commandId(commandId), m_img(img), m_subSheetIdx(std::move(subSheetIdx)) { - const auto &subsheet = m_img.getSubSheet(m_subSheetIdx); + const auto &subsheet = getSubSheet(m_img, m_subSheetIdx); for (const auto &p : cb.pixels()) { const auto dstPt = p.pt + dstStart; if (dstPt.x <= dstEnd.x && dstPt.y <= dstEnd.y) { - const auto idx = subsheet.idx(dstPt); - m_changes.emplace_back(static_cast(idx), p.colorIdx, subsheet.getPixel(m_img.bpp, idx)); + const auto idx = core::idx(subsheet, dstPt); + m_changes.emplace_back(static_cast(idx), p.colorIdx, getPixel(subsheet, m_img.bpp, idx)); } } } void CutPasteCommand::redo() noexcept { - auto &subsheet = m_img.getSubSheet(m_subSheetIdx); + auto &subsheet = getSubSheet(m_img, m_subSheetIdx); for (const auto &c : m_changes) { - subsheet.setPixel(m_img.bpp, c.idx, static_cast(c.newPalIdx)); + setPixel(subsheet, m_img.bpp, c.idx, static_cast(c.newPalIdx)); } } void CutPasteCommand::undo() noexcept { - auto &subsheet = m_img.getSubSheet(m_subSheetIdx); + auto &subsheet = getSubSheet(m_img, m_subSheetIdx); for (const auto &c : m_changes) { - subsheet.setPixel(m_img.bpp, c.idx, static_cast(c.oldPalIdx)); + setPixel(subsheet, m_img.bpp, c.idx, static_cast(c.oldPalIdx)); } } diff --git a/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/deletetilescommand.cpp b/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/deletetilescommand.cpp index c678709..57af043 100644 --- a/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/deletetilescommand.cpp +++ b/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/deletetilescommand.cpp @@ -19,7 +19,7 @@ core::DeleteTilesCommand::DeleteTilesCommand( m_deletedPixels.resize(m_deleteSz); // copy pixels to be erased { - auto &s = m_img.getSubSheet(m_idx); + auto &s = getSubSheet(m_img, m_idx); auto &p = s.pixels; auto dst = m_deletedPixels.data(); auto src = p.data() + m_deletePos; @@ -29,7 +29,7 @@ core::DeleteTilesCommand::DeleteTilesCommand( } void core::DeleteTilesCommand::redo() noexcept { - auto &s = m_img.getSubSheet(m_idx); + auto &s = getSubSheet(m_img, m_idx); auto &p = s.pixels; auto srcPos = m_deletePos + m_deleteSz; const auto src = p.data() + srcPos; @@ -40,7 +40,7 @@ void core::DeleteTilesCommand::redo() noexcept { } void DeleteTilesCommand::undo() noexcept { - auto &s = m_img.getSubSheet(m_idx); + auto &s = getSubSheet(m_img, m_idx); auto &p = s.pixels; const auto src = p.data() + m_deletePos; const auto dst1 = p.data() + m_deletePos + m_deleteSz; diff --git a/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/drawcommand.cpp b/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/drawcommand.cpp index 99e9964..ea155ea 100644 --- a/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/drawcommand.cpp +++ b/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/drawcommand.cpp @@ -14,8 +14,8 @@ DrawCommand::DrawCommand( m_img(img), m_subSheetIdx(std::move(subSheetIdx)), m_palIdx(palIdx) { - auto &subsheet = m_img.getSubSheet(m_subSheetIdx); - m_changes.emplace_back(static_cast(idx), subsheet.getPixel(m_img.bpp, idx)); + auto &subsheet = getSubSheet(m_img, m_subSheetIdx); + m_changes.emplace_back(static_cast(idx), getPixel(subsheet, m_img.bpp, idx)); } DrawCommand::DrawCommand( @@ -26,22 +26,22 @@ DrawCommand::DrawCommand( m_img(img), m_subSheetIdx(std::move(subSheetIdx)), m_palIdx(palIdx) { - auto &subsheet = m_img.getSubSheet(m_subSheetIdx); + auto &subsheet = getSubSheet(m_img, m_subSheetIdx); for (const auto idx : idxList) { - m_changes.emplace_back(static_cast(idx), subsheet.getPixel(m_img.bpp, idx)); + m_changes.emplace_back(static_cast(idx), getPixel(subsheet, m_img.bpp, idx)); } } bool DrawCommand::append(std::size_t idx) noexcept { - auto &subsheet = m_img.getSubSheet(m_subSheetIdx); - if (m_changes.back().value->idx != idx && subsheet.getPixel(m_img.bpp, idx) != m_palIdx) { + auto &subsheet = getSubSheet(m_img, m_subSheetIdx); + if (m_changes.back().value->idx != idx && getPixel(subsheet, m_img.bpp, idx) != m_palIdx) { // duplicate entries are bad auto existing = ox::find_if(m_changes.cbegin(), m_changes.cend(), [idx](const auto &c) { return c.idx == idx; }); if (existing == m_changes.cend()) { - m_changes.emplace_back(static_cast(idx), subsheet.getPixel(m_img.bpp, idx)); - subsheet.setPixel(m_img.bpp, idx, static_cast(m_palIdx)); + m_changes.emplace_back(static_cast(idx), getPixel(subsheet, m_img.bpp, idx)); + setPixel(subsheet, m_img.bpp, idx, static_cast(m_palIdx)); return true; } } @@ -57,16 +57,16 @@ bool DrawCommand::append(const ox::Vector &idxList) noexcept { } void DrawCommand::redo() noexcept { - auto &subsheet = m_img.getSubSheet(m_subSheetIdx); + auto &subsheet = getSubSheet(m_img, m_subSheetIdx); for (const auto &c : m_changes) { - subsheet.setPixel(m_img.bpp, c.idx, static_cast(m_palIdx)); + setPixel(subsheet, m_img.bpp, c.idx, static_cast(m_palIdx)); } } void DrawCommand::undo() noexcept { - auto &subsheet = m_img.getSubSheet(m_subSheetIdx); + auto &subsheet = getSubSheet(m_img, m_subSheetIdx); for (const auto &c : m_changes) { - subsheet.setPixel(m_img.bpp, c.idx, static_cast(c.oldPalIdx)); + setPixel(subsheet, m_img.bpp, c.idx, static_cast(c.oldPalIdx)); } } diff --git a/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/inserttilescommand.cpp b/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/inserttilescommand.cpp index 52b2380..ac0083a 100644 --- a/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/inserttilescommand.cpp +++ b/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/inserttilescommand.cpp @@ -19,7 +19,7 @@ core::InsertTilesCommand::InsertTilesCommand( m_deletedPixels.resize(m_insertCnt); // copy pixels to be erased { - auto &s = m_img.getSubSheet(m_idx); + auto &s = getSubSheet(m_img, m_idx); auto &p = s.pixels; auto dst = m_deletedPixels.data(); auto src = p.data() + p.size() - m_insertCnt; @@ -29,7 +29,7 @@ core::InsertTilesCommand::InsertTilesCommand( } void InsertTilesCommand::redo() noexcept { - auto &s = m_img.getSubSheet(m_idx); + auto &s = getSubSheet(m_img, m_idx); auto &p = s.pixels; auto dstPos = m_insertPos + m_insertCnt; const auto dst = p.data() + dstPos; @@ -39,7 +39,7 @@ void InsertTilesCommand::redo() noexcept { } void InsertTilesCommand::undo() noexcept { - auto &s = m_img.getSubSheet(m_idx); + auto &s = getSubSheet(m_img, m_idx); auto &p = s.pixels; const auto srcIdx = m_insertPos + m_insertCnt; const auto src = p.data() + srcIdx; diff --git a/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/rmsubsheetcommand.cpp b/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/rmsubsheetcommand.cpp index ac7a9c3..afd3534 100644 --- a/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/rmsubsheetcommand.cpp +++ b/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/rmsubsheetcommand.cpp @@ -11,17 +11,17 @@ core::RmSubSheetCommand::RmSubSheetCommand(TileSheet &img, TileSheet::SubSheetId m_idx(std::move(idx)), m_parentIdx(m_idx) { m_parentIdx.pop_back(); - auto &parent = m_img.getSubSheet(m_parentIdx); + auto &parent = getSubSheet(m_img, m_parentIdx); m_sheet = parent.subsheets[*m_idx.back().value]; } void RmSubSheetCommand::redo() noexcept { - auto &parent = m_img.getSubSheet(m_parentIdx); + auto &parent = getSubSheet(m_img, m_parentIdx); oxLogError(parent.subsheets.erase(*m_idx.back().value).error); } void RmSubSheetCommand::undo() noexcept { - auto &parent = m_img.getSubSheet(m_parentIdx); + auto &parent = getSubSheet(m_img, m_parentIdx); auto i = *m_idx.back().value; parent.subsheets.insert(i, m_sheet); } diff --git a/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/updatesubsheetcommand.cpp b/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/updatesubsheetcommand.cpp index 5c16579..93e2c74 100644 --- a/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/updatesubsheetcommand.cpp +++ b/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/updatesubsheetcommand.cpp @@ -14,22 +14,22 @@ core::UpdateSubSheetCommand::UpdateSubSheetCommand( int rows) noexcept: m_img(img), m_idx(std::move(idx)), - m_sheet(m_img.getSubSheet(m_idx)), + m_sheet(getSubSheet(m_img, m_idx)), m_newName(std::move(name)), m_newCols(cols), m_newRows(rows) { } void UpdateSubSheetCommand::redo() noexcept { - auto &sheet = m_img.getSubSheet(m_idx); + auto &sheet = getSubSheet(m_img, m_idx); sheet.name = m_newName; sheet.columns = m_newCols; sheet.rows = m_newRows; - oxLogError(sheet.setPixelCount(m_img.bpp, static_cast(PixelsPerTile * m_newCols * m_newRows))); + oxLogError(setPixelCount(sheet, m_img.bpp, static_cast(PixelsPerTile * m_newCols * m_newRows))); } void UpdateSubSheetCommand::undo() noexcept { - auto &sheet = m_img.getSubSheet(m_idx); + auto &sheet = getSubSheet(m_img, m_idx); sheet = m_sheet; } diff --git a/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheeteditor-imgui.cpp b/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheeteditor-imgui.cpp index d0ab8d6..9d2ff65 100644 --- a/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheeteditor-imgui.cpp +++ b/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheeteditor-imgui.cpp @@ -191,7 +191,7 @@ void TileSheetEditorImGui::draw(turbine::Context&) noexcept { ImGui::SameLine(); if (ImGui::Button("-", btnSize)) { const auto &activeSubsheetIdx = m_model.activeSubSheetIdx(); - if (activeSubsheetIdx.size() > 0) { + if (!activeSubsheetIdx.empty()) { m_model.rmSubsheet(activeSubsheetIdx); } } @@ -205,12 +205,13 @@ void TileSheetEditorImGui::draw(turbine::Context&) noexcept { } TileSheet::SubSheetIdx path; static constexpr auto flags = ImGuiTableFlags_RowBg | ImGuiTableFlags_NoBordersInBody; - if (ImGui::BeginTable("Subsheets", 3, flags)) { + if (ImGui::BeginTable("Subsheets", 4, flags)) { ImGui::TableSetupColumn("Subsheet", ImGuiTableColumnFlags_NoHide); + ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_WidthFixed, 25); ImGui::TableSetupColumn("Columns", ImGuiTableColumnFlags_WidthFixed, 50); ImGui::TableSetupColumn("Rows", ImGuiTableColumnFlags_WidthFixed, 50); ImGui::TableHeadersRow(); - drawSubsheetSelector(&m_view.img().subsheet, &path); + drawSubsheetSelector(m_view.img().subsheet, path); ImGui::EndTable(); } } @@ -221,31 +222,34 @@ void TileSheetEditorImGui::draw(turbine::Context&) noexcept { m_exportMenu.draw(); } -void TileSheetEditorImGui::drawSubsheetSelector(TileSheet::SubSheet *subsheet, TileSheet::SubSheetIdx *path) { +void TileSheetEditorImGui::drawSubsheetSelector(TileSheet::SubSheet &subsheet, TileSheet::SubSheetIdx &path) { + constexpr auto indentReduce = 14; ImGui::TableNextRow(0, 5); using Str = ox::BasicString<100>; - auto pathStr = ox::join("##", *path).value; - auto lbl = ox::sfmt("{}##{}", subsheet->name, pathStr); - const auto rowSelected = *path == m_model.activeSubSheetIdx(); + auto pathStr = ox::join("##", path).value; + auto lbl = ox::sfmt("{}##{}", subsheet.name, pathStr); + const auto rowSelected = path == m_model.activeSubSheetIdx(); const auto flags = ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_DefaultOpen - | (subsheet->subsheets.empty() ? ImGuiTreeNodeFlags_Leaf : 0) + | (subsheet.subsheets.empty() ? ImGuiTreeNodeFlags_Leaf : 0) | (rowSelected ? ImGuiTreeNodeFlags_Selected : 0); ImGui::TableNextColumn(); const auto open = ImGui::TreeNodeEx(lbl.c_str(), flags); ImGui::SameLine(); if (ImGui::IsItemClicked()) { - m_model.setActiveSubsheet(*path); + m_model.setActiveSubsheet(path); } if (ImGui::IsMouseDoubleClicked(0) && ImGui::IsItemHovered()) { showSubsheetEditor(); } - if (subsheet->subsheets.empty()) { + ImGui::TableNextColumn(); + ImGui::Text("%d", subsheet.id); + if (subsheet.subsheets.empty()) { ImGui::TableNextColumn(); - ImGui::Text("%d", subsheet->columns); + ImGui::Text("%d", subsheet.columns); ImGui::TableNextColumn(); - ImGui::Text("%d", subsheet->rows); + ImGui::Text("%d", subsheet.rows); } else { ImGui::TableNextColumn(); ImGui::Text("--"); @@ -253,12 +257,14 @@ void TileSheetEditorImGui::drawSubsheetSelector(TileSheet::SubSheet *subsheet, T ImGui::Text("--"); } if (open) { - for (auto i = 0ul; auto &child : subsheet->subsheets) { - path->push_back(i); + for (auto i = 0ul; auto &child : subsheet.subsheets) { + path.push_back(i); ImGui::PushID(static_cast(i)); - drawSubsheetSelector(&child, path); + ImGui::Indent(-indentReduce); + drawSubsheetSelector(child, path); + ImGui::Indent(indentReduce); ImGui::PopID(); - path->pop_back(); + path.pop_back(); ++i; } ImGui::TreePop(); diff --git a/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheeteditor-imgui.hpp b/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheeteditor-imgui.hpp index 5f61770..de35406 100644 --- a/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheeteditor-imgui.hpp +++ b/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheeteditor-imgui.hpp @@ -83,7 +83,7 @@ class TileSheetEditorImGui: public studio::Editor { void draw(turbine::Context&) noexcept override; - void drawSubsheetSelector(TileSheet::SubSheet*, TileSheet::SubSheetIdx *path); + void drawSubsheetSelector(TileSheet::SubSheet&, TileSheet::SubSheetIdx &path); [[nodiscard]] static ox::Vec2 clickPos(ImVec2 const&winPos, ox::Vec2 clickPos) noexcept; diff --git a/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheeteditormodel.cpp b/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheeteditormodel.cpp index b0f4d8e..51d1e2c 100644 --- a/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheeteditormodel.cpp +++ b/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheeteditormodel.cpp @@ -47,8 +47,8 @@ void TileSheetEditorModel::cut() { for (int y = m_selectionBounds.y; y <= m_selectionBounds.y2(); ++y) { for (int x = m_selectionBounds.x; x <= m_selectionBounds.x2(); ++x) { auto pt = ox::Point(x, y); - const auto idx = s->idx(pt); - const auto c = s->getPixel(m_img.bpp, idx); + const auto idx = core::idx(*s, pt); + const auto c = getPixel(*s, m_img.bpp, idx); pt.x -= m_selectionBounds.x; pt.y -= m_selectionBounds.y; cb->addPixel(pt, c); @@ -67,8 +67,8 @@ void TileSheetEditorModel::copy() { for (int x = m_selectionBounds.x; x <= m_selectionBounds.x2(); ++x) { auto pt = ox::Point(x, y); const auto s = activeSubSheet(); - const auto idx = s->idx(pt); - const auto c = s->getPixel(m_img.bpp, idx); + const auto idx = core::idx(*s, pt); + const auto c = getPixel(*s, m_img.bpp, idx); pt.x -= m_selectionBounds.x; pt.y -= m_selectionBounds.y; cb->addPixel(pt, c); @@ -115,14 +115,14 @@ ox::Error TileSheetEditorModel::setPalette(ox::StringView path) noexcept { } void TileSheetEditorModel::drawCommand(ox::Point const&pt, std::size_t palIdx) noexcept { - const auto &activeSubSheet = m_img.getSubSheet(m_activeSubsSheetIdx); + const auto &activeSubSheet = getSubSheet(m_img, m_activeSubsSheetIdx); if (pt.x >= activeSubSheet.columns * TileWidth || pt.y >= activeSubSheet.rows * TileHeight) { return; } - const auto idx = activeSubSheet.idx(pt); + const auto idx = core::idx(activeSubSheet, pt); if (m_ongoingDrawCommand) { m_updated = m_updated || m_ongoingDrawCommand->append(idx); - } else if (activeSubSheet.getPixel(m_img.bpp, idx) != palIdx) { + } else if (getPixel(activeSubSheet, m_img.bpp, idx) != palIdx) { pushCommand(ox::make(m_img, m_activeSubsSheetIdx, idx, static_cast(palIdx))); } } @@ -147,7 +147,7 @@ void TileSheetEditorModel::deleteTiles(TileSheet::SubSheetIdx const&idx, std::si pushCommand(ox::make(m_img, idx, tileIdx, tileCnt)); } -ox::Error TileSheetEditorModel::updateSubsheet(TileSheet::SubSheetIdx const&idx, const ox::StringView &name, int cols, int rows) noexcept { +ox::Error TileSheetEditorModel::updateSubsheet(TileSheet::SubSheetIdx const&idx, ox::StringView const&name, int cols, int rows) noexcept { pushCommand(ox::make(m_img, idx, ox::String(name), cols, rows)); return {}; } @@ -158,16 +158,16 @@ void TileSheetEditorModel::setActiveSubsheet(TileSheet::SubSheetIdx const&idx) n } void TileSheetEditorModel::fill(ox::Point const&pt, int palIdx) noexcept { - const auto &s = m_img.getSubSheet(m_activeSubsSheetIdx); + const auto &s = getSubSheet(m_img, m_activeSubsSheetIdx); // build idx list ox::Array updateMap = {}; - const auto oldColor = s.getPixel(m_img.bpp, pt); + const auto oldColor = getPixel(s, m_img.bpp, pt); if (pt.x >= s.columns * TileWidth || pt.y >= s.rows * TileHeight) { return; } getFillPixels(updateMap.data(), pt, oldColor); ox::Vector idxList; - auto i = s.idx(pt) / PixelsPerTile * PixelsPerTile; + auto i = core::idx(s, pt) / PixelsPerTile * PixelsPerTile; for (auto u : updateMap) { if (u) { idxList.emplace_back(i); @@ -177,7 +177,7 @@ void TileSheetEditorModel::fill(ox::Point const&pt, int palIdx) noexcept { // do updates to sheet if (m_ongoingDrawCommand) { m_updated = m_updated || m_ongoingDrawCommand->append(idxList); - } else if (s.getPixel(m_img.bpp, pt) != palIdx) { + } else if (getPixel(s, m_img.bpp, pt) != palIdx) { pushCommand(ox::make(m_img, m_activeSubsSheetIdx, idxList, palIdx)); } } @@ -213,7 +213,7 @@ bool TileSheetEditorModel::updated() const noexcept { return m_updated; } -ox::Error TileSheetEditorModel::markUpdatedCmdId(const studio::UndoCommand *cmd) noexcept { +ox::Error TileSheetEditorModel::markUpdatedCmdId(studio::UndoCommand const*cmd) noexcept { m_updated = true; const auto cmdId = cmd->commandId(); if (static_cast(cmdId) == CommandId::PaletteChange) { @@ -221,7 +221,7 @@ ox::Error TileSheetEditorModel::markUpdatedCmdId(const studio::UndoCommand *cmd) paletteChanged.emit(); } auto tsCmd = dynamic_cast(cmd); - auto idx = m_img.validateSubSheetIdx(tsCmd->subsheetIdx()); + auto idx = validateSubSheetIdx(m_img, tsCmd->subsheetIdx()); if (idx != m_activeSubsSheetIdx) { setActiveSubsheet(idx); } @@ -239,7 +239,7 @@ void TileSheetEditorModel::ackUpdate() noexcept { ox::Error TileSheetEditorModel::saveFile() noexcept { const auto sctx = applicationData(m_ctx); - return sctx->project->writeObj(m_path, m_img); + return sctx->project->writeObj(m_path, m_img, ox::ClawFormat::Metal); } bool TileSheetEditorModel::pixelSelected(std::size_t idx) const noexcept { @@ -267,16 +267,16 @@ void TileSheetEditorModel::getFillPixels(bool *pixels, ox::Point const&pt, int o const auto tile = tileIdx(pt); // mark pixels to update pixels[idx % PixelsPerTile] = true; - if (!pixels[leftIdx % PixelsPerTile] && tile == tileIdx(leftPt) && activeSubSheet.getPixel(m_img.bpp, leftIdx) == oldColor) { + if (!pixels[leftIdx % PixelsPerTile] && tile == tileIdx(leftPt) && getPixel(activeSubSheet, m_img.bpp, leftIdx) == oldColor) { getFillPixels(pixels, leftPt, oldColor); } - if (!pixels[rightIdx % PixelsPerTile] && tile == tileIdx(rightPt) && activeSubSheet.getPixel(m_img.bpp, rightIdx) == oldColor) { + if (!pixels[rightIdx % PixelsPerTile] && tile == tileIdx(rightPt) && getPixel(activeSubSheet, m_img.bpp, rightIdx) == oldColor) { getFillPixels(pixels, rightPt, oldColor); } - if (!pixels[topIdx % PixelsPerTile] && tile == tileIdx(topPt) && activeSubSheet.getPixel(m_img.bpp, topIdx) == oldColor) { + if (!pixels[topIdx % PixelsPerTile] && tile == tileIdx(topPt) && getPixel(activeSubSheet, m_img.bpp, topIdx) == oldColor) { getFillPixels(pixels, topPt, oldColor); } - if (!pixels[bottomIdx % PixelsPerTile] && tile == tileIdx(bottomPt) && activeSubSheet.getPixel(m_img.bpp, bottomIdx) == oldColor) { + if (!pixels[bottomIdx % PixelsPerTile] && tile == tileIdx(bottomPt) && getPixel(activeSubSheet, m_img.bpp, bottomIdx) == oldColor) { getFillPixels(pixels, bottomPt, oldColor); } } diff --git a/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheeteditormodel.hpp b/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheeteditormodel.hpp index 11bb015..6975926 100644 --- a/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheeteditormodel.hpp +++ b/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheeteditormodel.hpp @@ -61,31 +61,31 @@ class TileSheetEditorModel: public ox::SignalHandler { ox::Error setPalette(ox::StringView path) noexcept; - void drawCommand(const ox::Point &pt, std::size_t palIdx) noexcept; + void drawCommand(ox::Point const&pt, std::size_t palIdx) noexcept; void endDrawCommand() noexcept; - void addSubsheet(const TileSheet::SubSheetIdx &parentIdx) noexcept; + void addSubsheet(TileSheet::SubSheetIdx const&parentIdx) noexcept; - void rmSubsheet(const TileSheet::SubSheetIdx &idx) noexcept; + void rmSubsheet(TileSheet::SubSheetIdx const&idx) noexcept; - void insertTiles(const TileSheet::SubSheetIdx &idx, std::size_t tileIdx, std::size_t tileCnt) noexcept; + void insertTiles(TileSheet::SubSheetIdx const&idx, std::size_t tileIdx, std::size_t tileCnt) noexcept; - void deleteTiles(const TileSheet::SubSheetIdx &idx, std::size_t tileIdx, std::size_t tileCnt) noexcept; + void deleteTiles(TileSheet::SubSheetIdx const&idx, std::size_t tileIdx, std::size_t tileCnt) noexcept; - ox::Error updateSubsheet(const TileSheet::SubSheetIdx &idx, const ox::StringView &name, int cols, int rows) noexcept; + ox::Error updateSubsheet(TileSheet::SubSheetIdx const&idx, ox::StringView const&name, int cols, int rows) noexcept; - void setActiveSubsheet(const TileSheet::SubSheetIdx&) noexcept; + void setActiveSubsheet(TileSheet::SubSheetIdx const&) noexcept; [[nodiscard]] const TileSheet::SubSheet *activeSubSheet() const noexcept { - auto &activeSubSheet = m_img.getSubSheet(m_activeSubsSheetIdx); + auto &activeSubSheet = getSubSheet(m_img, m_activeSubsSheetIdx); return &activeSubSheet; } [[nodiscard]] TileSheet::SubSheet *activeSubSheet() noexcept { - auto &activeSubSheet = m_img.getSubSheet(m_activeSubsSheetIdx); + auto &activeSubSheet = getSubSheet(m_img, m_activeSubsSheetIdx); return &activeSubSheet; } @@ -105,7 +105,7 @@ class TileSheetEditorModel: public ox::SignalHandler { [[nodiscard]] bool updated() const noexcept; - ox::Error markUpdatedCmdId(const studio::UndoCommand *cmd) noexcept; + ox::Error markUpdatedCmdId(studio::UndoCommand const*cmd) noexcept; ox::Error markUpdated() noexcept; @@ -118,10 +118,9 @@ class TileSheetEditorModel: public ox::SignalHandler { bool pixelSelected(std::size_t idx) const noexcept; - protected: + private: void getFillPixels(bool *pixels, ox::Point const&pt, int oldColor) const noexcept; - private: void pushCommand(studio::UndoCommand *cmd) noexcept; }; diff --git a/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheeteditorview.cpp b/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheeteditorview.cpp index f1117c3..5e6abb4 100644 --- a/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheeteditorview.cpp +++ b/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheeteditorview.cpp @@ -13,7 +13,7 @@ namespace nostalgia::core { TileSheetEditorView::TileSheetEditorView(turbine::Context &ctx, ox::StringView path, studio::UndoStack &undoStack): m_model(ctx, path, undoStack), - m_pixelsDrawer(&m_model) { + m_pixelsDrawer(m_model) { // build shaders oxThrowError(m_pixelsDrawer.buildShader()); oxThrowError(m_pixelGridDrawer.buildShader()); diff --git a/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheetpixels.cpp b/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheetpixels.cpp index bf471fd..8bf018b 100644 --- a/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheetpixels.cpp +++ b/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheetpixels.cpp @@ -9,7 +9,7 @@ namespace nostalgia::core { -TileSheetPixels::TileSheetPixels(TileSheetEditorModel *model) noexcept: m_model(model) { +TileSheetPixels::TileSheetPixels(TileSheetEditorModel &model) noexcept: m_model(model) { } void TileSheetPixels::setPixelSizeMod(float sm) noexcept { @@ -94,15 +94,15 @@ void TileSheetPixels::setPixelBufferObject( void TileSheetPixels::setBufferObjects(ox::Vec2 const&paneSize) noexcept { // set buffer lengths - const auto subSheet = m_model->activeSubSheet(); - const auto pal = m_model->pal(); + const auto subSheet = m_model.activeSubSheet(); + const auto pal = m_model.pal(); const auto width = subSheet->columns * TileWidth; const auto height = subSheet->rows * TileHeight; const auto pixels = static_cast(width * height); m_bufferSet.vertices.resize(pixels * VertexVboLength); m_bufferSet.elements.resize(pixels * VertexEboLength); // set pixels - subSheet->walkPixels(m_model->img().bpp, [&](std::size_t i, uint8_t p) { + walkPixels(*subSheet, m_model.img().bpp, [&](std::size_t i, uint8_t p) { auto color = pal->color(p); const auto pt = idxToPt(static_cast(i), subSheet->columns); const auto fx = static_cast(pt.x); @@ -115,7 +115,7 @@ void TileSheetPixels::setBufferObjects(ox::Vec2 const&paneSize) noexcept { if (i * VertexEboLength + VertexEboLength > m_bufferSet.elements.size()) { return; } - if (m_model->pixelSelected(i)) { + if (m_model.pixelSelected(i)) { const auto r = red16(color) / 2; const auto g = (green16(color) + 20) / 2; const auto b = (blue16(color) + 31) / 2; diff --git a/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheetpixels.hpp b/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheetpixels.hpp index 80aa8ae..7725462 100644 --- a/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheetpixels.hpp +++ b/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheetpixels.hpp @@ -43,10 +43,10 @@ class TileSheetPixels { float m_pixelSizeMod = 1; glutils::GLProgram m_shader; glutils::BufferSet m_bufferSet; - const class TileSheetEditorModel *m_model = nullptr; + const class TileSheetEditorModel &m_model; public: - explicit TileSheetPixels(class TileSheetEditorModel *model) noexcept; + explicit TileSheetPixels(class TileSheetEditorModel &model) noexcept; void setPixelSizeMod(float sm) noexcept; diff --git a/src/nostalgia/modules/core/src/tilesheet.cpp b/src/nostalgia/modules/core/src/tilesheet.cpp index 98b1f78..91048e7 100644 --- a/src/nostalgia/modules/core/src/tilesheet.cpp +++ b/src/nostalgia/modules/core/src/tilesheet.cpp @@ -10,133 +10,47 @@ namespace nostalgia::core { -TileSheet::SubSheet::SubSheet(SubSheet &&other) noexcept: - id (other.id), - name (std::move(other.name)), - columns (other.columns), - rows (other.rows), - subsheets(std::move(other.subsheets)), - pixels (std::move(other.pixels)) { - other.name = ""; - other.columns = {}; - other.rows = {}; +std::size_t idx(TileSheet::SubSheet const&ss, ox::Point const&pt) noexcept { + return ptToIdx(pt, ss.columns); } -TileSheet::SubSheet::SubSheet( - SubSheetId pId, - ox::CRStringView pName, - int pColumns, - int pRows, - int bpp) noexcept: - id(pId), - name(pName), - columns(pColumns), - rows(pRows), - pixels(static_cast(columns * rows * PixelsPerTile) / (bpp == 4 ? 2u : 1u)) { -} - -TileSheet::SubSheet::SubSheet( - SubSheetId pId, - ox::CRStringView pName, - int pColumns, - int pRows, - ox::Vector pPixels) noexcept: - id(pId), - name(pName), - columns(pColumns), - rows(pRows), - pixels(std::move(pPixels)) { -} - -TileSheet::SubSheet &TileSheet::SubSheet::operator=(TileSheet::SubSheet &&other) noexcept { - name = std::move(other.name); - columns = other.columns; - rows = other.rows; - subsheets = std::move(other.subsheets); - pixels = std::move(other.pixels); - return *this; -} - -std::size_t TileSheet::SubSheet::idx(ox::Point const&pt) const noexcept { - return ptToIdx(pt, columns); -} - -void TileSheet::SubSheet::readPixelsTo(ox::Vector *pPixels, int8_t pBpp) const noexcept { - if (!subsheets.empty()) { - for (auto &s: subsheets) { - s.readPixelsTo(pPixels); - } - } else { - if (pBpp == 4) { - for (auto p: this->pixels) { - pPixels->emplace_back(static_cast(p & 0b1111)); - pPixels->emplace_back(static_cast(p >> 4)); - } - } else { - for (auto p: this->pixels) { - pPixels->emplace_back(p); - } - } - } -} - -void TileSheet::SubSheet::readPixelsTo(ox::Vector *pPixels) const noexcept { - if (!subsheets.empty()) { - for (auto &s: subsheets) { - s.readPixelsTo(pPixels); - } - } else { - for (auto p : this->pixels) { - pPixels->emplace_back(p); - } - } -} - -std::size_t TileSheet::SubSheet::unusedPixels() const noexcept { - std::size_t childrenSize = 0; - for (auto &c : subsheets) { - childrenSize += c.size(); - } - return size() - childrenSize; -} - -uint8_t TileSheet::SubSheet::getPixel4Bpp(std::size_t idx) const noexcept { +uint8_t getPixel4Bpp(TileSheet::SubSheet const&ss, std::size_t idx) noexcept { if (idx & 1) { - return this->pixels[idx / 2] >> 4; + return ss.pixels[idx / 2] >> 4; } else { - return this->pixels[idx / 2] & 0b0000'1111; + return ss.pixels[idx / 2] & 0b0000'1111; } } -uint8_t TileSheet::SubSheet::getPixel8Bpp(std::size_t idx) const noexcept { - return this->pixels[idx]; +uint8_t getPixel8Bpp(TileSheet::SubSheet const&ss, std::size_t idx) noexcept { + return ss.pixels[idx]; } -uint8_t TileSheet::SubSheet::getPixel(int8_t pBpp, std::size_t idx) const noexcept { +uint8_t getPixel(TileSheet::SubSheet const&ss, int8_t pBpp, std::size_t idx) noexcept { if (pBpp == 4) { - return getPixel4Bpp(idx); + return getPixel4Bpp(ss, idx); } else { - return getPixel8Bpp(idx); + return getPixel8Bpp(ss, idx); } } -uint8_t TileSheet::SubSheet::getPixel4Bpp(ox::Point const&pt) const noexcept { - const auto idx = ptToIdx(pt, columns); - return getPixel4Bpp(idx); +uint8_t getPixel4Bpp(TileSheet::SubSheet const&ss, ox::Point const&pt) noexcept { + const auto idx = ptToIdx(pt, ss.columns); + return getPixel4Bpp(ss, idx); } -uint8_t TileSheet::SubSheet::getPixel8Bpp(ox::Point const&pt) const noexcept { - const auto idx = ptToIdx(pt, columns); - return getPixel8Bpp(idx); +uint8_t getPixel8Bpp(TileSheet::SubSheet const&ss, ox::Point const&pt) noexcept { + const auto idx = ptToIdx(pt, ss.columns); + return getPixel8Bpp(ss, idx); } -uint8_t TileSheet::SubSheet::getPixel(int8_t pBpp, ox::Point const&pt) const noexcept { - const auto idx = ptToIdx(pt, columns); - return getPixel(pBpp, idx); +uint8_t getPixel(TileSheet::SubSheet const&ss, int8_t pBpp, ox::Point const&pt) noexcept { + const auto idx = ptToIdx(pt, ss.columns); + return getPixel(ss, pBpp, idx); } -void TileSheet::SubSheet::setPixel(int8_t pBpp, uint64_t idx, uint8_t palIdx) noexcept { - auto &pixel = this->pixels[static_cast(idx / 2)]; +void setPixel(TileSheet::SubSheet &ss, int8_t pBpp, uint64_t idx, uint8_t palIdx) noexcept { + auto &pixel = ss.pixels[static_cast(idx / 2)]; if (pBpp == 4) { if (idx & 1) { pixel = static_cast((pixel & 0b0000'1111) | (palIdx << 4)); @@ -148,72 +62,35 @@ void TileSheet::SubSheet::setPixel(int8_t pBpp, uint64_t idx, uint8_t palIdx) no } } -void TileSheet::SubSheet::setPixel(int8_t pBpp, ox::Point const&pt, uint8_t palIdx) noexcept { - const auto idx = ptToIdx(pt, columns); - setPixel(pBpp, idx, palIdx); +void setPixel(TileSheet::SubSheet &ss, int8_t pBpp, ox::Point const&pt, uint8_t palIdx) noexcept { + const auto idx = ptToIdx(pt, ss.columns); + setPixel(ss, pBpp, idx, palIdx); } -ox::Error TileSheet::SubSheet::setPixelCount(int8_t pBpp, std::size_t cnt) noexcept { +ox::Error setPixelCount(TileSheet::SubSheet &ss, int8_t pBpp, std::size_t cnt) noexcept { switch (pBpp) { case 4: - pixels.resize(cnt / 2); + ss.pixels.resize(cnt / 2); return OxError(0); case 8: - pixels.resize(cnt); + ss.pixels.resize(cnt); return OxError(0); default: return OxError(1, "Invalid pBpp used for TileSheet::SubSheet::setPixelCount"); } } -unsigned TileSheet::SubSheet::pixelCnt(int8_t pBpp) const noexcept { - const auto pixelsSize = static_cast(pixels.size()); +unsigned pixelCnt(TileSheet::SubSheet const&ss, int8_t pBpp) noexcept { + const auto pixelsSize = static_cast(ss.pixels.size()); return pBpp == 4 ? pixelsSize * 2 : pixelsSize; } -ox::Result TileSheet::SubSheet::getTileOffset( - ox::SpanView const&pNamePath, - int8_t pBpp, - std::size_t pIt, - unsigned pCurrentTotal) const noexcept { - // pIt == pNamePath.size() - 1 && - if (name != pNamePath[pIt]) { - return OxError(2, "Wrong branch"); +ox::Result getNameFor(TileSheet::SubSheet const&ss, SubSheetId pId) noexcept { + if (ss.id == pId) { + return ox::StringView(ss.name); } - if (pIt == pNamePath.size() - 1) { - return pCurrentTotal; - } - for (auto &sub : subsheets) { - auto [offset, err] = sub.getTileOffset( - pNamePath, pBpp, pIt + 1, pCurrentTotal); - if (!err) { - return offset; - } - pCurrentTotal += sub.pixelCnt(pBpp) / PixelsPerTile; - } - return OxError(1, "SubSheet not found"); -} - -ox::Result TileSheet::SubSheet::getIdFor( - ox::SpanView const&pNamePath, - std::size_t pIt) const noexcept { - for (auto &sub : subsheets) { - if (sub.name == pNamePath[pIt]) { - if (pIt == pNamePath.size()) { - return id; - } - return getIdFor(pNamePath, pIt + 1); - } - } - return OxError(1, "SubSheet not found"); -} - -ox::Result TileSheet::SubSheet::getNameFor(SubSheetId pId) const noexcept { - if (id == pId) { - return ox::StringView(name); - } - for (const auto &sub : subsheets) { - const auto [name, err] = sub.getNameFor(pId); + for (const auto &sub : ss.subsheets) { + const auto [name, err] = getNameFor(sub, pId); if (!err) { return name; } @@ -222,138 +99,181 @@ ox::Result TileSheet::SubSheet::getNameFor(SubSheetId pId) const } -TileSheet &TileSheet::operator=(TileSheet const&other) noexcept { - if (this != &other) { - bpp = other.bpp; - idIt = other.idIt; - defaultPalette = other.defaultPalette; - subsheet = other.subsheet; - } - return *this; -} - -TileSheet &TileSheet::operator=(TileSheet &&other) noexcept { - bpp = other.bpp; - idIt = other.idIt; - defaultPalette = std::move(other.defaultPalette); - subsheet = std::move(other.subsheet); - return *this; -} - -TileSheet::SubSheetIdx TileSheet::validateSubSheetIdx( - const SubSheetIdx &pIdx, +TileSheet::SubSheetIdx validateSubSheetIdx( + TileSheet::SubSheetIdx const&pIdx, std::size_t pIdxIt, - const SubSheet *pSubsheet) noexcept { + TileSheet::SubSheet const&pSubsheet) noexcept { if (pIdxIt == pIdx.size()) { return pIdx; } const auto currentIdx = pIdx[pIdxIt]; - if (pSubsheet->subsheets.size() <= currentIdx) { + if (pSubsheet.subsheets.size() <= currentIdx) { auto out = pIdx; - if (!pSubsheet->subsheets.empty()) { - *out.back().value = pSubsheet->subsheets.size() - 1; + if (!pSubsheet.subsheets.empty()) { + *out.back().value = pSubsheet.subsheets.size() - 1; } else { out.pop_back(); } return out; } - return validateSubSheetIdx(pIdx, pIdxIt + 1, &pSubsheet->subsheets[pIdx[pIdxIt]]); + return validateSubSheetIdx(pIdx, pIdxIt + 1, pSubsheet.subsheets[pIdx[pIdxIt]]); } -TileSheet::SubSheetIdx TileSheet::validateSubSheetIdx(const SubSheetIdx &idx) noexcept { - return validateSubSheetIdx(idx, 0, &subsheet); +TileSheet::SubSheetIdx validateSubSheetIdx(TileSheet const&ts, TileSheet::SubSheetIdx const&idx) noexcept { + return validateSubSheetIdx(idx, 0, ts.subsheet); } -const TileSheet::SubSheet &TileSheet::getSubSheet( +const TileSheet::SubSheet &getSubSheet( TileSheet::SubSheetIdx const&idx, std::size_t idxIt, - SubSheet const*pSubsheet) noexcept { + TileSheet::SubSheet const&pSubsheet) noexcept { if (idxIt == idx.size()) { - return *pSubsheet; + return pSubsheet; } const auto currentIdx = idx[idxIt]; - if (pSubsheet->subsheets.size() < currentIdx) { - return *pSubsheet; + if (pSubsheet.subsheets.size() < currentIdx) { + return pSubsheet; } - return getSubSheet(idx, idxIt + 1, &pSubsheet->subsheets[currentIdx]); + return getSubSheet(idx, idxIt + 1, pSubsheet.subsheets[currentIdx]); } -TileSheet::SubSheet &TileSheet::getSubSheet( +TileSheet::SubSheet &getSubSheet( TileSheet::SubSheetIdx const&idx, std::size_t idxIt, - TileSheet::SubSheet *pSubsheet) noexcept { + TileSheet::SubSheet &pSubsheet) noexcept { if (idxIt == idx.size()) { - return *pSubsheet; + return pSubsheet; } - return getSubSheet(idx, idxIt + 1, &pSubsheet->subsheets[idx[idxIt]]); + return getSubSheet(idx, idxIt + 1, pSubsheet.subsheets[idx[idxIt]]); } -const TileSheet::SubSheet &TileSheet::getSubSheet(TileSheet::SubSheetIdx const&idx) const noexcept { - return getSubSheet(idx, 0, &subsheet); +TileSheet::SubSheet const&getSubSheet(TileSheet const&ts, TileSheet::SubSheetIdx const&idx) noexcept { + return core::getSubSheet(idx, 0, ts.subsheet); } -TileSheet::SubSheet &TileSheet::getSubSheet(TileSheet::SubSheetIdx const&idx) noexcept { - return getSubSheet(idx, 0, &subsheet); +TileSheet::SubSheet &getSubSheet(TileSheet &ts, TileSheet::SubSheetIdx const&idx) noexcept { + return core::getSubSheet(idx, 0, ts.subsheet); } -ox::Error TileSheet::addSubSheet(TileSheet::SubSheetIdx const&idx) noexcept { - auto &parent = getSubSheet(idx); +ox::Error addSubSheet(TileSheet &ts, TileSheet::SubSheetIdx const&idx) noexcept { + auto &parent = getSubSheet(ts, idx); if (parent.subsheets.size() < 2) { - parent.subsheets.emplace_back(idIt++, ox::sfmt("Subsheet {}", parent.subsheets.size()), 1, 1, bpp); + parent.subsheets.emplace_back(++ts.idIt, ox::sfmt("Subsheet {}", parent.subsheets.size()), 1, 1, ts.bpp); } else { - parent.subsheets.emplace_back(idIt++, "Subsheet 0", parent.columns, parent.rows, bpp); - parent.subsheets.emplace_back(idIt++, "Subsheet 1", 1, 1, bpp); + parent.subsheets.emplace_back(++ts.idIt, "Subsheet 0", parent.columns, parent.rows, ts.bpp); + parent.subsheets.emplace_back(++ts.idIt, "Subsheet 1", 1, 1, ts.bpp); } return OxError(0); } -ox::Error TileSheet::rmSubSheet( - SubSheetIdx const&idx, +ox::Error rmSubSheet( + TileSheet &ts, + TileSheet::SubSheetIdx const&idx, std::size_t idxIt, - SubSheet *pSubsheet) noexcept { + TileSheet::SubSheet &pSubsheet) noexcept { if (idxIt == idx.size() - 1) { - return pSubsheet->subsheets.erase(idx[idxIt]).error; + return pSubsheet.subsheets.erase(idx[idxIt]).error; } - return rmSubSheet(idx, idxIt + 1, &pSubsheet->subsheets[idx[idxIt]]); + return rmSubSheet(ts, idx, idxIt + 1, pSubsheet.subsheets[idx[idxIt]]); } -ox::Error TileSheet::rmSubSheet(TileSheet::SubSheetIdx const&idx) noexcept { - return rmSubSheet(idx, 0, &subsheet); +ox::Error rmSubSheet(TileSheet &ts, TileSheet::SubSheetIdx const&idx) noexcept { + return rmSubSheet(ts, idx, 0, ts.subsheet); } -uint8_t TileSheet::getPixel4Bpp( +uint8_t getPixel4Bpp( + TileSheet const&ts, ox::Point const&pt, - TileSheet::SubSheetIdx const&subsheetIdx) const noexcept { - oxAssert(bpp == 4, "TileSheet::getPixel4Bpp: wrong bpp"); - auto &s = this->getSubSheet(subsheetIdx); + TileSheet::SubSheetIdx const&subsheetIdx) noexcept { + oxAssert(ts.bpp == 4, "TileSheet::getPixel4Bpp: wrong bpp"); + auto &s = getSubSheet(ts, subsheetIdx); const auto idx = ptToIdx(pt, s.columns); - return s.getPixel4Bpp(idx); + return getPixel4Bpp(s, idx); } -uint8_t TileSheet::getPixel8Bpp( +uint8_t getPixel8Bpp( + TileSheet const&ts, ox::Point const&pt, - TileSheet::SubSheetIdx const&subsheetIdx) const noexcept { - oxAssert(bpp == 8, "TileSheet::getPixel8Bpp: wrong bpp"); - auto &s = this->getSubSheet(subsheetIdx); + TileSheet::SubSheetIdx const&subsheetIdx) noexcept { + oxAssert(ts.bpp == 8, "TileSheet::getPixel8Bpp: wrong bpp"); + auto &s = getSubSheet(ts, subsheetIdx); const auto idx = ptToIdx(pt, s.columns); - return s.getPixel8Bpp(idx); + return getPixel8Bpp(s, idx); } -ox::Result TileSheet::getIdFor(ox::CRStringView path) const noexcept { - return subsheet.getIdFor(ox::split<8>(path, '.')); +static ox::Result getIdFor( + TileSheet::SubSheet const&ss, + ox::SpanView const&pNamePath, + std::size_t pIt = 0) noexcept { + for (auto &sub : ss.subsheets) { + if (sub.name == pNamePath[pIt]) { + if (pIt == pNamePath.size()) { + return ss.id; + } + return getIdFor(ss, pNamePath, pIt + 1); + } + } + return OxError(1, "SubSheet not found"); } -ox::Result TileSheet::getTileOffset(ox::CRStringView pNamePath) const noexcept { - return subsheet.getTileOffset(ox::split<8>(pNamePath, '.'), bpp); +ox::Result getIdFor(TileSheet const&ts, ox::CRStringView path) noexcept { + return getIdFor(ts.subsheet, ox::split<8>(path, '.')); } -ox::Result TileSheet::getNameFor(SubSheetId pId) const noexcept { - return subsheet.getNameFor(pId); +/** + * Gets the offset in tiles of the desired subsheet. + */ +static ox::Result getTileOffset( + TileSheet::SubSheet const&ss, + ox::SpanView const&pNamePath, + int8_t pBpp, + std::size_t pIt = 0, + unsigned pCurrentTotal = 0) noexcept { + // pIt == pNamePath.size() - 1 && + if (ss.name != pNamePath[pIt]) { + return OxError(2, "Wrong branch"); + } + if (pIt == pNamePath.size() - 1) { + return pCurrentTotal; + } + for (auto &sub : ss.subsheets) { + auto [offset, err] = getTileOffset( + sub, pNamePath, pBpp, pIt + 1, pCurrentTotal); + if (!err) { + return offset; + } + pCurrentTotal += pixelCnt(sub, pBpp) / PixelsPerTile; + } + return OxError(1, "SubSheet not found"); } -ox::Vector TileSheet::pixels() const noexcept { +ox::Result getTileOffset(TileSheet const&ts, ox::CRStringView pNamePath) noexcept { + return core::getTileOffset(ts.subsheet, ox::split<8>(pNamePath, '.'), ts.bpp); +} + +ox::Result getNameFor(TileSheet &ts, SubSheetId pId) noexcept { + return core::getNameFor(ts.subsheet, pId); +} + +ox::Result getNameFor(TileSheet const&ts, SubSheetId pId) noexcept { + return core::getNameFor(ts.subsheet, pId); +} + +static void readPixelsTo(TileSheet::SubSheet &ss, ox::Vector &pPixels) noexcept { + if (!ss.subsheets.empty()) { + for (auto &s: ss.subsheets) { + readPixelsTo(s, pPixels); + } + } else { + for (auto p : ss.pixels) { + pPixels.emplace_back(p); + } + } +} + +ox::Vector pixels(TileSheet &ts) noexcept { ox::Vector out; - subsheet.readPixelsTo(&out); + readPixelsTo(ts.subsheet, out); return out; } diff --git a/src/nostalgia/modules/scene/include/nostalgia/scene/scenestatic.hpp b/src/nostalgia/modules/scene/include/nostalgia/scene/scenestatic.hpp index 94c9db5..5f09ac4 100644 --- a/src/nostalgia/modules/scene/include/nostalgia/scene/scenestatic.hpp +++ b/src/nostalgia/modules/scene/include/nostalgia/scene/scenestatic.hpp @@ -42,7 +42,7 @@ struct TileDoc { if (subsheetId > -1) { return subsheetId; } - return ts.getIdFor(subsheetPath); + return getIdFor(ts, subsheetPath); } [[nodiscard]] @@ -50,7 +50,7 @@ struct TileDoc { core::TileSheet const&ts) const noexcept { // prefer the already present path if (!subsheetPath.len()) { - return ts.getNameFor(subsheetId); + return core::getNameFor(ts, subsheetId); } return ox::StringView(subsheetPath); } diff --git a/src/nostalgia/modules/scene/src/keel/typeconv.cpp b/src/nostalgia/modules/scene/src/keel/typeconv.cpp index b502b2f..0eae97c 100644 --- a/src/nostalgia/modules/scene/src/keel/typeconv.cpp +++ b/src/nostalgia/modules/scene/src/keel/typeconv.cpp @@ -54,7 +54,7 @@ ox::Error SceneDocToSceneStaticConverter::convert( auto dstTile = dstLayer.tile(tileIdx); dstTile.tileType = srcTile.type; oxRequire(path, srcTile.getSubsheetPath(*ts)); - oxRequire(mapIdx, ts->getTileOffset(path)); + oxRequire(mapIdx, getTileOffset(*ts, path)); dstTile.tileMapIdx = static_cast(mapIdx); setLayerAttachments(layerIdx, srcTile, dstTile); ++tileIdx; diff --git a/src/nostalgia/player/app.cpp b/src/nostalgia/player/app.cpp index acf2691..da4a6ad 100644 --- a/src/nostalgia/player/app.cpp +++ b/src/nostalgia/player/app.cpp @@ -82,7 +82,8 @@ static ox::Error runTest(turbine::Context &tctx) { [[maybe_unused]] static ox::Error runTileSheetSetTest(turbine::Context &tctx) { - // this should make the screen display 'ABCDB' + // this should make the screen display 'ABCDB', with the A being upside down + // and the first B being backwards constexpr ox::FileAddress PaletteAddr = ox::StringLiteral("/Palettes/Charset.npal"); oxRequireM(cctx, core::init(tctx)); turbine::setApplicationData(tctx, cctx.get()); @@ -103,9 +104,9 @@ static ox::Error runTileSheetSetTest(turbine::Context &tctx) { oxReturnError(core::loadBgPalette(*cctx, 0, ox::StringLiteral("/Palettes/Chester.npal"))); oxReturnError(core::loadSpritePalette(*cctx, PaletteAddr)); core::setBgStatus(*cctx, 0, true); - core::setBgTile(*cctx, 0, 10, 9, 1, bgPalBank); - core::setBgTile(*cctx, 0, 11, 9, 2, bgPalBank); - core::setBgTile(*cctx, 0, 13, 9, 4, bgPalBank); + core::setBgTile(*cctx, 0, 10, 9, { .tileIdx = 1, .palBank = bgPalBank, .flipX = 0, .flipY = 1 }); + core::setBgTile(*cctx, 0, 11, 9, { .tileIdx = 2, .palBank = bgPalBank, .flipX = 1, .flipY = 0 }); + core::setBgTile(*cctx, 0, 13, 9, { .tileIdx = 4, .palBank = bgPalBank, .flipX = 0, .flipY = 0 }); core::setSprite(*cctx, 16, { .enabled = true, .x = 12 * 8, diff --git a/src/olympic/keel/include/keel/typeconv.hpp b/src/olympic/keel/include/keel/typeconv.hpp index d58600a..f912438 100644 --- a/src/olympic/keel/include/keel/typeconv.hpp +++ b/src/olympic/keel/include/keel/typeconv.hpp @@ -148,7 +148,7 @@ ox::Error convert(keel::Context &ctx, ox::Buffer const&buff, DstType *outObj) no } template -ox::Result convertBuffToBuff(keel::Context &ctx, const ox::Buffer &srcBuffer, ox::ClawFormat fmt) noexcept { +ox::Result convertBuffToBuff(keel::Context &ctx, ox::Buffer const&srcBuffer, ox::ClawFormat fmt) noexcept { static constexpr auto DstTypeName = ox::requireModelTypeName(); static constexpr auto DstTypeVersion = ox::requireModelTypeVersion(); oxRequire(out, convert(ctx, srcBuffer, DstTypeName, DstTypeVersion)); diff --git a/src/olympic/studio/applib/src/CMakeLists.txt b/src/olympic/studio/applib/src/CMakeLists.txt index fc40217..2dfce8d 100644 --- a/src/olympic/studio/applib/src/CMakeLists.txt +++ b/src/olympic/studio/applib/src/CMakeLists.txt @@ -5,6 +5,7 @@ add_library( filedialogmanager.cpp main.cpp newmenu.cpp + newproject.cpp projectexplorer.cpp projecttreemodel.cpp studioapp.cpp diff --git a/src/olympic/studio/applib/src/newmenu.cpp b/src/olympic/studio/applib/src/newmenu.cpp index 8b57dd8..f9cd84a 100644 --- a/src/olympic/studio/applib/src/newmenu.cpp +++ b/src/olympic/studio/applib/src/newmenu.cpp @@ -115,12 +115,12 @@ void NewMenu::drawLastPageButtons(turbine::Context &ctx) noexcept { } void NewMenu::finish(turbine::Context &ctx) noexcept { - auto const err = m_types[static_cast(m_selectedType)]->write(ctx, m_itemName); + auto const [path, err] = m_types[static_cast(m_selectedType)]->write(ctx, m_itemName); if (err) { oxLogError(err); return; } - finished.emit(""); + finished.emit(path); m_stage = Stage::Closed; } diff --git a/src/olympic/studio/applib/src/newproject.cpp b/src/olympic/studio/applib/src/newproject.cpp new file mode 100644 index 0000000..2310721 --- /dev/null +++ b/src/olympic/studio/applib/src/newproject.cpp @@ -0,0 +1,95 @@ +/* + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#include + +#include +#include + +#include "filedialogmanager.hpp" +#include "newproject.hpp" + +namespace studio { + +NewProject::NewProject(ox::String projectDatadir) noexcept: m_projectDataDir(std::move(projectDatadir)) { + setTitle(ox::String("New Project")); + setSize({230, 140}); +} + +void NewProject::open() noexcept { + m_stage = Stage::Opening; + m_projectPath = ""; + m_projectName = ""; +} + +void NewProject::close() noexcept { + m_stage = Stage::Closed; + m_open = false; +} + +bool NewProject::isOpen() const noexcept { + return m_open; +} + +void NewProject::draw(turbine::Context &ctx) noexcept { + switch (m_stage) { + case Stage::Opening: + ImGui::OpenPopup(title().c_str()); + m_stage = Stage::NewItemName; + m_open = true; + [[fallthrough]]; + case Stage::NewItemName: + drawNewProjectName(ctx); + break; + case Stage::Closed: + m_open = false; + break; + } +} + +void NewProject::drawNewProjectName(turbine::Context &ctx) noexcept { + drawWindow(ctx, &m_open, [this, &ctx] { + ImGui::InputText("Name", m_projectName.data(), m_projectName.cap()); + ImGui::Text("Path: %s", m_projectPath.c_str()); + if (ImGui::Button("Browse")) { + oxLogError(studio::chooseDirectory().moveTo(m_projectPath)); + } + drawLastPageButtons(ctx); + }); +} + +void NewProject::drawFirstPageButtons() noexcept { + ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - 130); + ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetContentRegionAvail().y - 20); + auto const btnSz = ImVec2(60, 20); + if (ImGui::Button("Next", btnSz)) { + m_stage = Stage::NewItemName; + } + ImGui::SameLine(); + if (ImGui::Button("Cancel", btnSz)) { + ImGui::CloseCurrentPopup(); + m_stage = Stage::Closed; + } +} + +void NewProject::drawLastPageButtons(turbine::Context&) noexcept { + ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - 95); + ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetContentRegionAvail().y - 20); + if (ImGui::Button("Finish")) { + finish(); + } + ImGui::SameLine(); + if (ImGui::Button("Quit")) { + ImGui::CloseCurrentPopup(); + m_stage = Stage::Closed; + } +} + +void NewProject::finish() noexcept { + auto projectPath = ox::sfmt("{}/{}", m_projectPath, m_projectName); + finished.emit(projectPath); + m_stage = Stage::Closed; +} + +} diff --git a/src/olympic/studio/applib/src/newproject.hpp b/src/olympic/studio/applib/src/newproject.hpp new file mode 100644 index 0000000..f3bd42b --- /dev/null +++ b/src/olympic/studio/applib/src/newproject.hpp @@ -0,0 +1,58 @@ +/* + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#pragma once + +#include +#include +#include + +#include +#include + +namespace studio { + +class NewProject: public studio::Popup { + public: + enum class Stage { + Closed, + Opening, + NewItemName, + }; + + // emits path parameter + ox::Signal finished; + + private: + Stage m_stage = Stage::Closed; + ox::String const m_projectDataDir; + ox::String m_projectPath; + ox::BString<255> m_projectName; + ox::Vector> m_types; + bool m_open = false; + + public: + NewProject(ox::String projectDatadir) noexcept; + + void open() noexcept override; + + void close() noexcept override; + + [[nodiscard]] + bool isOpen() const noexcept override; + + void draw(turbine::Context &ctx) noexcept override; + + private: + void drawNewProjectName(turbine::Context &ctx) noexcept; + + void drawFirstPageButtons() noexcept; + + void drawLastPageButtons(turbine::Context &ctx) noexcept; + + void finish() noexcept; + +}; + +} diff --git a/src/olympic/studio/applib/src/studioapp.cpp b/src/olympic/studio/applib/src/studioapp.cpp index 5253087..5061f22 100644 --- a/src/olympic/studio/applib/src/studioapp.cpp +++ b/src/olympic/studio/applib/src/studioapp.cpp @@ -2,6 +2,8 @@ * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. */ +#include + #include #include @@ -42,15 +44,18 @@ StudioUI::StudioUI(turbine::Context &ctx, ox::StringView projectDataDir) noexcep m_ctx(ctx), m_projectDataDir(projectDataDir), m_projectExplorer(m_ctx), + m_newProject(ox::String(projectDataDir)), m_aboutPopup(m_ctx) { m_projectExplorer.fileChosen.connect(this, &StudioUI::openFile); + m_newProject.finished.connect(this, &StudioUI::createOpenProject); + m_newMenu.finished.connect(this, &StudioUI::openFile); ImGui::GetIO().IniFilename = nullptr; loadModules(); // open project and files auto const [config, err] = studio::readConfig(keelCtx(m_ctx)); m_showProjectExplorer = config.showProjectExplorer; if (!err) { - auto const openProjErr = openProject(config.projectPath); + auto const openProjErr = openProjectPath(config.projectPath); if (!openProjErr) { for (auto const&f: config.openFiles) { auto openFileErr = openFileActiveTab(f, config.activeTabItemName == f); @@ -95,10 +100,14 @@ void StudioUI::handleKeyEvent(turbine::Key key, bool down) noexcept { } break; case turbine::Key::Alpha_N: - m_newMenu.open(); + if (turbine::buttonDown(m_ctx, turbine::Key::Mod_Shift)) { + m_newProject.open(); + } else { + m_newMenu.open(); + } break; case turbine::Key::Alpha_O: - m_taskRunner.add(*ox::make(this, &StudioUI::openProject)); + m_taskRunner.add(*ox::make(this, &StudioUI::openProjectPath)); break; case turbine::Key::Alpha_Q: turbine::requestShutdown(m_ctx); @@ -168,8 +177,11 @@ void StudioUI::drawMenu() noexcept { if (ImGui::MenuItem("New...", "Ctrl+N")) { m_newMenu.open(); } + if (ImGui::MenuItem("New Project...", "Ctrl+Shift+N")) { + m_newProject.open(); + } if (ImGui::MenuItem("Open Project...", "Ctrl+O")) { - m_taskRunner.add(*ox::make(this, &StudioUI::openProject)); + m_taskRunner.add(*ox::make(this, &StudioUI::openProjectPath)); } if (ImGui::MenuItem("Save", "Ctrl+S", false, m_activeEditor && m_activeEditor->unsavedChanges())) { m_activeEditor->save(); @@ -317,19 +329,27 @@ void StudioUI::save() noexcept { } } -ox::Error StudioUI::openProject(ox::CRStringView path) noexcept { +ox::Error StudioUI::createOpenProject(ox::CRStringView path) noexcept { + std::error_code ec; + std::filesystem::create_directories(toStdStringView(path), ec); + oxReturnError(OxError(ec.value() != 0, "Could not create project directory")); + oxReturnError(openProjectPath(path)); + return m_project->writeAllTypeDescriptors(); +} + +ox::Error StudioUI::openProjectPath(ox::CRStringView path) noexcept { oxRequireM(fs, keel::loadRomFs(path)); oxReturnError(keel::setRomFs(keelCtx(m_ctx), std::move(fs))); - turbine::setWindowTitle(m_ctx, ox::sfmt("{} - {}", keelCtx(m_ctx).appName, path)); m_project = ox::make_unique(keelCtx(m_ctx), ox::String(path), m_projectDataDir); - auto sctx = applicationData(m_ctx); + auto const sctx = applicationData(m_ctx); sctx->project = m_project.get(); + turbine::setWindowTitle(m_ctx, ox::sfmt("{} - {}", keelCtx(m_ctx).appName, m_project->projectPath())); m_project->fileAdded.connect(&m_projectExplorer, &ProjectExplorer::refreshProjectTreeModel); m_project->fileDeleted.connect(&m_projectExplorer, &ProjectExplorer::refreshProjectTreeModel); m_openFiles.clear(); m_editors.clear(); studio::editConfig(keelCtx(m_ctx), [&](StudioConfig *config) { - config->projectPath = ox::String(path); + config->projectPath = ox::String(m_project->projectPath()); config->openFiles.clear(); }); return m_projectExplorer.refreshProjectTreeModel(); diff --git a/src/olympic/studio/applib/src/studioapp.hpp b/src/olympic/studio/applib/src/studioapp.hpp index 97be957..e372775 100644 --- a/src/olympic/studio/applib/src/studioapp.hpp +++ b/src/olympic/studio/applib/src/studioapp.hpp @@ -14,6 +14,7 @@ #include #include "aboutpopup.hpp" #include "newmenu.hpp" +#include "newproject.hpp" #include "projectexplorer.hpp" #include "projecttreemodel.hpp" @@ -36,9 +37,11 @@ class StudioUI: public ox::SignalHandler { studio::BaseEditor *m_activeEditor = nullptr; studio::BaseEditor *m_activeEditorUpdatePending = nullptr; NewMenu m_newMenu; + NewProject m_newProject; AboutPopup m_aboutPopup; - ox::Array const m_popups = { + ox::Array const m_popups = { &m_newMenu, + &m_newProject, &m_aboutPopup }; bool m_showProjectExplorer = true; @@ -79,7 +82,9 @@ class StudioUI: public ox::SignalHandler { void save() noexcept; - ox::Error openProject(ox::CRStringView path) noexcept; + ox::Error createOpenProject(ox::CRStringView path) noexcept; + + ox::Error openProjectPath(ox::CRStringView path) noexcept; ox::Error openFile(ox::CRStringView path) noexcept; diff --git a/src/olympic/studio/modlib/include/studio/itemmaker.hpp b/src/olympic/studio/modlib/include/studio/itemmaker.hpp index 8822524..dd7bb51 100644 --- a/src/olympic/studio/modlib/include/studio/itemmaker.hpp +++ b/src/olympic/studio/modlib/include/studio/itemmaker.hpp @@ -24,7 +24,14 @@ class ItemMaker { fileExt(pFileExt) { } virtual ~ItemMaker() noexcept = default; - virtual ox::Error write(turbine::Context &ctx, ox::CRStringView pName) const noexcept = 0; + + /** + * Returns path of the file created. + * @param ctx + * @param pName + * @return path of file or error in Result + */ + virtual ox::Result write(turbine::Context &ctx, ox::CRStringView pName) const noexcept = 0; }; template @@ -61,11 +68,12 @@ class ItemMakerT: public ItemMaker { m_item(std::move(pItem)), m_fmt(pFmt) { } - ox::Error write(turbine::Context &ctx, ox::CRStringView pName) const noexcept override { + ox::Result write(turbine::Context &ctx, ox::CRStringView pName) const noexcept override { auto const path = ox::sfmt("/{}/{}.{}", parentDir, pName, fileExt); auto sctx = turbine::applicationData(ctx); keel::createUuidMapping(keelCtx(ctx), path, ox::UUID::generate().unwrap()); - return sctx->project->writeObj(path, m_item, m_fmt); + oxReturnError(sctx->project->writeObj(path, m_item, m_fmt)); + return path; } }; diff --git a/src/olympic/studio/modlib/include/studio/project.hpp b/src/olympic/studio/modlib/include/studio/project.hpp index 9908889..d54313e 100644 --- a/src/olympic/studio/modlib/include/studio/project.hpp +++ b/src/olympic/studio/modlib/include/studio/project.hpp @@ -36,11 +36,21 @@ constexpr ox::Result fileExt(ox::CRStringView path) noexcept { return substr(path, extStart + 1); } +[[nodiscard]] +constexpr ox::StringView parentDir(ox::StringView path) noexcept { + if (path.len() && path[path.len() - 1] == '/') { + path = substr(path, 0, path.len() - 1); + } + auto const extStart = ox::find(path.crbegin(), path.crend(), '/').offset(); + return substr(path, 0, extStart); +} + class Project { private: keel::Context &m_ctx; ox::String m_path; ox::String m_projectDataDir; + ox::String m_typeDescPath; mutable keel::TypeStore m_typeStore; ox::FileSystem &m_fs; ox::HashMap> m_fileExtFileMap; @@ -50,6 +60,9 @@ class Project { ox::Error create() noexcept; + [[nodiscard]] + ox::String const&projectPath() const noexcept; + [[nodiscard]] ox::FileSystem *romFs() noexcept; @@ -78,6 +91,8 @@ class Project { [[nodiscard]] ox::Vector const&fileList(ox::CRStringView ext) noexcept; + ox::Error writeAllTypeDescriptors() noexcept; + private: void buildFileIndex() noexcept; @@ -108,21 +123,16 @@ template ox::Error Project::writeObj(ox::CRStringView path, T const&obj, ox::ClawFormat fmt) noexcept { oxRequireM(buff, ox::writeClaw(obj, fmt)); // write to FS + oxReturnError(mkdir(parentDir(path))); oxReturnError(writeBuff(path, buff)); // write type descriptor if (m_typeStore.get().error) { oxReturnError(ox::buildTypeDef(&m_typeStore, &obj)); } - // write out type store - auto const descPath = ox::sfmt("/{}/type_descriptors", m_projectDataDir); - oxReturnError(mkdir(descPath)); - for (auto const&t : m_typeStore.typeList()) { - oxRequireM(typeOut, ox::writeClaw(*t, ox::ClawFormat::Organic)); - // replace garbage last character with new line - *typeOut.back().value = '\n'; - // write to FS - auto const typePath = ox::sfmt("/{}/{}", descPath, buildTypeId(*t)); - oxReturnError(writeBuff(typePath, typeOut)); + oxRequire(desc, m_typeStore.get()); + auto const descExists = m_fs.stat(ox::sfmt("{}/{}", m_typeDescPath, buildTypeId(*desc))).error != 0; + if (!descExists || ox::defines::Debug) { + oxReturnError(writeAllTypeDescriptors()); } oxReturnError(keel::setAsset(m_ctx, path, obj)); fileUpdated.emit(path); diff --git a/src/olympic/studio/modlib/src/project.cpp b/src/olympic/studio/modlib/src/project.cpp index cd0d2f5..76ee9b3 100644 --- a/src/olympic/studio/modlib/src/project.cpp +++ b/src/olympic/studio/modlib/src/project.cpp @@ -13,6 +13,11 @@ 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()) { @@ -25,6 +30,7 @@ Project::Project(keel::Context &ctx, ox::String path, ox::CRStringView projectDa 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); @@ -38,6 +44,10 @@ ox::Error Project::create() noexcept { return OxError(static_cast(ec.value()), "PassThroughFS: mkdir failed"); } +ox::String const&Project::projectPath() const noexcept { + return m_path; +} + ox::FileSystem *Project::romFs() noexcept { return &m_fs; } @@ -60,6 +70,20 @@ ox::Vector const&Project::fileList(ox::CRStringView ext) noexcept { return m_fileExtFileMap[ext]; } +ox::Error Project::writeAllTypeDescriptors() noexcept { + // write all descriptors because we don't know which types T depends on + oxReturnError(mkdir(m_typeDescPath)); + for (auto const &t: m_typeStore.typeList()) { + oxRequireM(typeOut, ox::writeClaw(*t, ox::ClawFormat::Organic)); + // replace garbage last character with new line + *typeOut.back().value = '\n'; + // write to FS + auto const typePath = ox::sfmt("{}/{}", m_typeDescPath, buildTypeId(*t)); + oxReturnError(writeBuff(typePath, typeOut)); + } + return {}; +} + void Project::buildFileIndex() noexcept { auto [files, err] = listFiles(); if (err) { diff --git a/src/olympic/turbine/src/glfw/gfx.cpp b/src/olympic/turbine/src/glfw/gfx.cpp index 13658e7..48d58b2 100644 --- a/src/olympic/turbine/src/glfw/gfx.cpp +++ b/src/olympic/turbine/src/glfw/gfx.cpp @@ -54,6 +54,8 @@ static void handleKeyPress(Context &ctx, int key, bool down) noexcept { map[GLFW_KEY_RIGHT_CONTROL] = Key::Mod_Ctrl; map[GLFW_KEY_LEFT_SUPER] = Key::Mod_Super; map[GLFW_KEY_RIGHT_SUPER] = Key::Mod_Super; + map[GLFW_KEY_LEFT_SHIFT] = Key::Mod_Shift; + map[GLFW_KEY_RIGHT_SHIFT] = Key::Mod_Shift; map[GLFW_KEY_ESCAPE] = Key::Escape; return map; }();