diff --git a/include/DOM/DOMNodeBase.hpp b/include/DOM/DOMNodeBase.hpp index c0d76be..1205b5f 100644 --- a/include/DOM/DOMNodeBase.hpp +++ b/include/DOM/DOMNodeBase.hpp @@ -200,7 +200,7 @@ class LDOMNodeBase : public std::enable_shared_from_this void RemoveChild(std::shared_ptr child) { - ptrdiff_t index = LGenUtility::VectorIndexOf(Children, child); + std::ptrdiff_t index = LGenUtility::VectorIndexOf(Children, child); if (index == -1) return; diff --git a/include/GenUtil.hpp b/include/GenUtil.hpp index 2a5df3e..266b9a3 100644 --- a/include/GenUtil.hpp +++ b/include/GenUtil.hpp @@ -13,9 +13,9 @@ namespace LGenUtility extern std::fstream Log; // Returns the index of the given element in the given vector, or -1 if the element is not in that vector. template - ptrdiff_t VectorIndexOf(const std::vector& vec, const T& elem) + std::ptrdiff_t VectorIndexOf(const std::vector& vec, const T& elem) { - ptrdiff_t result = -1; + std::ptrdiff_t result = -1; auto it = std::find(vec.begin(), vec.end(), elem); if (it != vec.end()) @@ -26,14 +26,14 @@ namespace LGenUtility // Returns whether the given element is contained in the given vector. template - ptrdiff_t VectorContains(const std::vector& vec, const T& elem) + std::ptrdiff_t VectorContains(const std::vector& vec, const T& elem) { return VectorIndexOf(vec, elem) != -1; } // Returns whether the given element is contained in the given vector, with the index parameter set to the element's position. template - ptrdiff_t VectorContains(const std::vector& vec, const T& elem, ptrdiff_t& index) + std::ptrdiff_t VectorContains(const std::vector& vec, const T& elem, std::ptrdiff_t& index) { index = VectorIndexOf(vec, elem); return index != -1; diff --git a/include/io/BloIO.hpp b/include/io/BloIO.hpp index d38cf90..b033763 100644 --- a/include/io/BloIO.hpp +++ b/include/io/BloIO.hpp @@ -7,7 +7,7 @@ #include namespace Blo { - enum class ResourceType : uint32_t { + enum class ResourceType : uint8_t { None, Unknown, Directory, @@ -44,7 +44,7 @@ namespace Blo { Bottom = (1 << 0), Top = (1 << 1), Right = (1 << 2), - Left = (1 << 3) + Left = (1 << 3), }; enum class Anchor : uint32_t { @@ -69,9 +69,10 @@ namespace Blo { }; struct Resource { - ResourceType mType; + uint8_t mType; std::string mPath; virtual void Load(bStream::CStream* stream, std::shared_ptr timg); + void Save(bStream::CStream* stream); }; struct Image : Resource { @@ -99,36 +100,41 @@ namespace Blo { protected: ElementType mType; std::shared_ptr mParent; - std::vector> mChildren; - uint32_t mID; bool mVisible; uint8_t mCullMode; - Anchor mAnchor; + uint8_t mAnchor; float mAngle; bool mInheritAlpha; bool mConnectParent; uint8_t mAccumulateAlpha; + std::map mPaneArgs; public: + uint32_t mID; + int16_t mRect[4] {32, 32, 100, 100}; uint8_t mAlpha; ElementType Type() { return mType; } - int16_t mRect[4]; + std::vector> mChildren; virtual bool Load(bStream::CStream* stream, std::shared_ptr parent, std::shared_ptr timg); virtual void DrawHierarchy(std::shared_ptr& selection); virtual void Draw(std::shared_ptr& selection); + virtual void Save(bStream::CStream* stream); + + Pane(); + Pane(ElementType t, uint32_t id); }; class Picture : public Pane { std::shared_ptr mTextures[4] { nullptr, nullptr, nullptr, nullptr }; Palette mPalette; - Binding mBinding; + uint8_t mBinding; double mBlendFactors[4]; double mBlendAlphaFactors[4]; int mTextureCount; - WrapMode mWrapX; - WrapMode mWrapY; + uint8_t mWrapX; + uint8_t mWrapY; uint8_t mMirror; bool mRotate; @@ -136,12 +142,19 @@ namespace Blo { glm::vec4 mToColor; glm::vec4 mColors[4]; + std::map mPictArgs; public: std::shared_ptr GetTexture(){ return mTextures[0]; } bool Load(bStream::CStream* stream, std::shared_ptr parent, std::shared_ptr timg); void DrawHierarchy(std::shared_ptr& selection); void Draw(std::shared_ptr& selection); + void Save(bStream::CStream* stream); + + void SetWidth(uint16_t w) { mRect[2] = w; } + void SetHeight(uint16_t h) { mRect[3] = h; } + + Picture(); }; class Window : public Pane { @@ -153,15 +166,22 @@ namespace Blo { int16_t mContentRect[4]; glm::vec4 mFromColor; glm::vec4 mToColor; + std::map mWindowArgs; public: bool Load(bStream::CStream* stream, std::shared_ptr parent, std::shared_ptr timg); void DrawHierarchy(std::shared_ptr& selection); void Draw(std::shared_ptr& selection); + void Save(bStream::CStream* stream); + + std::shared_ptr GetTexture(uint32_t id) { if(id < 4) { return mTextures[id]; } else { return nullptr; } } + void SetTexture(std::shared_ptr img, uint32_t id) { if(id < 4) { mTextures[id] = img; } } + + Window(); }; class Textbox : public Pane { Font mFont; - glm::vec4 mTopColor, mBottomColor; + glm::vec4 mTopColor {1.0f, 1.0f, 1.0f, 1.0f}, mBottomColor {1.0f, 1.0f, 1.0f, 1.0f}; uint8_t mHAlign, mVAlign; uint16_t mFontSpacing; @@ -173,10 +193,17 @@ namespace Blo { glm::vec4 mFromColor; glm::vec4 mToColor; + std::map mTextboxArgs; public: bool Load(bStream::CStream* stream, std::shared_ptr parent, std::shared_ptr timg); void DrawHierarchy(std::shared_ptr& selection); void Draw(std::shared_ptr& selection); + void Save(bStream::CStream* stream); + + Font* GetFont() { return &mFont; }; + std::string* GetText() { return &mText; } + + Textbox(); }; class Screen : public Pane { @@ -187,6 +214,8 @@ namespace Blo { bool Load(bStream::CStream* stream, std::shared_ptr timg); void DrawHierarchy(std::shared_ptr& selection); void Draw(std::shared_ptr& selection); + + Screen(); }; }; \ No newline at end of file diff --git a/include/io/BtiIO.hpp b/include/io/BtiIO.hpp index b3e6fef..9c35eb8 100644 --- a/include/io/BtiIO.hpp +++ b/include/io/BtiIO.hpp @@ -59,6 +59,7 @@ class Bti { uint8_t* Load(bStream::CStream* stream); void Save(bStream::CStream* stream, uint16_t width, uint16_t height, std::vector& imageData); + void SetFormat(uint8_t fmt) { mFormat = fmt; } Bti(){} ~Bti(){ if(mImageData != nullptr){ delete[] mImageData; } } diff --git a/src/DOM/CharacterDOMNode.cpp b/src/DOM/CharacterDOMNode.cpp index 0eb7386..9b687dc 100644 --- a/src/DOM/CharacterDOMNode.cpp +++ b/src/DOM/CharacterDOMNode.cpp @@ -188,7 +188,7 @@ void LCharacterDOMNode::PreProcess() auto itemAppearNodes = mapNodeLocked->GetChildrenOfType(EDOMNodeType::ItemAppear); auto lockedItemRef = mItemTableRef.lock(); - ptrdiff_t index = LGenUtility::VectorIndexOf(itemAppearNodes, lockedItemRef); + std::ptrdiff_t index = LGenUtility::VectorIndexOf(itemAppearNodes, lockedItemRef); if (index == -1) mItemTableIndex = 0; diff --git a/src/DOM/EnemyDOMNode.cpp b/src/DOM/EnemyDOMNode.cpp index 9158966..84aaae5 100644 --- a/src/DOM/EnemyDOMNode.cpp +++ b/src/DOM/EnemyDOMNode.cpp @@ -252,7 +252,7 @@ void LEnemyDOMNode::PreProcess() auto furnitureNodes = parentShared->GetChildrenOfType(EDOMNodeType::Furniture); // Grab the index of the furniture node in the list of furniture. Report an error if it doesn't exist, because that shouldn't happen. - ptrdiff_t furnitureIndex = LGenUtility::VectorIndexOf(furnitureNodes, furnitureShared); + std::ptrdiff_t furnitureIndex = LGenUtility::VectorIndexOf(furnitureNodes, furnitureShared); if (furnitureIndex == -1) { LGenUtility::Log << "[EnemyDOMNode]: Tried to set furniture access name to nonexistent furniture node!"; @@ -284,7 +284,7 @@ void LEnemyDOMNode::PreProcess() auto itemAppearNodes = mapNodeLocked->GetChildrenOfType(EDOMNodeType::ItemAppear); auto lockedItemRef = mItemTableRef.lock(); - ptrdiff_t index = LGenUtility::VectorIndexOf(itemAppearNodes, lockedItemRef); + std::ptrdiff_t index = LGenUtility::VectorIndexOf(itemAppearNodes, lockedItemRef); if (index == -1) mItemTableIndex = 0; diff --git a/src/DOM/FurnitureDOMNode.cpp b/src/DOM/FurnitureDOMNode.cpp index 190cc0c..6df6e1d 100644 --- a/src/DOM/FurnitureDOMNode.cpp +++ b/src/DOM/FurnitureDOMNode.cpp @@ -277,7 +277,7 @@ void LFurnitureDOMNode::PreProcess() auto itemAppearNodes = mapNodeLocked->GetChildrenOfType(EDOMNodeType::ItemAppear); auto lockedItemRef = mItemTableRef.lock(); - ptrdiff_t index = LGenUtility::VectorIndexOf(itemAppearNodes, lockedItemRef); + std::ptrdiff_t index = LGenUtility::VectorIndexOf(itemAppearNodes, lockedItemRef); if (index == -1) mItemTableIndex = 0; diff --git a/src/DOM/RoomDOMNode.cpp b/src/DOM/RoomDOMNode.cpp index 7da32c2..71bc3a8 100644 --- a/src/DOM/RoomDOMNode.cpp +++ b/src/DOM/RoomDOMNode.cpp @@ -729,6 +729,7 @@ void LRoomDOMNode::RenderDetailsUI(float dt) std::vector imgData(x*y*n); std::memcpy(imgData.data(), img, x*y*n); + stbi_image_free(img); auto fileData = GCResourceManager.mGameArchive->GetFile(std::format("/kawano/roomname/{}", LResUtility::GetNameMap("MapTitlecards")["titlecards"][mRoomNumber].get())); bStream::CMemoryStream file(0x20 + (x * y), bStream::Endianess::Big, bStream::OpenMode::Out); diff --git a/src/io/BloIO.cpp b/src/io/BloIO.cpp index e0db825..dfb77cf 100644 --- a/src/io/BloIO.cpp +++ b/src/io/BloIO.cpp @@ -8,16 +8,39 @@ namespace Blo { +static int blockCount = 0; + +Pane::Pane() : mType(ElementType::Pane), mID(0x50414E45) {} +Pane::Pane(ElementType t, uint32_t id) : mType(t), mID(id) {} +Screen::Screen() : Pane(ElementType::Screen, 0x5343524E) {} +Picture::Picture() : Pane(ElementType::Picture, 0x50494354) { mTextures[0] = std::make_shared(); } +Textbox::Textbox() : Pane(ElementType::Textbox, 0x54585442) { mText = "Text"; } +Window::Window() : Pane(ElementType::Window, 0x57494E44) {} + void Resource::Load(bStream::CStream* stream, std::shared_ptr timg){ - mType = static_cast(stream->readUInt8()); + mType = stream->readUInt8(); uint8_t len = stream->readUInt8(); mPath = stream->readString(len); } +void Resource::Save(bStream::CStream* stream){ + stream->writeUInt8(mType); + stream->writeUInt8(mPath.size()); + if(mPath.size() != 0){ + stream->writeString(mPath); + } +} + void Image::Load(bStream::CStream* stream, std::shared_ptr timg){ Resource::Load(stream, timg); std::shared_ptr imgFile = timg->GetFile(mPath); + if(imgFile == nullptr){ + std::string lowerPath = mPath; + std::transform(lowerPath.begin(), lowerPath.end(), lowerPath.begin(), [](unsigned char c){ return std::tolower(c); }); + imgFile = timg->GetFile(lowerPath); + } + if(imgFile != nullptr){ //std::cout << "Loading Image " << mPath << std::endl; bStream::CMemoryStream img(imgFile->GetData(), imgFile->GetSize(), bStream::Endianess::Big, bStream::OpenMode::In); @@ -46,17 +69,17 @@ void Palette::Load(bStream::CStream* stream, std::shared_ptr ti bool Screen::Load(bStream::CStream* stream, std::shared_ptr timg){ mType = ElementType::Screen; - if(stream->readUInt32() != 0x5343524e){ // 'SCRN' + if(stream->readUInt32() != 0x5343524E){ return false; } - if(stream->readUInt32() != 0x626c6f31){ // 'SCRN' + if(stream->readUInt32() != 0x626C6f31){ return false; } stream->skip(24); - if(stream->readUInt32() != 0x494e4631){ // 'SCRN' + if(stream->readUInt32() != 0x494E4631){ return false; } @@ -74,7 +97,268 @@ bool Screen::Load(bStream::CStream* stream, std::shared_ptr tim return true; } +void SaveBlo1(bStream::CStream* stream, std::vector>& children){ + LGenUtility::Log << "Writing Children" << std::endl; + blockCount++; + stream->writeUInt32(0x42474E31); // BGN1 + stream->writeUInt32(8); + + for(std::size_t child = 0; child < children.size(); child++){ + uint32_t paneType = 0; + std::size_t panePos = stream->tell(); + stream->writeUInt32(0); + stream->writeUInt32(0); + switch (children[child]->Type()){ + case ElementType::Pane: + paneType = 0x50414E31; + children[child]->Save(stream); + break; + case ElementType::Picture: + paneType = 0x50494331; + std::reinterpret_pointer_cast(children[child])->Save(stream); + break; + case ElementType::Window: + paneType = 0x57494E31; + std::reinterpret_pointer_cast(children[child])->Save(stream); + break; + case ElementType::Textbox: + paneType = 0x54425831; + std::reinterpret_pointer_cast(children[child])->Save(stream); + break; + } + std::size_t listPos = stream->tell(); + stream->seek(panePos); + stream->writeUInt32(paneType); + stream->writeUInt32(listPos - panePos); + stream->seek(listPos); + + if(children[child]->mChildren.size() > 0){ + SaveBlo1(stream, children[child]->mChildren); + } + + } + stream->writeUInt32(0x454E4431); // END1 + blockCount++; + stream->writeUInt32(8); + LGenUtility::Log << "Finished writing children" << std::endl; +} + void Screen::Save(bStream::CStream* stream){ + blockCount = 0; + LGenUtility::Log << "Writing Screen" << std::endl; + stream->writeUInt32(0x5343524E); + stream->writeUInt32(0x626C6f31); + + stream->writeUInt32(0); + stream->writeUInt32(0); + for(;stream->tell() < 0x20;) stream->writeUInt8(0); + + stream->writeUInt32(0x494E4631); + blockCount++; + stream->writeUInt32(0x10); + + stream->writeInt16(mRect[2]); + stream->writeInt16(mRect[3]); + stream->writeUInt32(mColor); + + LGenUtility::Log << "Writing Screen Children" << std::endl; + for(std::size_t child = 0; child < mChildren.size(); child++){ + uint32_t paneType = 0; + std::size_t panePos = stream->tell(); + stream->writeUInt32(0); + stream->writeUInt32(0); + switch (mChildren[child]->Type()){ + case ElementType::Pane: + paneType = 0x50414E31; + mChildren[child]->Save(stream); + break; + case ElementType::Picture: + paneType = 0x50494331; + std::reinterpret_pointer_cast(mChildren[child])->Save(stream); + break; + case ElementType::Window: + paneType = 0x57494E31; + std::reinterpret_pointer_cast(mChildren[child])->Save(stream); + break; + case ElementType::Textbox: + paneType = 0x54425831; + std::reinterpret_pointer_cast(mChildren[child])->Save(stream); + break; + } + std::size_t listPos = stream->tell(); + stream->seek(panePos); + stream->writeUInt32(paneType); + stream->writeUInt32(listPos - panePos); + stream->seek(listPos); + + if(mChildren[child]->mChildren.size() > 0){ + SaveBlo1(stream, mChildren[child]->mChildren); + } + + } + LGenUtility::Log << "Finished Eriting Screen Children" << std::endl; + + stream->writeUInt32(0x45585431); + blockCount++; + stream->writeUInt32(8); + for(int x = 0; x < Util::AlignTo(stream->tell(), 32) - stream->tell(); x++){ + stream->writeUInt8(0); + } + std::size_t endPos = stream->tell(); + stream->seek(0x08); + stream->writeUInt32(endPos); + stream->writeUInt32(blockCount); +} + +void Pane::Save(bStream::CStream* stream){ + blockCount++; + uint32_t id = LGenUtility::SwapEndian(mID); + char drawID[sizeof(uint32_t)+1] = {0}; + std::memcpy(drawID, &id, sizeof(uint32_t)); + //LGenUtility::Log << "ID is " << drawID << " arg count " << (6 + mPaneArgs["angle"] + mPaneArgs["anchor"] + mPaneArgs["alpha"] + mPaneArgs["inheritAlpha"]) << " writing at " << std::hex << stream->tell() << std::dec << std::endl; + stream->writeUInt8(6 + mPaneArgs["angle"] + mPaneArgs["anchor"] + mPaneArgs["alpha"] + mPaneArgs["inheritAlpha"]); + stream->writeUInt8(mVisible); + stream->writeUInt16(0); + stream->writeUInt32(mID); + stream->writeInt16(mRect[0]); + stream->writeInt16(mRect[1]); + stream->writeInt16(mRect[2]); + stream->writeInt16(mRect[3]); + + if(mPaneArgs["angle"]){ + stream->writeUInt16(mAngle); + } + + if(mPaneArgs["anchor"]){ + stream->writeUInt8((uint8_t)mAnchor); + } + + if(mPaneArgs["alpha"]){ + stream->writeUInt8(mAlpha); + } + + if(mPaneArgs["inheritAlpha"]){ + stream->writeUInt8(mInheritAlpha); + } + + //stream->writeUInt32(0); +} + +void Picture::Save(bStream::CStream* stream){ + LGenUtility::Log << "Writing Picture " << std::endl; + Pane::Save(stream); + + std::cout << "writing arg count " << 3 + mPictArgs["mirror"] + mPictArgs["wrap"] + mPictArgs["fromColor"] + mPictArgs["toColor"] + mPictArgs["color0"] + mPictArgs["color1"] + mPictArgs["color2"] + mPictArgs["color3"] << std::endl; + stream->writeUInt8(3 + mPictArgs["mirror"] + mPictArgs["wrap"] + mPictArgs["fromColor"] + mPictArgs["toColor"] + mPictArgs["color0"] + mPictArgs["color1"] + mPictArgs["color2"] + mPictArgs["color3"]); + mTextures[0]->Save(stream); + mPalette.Save(stream); + stream->writeUInt8(mBinding); + + if(mPictArgs["mirror"]){ + stream->writeUInt8((mMirror << 4 )| mRotate); + } + + if(mPictArgs["wrap"]){ + stream->writeUInt8((mWrapX << 2) | mWrapY); + } + + if(mPictArgs["fromColor"]){ + stream->writeUInt32((static_cast(mFromColor.r * 255) << 24) | (static_cast(mFromColor.g * 255) << 16) | (static_cast(mFromColor.b * 255) << 8) | (static_cast(mFromColor.a * 255))); + } + + if(mPictArgs["toColor"]){ + stream->writeUInt32((static_cast(mToColor.r * 255) << 24) | (static_cast(mToColor.g * 255) << 16) | (static_cast(mToColor.b * 255) << 8) | (static_cast(mToColor.a * 255))); + } + + for(int c = 0; c < 4; c++){ + if(mPictArgs[std::format("color{}", c)]){ + stream->writeUInt32((static_cast(mColors[c].r * 255) << 24) | (static_cast(mColors[c].g * 255) << 16) | (static_cast(mColors[c].b * 255) << 8) | (static_cast(mColors[c].a * 255))); + } + } + + std::size_t paddedEnd = Util::AlignTo(stream->tell(), 4); + for(; stream->tell() < paddedEnd;) stream->writeUInt8(0); +} + +void Window::Save(bStream::CStream* stream){ + LGenUtility::Log << "Writing Window " << std::endl; + Pane::Save(stream); + + stream->writeUInt8(14 + mWindowArgs["contenttex"] + mWindowArgs["fromColor"] + mWindowArgs["toColor"]); + stream->writeUInt16(mContentRect[0]); + stream->writeUInt16(mContentRect[1]); + stream->writeUInt16(mContentRect[2]); + stream->writeUInt16(mContentRect[3]); + + for(std::size_t i = 0; i < 4; i++){ + mTextures[i]->Save(stream); + } + + mPalette.Save(stream); + + uint8_t bits = 0; + for(std::size_t i = 0; i < 4; i++){ + // fuck + bits |= ((mTextures[i]->mMirror & 3) << (6 - (i * 2))); //mTextures[i]->mMirror = ((bits >> (6 - (i * 2))) & 3); + } + stream->writeUInt8(bits); + + for(std::size_t i = 0; i < 4; i++){ + stream->writeUInt32((static_cast(mTextures[i]->mColor.r * 255) << 24) | (static_cast(mTextures[i]->mColor.g * 255) << 16) | (static_cast(mTextures[i]->mColor.b * 255) << 8) | static_cast(mTextures[i]->mColor.a * 255)); + } + + if(mWindowArgs["contenttex"]){ + mContentTexture->Save(stream); + } + + if(mWindowArgs["fromColor"]){ + stream->writeUInt32((static_cast(mFromColor.r * 255) << 24) | (static_cast(mFromColor.g * 255) << 16) | (static_cast(mFromColor.b * 255) << 8) | (static_cast(mFromColor.a * 255))); + } + + if(mWindowArgs["toColor"]){ + stream->writeUInt32((static_cast(mToColor.r * 255) << 24) | (static_cast(mToColor.g * 255) << 16) | (static_cast(mToColor.b * 255) << 8) | (static_cast(mToColor.a * 255))); + } + + std::size_t paddedEnd = Util::AlignTo(stream->tell(), 4); + for(; stream->tell() < paddedEnd;) stream->writeUInt8(0); + +} + +void Textbox::Save(bStream::CStream* stream){ + LGenUtility::Log << "Writing Textbox " << std::endl; + Pane::Save(stream); + + stream->writeUInt8(10 + mTextboxArgs["connectParent"] + mTextboxArgs["fromColor"] + mTextboxArgs["toColor"]); + + mFont.Save(stream); + + stream->writeUInt32((static_cast(mTopColor.r * 255) << 24) | (static_cast(mTopColor.g * 255) << 16) | (static_cast(mTopColor.b * 255) << 8) | (static_cast(mTopColor.a * 255))); + stream->writeUInt32((static_cast(mBottomColor.r * 255) << 24) | (static_cast(mBottomColor.g * 255) << 16) | (static_cast(mBottomColor.b * 255) << 8) | (static_cast(mBottomColor.a * 255))); + + stream->writeUInt8(((mHAlign & 3) << 2) | (mVAlign & 3)); + + stream->writeUInt16(mFontSpacing); + stream->writeUInt16(mFontLeading); + stream->writeUInt16(mFontWidth); + stream->writeUInt16(mFontHeight); + + stream->writeUInt16(mText.size()); + stream->writeString(mText); + + if(mTextboxArgs["connectParent"]){ + stream->writeUInt8(mConnectParent); + } + + if(mTextboxArgs["fromColor"]){ + stream->writeUInt32((static_cast(mFromColor.r * 255) << 24) | (static_cast(mFromColor.g * 255) << 16) | (static_cast(mFromColor.b * 255) << 8) | (static_cast(mFromColor.a * 255))); + } + + if(mTextboxArgs["toColor"]){ + stream->writeUInt32((static_cast(mToColor.r * 255) << 24) | (static_cast(mToColor.g * 255) << 16) | (static_cast(mToColor.b * 255) << 8) | (static_cast(mToColor.a * 255))); + } + + std::size_t paddedEnd = Util::AlignTo(stream->tell(), 4); + for(; stream->tell() < paddedEnd;) stream->writeUInt8(0); } @@ -84,6 +368,7 @@ bool Screen::LoadBlo1(bStream::CStream* stream, std::shared_ptr parent, st std::size_t paneStart = stream->tell(); if(stream->tell() >= stream->getSize()) return true; + LGenUtility::Log << "[BLOIO]: Reading pane at 0x" << std::hex << stream->tell() << std::dec << std::endl; uint32_t paneType = stream->readUInt32(); uint32_t paneLen = stream->readUInt32(); @@ -129,9 +414,9 @@ bool Screen::LoadBlo1(bStream::CStream* stream, std::shared_ptr parent, st } break; case 0x454E4431: // END1 + case 0x45585431: // EXT1 stream->seek(paneStart + paneLen); return true; - case 0x45585431: // EXT1 break; default: return false; @@ -144,7 +429,7 @@ bool Pane::Load(bStream::CStream* stream, std::shared_ptr parent, std::sha mParent = parent; parent->mChildren.push_back(shared_from_this()); - int numParams = stream->readUInt8() - 6; + int numParams = (int)stream->readUInt8() - 6; //std::cout << "Loading pane " << (int)numParams << " params..." << std::endl; mVisible = stream->readUInt8() != 0; stream->skip(2); @@ -156,39 +441,45 @@ bool Pane::Load(bStream::CStream* stream, std::shared_ptr parent, std::sha mRect[2] = stream->readUInt16(); mRect[3] = stream->readUInt16(); - char temp[4]; - memcpy(temp, &mID, 4); + //char temp[4]; + //memcpy(temp, &mID, 4); //std::cout << "Pane ID " << temp << " params..." << std::endl; if(numParams > 0){ + mPaneArgs["angle"] = true; mAngle = stream->readUInt16(); numParams--; } else { + mPaneArgs["angle"] = false; mAngle = 0; } if(numParams > 0){ - mAnchor = static_cast(stream->readUInt8()); + mPaneArgs["anchor"] = true; + mAnchor = stream->readUInt8(); numParams--; } else { - mAnchor = Anchor::TopLeft; + mPaneArgs["anchor"] = false; + mAnchor = (uint8_t)Anchor::TopLeft; } if(numParams > 0){ + mPaneArgs["alpha"] = true; mAlpha = stream->readUInt8(); numParams--; } else { + mPaneArgs["alpha"] = false; mAlpha = 0xFF; } if(numParams > 0){ + mPaneArgs["inheritAlpha"] = true; mInheritAlpha = stream->readUInt8() != 0; numParams--; } else { + mPaneArgs["inheritAlpha"] = false; mInheritAlpha = true; } - - //stream->skip(4); //std::cout << "Finished Loading Pane at " << std::hex << stream->tell() << std::dec << std::endl; @@ -199,57 +490,79 @@ bool Picture::Load(bStream::CStream* stream, std::shared_ptr parent, std:: Pane::Load(stream, parent, timg); mType = ElementType::Picture; - uint8_t numParams = stream->readUInt8() - 3; - //std::cout << "Loading picture " << (int)numParams << " params..." << std::endl; + std::cout << "Loading param count at " << std::hex << stream->tell() << std::dec << std::endl; + int numParams = (int)stream->readUInt8() - 3; + std::cout << "Loading picture " << (int)numParams << " params..." << std::endl; mTextures[0] = std::make_shared(); mTextures[0]->Load(stream, timg); mPalette.Load(stream, timg); - mBinding = static_cast(stream->readUInt8()); + mBinding = stream->readUInt8(); if(numParams > 0){ uint8_t bits = stream->readUInt8(); mMirror = bits & 3; mRotate = ((bits & 4) != 0); + mPictArgs["mirror"] = true; numParams--; } else { mMirror = 0; mRotate = false; + mPictArgs["mirror"] = false; } + std::cout << (int)numParams << " params remaining..." << std::endl; + if(numParams > 0){ uint8_t bits = stream->readUInt8(); - mWrapX = static_cast((bits >> 2) & 3); - mWrapY = static_cast((bits >> 0) & 3); + mWrapX = ((bits >> 2) & 3); + mWrapY = ((bits >> 0) & 3); + mPictArgs["wrap"] = true; numParams--; } else { - mWrapX = WrapMode::None; - mWrapY = WrapMode::None; + mPictArgs["wrap"] = false; + mWrapX = (uint8_t)WrapMode::None; + mWrapY = (uint8_t)WrapMode::None; } + std::cout << (int)numParams << " params remaining..." << std::endl; + if(numParams > 0){ + mPictArgs["fromColor"] = true; uint32_t color = stream->readUInt32(); mFromColor = {((color >> 24) & 0xFF) / 255.0f, ((color >> 16) & 0xFF) / 255.0f, ((color >> 8) & 0xFF) / 255.0f, (color & 0xFF) / 255.0f}; numParams--; } else { + mPictArgs["fromColor"] = false; mFromColor = {0.0f, 0.0f, 0.0f, 1.0f}; } + std::cout << (int)numParams << " params remaining..." << std::endl; + if(numParams > 0){ uint32_t color = stream->readUInt32(); mToColor = {((color >> 24) & 0xFF) / 255.0f, ((color >> 16) & 0xFF) / 255.0f, ((color >> 8) & 0xFF) / 255.0f, (color & 0xFF) / 255.0f}; + mPictArgs["toColor"] = true; numParams--; } else { + mPictArgs["toColor"] = false; mToColor = {1.0f, 1.0f, 1.0f, 1.0f}; } + std::cout << (int)numParams << " params remaining..." << std::endl; + + std::cout << "Reading Colors for " << (char*)&mID << std::endl; for(int c = 0; c < 4; c++){ + std::cout << "Reading Color at " << std::hex << stream->tell() << std::dec << std::endl; if(numParams > 0){ uint32_t color = stream->readUInt32(); + mPictArgs[std::format("color{}", c)] = true; mColors[c] = {((color >> 24) & 0xFF) / 255.0f, ((color >> 16) & 0xFF) / 255.0f, ((color >> 8) & 0xFF) / 255.0f, (color & 0xFF) / 255.0f}; numParams--; } else { + mPictArgs[std::format("color{}", c)] = false; mColors[c] = {1.0f, 1.0f, 1.0f, 1.0f}; } + std::cout << (int)numParams << " params remaining..." << std::endl; } stream->skip(4); @@ -260,8 +573,7 @@ bool Window::Load(bStream::CStream* stream, std::shared_ptr parent, std::s Pane::Load(stream, parent, timg); mType = ElementType::Window; - uint8_t numParams = stream->readUInt8() - 14; - + int numParams = (int)stream->readUInt8() - 14; mContentRect[0] = stream->readUInt16(); mContentRect[1] = stream->readUInt16(); @@ -290,22 +602,29 @@ bool Window::Load(bStream::CStream* stream, std::shared_ptr parent, std::s mContentTexture = std::make_shared(); mContentTexture->Load(stream, timg); numParams--; + mWindowArgs["contenttex"] = true; + } else { + mWindowArgs["contenttex"] = false; } if(numParams > 0){ uint32_t color = stream->readUInt32(); mFromColor = {((color >> 24) & 0xFF) / 255.0f, ((color >> 16) & 0xFF) / 255.0f, ((color >> 8) & 0xFF) / 255.0f, (color & 0xFF) / 255.0f}; + mWindowArgs["fromColor"] = true; numParams--; } else { mFromColor = {0.0f, 0.0f, 0.0f, 0.0f}; + mWindowArgs["fromColor"] = false; } if(numParams > 0){ uint32_t color = stream->readUInt32(); mToColor = {((color >> 24) & 0xFF) / 255.0f, ((color >> 16) & 0xFF) / 255.0f, ((color >> 8) & 0xFF) / 255.0f, (color & 0xFF) / 255.0f}; + mWindowArgs["toColor"] = true; numParams--; } else { mToColor = {1.0f, 1.0f, 1.0f, 1.0f}; + mWindowArgs["toColor"] = false; } stream->skip(4); @@ -317,7 +636,7 @@ bool Textbox::Load(bStream::CStream* stream, std::shared_ptr parent, std:: Pane::Load(stream, parent, timg); mType = ElementType::Textbox; - uint8_t numParams = stream->readUInt8() - 10; + int numParams = (int)stream->readUInt8() - 10; mFont.Load(stream, timg); @@ -343,23 +662,30 @@ bool Textbox::Load(bStream::CStream* stream, std::shared_ptr parent, std:: if(stream->readUInt8() != 0){ mConnectParent = true; } + mTextboxArgs["connectParent"] = true; numParams--; + } else { + mTextboxArgs["connectParent"] = false; } if(numParams > 0){ uint32_t color = stream->readUInt32(); mFromColor = {((color >> 24) & 0xFF) / 255.0f, ((color >> 16) & 0xFF) / 255.0f, ((color >> 8) & 0xFF) / 255.0f, (color & 0xFF) / 255.0f}; + mTextboxArgs["fromColor"] = true; numParams--; } else { mFromColor = {0.0f, 0.0f, 0.0f, 0.0f}; + mTextboxArgs["fromColor"] = false; } if(numParams > 0){ uint32_t color = stream->readUInt32(); mToColor = {((color >> 24) & 0xFF) / 255.0f, ((color >> 16) & 0xFF) / 255.0f, ((color >> 8) & 0xFF) / 255.0f, (color & 0xFF) / 255.0f}; + mTextboxArgs["toColor"] = true; numParams--; } else { mToColor = {1.0f, 1.0f, 1.0f, 1.0f}; + mTextboxArgs["toColor"] = false; } stream->skip(4); @@ -456,16 +782,17 @@ void Picture::Draw(std::shared_ptr& selection){ ImGui::SetCursorPosY(mRect[1]); } + if(mVisible && mTextures[0] != nullptr && mTextures[0]->mTextureID != 0xFFFFFFFF){ ImVec2 UV0 (0.0f, 0.0f); ImVec2 UV1 (1.0f, 1.0f); - if(mRect[2] > mTextures[0]->mTexture.mWidth){ + if(mRect[2] > mTextures[0]->mTexture.mWidth && mTextures[0]->mTexture.mWidth != 0){ UV1.x = mRect[2] / mTextures[0]->mTexture.mWidth; } - if(mRect[3] > mTextures[0]->mTexture.mHeight){ + if(mRect[3] > mTextures[0]->mTexture.mHeight && mTextures[0]->mTexture.mHeight != 0){ UV1.y = mRect[3] / mTextures[0]->mTexture.mHeight; } @@ -521,9 +848,33 @@ void Textbox::Draw(std::shared_ptr& selection){ ImGui::SetCursorPosX(mRect[0]); ImGui::SetCursorPosY(mRect[1]); - int vidx = ImGui::GetWindowDrawList()->VtxBuffer.Size; - ImGui::Text(mText.c_str()); - ImGui::ShadeVertsLinearColorGradientKeepAlpha(ImGui::GetWindowDrawList(), vidx + 0, vidx + 2, {0.0, 0.0}, {0.0, 1.0}, ImColor(ImVec4(mTopColor.r, mTopColor.g, mTopColor.b, mTopColor.a)), ImColor(ImVec4(mBottomColor.r, mBottomColor.g, mBottomColor.b, mBottomColor.a))); + ImVec2 textSize = ImGui::CalcTextSize(mText.c_str()); + + switch((TextBoxHAlign)mHAlign){ + case TextBoxHAlign::Right: + ImGui::SetCursorPosX(mRect[0] + (mRect[2] - textSize.x)); + break; + case TextBoxHAlign::Center: + ImGui::SetCursorPosX(mRect[0] + ((mRect[2] / 2) - (textSize.x / 2))); + break; + } + + switch((TextBoxVAlign)mVAlign){ + case TextBoxVAlign::Bottom: + ImGui::SetCursorPosY(mRect[1] + (mRect[3] - textSize.y)); + break; + case TextBoxVAlign::Center: + ImGui::SetCursorPosY(mRect[1] + ((mRect[3] / 2) - (textSize.y / 2))); + break; + } + + ImGui::TextColored(ImColor(ImVec4(mTopColor.r, mTopColor.g, mTopColor.b, mTopColor.a)), mText.c_str()); + + if(ImGui::IsItemClicked()){ + selection = shared_from_this(); + } + + //ImGui::ShadeVertsLinearColorGradientKeepAlpha(ImGui::GetWindowDrawList(), vidx + 2, vidx + 4, {0.0, 0.0}, {0.0, 1.0}, ImColor(ImVec4(mTopColor.r, mTopColor.g, mTopColor.b, mTopColor.a)), ImColor(ImVec4(mBottomColor.r, mBottomColor.g, mBottomColor.b, mBottomColor.a))); ImGui::PushID(std::format("##PreviewPane{}", mID).c_str()); for(auto child: mChildren){ @@ -545,15 +896,39 @@ void Screen::Draw(std::shared_ptr& selection){ } void Screen::DrawHierarchy(std::shared_ptr& selection){ - ImGui::Text("Screen Elements"); - uint32_t id = LGenUtility::SwapEndian(mID); char drawID[sizeof(uint32_t)] = {0}; std::memcpy(drawID, &id, sizeof(uint32_t)); ImGuiTreeNodeFlags flags = mChildren.size() == 0 ? ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_SpanFullWidth : ImGuiTreeNodeFlags_None; if(shared_from_this() == selection) { flags |= ImGuiTreeNodeFlags_Selected; } - if(ImGui::TreeNodeEx(std::format("{}##Hierarchy{:x}", drawID, mID).c_str(), flags, "Screen %4s", drawID)){ + bool opened = ImGui::TreeNodeEx(std::format("Screen {}##Hierarchy{:x}", drawID, mID).c_str(), flags); + + if(ImGui::BeginPopupContextItem()){ + ImGui::Text("Add"); + ImGui::Separator(); + if(ImGui::Selectable("Pane")){ + mChildren.push_back(std::make_shared()); + } + if(ImGui::Selectable("Window")){ + mChildren.push_back(std::make_shared()); + } + if(ImGui::Selectable("Picture")){ + mChildren.push_back(std::make_shared()); + } + if(ImGui::Selectable("TextBox")){ + mChildren.push_back(std::make_shared()); + } + ImGui::Separator(); + if(ImGui::Selectable("Delete")){ + auto iter = std::find(mParent->mChildren.begin(), mParent->mChildren.end(), shared_from_this()); + mParent->mChildren.erase(iter); + } + ImGui::EndPopup(); + } + + if(opened){ + if(ImGui::IsItemClicked()){ selection = shared_from_this(); } @@ -578,7 +953,28 @@ void Pane::DrawHierarchy(std::shared_ptr& selection){ ImGuiTreeNodeFlags flags = mChildren.size() == 0 ? ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_SpanFullWidth : ImGuiTreeNodeFlags_None; if(shared_from_this() == selection) { flags |= ImGuiTreeNodeFlags_Selected; } - if(ImGui::TreeNodeEx(std::format("{}##Hierarchy{:x}", drawID, mID).c_str(), flags, "%4s", drawID)){ + bool opened = ImGui::TreeNodeEx(std::format("{}##Hierarchy{:x}", drawID, mID).c_str(), flags); + + if(ImGui::BeginPopupContextItem()){ + ImGui::Text("Add"); + ImGui::Separator(); + if(ImGui::Selectable("Pane")){ + mChildren.push_back(std::make_shared()); + } + if(ImGui::Selectable("Window")){ + mChildren.push_back(std::make_shared()); + } + if(ImGui::Selectable("Picture")){ + mChildren.push_back(std::make_shared()); + } + if(ImGui::Selectable("TextBox")){ + mChildren.push_back(std::make_shared()); + } + ImGui::EndPopup(); + } + + if(opened){ + if(ImGui::IsItemClicked()){ selection = shared_from_this(); } @@ -604,7 +1000,28 @@ void Picture::DrawHierarchy(std::shared_ptr& selection){ ImGuiTreeNodeFlags flags = mChildren.size() == 0 ? ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_SpanFullWidth : ImGuiTreeNodeFlags_None; if(shared_from_this() == selection) { flags |= ImGuiTreeNodeFlags_Selected; } - if(ImGui::TreeNodeEx(std::format("{}##Hierarchy{:x}", drawID, mID).c_str(), flags, "%4s", drawID)){ + bool opened = ImGui::TreeNodeEx(std::format("{}##Hierarchy{:x}", drawID, mID).c_str(), flags); + + if(ImGui::BeginPopupContextItem()){ + ImGui::Text("Add"); + ImGui::Separator(); + if(ImGui::Selectable("Pane")){ + mChildren.push_back(std::make_shared()); + } + if(ImGui::Selectable("Window")){ + mChildren.push_back(std::make_shared()); + } + if(ImGui::Selectable("Picture")){ + mChildren.push_back(std::make_shared()); + } + if(ImGui::Selectable("TextBox")){ + mChildren.push_back(std::make_shared()); + } + ImGui::EndPopup(); + } + + if(opened){ + if(ImGui::IsItemClicked()){ selection = shared_from_this(); } @@ -630,7 +1047,28 @@ void Window::DrawHierarchy(std::shared_ptr& selection){ ImGuiTreeNodeFlags flags = mChildren.size() == 0 ? ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_SpanFullWidth : ImGuiTreeNodeFlags_None; if(shared_from_this() == selection) { flags |= ImGuiTreeNodeFlags_Selected; } - if(ImGui::TreeNodeEx(std::format("{}##Hierarchy{:x}", drawID, mID).c_str(), flags)){ + bool opened = ImGui::TreeNodeEx(std::format("{}##Hierarchy{:x}", drawID, mID).c_str(), flags); + + if(ImGui::BeginPopupContextItem()){ + ImGui::Text("Add"); + ImGui::Separator(); + if(ImGui::Selectable("Pane")){ + mChildren.push_back(std::make_shared()); + } + if(ImGui::Selectable("Window")){ + mChildren.push_back(std::make_shared()); + } + if(ImGui::Selectable("Picture")){ + mChildren.push_back(std::make_shared()); + } + if(ImGui::Selectable("TextBox")){ + mChildren.push_back(std::make_shared()); + } + ImGui::EndPopup(); + } + + if(opened){ + if(ImGui::IsItemClicked()){ selection = shared_from_this(); } @@ -656,7 +1094,28 @@ void Textbox::DrawHierarchy(std::shared_ptr& selection) { ImGuiTreeNodeFlags flags = mChildren.size() == 0 ? ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_SpanFullWidth : ImGuiTreeNodeFlags_None; if(shared_from_this() == selection) { flags |= ImGuiTreeNodeFlags_Selected; } - if(ImGui::TreeNodeEx(std::format("{}##Hierarchy{:x}", drawID, mID).c_str(), flags)){ + + bool opened = ImGui::TreeNodeEx(std::format("{}##Hierarchy{:x}", drawID, mID).c_str(), flags); + + if(ImGui::BeginPopupContextItem()){ + ImGui::Text("Add"); + ImGui::Separator(); + if(ImGui::Selectable("Pane")){ + mChildren.push_back(std::make_shared()); + } + if(ImGui::Selectable("Window")){ + mChildren.push_back(std::make_shared()); + } + if(ImGui::Selectable("Picture")){ + mChildren.push_back(std::make_shared()); + } + if(ImGui::Selectable("TextBox")){ + mChildren.push_back(std::make_shared()); + } + ImGui::EndPopup(); + } + + if(opened){ if(ImGui::IsItemClicked()){ selection = shared_from_this(); } @@ -667,6 +1126,7 @@ void Textbox::DrawHierarchy(std::shared_ptr& selection) { ImGui::TreePop(); } + } } \ No newline at end of file diff --git a/src/io/BtiIO.cpp b/src/io/BtiIO.cpp index 203f91e..c03263f 100644 --- a/src/io/BtiIO.cpp +++ b/src/io/BtiIO.cpp @@ -38,6 +38,14 @@ uint16_t RGBA8toIA8(uint8_t r, uint8_t g, uint8_t b, uint8_t a){ return output; } +uint16_t RGBA8toRGB565(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + uint16_t color = 0x0000; + color |= ((r >> 3) & 0x1F) << 11; + color |= ((g >> 2) & 0x3F) << 5; + color |= ((b >> 3) & 0x1F) << 0; + return color; +} + uint16_t RGBA8toRGB5A3(uint8_t r, uint8_t g, uint8_t b, uint8_t a){ uint16_t color = 0x0000; if(a != 0xFF){ @@ -75,7 +83,6 @@ uint32_t RGB565toRGBA8(uint16_t data) { return output; } - uint32_t RGB5A3toRGBA8(uint16_t data) { uint8_t r, g, b, a; @@ -410,7 +417,40 @@ namespace Decode { namespace Encode { void CMPR(bStream::CStream* stream, uint16_t width, uint16_t height, std::vector& imageData){} - void RGB565(bStream::CStream* stream, uint16_t width, uint16_t height, std::vector& imageData){} + void RGB565(bStream::CStream* stream, uint16_t width, uint16_t height, std::vector& imageData){ + if (imageData.size() == 0) + return; + + uint32_t numBlocksW = width / 4; + uint32_t numBlocksH = height / 4; + + // Iterate the blocks in the image + for (int blockY = 0; blockY < numBlocksH; blockY++) { + for (int blockX = 0; blockX < numBlocksW; blockX++) { + // Iterate the pixels in the current block + for (int pixelY = 0; pixelY < 4; pixelY++) { + for (int pixelX = 0; pixelX < 4; pixelX++) { + // Bounds check to ensure the pixel is within the image. + if ((blockX * 4 + pixelX >= width) || (blockY * 4 + pixelY >= height)){ + stream->writeUInt16(0xFFFF); + continue; + } + + // RGB values for this pixel are stored in a 16-bit integer. + uint32_t destIndex = (width * ((blockY * 4) + pixelY) + (blockX * 4) + pixelX) * 4; + uint8_t r = imageData[destIndex]; + uint8_t g = imageData[destIndex + 1]; + uint8_t b = imageData[destIndex + 2]; + uint8_t a = imageData[destIndex + 3]; + + uint16_t rgb565 = ColorFormat::RGBA8toRGB565(r,g,b,a); + stream->writeUInt16(rgb565); + + } + } + } + } + } void RGB5A3(bStream::CStream* stream, uint16_t width, uint16_t height, std::vector& imageData){ if (imageData.size() == 0) @@ -426,8 +466,10 @@ namespace Encode { for (int pixelY = 0; pixelY < 4; pixelY++) { for (int pixelX = 0; pixelX < 4; pixelX++) { // Bounds check to ensure the pixel is within the image. - if ((blockX * 4 + pixelX >= width) || (blockY * 4 + pixelY >= height)) + if ((blockX * 4 + pixelX >= width) || (blockY * 4 + pixelY >= height)){ + stream->writeUInt16(0xFFFF); continue; + } // RGB values for this pixel are stored in a 16-bit integer. uint32_t destIndex = (width * ((blockY * 4) + pixelY) + (blockX * 4) + pixelX) * 4; @@ -603,7 +645,7 @@ void Bti::Save(bStream::CStream* stream, uint16_t width, uint16_t height, std::v //ImageFormat::Encode::RGB565(stream, mWidth, mHeight, imageData); break; case 0x05: - //ImageFormat::Encode::RGB5A3(stream, mWidth, mHeight, imageData); + ImageFormat::Encode::RGB5A3(stream, mWidth, mHeight, imageData); break; case 0x0E: //ImageFormat::Encode::CMPR(stream, mWidth, mHeight, imageData); diff --git a/src/modes/PathMode.cpp b/src/modes/PathMode.cpp index 513960b..27518ec 100644 --- a/src/modes/PathMode.cpp +++ b/src/modes/PathMode.cpp @@ -212,13 +212,13 @@ bool LPathMode::RenderPointContextMenu(std::shared_ptr path, std:: if (ImGui::Selectable("Insert Point Above")) { - ptrdiff_t index = LGenUtility::VectorIndexOf(path->Children, std::static_pointer_cast(point)); + std::ptrdiff_t index = LGenUtility::VectorIndexOf(path->Children, std::static_pointer_cast(point)); path->AddChildAtIndex(std::make_shared("Path Point"), index); } if (ImGui::Selectable("Insert Point Below")) { - ptrdiff_t index = LGenUtility::VectorIndexOf(path->Children, std::static_pointer_cast(point)); + std::ptrdiff_t index = LGenUtility::VectorIndexOf(path->Children, std::static_pointer_cast(point)); path->AddChildAtIndex(std::make_shared("Path Point"), index + 1); } diff --git a/src/ui/BloEditor.cpp b/src/ui/BloEditor.cpp index ba0084b..73b3cc0 100644 --- a/src/ui/BloEditor.cpp +++ b/src/ui/BloEditor.cpp @@ -1,29 +1,37 @@ #include "ResUtil.hpp" #include "Options.hpp" #include "ui/BloEditor.hpp" +#include "UIUtil.hpp" #include "imgui.h" #include #include +#include "ImGuiFileDialog/ImGuiFileDialog.h" +#include "io/BtiIO.hpp" +#include "stb_image.h" namespace BloEditor { -std::map> Menus { - { "Save Screen", {"Kawano/ENGLISH/res_save.szp", "save_2.blo", "timg"}}, - { "File Select", {"Kawano/ENGLISH/res_slct.szp", "blo/file_select_1.blo", "timg"}}, - { "Options Menu", {"Kawano/ENGLISH/res_slct.szp", "blo/option_1.blo", "timg"}}, - { "Area Complete 0", {"Kawano/ENGLISH/res_acnt.szp", "blo/adjustment_0.blo", "timg"}}, - { "Area Complete 1", {"Kawano/ENGLISH/res_acnt.szp", "blo/adjustment_1.blo", "timg"}}, - { "Area Complete 2", {"Kawano/ENGLISH/res_acnt.szp", "blo/adjustment_2.blo", "timg"}}, - { "Controls Explantion", {"Kawano/ENGLISH/res_cont.szp", "controller_2.blo", "timg"}}, - { "GBH Treasure", {"game", "kawano/list/blo/sgb_1.blo", "kawano/base/timg"}}, - { "Map Screen", {"game", "kawano/base/blo/map_1.blo", "kawano/base/timg"}}, - { "GBH Scan Menu", {"game", "kawano/base/blo/gbf_1.blo", "kawano/base/timg"}}, - { "GBH Border Menu", {"game", "kawano/base/blo/gbf_0.blo", "kawano/base/timg"}}, - { "Hidden Mansion Star", {"game", "kawano/base/blo/star_1.blo", "kawano/base/timg"}} +std::map> Menus { + { "Save Screen", {"Kawano/ENGLISH/res_save.szp", "save_2.blo", "timg", "font"}}, + { "File Select", {"Kawano/ENGLISH/res_slct.szp", "blo/file_select_1.blo", "timg", "font"}}, + { "New Game", {"Kawano/ENGLISH/res_slct.szp", "blo/file_select_2.blo", "timg", "font"}}, + { "Options Menu", {"Kawano/ENGLISH/res_slct.szp", "blo/option_1.blo", "timg", "font"}}, + { "Area Complete 0", {"Kawano/ENGLISH/res_acnt.szp", "blo/adjustment_0.blo", "timg", "font"}}, + { "Area Complete 1", {"Kawano/ENGLISH/res_acnt.szp", "blo/adjustment_1.blo", "timg", "font"}}, + { "Area Complete 2", {"Kawano/ENGLISH/res_acnt.szp", "blo/adjustment_2.blo", "timg", "font"}}, + { "Controls Explantion", {"Kawano/ENGLISH/res_cont.szp", "controller_2.blo", "timg", "font"}}, + { "GBH Treasure", {"game", "kawano/list/blo/sgb_1.blo", "kawano/base/timg", "kawano/base/font"}}, + { "Map Screen", {"game", "kawano/base/blo/map_1.blo", "kawano/base/timg", "kawano/base/font"}}, + { "GBH Scan Menu", {"game", "kawano/base/blo/gbf_1.blo", "kawano/base/timg", "kawano/base/font"}}, + { "GBH Border Menu", {"game", "kawano/base/blo/gbf_0.blo", "kawano/base/timg", "kawano/base/font"}}, + { "Hidden Mansion Star", {"game", "kawano/base/blo/star_1.blo", "kawano/base/timg", "kawano/base/font"}} }; bool BloEditorOpen { false }; std::string MenuSelected = "Save Screen"; +std::shared_ptr MenuArc { nullptr }; +std::shared_ptr ImageFolder { nullptr }; +std::shared_ptr FontFolder { nullptr }; std::shared_ptr ScreenLoaded { nullptr }; std::shared_ptr SelectedNode { nullptr }; std::shared_ptr DraggingNode { nullptr }; @@ -44,12 +52,34 @@ void Render(){ std::filesystem::path menuResPath = std::get<0>(menuInfo); std::string menuBloPath = std::get<1>(menuInfo); std::string menuTimgPath = std::get<2>(menuInfo); + std::string menuFontPath = std::get<3>(menuInfo); std::shared_ptr menuarc = Archive::Rarc::Create(); bStream::CFileStream fstrm((std::filesystem::path(OPTIONS.mRootPath) / "files" / menuResPath).string(), bStream::Endianess::Big, bStream::OpenMode::In); if(menuarc->Load(&fstrm)){ auto file = menuarc->GetFile(menuBloPath); auto timg = menuarc->GetFolder(menuTimgPath); + + MenuArc = menuarc; + + ImageFolder = timg; + if(ImageFolder == nullptr){ + ImageFolder = Archive::Folder::Create(MenuArc); + ImageFolder->SetName("timg"); + + auto parent = MenuArc->GetFolder(std::filesystem::path(menuTimgPath).parent_path()); + if(parent != nullptr) parent->AddSubdirectory(ImageFolder); + } + + FontFolder = menuarc->GetFolder(menuFontPath); + if(FontFolder == nullptr){ + FontFolder = Archive::Folder::Create(MenuArc); + FontFolder->SetName("tfon"); + + auto parent = MenuArc->GetFolder(std::filesystem::path(menuTimgPath).parent_path()); + if(parent != nullptr) parent->AddSubdirectory(FontFolder); + } + bStream::CMemoryStream stream(file->GetData(), file->GetSize(), bStream::Endianess::Big, bStream::OpenMode::In); ScreenLoaded->Load(&stream, timg); } @@ -67,6 +97,8 @@ void Render(){ if(menuResPath.string() == "game" && GCResourceManager.mLoadedGameArchive){ auto file = GCResourceManager.mGameArchive->GetFile(menuBloPath); auto timg = GCResourceManager.mGameArchive->GetFolder(menuTimgPath); + ImageFolder = timg; + MenuArc = GCResourceManager.mGameArchive; bStream::CMemoryStream stream(file->GetData(), file->GetSize(), bStream::Endianess::Big, bStream::OpenMode::In); ScreenLoaded->Load(&stream, timg); } else if(menuResPath.string() != "game") { @@ -75,9 +107,11 @@ void Render(){ if(menuarc->Load(&fstrm)){ auto file = menuarc->GetFile(menuBloPath); auto timg = menuarc->GetFolder(menuTimgPath); + ImageFolder = timg; bStream::CMemoryStream stream(file->GetData(), file->GetSize(), bStream::Endianess::Big, bStream::OpenMode::In); ScreenLoaded->Load(&stream, timg); } + MenuArc = menuarc; } } } @@ -101,16 +135,27 @@ void Render(){ if(menuResPath.string() == "game" && GCResourceManager.mLoadedGameArchive){ auto file = GCResourceManager.mGameArchive->GetFile(menuBloPath); auto timg = GCResourceManager.mGameArchive->GetFolder(menuTimgPath); - bStream::CMemoryStream stream(file->GetData(), file->GetSize(), bStream::Endianess::Big, bStream::OpenMode::In); + + bStream::CMemoryStream stream(64, bStream::Endianess::Big, bStream::OpenMode::In); ScreenLoaded->Save(&stream); + file->SetData(stream.getBuffer(), stream.getSize()); + std::filesystem::path gameArcPath = std::filesystem::path(OPTIONS.mRootPath) / "files" / "Game" / "game_usa.szp"; + GCResourceManager.mGameArchive->SaveToFile(gameArcPath.string(), Compression::Format::YAY0); + } else if(menuResPath.string() != "game") { - std::shared_ptr menuarc = Archive::Rarc::Create(); - bStream::CFileStream fstrm((std::filesystem::path(OPTIONS.mRootPath) / "files" / menuResPath).string(), bStream::Endianess::Big, bStream::OpenMode::In); - if(menuarc->Load(&fstrm)){ - auto file = menuarc->GetFile(menuBloPath); - auto timg = menuarc->GetFolder(menuTimgPath); - bStream::CMemoryStream stream(file->GetData(), file->GetSize(), bStream::Endianess::Big, bStream::OpenMode::In); + if(MenuArc != nullptr){ + auto file = MenuArc->GetFile(menuBloPath); + auto timg = MenuArc->GetFolder(menuTimgPath); + + bStream::CMemoryStream stream(64, bStream::Endianess::Big, bStream::OpenMode::In); ScreenLoaded->Save(&stream); + + LGenUtility::Log << "Finished Saving BLO, writing to archive..." << std::endl; + + file->SetData(stream.getBuffer(), stream.getSize()); + + MenuArc->SaveToFile((std::filesystem::path(OPTIONS.mRootPath) / "files" / menuResPath).string(), Compression::Format::YAY0, 0, true); + } } @@ -129,30 +174,165 @@ void Render(){ ScreenLoaded->Draw(DraggingNode); if(DraggingNode != nullptr && DragStart.x == 0 && DragStart.y == 0){ SelectedNode = DraggingNode; - DragStart = {DraggingNode->mRect[0], DraggingNode->mRect[1]}; + if(ImGui::IsKeyDown(ImGuiKey_LeftShift)){ + DragStart = {DraggingNode->mRect[2], DraggingNode->mRect[3]}; + } else { + DragStart = {DraggingNode->mRect[0], DraggingNode->mRect[1]}; + } } ImGui::SameLine(); ImGui::BeginChild("##leftHandWindow"); - ImGui::BeginChild("##hierarchy", {ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y*0.5f}); + ImGui::Text("Resources"); + ImGui::Separator(); + ImGui::BeginChild("##resView", {ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y*0.15f}); + if(ImGui::TreeNode("Images (timg)")){ + std::shared_ptr toDelete = nullptr; + for(auto file : ImageFolder->GetFiles()){ + ImGui::Text(file->GetName().data()); + if(ImGui::BeginPopupContextItem(std::format("##rm{}", file->GetName()).c_str())){ + if(ImGui::Selectable("Remove")){ + toDelete = file; + } + ImGui::EndPopup(); + } + } + if(toDelete != nullptr){ + std::erase(ImageFolder->GetFiles(), toDelete); + } + ImGui::Text(ICON_FK_PLUS_CIRCLE); + if(ImGui::IsItemClicked(0) && MenuArc != nullptr){ + ImGuiFileDialog::Instance()->OpenModal("ImportBloPNG", "Import PNG", "PNG Image (*.png){.png}", std::filesystem::current_path().string()); + ImGui::CloseCurrentPopup(); + } + ImGui::TreePop(); + } + + if(ImGui::TreeNode("Fonts (font)")){ + std::shared_ptr toDelete = nullptr; + for(auto file : FontFolder->GetFiles()){ + ImGui::Text(file->GetName().data()); + if(ImGui::BeginPopupContextItem(std::format("##rm{}", file->GetName()).c_str())){ + if(ImGui::Selectable("Remove")){ + toDelete = file; + } + ImGui::EndPopup(); + } + } + if(toDelete != nullptr){ + std::erase(FontFolder->GetFiles(), toDelete); + } + ImGui::Text(ICON_FK_PLUS_CIRCLE); + if(ImGui::IsItemClicked(0) && MenuArc != nullptr){ + ImGuiFileDialog::Instance()->OpenModal("ImportBfnFont", "Import BFN", "BFN Font (*.bfn){.bfn}", std::filesystem::current_path().string()); + ImGui::CloseCurrentPopup(); + } + ImGui::TreePop(); + } + + ImGui::EndChild(); + ImGui::Text("Screen Elements"); + ImGui::Separator(); + ImGui::BeginChild("##hierarchy", {ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y*(0.5f-0.15f)}); ScreenLoaded->DrawHierarchy(SelectedNode); ImGui::EndChild(); + ImGui::BeginChild("##properties", {ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y}); ImGui::Text("Selected"); ImGui::Separator(); if(SelectedNode != nullptr){ - if(SelectedNode->Type() == Blo::ElementType::Picture){ - ImGui::Text(std::format("Texture Path {}", std::reinterpret_pointer_cast(SelectedNode)->GetTexture()->mPath).c_str()); - } else if(SelectedNode->Type() == Blo::ElementType::Picture){ - + std::vector id(4, '\0'); + std::memcpy(id.data(), &SelectedNode->mID, sizeof(uint32_t)); + std::reverse(id.begin(), id.end()); + id.push_back('\0'); + + if(ImGui::InputText("##idEdit", id.data(), id.size())){ + id.pop_back(); + std::reverse(id.begin(), id.end()); + std::memcpy(&SelectedNode->mID, id.data(), sizeof(uint32_t)); + } + + ImGui::Text("Rect"); + int x = SelectedNode->mRect[0], y = SelectedNode->mRect[1], w = SelectedNode->mRect[2], h = SelectedNode->mRect[3]; + ImGui::InputInt("x", &x); + ImGui::InputInt("y", &y); + ImGui::InputInt("w", &w); + ImGui::InputInt("h", &h); + SelectedNode->mRect[0] = x, SelectedNode->mRect[1] = y, SelectedNode->mRect[2] = w, SelectedNode->mRect[3] = h; + + switch (SelectedNode->Type()) { + case Blo::ElementType::Picture: { + auto node = std::reinterpret_pointer_cast(SelectedNode); + if(LUIUtility::RenderTextInput("Texture Name", &node->GetTexture()->mPath)){ + std::shared_ptr imgFile = ImageFolder->GetFile(node->GetTexture()->mPath); + + if(imgFile == nullptr){ + std::string lowerPath = node->GetTexture()->mPath; + std::transform(lowerPath.begin(), lowerPath.end(), lowerPath.begin(), [](unsigned char c){ return std::tolower(c); }); + imgFile = ImageFolder->GetFile(lowerPath); + } + + if(imgFile != nullptr){ + //std::cout << "Loading Image " << mPath << std::endl; + bStream::CMemoryStream img(imgFile->GetData(), imgFile->GetSize(), bStream::Endianess::Big, bStream::OpenMode::In); + node->GetTexture()->mTexture.Load(&img); + node->SetWidth(node->GetTexture()->mTexture.mWidth); + node->SetHeight(node->GetTexture()->mTexture.mHeight); + + glCreateTextures(GL_TEXTURE_2D, 1, &node->GetTexture()->mTextureID); + glTextureParameteri(node->GetTexture()->mTextureID, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTextureParameteri(node->GetTexture()->mTextureID, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTextureParameteri(node->GetTexture()->mTextureID, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTextureParameteri(node->GetTexture()->mTextureID, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTextureStorage2D(node->GetTexture()->mTextureID, 1, GL_RGBA8, node->GetTexture()->mTexture.mWidth, node->GetTexture()->mTexture.mHeight); + glTextureSubImage2D(node->GetTexture()->mTextureID, 0, 0, 0, node->GetTexture()->mTexture.mWidth, node->GetTexture()->mTexture.mHeight, GL_RGBA, GL_UNSIGNED_BYTE, node->GetTexture()->mTexture.GetData()); + } else { + node->GetTexture()->mTextureID = 0xFFFFFFFF; + LGenUtility::Log << "Couldn't load image resource " << node->GetTexture()->mPath << std::endl; + } + } + Blo::ResourceType type = (Blo::ResourceType)node->GetTexture()->mType; + if(LUIUtility::RenderComboEnum("Resource Type", type)){ + node->GetTexture()->mType = (uint8_t)type; + } + break; + } + case Blo::ElementType::Textbox: { + auto node = std::reinterpret_pointer_cast(SelectedNode); + LUIUtility::RenderTextInput("Font Path ", &node->GetFont()->mPath); + LUIUtility::RenderTextInput("Text ", node->GetText()); + Blo::ResourceType type = (Blo::ResourceType)node->GetFont()->mType; + if(LUIUtility::RenderComboEnum("Resource Type", type)){ + node->GetFont()->mType = (uint8_t)type; + } + + break; + } + case Blo::ElementType::Window: { + auto node = std::reinterpret_pointer_cast(SelectedNode); + LUIUtility::RenderTextInput("Texture 1 Name", &node->GetTexture(0)->mPath); + LUIUtility::RenderTextInput("Texture 2 Name", &node->GetTexture(1)->mPath); + LUIUtility::RenderTextInput("Texture 3 Name", &node->GetTexture(2)->mPath); + LUIUtility::RenderTextInput("Texture 4 Name", &node->GetTexture(3)->mPath); + break; + } + default: + break; } } ImGui::EndChild(); + ImGui::EndChild(); } if(DraggingNode != nullptr){ auto delta = ImGui::GetMouseDragDelta(); - DraggingNode->mRect[0] = DragStart.x + delta.x; - DraggingNode->mRect[1] = DragStart.y + delta.y; - if(ImGui::IsMouseReleased(0)){ + if(ImGui::IsKeyDown(ImGuiKey_LeftShift)){ + DraggingNode->mRect[2] = DragStart.x + delta.x; + DraggingNode->mRect[3] = DragStart.y + delta.y; + } else { + DraggingNode->mRect[0] = DragStart.x + delta.x; + DraggingNode->mRect[1] = DragStart.y + delta.y; + } + + if(ImGui::IsMouseReleased(0) || ImGui::IsKeyReleased(ImGuiKey_LeftShift) || ImGui::IsKeyPressed(ImGuiKey_LeftShift)){ DraggingNode = nullptr; DragStart = {0, 0}; } @@ -160,6 +340,43 @@ void Render(){ ImGui::EndPopup(); } + + std::string resPath; + if(LUIUtility::RenderFileDialog("ImportBloPNG", resPath)){ + Bti img; + img.SetFormat(0x05); + + int x,y,n; + unsigned char* imgBuffer = stbi_load(resPath.c_str(), &x, &y, &n, 0); + + std::vector imgData(x*y*n); + std::memcpy(imgData.data(), imgBuffer, x*y*n); + stbi_image_free(imgBuffer); + + bStream::CMemoryStream file(0x20 + (x * y), bStream::Endianess::Big, bStream::OpenMode::Out); + img.Save(&file, x, y, imgData); + + std::shared_ptr newImg = Archive::File::Create(); + newImg->SetData(file.getBuffer(), file.getSize()); + newImg->SetName(std::filesystem::path(resPath).stem().replace_extension(".bti").string()); + ImageFolder->AddFile(newImg); + + ImGui::OpenPopup("BloEditorTool"); + } + + if(LUIUtility::RenderFileDialog("ImportBfnFont", resPath)){ + std::shared_ptr newImg = Archive::File::Create(); + + bStream::CFileStream file(resPath, bStream::Endianess::Big, bStream::OpenMode::In); + + std::vector fileData(file.getSize()); + file.readBytesTo(fileData.data(), fileData.size()); + + newImg->SetData(fileData.data(), fileData.size()); + newImg->SetName(std::filesystem::path(resPath).filename().string()); + ImageFolder->AddFile(newImg); + ImGui::OpenPopup("BloEditorTool"); + } } } \ No newline at end of file