├── main.h ├── icon1.ico ├── tests.cpp ├── Resource.rc ├── resource.h ├── tests.h ├── resources ├── logo.png ├── sword.dff ├── sphere.dff ├── HelpMarker.png ├── SpawnStar.dff ├── ToolbarIcons.png ├── UbuntuMono-R.ttf ├── Beacons_Other.json ├── Beacons_Arthur.json ├── Beacons_Olympic.json ├── Beacons_XXL2.json ├── Events_XXL2.json ├── Encyclopedia.json ├── Beacons_XXL1.json ├── Properties_Common.json ├── Events_XXL2Plus.json └── UbuntuMono-LICENCE.txt ├── docs ├── screenshot01.jpg ├── screenshot02.jpg └── screenshot03.jpg ├── EditorUI ├── IGMisc.h ├── IGAbout.h ├── IGCameraEditor.h ├── IGCloneEditor.h ├── IGEventEditor.h ├── IGGroundEditor.h ├── IGHookEditor.h ├── IGLineEditor.h ├── IGMarkerEditor.h ├── IGObjectList.h ├── IGSekensEditor.h ├── IGSoundEditor.h ├── IGCounterEditor.h ├── IGTextureEditor.h ├── IGTriggerEditor.h ├── IGCinematicEditor.h ├── IGCollisionEditor.h ├── IGLevelInfoEditor.h ├── IGObjectInspector.h ├── PropFlagsEditor.h ├── IGAnimationViewer.h ├── IGSquadEditor.h ├── IGDetectorEditor.h ├── IGMusicEditor.h ├── IGPathfindingEditor.h ├── IGSceneNodeEditor.h ├── IGBeaconEditor.h ├── EditorUtils.h ├── DictionaryEditors.h ├── GeoUtils.h ├── IGAbout.cpp ├── EditorUtils.cpp ├── ImGuiMemberListener.h ├── IGMarkerEditor.cpp ├── EditorWidgets.h ├── IGObjectInspector.cpp ├── IGObjectList.cpp ├── IGCounterEditor.cpp ├── PropFlagsEditor.cpp ├── IGLineEditor.cpp ├── IGCloneEditor.cpp ├── IGCollisionEditor.cpp └── IGSoundEditor.cpp ├── stb_implementation.cpp ├── FatalError.h ├── NatvisFile.natvis ├── HexEditor.h ├── NodeCollisionUtils.h ├── imguiimpl.h ├── Camera.h ├── ClassRegister.h ├── GameLauncher.h ├── Camera.cpp ├── FatalError.cpp ├── CKPartlyUnknown.h ├── WavDocument.h ├── CoreClasses ├── IKRenderable.h ├── IKRenderable.cpp ├── CKGeometry.h ├── CKDictionary.h ├── CKHook.h ├── CKComponent.h ├── CKManager.h ├── CKComponent.cpp └── CKCamera.h ├── Image.h ├── LocaleEditor.h ├── GamePatcher.h ├── HomeInterface.h ├── Encyclopedia.h ├── LICENSE ├── GuiUtils.h ├── imgui ├── LICENSE_imnodes.md ├── imnodes_LICENSE.md ├── LICENSE.txt └── imgui.vcxproj.filters ├── Events.h ├── window.h ├── adpcm-xq ├── adpcm-xq.vcxproj.filters ├── license.txt └── adpcm-lib.h ├── GroundRenderer.h ├── DynArray.h ├── rwrenderer.h ├── KObject.cpp ├── Shape.h ├── KLocalObject.cpp ├── CKLocalObjectSubs.h ├── WavDocument.cpp ├── KLocalObject.h ├── renderer.h ├── Duplicator.h ├── Events.cpp ├── .gitattributes ├── XXL-Editor.sln ├── GameLauncher.cpp ├── File.h ├── rwrenderer.cpp ├── README.md ├── File.cpp ├── imguiimpl.cpp ├── Image.cpp ├── GamePatcherKClasses.h ├── Shape.cpp ├── rwsound.h ├── vecmat.h ├── rwext.h ├── window.cpp ├── Encyclopedia.cpp └── GroundRenderer.cpp /main.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Can be removed -------------------------------------------------------------------------------- /icon1.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdrienTD/XXL-Editor/HEAD/icon1.ico -------------------------------------------------------------------------------- /tests.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdrienTD/XXL-Editor/HEAD/tests.cpp -------------------------------------------------------------------------------- /Resource.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdrienTD/XXL-Editor/HEAD/Resource.rc -------------------------------------------------------------------------------- /resource.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdrienTD/XXL-Editor/HEAD/resource.h -------------------------------------------------------------------------------- /tests.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace Tests { 4 | void TestPrompt(); 5 | } -------------------------------------------------------------------------------- /resources/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdrienTD/XXL-Editor/HEAD/resources/logo.png -------------------------------------------------------------------------------- /resources/sword.dff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdrienTD/XXL-Editor/HEAD/resources/sword.dff -------------------------------------------------------------------------------- /docs/screenshot01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdrienTD/XXL-Editor/HEAD/docs/screenshot01.jpg -------------------------------------------------------------------------------- /docs/screenshot02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdrienTD/XXL-Editor/HEAD/docs/screenshot02.jpg -------------------------------------------------------------------------------- /docs/screenshot03.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdrienTD/XXL-Editor/HEAD/docs/screenshot03.jpg -------------------------------------------------------------------------------- /resources/sphere.dff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdrienTD/XXL-Editor/HEAD/resources/sphere.dff -------------------------------------------------------------------------------- /resources/HelpMarker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdrienTD/XXL-Editor/HEAD/resources/HelpMarker.png -------------------------------------------------------------------------------- /resources/SpawnStar.dff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdrienTD/XXL-Editor/HEAD/resources/SpawnStar.dff -------------------------------------------------------------------------------- /resources/ToolbarIcons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdrienTD/XXL-Editor/HEAD/resources/ToolbarIcons.png -------------------------------------------------------------------------------- /resources/UbuntuMono-R.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdrienTD/XXL-Editor/HEAD/resources/UbuntuMono-R.ttf -------------------------------------------------------------------------------- /EditorUI/IGMisc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace EditorUI 4 | { 5 | struct EditorInterface; 6 | 7 | void IGMisc(EditorInterface& ui); 8 | } -------------------------------------------------------------------------------- /EditorUI/IGAbout.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace EditorUI 4 | { 5 | struct EditorInterface; 6 | 7 | void IGAbout(EditorInterface& ui); 8 | } -------------------------------------------------------------------------------- /EditorUI/IGCameraEditor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace EditorUI 4 | { 5 | struct EditorInterface; 6 | 7 | void IGCameraEditor(EditorInterface& ui); 8 | } -------------------------------------------------------------------------------- /EditorUI/IGCloneEditor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace EditorUI 4 | { 5 | struct EditorInterface; 6 | 7 | void IGCloneEditor(EditorInterface& ui); 8 | } -------------------------------------------------------------------------------- /EditorUI/IGEventEditor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace EditorUI 4 | { 5 | struct EditorInterface; 6 | 7 | void IGEventEditor(EditorInterface& ui); 8 | } -------------------------------------------------------------------------------- /EditorUI/IGGroundEditor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace EditorUI 4 | { 5 | struct EditorInterface; 6 | 7 | void IGGroundEditor(EditorInterface& ui); 8 | } -------------------------------------------------------------------------------- /EditorUI/IGHookEditor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace EditorUI 4 | { 5 | struct EditorInterface; 6 | 7 | void IGHookEditor(EditorInterface& ui); 8 | } -------------------------------------------------------------------------------- /EditorUI/IGLineEditor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace EditorUI 4 | { 5 | struct EditorInterface; 6 | 7 | void IGLineEditor(EditorInterface& ui); 8 | } -------------------------------------------------------------------------------- /EditorUI/IGMarkerEditor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace EditorUI 4 | { 5 | struct EditorInterface; 6 | 7 | void IGMarkerEditor(EditorInterface& ui); 8 | } -------------------------------------------------------------------------------- /EditorUI/IGObjectList.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace EditorUI 4 | { 5 | struct EditorInterface; 6 | 7 | void IGObjectList(EditorInterface& ui); 8 | } -------------------------------------------------------------------------------- /EditorUI/IGSekensEditor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace EditorUI 4 | { 5 | struct EditorInterface; 6 | 7 | void IGSekensEditor(EditorInterface& ui); 8 | } -------------------------------------------------------------------------------- /EditorUI/IGSoundEditor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace EditorUI 4 | { 5 | struct EditorInterface; 6 | 7 | void IGSoundEditor(EditorInterface& ui); 8 | } -------------------------------------------------------------------------------- /EditorUI/IGCounterEditor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace EditorUI 4 | { 5 | struct EditorInterface; 6 | 7 | void IGCounterEditor(EditorInterface& ui); 8 | } -------------------------------------------------------------------------------- /EditorUI/IGTextureEditor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace EditorUI 4 | { 5 | struct EditorInterface; 6 | 7 | void IGTextureEditor(EditorInterface& ui); 8 | } -------------------------------------------------------------------------------- /EditorUI/IGTriggerEditor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace EditorUI 4 | { 5 | struct EditorInterface; 6 | 7 | void IGTriggerEditor(EditorInterface& ui); 8 | } -------------------------------------------------------------------------------- /EditorUI/IGCinematicEditor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace EditorUI 4 | { 5 | struct EditorInterface; 6 | 7 | void IGCinematicEditor(EditorInterface& ui); 8 | } -------------------------------------------------------------------------------- /EditorUI/IGCollisionEditor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace EditorUI 4 | { 5 | struct EditorInterface; 6 | 7 | void IGCollisionEditor(EditorInterface& ui); 8 | } -------------------------------------------------------------------------------- /EditorUI/IGLevelInfoEditor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace EditorUI 4 | { 5 | struct EditorInterface; 6 | 7 | void IGLevelInfoEditor(EditorInterface& ui); 8 | } -------------------------------------------------------------------------------- /EditorUI/IGObjectInspector.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace EditorUI 4 | { 5 | struct EditorInterface; 6 | 7 | void IGObjectInspector(EditorInterface& ui); 8 | } -------------------------------------------------------------------------------- /resources/Beacons_Other.json: -------------------------------------------------------------------------------- 1 | { 2 | "beacons": { 3 | "5": "Simple", 4 | "39": { 5 | "name": "Marker", 6 | "orientable": true 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /EditorUI/PropFlagsEditor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | bool PropFlagsEditor(unsigned int& flagsValue, const nlohmann::json& flagsInfo); -------------------------------------------------------------------------------- /stb_implementation.cpp: -------------------------------------------------------------------------------- 1 | #define _CRT_SECURE_NO_WARNINGS 2 | #define STB_IMAGE_IMPLEMENTATION 3 | #include 4 | #define STB_IMAGE_WRITE_IMPLEMENTATION 5 | #include 6 | -------------------------------------------------------------------------------- /EditorUI/IGAnimationViewer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace EditorUI 4 | { 5 | struct EditorInterface; 6 | 7 | void IGAnimationViewer(EditorInterface& ui); 8 | void RenderAnimation(EditorInterface& ui); 9 | } -------------------------------------------------------------------------------- /EditorUI/IGSquadEditor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace EditorUI 4 | { 5 | struct EditorInterface; 6 | 7 | void IGSquadEditorXXL1(EditorInterface& ui); 8 | void IGSquadEditorXXL2Plus(EditorInterface& ui); 9 | } -------------------------------------------------------------------------------- /EditorUI/IGDetectorEditor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace EditorUI 4 | { 5 | struct EditorInterface; 6 | 7 | void IGDetectorEditorXXL1(EditorInterface& ui); 8 | void IGDetectorEditorXXL2Plus(EditorInterface& ui); 9 | } -------------------------------------------------------------------------------- /FatalError.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | [[noreturn]] void FatalError(const wchar_t* message); 6 | [[noreturn]] void FatalError(const std::wstring& message); 7 | [[noreturn]] void FatalErrorCFormat(const wchar_t* message, ...); 8 | -------------------------------------------------------------------------------- /resources/Beacons_Arthur.json: -------------------------------------------------------------------------------- 1 | { 2 | "beacons": { 3 | "5": "Simple", 4 | "39": { 5 | "name": "Marker", 6 | "orientable": true 7 | }, 8 | "45": "Egg", 9 | "46": "Burried Surprise", 10 | "61": "Eggbag" 11 | } 12 | } -------------------------------------------------------------------------------- /EditorUI/IGMusicEditor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace EditorUI 4 | { 5 | struct EditorInterface; 6 | 7 | void IGMusicEditor(EditorInterface& ui); 8 | void IGPlayListEditor(EditorInterface& ui); 9 | void IGAudioStreamEditor(EditorInterface& ui); 10 | } -------------------------------------------------------------------------------- /NatvisFile.natvis: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | _pointer 5 | 6 | -------------------------------------------------------------------------------- /EditorUI/IGPathfindingEditor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | struct ImVec4; 6 | 7 | namespace EditorUI 8 | { 9 | struct EditorInterface; 10 | 11 | ImVec4 getPFCellColor(uint8_t val); 12 | void IGPathfindingEditor(EditorInterface& ui); 13 | } -------------------------------------------------------------------------------- /EditorUI/IGSceneNodeEditor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace EditorUI 4 | { 5 | struct EditorInterface; 6 | 7 | void IGSceneNodeEditor(EditorInterface& ui); 8 | 9 | void IGSceneGraph(EditorInterface& ui); 10 | void IGSceneNodeProperties(EditorInterface& ui); 11 | } -------------------------------------------------------------------------------- /HexEditor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | struct Window; 6 | struct Renderer; 7 | 8 | void HexEditorUI(const std::string& gamePath, const std::string& outGamePath, int gameVersion, int platform, bool isRemaster, int initlevel, Window& window, Renderer* gfx); 9 | -------------------------------------------------------------------------------- /EditorUI/IGBeaconEditor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "KEnvironment.h" 4 | 5 | namespace EditorUI 6 | { 7 | struct EditorInterface; 8 | 9 | const int maxGameSupportingAdvancedBeaconEditing = KEnvironment::KVERSION_XXL1; 10 | 11 | void IGBeaconEditor(EditorInterface& ui); 12 | } -------------------------------------------------------------------------------- /NodeCollisionUtils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct KEnvironment; 4 | struct CKObject; 5 | struct CKBoundingShape; 6 | 7 | namespace NodeCollisionUtils 8 | { 9 | void LoadNodeCollisionInfo(int gameVersion); 10 | void CreateCollisionsForObject(KEnvironment& kenv, CKObject* owner); 11 | } -------------------------------------------------------------------------------- /imguiimpl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct Renderer; 4 | struct Window; 5 | 6 | extern const char *g_imguiFontFile; 7 | extern float g_imguiFontSize; 8 | 9 | void ImGuiImpl_Init(Window *window); 10 | void ImGuiImpl_CreateFontsTexture(Renderer *gfx); 11 | void ImGuiImpl_NewFrame(Window *window); 12 | void ImGuiImpl_Render(Renderer *gfx); 13 | -------------------------------------------------------------------------------- /EditorUI/EditorUtils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | struct RwSound; 6 | struct WavDocument; 7 | 8 | namespace EditorUI 9 | { 10 | void InitSnd(int freq, int numChannels, bool byteSwapped); 11 | void PlaySnd(const RwSound& snd); 12 | void PlaySnd(const WavDocument& wav); 13 | 14 | float decode8bitAngle(uint8_t byte); 15 | } -------------------------------------------------------------------------------- /EditorUI/DictionaryEditors.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct CAnimationDictionary; 4 | struct CKSoundDictionaryID; 5 | 6 | namespace EditorUI 7 | { 8 | struct EditorInterface; 9 | 10 | void AnimDictEditor(EditorInterface& ui, CAnimationDictionary* animDict, bool showHeader = true); 11 | void SoundDictIDEditor(EditorInterface& ui, CKSoundDictionaryID* sndDictID, bool showHeader = true); 12 | } -------------------------------------------------------------------------------- /Camera.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "vecmat.h" 4 | 5 | struct Camera { 6 | Vector3 position, orientation; 7 | float aspect, nearDist = 0.1f, farDist = 1000.0f; 8 | bool orthoMode = false; 9 | Matrix sceneMatrix, viewMatrix, projMatrix; 10 | Vector3 direction; 11 | void updateMatrix(); 12 | Camera() = default; 13 | Camera(const Vector3 &position, const Vector3 &orientation) : position(position), orientation(orientation) {} 14 | }; -------------------------------------------------------------------------------- /resources/Beacons_Olympic.json: -------------------------------------------------------------------------------- 1 | { 2 | "beacons": { 3 | "5": "Simple", 4 | "38": "Enemy spawn", 5 | "39": { 6 | "name": "Marker", 7 | "orientable": true 8 | }, 9 | "46": "Burried Surprise", 10 | "64": "Helmet", 11 | "65": "Golden Helmet", 12 | "66": "Glue", 13 | "67": "Powder", 14 | "68": "x3 Multiplier", 15 | "69": "x10 Multiplier", 16 | "70": "Ham", 17 | "71": "Shield", 18 | "72": "Potion", 19 | "73": "Bird Cage" 20 | } 21 | } -------------------------------------------------------------------------------- /ClassRegister.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct KEnvironment; 4 | 5 | namespace ClassRegister { 6 | void registerClassesForXXL1PC(KEnvironment& kenv); 7 | void registerClassesForXXL1PS2(KEnvironment& kenv); 8 | void registerClassesForXXL2PlusPC(KEnvironment& kenv); 9 | void registerClassesForXXL2(KEnvironment& kenv); 10 | void registerClassesForOlympic(KEnvironment& kenv); 11 | void registerClassesForSpyro(KEnvironment& kenv); 12 | void registerClasses(KEnvironment& kenv, int gameVersion, int gamePlatform, bool isRemaster); 13 | }; -------------------------------------------------------------------------------- /EditorUI/GeoUtils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | struct RwClump; 7 | struct RwGeometry; 8 | struct RwExtHAnim; 9 | struct KEnvironment; 10 | struct CNode; 11 | 12 | namespace GeoUtils 13 | { 14 | std::unique_ptr LoadDFF(const std::filesystem::path& filename); 15 | RwClump CreateClumpFromGeo(std::shared_ptr rwgeo, RwExtHAnim* hanim = nullptr); 16 | RwGeometry createEmptyGeo(); 17 | void ChangeNodeGeometry(KEnvironment& kenv, CNode* geonode, RwGeometry** rwgeos, size_t numRwgeos); // TODO use span 18 | } -------------------------------------------------------------------------------- /GameLauncher.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | struct GameLauncher { 7 | std::string modulePath, gamePath; 8 | int version; 9 | 10 | void *processHandle = nullptr; 11 | void *threadHandle = nullptr; 12 | void *windowHandle = nullptr; 13 | 14 | GameLauncher(const std::string& modulePath, const std::string& gamePath, int version) : 15 | modulePath(modulePath), gamePath(gamePath), version(version) {} 16 | 17 | bool openGame(); 18 | void closeGame(); 19 | bool isGameRunning(); 20 | bool loadLevel(uint32_t lvlNum); 21 | }; -------------------------------------------------------------------------------- /Camera.cpp: -------------------------------------------------------------------------------- 1 | #include "Camera.h" 2 | #include "window.h" 3 | 4 | void Camera::updateMatrix() { 5 | this->direction = Vector3( 6 | -sin(orientation.y)*cos(orientation.x), 7 | sin(orientation.x), 8 | cos(orientation.y)*cos(orientation.x) 9 | ); 10 | if (orthoMode) 11 | projMatrix = Matrix::getRHOrthoMatrix(position.y*aspect, position.y, nearDist, farDist); 12 | else 13 | projMatrix = Matrix::getRHPerspectiveMatrix(0.9f, aspect, nearDist, farDist); 14 | viewMatrix = Matrix::getRHLookAtViewMatrix(position, position + direction, Vector3(0, 1, 0)); 15 | this->sceneMatrix = viewMatrix * projMatrix; 16 | } -------------------------------------------------------------------------------- /FatalError.cpp: -------------------------------------------------------------------------------- 1 | #include "FatalError.h" 2 | #define WIN32_LEAN_AND_MEAN 3 | #include 4 | #include 5 | 6 | void FatalError(const wchar_t* message) 7 | { 8 | MessageBoxW(NULL, message, L"XXL Editor Failure", 16); 9 | #ifndef XEC_RELEASE 10 | DebugBreak(); 11 | #endif 12 | exit(-3); 13 | } 14 | 15 | void FatalError(const std::wstring& message) 16 | { 17 | FatalError(message.c_str()); 18 | } 19 | 20 | void FatalErrorCFormat(const wchar_t* message, ...) 21 | { 22 | va_list args; 23 | va_start(args, message); 24 | wchar_t buffer[1024]; 25 | vswprintf_s(buffer, message, args); 26 | FatalError(buffer); 27 | va_end(args); 28 | } 29 | -------------------------------------------------------------------------------- /CKPartlyUnknown.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "KObject.h" 4 | #include "File.h" 5 | #include 6 | 7 | template struct CKPartlyUnknown : CKSubclass { 8 | std::vector unkPart; 9 | void deserialize(KEnvironment* kenv, File *file, size_t length) override { 10 | size_t start = file->tell(); 11 | T::deserialize(kenv, file, length); 12 | size_t unkPartSize = length - (file->tell() - start); 13 | unkPart.resize(unkPartSize); 14 | file->read(unkPart.data(), unkPartSize); 15 | } 16 | void serialize(KEnvironment *kenv, File *file) override { 17 | T::serialize(kenv, file); 18 | file->write(unkPart.data(), unkPart.size()); 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /resources/Beacons_XXL2.json: -------------------------------------------------------------------------------- 1 | { 2 | "beacons": { 3 | "3": "Wooden Crate", 4 | "5": "Simple", 5 | "26": "Potion", 6 | "27": "Helmet", 7 | "28": "x3 Multiplier", 8 | "29": "x10 Multiplier", 9 | "30": "Ham", 10 | "31": "Shield", 11 | "32": "Golden Helmet", 12 | "33": "Diamond Helmet", 13 | "38": { 14 | "name": "Enemy spawn", 15 | "orientable": true 16 | }, 17 | "39": { 18 | "name": "Marker", 19 | "orientable": true 20 | }, 21 | "42": { 22 | "name": "Food Basket", 23 | "orientable": true, 24 | "params": { 25 | "8-15": "Num. gigots" 26 | }, 27 | "defaultParams": "0A00" 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /WavDocument.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "DynArray.h" 5 | 6 | struct File; 7 | 8 | struct WavDocument { 9 | uint16_t formatTag; 10 | uint16_t numChannels; 11 | uint32_t samplesPerSec; 12 | uint32_t avgBytesPerSec; 13 | uint16_t blockAlign; 14 | uint16_t pcmBitsPerSample; 15 | DynArray data; 16 | 17 | void read(File *file); 18 | void write(File *file); 19 | 20 | size_t getNumSamples() { return data.size() / blockAlign; } 21 | }; 22 | 23 | struct WavSampleReader { 24 | const WavDocument *_wav; 25 | const uint8_t *_pnt; 26 | 27 | WavSampleReader(const WavDocument *wav) { _wav = wav; _pnt = wav->data.data(); } 28 | 29 | void nextSample(); 30 | float getSample(int channelIndex); 31 | bool available(); 32 | }; -------------------------------------------------------------------------------- /CoreClasses/IKRenderable.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "KObject.h" 4 | 5 | struct CLightSet; 6 | struct MemberListener; 7 | struct KEnvironment; 8 | 9 | // For now, instead of deriving from it, we will compose it in every class 10 | // based on it, because right now doing by derivation can be problematic 11 | // (varying class CATEGORY, etc.) 12 | struct IKRenderable : CKSubclass, 42, 13> { 13 | kobjref anotherRenderable; 14 | uint32_t spUnk1; 15 | kobjref lightSet; 16 | uint32_t flags = 0; 17 | 18 | void deserialize(KEnvironment* kenv, File* file, size_t length = 0) override; 19 | void serialize(KEnvironment* kenv, File* file) override; 20 | void reflectMembers2(MemberListener& r, KEnvironment* kenv); 21 | }; 22 | -------------------------------------------------------------------------------- /Image.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "DynArray.h" 4 | #include 5 | #include 6 | #include 7 | 8 | struct Image { 9 | uint32_t width, height, bpp, pitch; 10 | DynArray pixels; 11 | DynArray palette; 12 | 13 | enum Format { 14 | // bpp <= 8 -> 8-bit palettized with 2^bpp palette colors 15 | ImageFormat_P8 = 8, 16 | ImageFormat_RGBA8888 = 32, 17 | ImageFormat_DXT1 = 0x101, 18 | ImageFormat_DXT2 = 0x102, 19 | ImageFormat_DXT4 = 0x103, 20 | }; 21 | 22 | Image convertToRGBA32() const; 23 | std::optional palettize() const; 24 | 25 | static Image loadFromFile(const char* filename); 26 | static Image loadFromFile(const wchar_t* filename); 27 | static Image loadFromFile(FILE* file); 28 | static Image loadFromMemory(void* ptr, size_t len); 29 | }; 30 | -------------------------------------------------------------------------------- /LocaleEditor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include "KLocalObject.h" 6 | #include "renderer.h" 7 | #include "CKLocalObjectSubs.h" 8 | 9 | struct KEnvironment; 10 | struct Window; 11 | 12 | struct LocaleEditor { 13 | // Instance of GLOC file loaded in memory 14 | struct LocalDocument { 15 | KLocalPack locpack; 16 | std::vector fontTextures; 17 | std::map fntTexMap; 18 | std::vector trcTextU8, stdTextU8; 19 | uint32_t langStrIndex, langID, langArIndex; 20 | std::map lvlLocpacks; 21 | std::map> lvlTextures; 22 | Loc_CManager2d cmgr2d; 23 | }; 24 | 25 | KEnvironment& kenv; 26 | Renderer* gfx; 27 | Window* window; 28 | std::vector documents; 29 | bool langLoaded = false; 30 | 31 | LocaleEditor(KEnvironment& kenv, Renderer *gfx, Window* window) : kenv(kenv), gfx(gfx), window(window) {} 32 | void gui(); 33 | }; -------------------------------------------------------------------------------- /GamePatcher.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace GamePatcher { 8 | struct GamePatcherException : std::exception { using std::exception::exception; }; 9 | 10 | struct GamePatcherThread { 11 | std::filesystem::path inputPath, elbPath, outputPath; 12 | int progress = 0; 13 | bool done = false; 14 | std::string status; 15 | std::mutex statusMutex; 16 | virtual ~GamePatcherThread() = default; 17 | virtual void start() = 0; 18 | std::string getStatus(); 19 | void setStatus(const std::string& text); 20 | void setStatusFmt(const char* format, ...); 21 | }; 22 | 23 | struct GamePatcherThreadX1 : GamePatcherThread { 24 | void start(); 25 | }; 26 | 27 | struct GamePatcherThreadX2 : GamePatcherThread { 28 | void start(); 29 | }; 30 | 31 | struct GamePatcherThreadCopy : GamePatcherThread { 32 | int platform; 33 | GamePatcherThreadCopy(int platform) : platform(platform) {} 34 | void start(); 35 | }; 36 | } -------------------------------------------------------------------------------- /HomeInterface.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | struct Window; 7 | struct Renderer; 8 | 9 | struct HomeInterface { 10 | public: 11 | bool goToEditor = false; 12 | bool quitApp = false; 13 | bool hexMode = false; 14 | Window* window; 15 | Renderer* gfx; 16 | 17 | bool projectChosen = false; 18 | std::string gamePath = "."; 19 | std::string outGamePath = "."; 20 | int gameVersion = 1; 21 | std::string cfgPlatformName = "KWN"; 22 | int gamePlatform = 1; 23 | bool isRemaster = false; 24 | std::string gameModule; 25 | int initialLevel = 8; 26 | 27 | void* logoTexture = nullptr, *helpTexture = nullptr; 28 | int logoWidth, logoHeight; 29 | 30 | HomeInterface(Window* window, Renderer* gfx); 31 | void iter(); 32 | void openProject(const std::string& u8filename); 33 | 34 | private: 35 | std::vector projectPaths; 36 | 37 | void readProjectPaths(); 38 | void writeProjectPaths(); 39 | 40 | void HelpMarker(const char* message, bool padding = false); 41 | }; -------------------------------------------------------------------------------- /Encyclopedia.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | struct Window; 11 | 12 | struct Encyclopedia { 13 | Encyclopedia(); 14 | ~Encyclopedia(); 15 | 16 | void setKVersion(int ver); 17 | void load(); 18 | void clear(); 19 | const nlohmann::json* getClassJson(int clsFullID); 20 | const nlohmann::json* getEventJson(int fid, int event); 21 | const nlohmann::json* getEventJson(std::span fids, int event); 22 | std::string getEventName(const nlohmann::json* ev, int event); 23 | static std::pair decodeRange(std::string_view sv); 24 | const nlohmann::json* getBeaconJson(int beaconTypeId); 25 | const std::string& getBeaconName(int beaconTypeId); 26 | 27 | std::unordered_map kclasses; 28 | std::unordered_map eventSets; 29 | std::unordered_map beaconInfos; 30 | int kversion = 1; 31 | bool loaded = false; 32 | 33 | Window* window = nullptr; 34 | private: 35 | void loadFile(const std::string& filename); 36 | }; -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 AdrienTD 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /GuiUtils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | struct Window; 9 | struct ImGuiInputTextCallbackData; 10 | 11 | namespace GuiUtils { 12 | // ImGui InputCallback for std::string 13 | int IGStdStringInputCallback(ImGuiInputTextCallbackData* data); 14 | 15 | int MsgBox(Window* window, const char* message, int flags = 0); 16 | std::filesystem::path OpenDialogBox(Window* window, const char* filter, const char* defExt); 17 | std::vector MultiOpenDialogBox(Window* window, const char* filter, const char* defExt); 18 | std::filesystem::path SaveDialogBox(Window* window, const char* filter, const char* defExt, const std::filesystem::path& defName = {}); 19 | std::filesystem::path SelectFolderDialogBox(Window* window, const char* text = nullptr); 20 | std::string latinToUtf8(std::string_view text); 21 | std::string utf8ToLatin(std::string_view text); 22 | std::string wcharToUtf8(std::wstring_view text); 23 | std::wstring utf8ToWchar(std::string_view text); 24 | errno_t fsfopen_s(FILE** streamptr, const std::filesystem::path& filename, const char* mode); 25 | } -------------------------------------------------------------------------------- /imgui/LICENSE_imnodes.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Johann Muszynski 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /imgui/imnodes_LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Johann Muszynski 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /imgui/LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014-2024 Omar Cornut 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /resources/Events_XXL2.json: -------------------------------------------------------------------------------- 1 | { 2 | "classes": [ 3 | { 4 | "category": 12, 5 | "id": 167, 6 | "name": "CKA2GameManager", 7 | "events": [ 8 | { 9 | "id": "2604", 10 | "name": "Set Spawn point and save", 11 | "dataType": "ref", 12 | "parameter": "Spawn point" 13 | }, 14 | { 15 | "id": "266A", 16 | "name": "Acquire combo", 17 | "dataType": "int", 18 | "parameter": "Combo (0,1,2)" 19 | }, 20 | { 21 | "id": "266B", 22 | "name": "Display message box OK", 23 | "dataType": "int", 24 | "parameter": "Text ID" 25 | }, 26 | { 27 | "id": "266C", 28 | "name": "Display message box Yes/No", 29 | "dataType": "int", 30 | "parameter": "Text ID" 31 | }, 32 | { 33 | "id": "266D", 34 | "name": "Show boss health", 35 | "dataType": "none" 36 | }, 37 | { 38 | "id": "266E", 39 | "name": "Hide boss health", 40 | "dataType": "none" 41 | } 42 | ] 43 | } 44 | ] 45 | } -------------------------------------------------------------------------------- /Events.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "KObject.h" 6 | #include "CKUtils.h" 7 | 8 | struct KEnvironment; 9 | struct File; 10 | struct CKObject; 11 | struct CKComparedData; 12 | 13 | struct EventNodeX1 { 14 | uint8_t bit = 0; 15 | int16_t seqIndex = -1; 16 | 17 | void write(KEnvironment *kenv, File *file) const; 18 | void read(KEnvironment *kenv, File *file, CKObject *user); 19 | void operator=(const EventNodeX1& o) noexcept { bit = 0; seqIndex = -1; } 20 | }; 21 | 22 | struct EventNodeX2 { 23 | std::vector> datas; 24 | void write(KEnvironment* kenv, File* file); 25 | void read(KEnvironment* kenv, File* file, CKObject* user); 26 | void clean(); 27 | void operator=(const EventNodeX2& o) noexcept { datas.clear(); } 28 | }; 29 | 30 | struct EventNode { 31 | EventNodeX1 enx1; 32 | EventNodeX2 enx2; 33 | void write(KEnvironment* kenv, File* file); 34 | void read(KEnvironment* kenv, File* file, CKObject* user); 35 | }; 36 | 37 | struct MarkerIndex { 38 | int32_t index = -1; 39 | int32_t arSecondIndex = -1; 40 | void write(KEnvironment* kenv, File* file) const; 41 | void read(KEnvironment* kenv, File* file); 42 | }; -------------------------------------------------------------------------------- /window.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct SDL_Window; 4 | 5 | struct Window { 6 | private: 7 | SDL_Window *_sw; 8 | int _width, _height; 9 | bool _keyDown[512] = {}, _keyPressed[512] = {}; 10 | bool _ctrlPressed = false, _shiftPressed = false, _altPressed = false; 11 | bool _mouseDown[16] = {}, _mousePressed[16] = {}; 12 | int _mouseX, _mouseY, _mouseWheel = 0; 13 | bool _quit = false; 14 | 15 | public: 16 | Window(); 17 | 18 | int getWidth() { return _width; } 19 | int getHeight() { return _height; } 20 | bool getKeyDown(int key) { return _keyDown[key]; } 21 | bool getKeyPressed(int key) { return _keyPressed[key]; } 22 | bool isCtrlPressed() { return _ctrlPressed; } 23 | bool isShiftPressed() { return _shiftPressed; } 24 | bool isAltPressed() { return _altPressed; } 25 | bool getMouseDown(int button) { return _mouseDown[button]; } 26 | bool getMousePressed(int button) { return _mousePressed[button]; } 27 | int getMouseX() { return _mouseX; } 28 | int getMouseY() { return _mouseY; } 29 | int getMouseWheel() { return _mouseWheel; } 30 | bool quitted() { return _quit; } 31 | SDL_Window *getSDLWindow() { return _sw; } 32 | void *getNativeWindow(); 33 | 34 | void handle(); 35 | }; -------------------------------------------------------------------------------- /CoreClasses/IKRenderable.cpp: -------------------------------------------------------------------------------- 1 | #include "IKRenderable.h" 2 | #include "KEnvironment.h" 3 | #include "File.h" 4 | #include "CKLogic.h" 5 | #include "CKGraphical.h" 6 | 7 | void IKRenderable::deserialize(KEnvironment* kenv, File* file, size_t length) 8 | { 9 | if (kenv->version == KEnvironment::KVERSION_XXL1) 10 | return; 11 | if (kenv->version >= kenv->KVERSION_SPYRO) 12 | spUnk1 = file->readUint32(); 13 | else 14 | anotherRenderable = kenv->readObjRef(file); 15 | lightSet = kenv->readObjRef(file); 16 | flags = file->readUint32(); 17 | } 18 | 19 | void IKRenderable::serialize(KEnvironment* kenv, File* file) 20 | { 21 | if (kenv->version == KEnvironment::KVERSION_XXL1) 22 | return; 23 | if (kenv->version >= kenv->KVERSION_SPYRO) 24 | file->writeUint32(spUnk1); 25 | else 26 | kenv->writeObjRef(file, anotherRenderable); 27 | kenv->writeObjRef(file, lightSet); 28 | file->writeUint32(flags); 29 | } 30 | 31 | void IKRenderable::reflectMembers2(MemberListener& r, KEnvironment* kenv) 32 | { 33 | if (kenv->version == KEnvironment::KVERSION_XXL1) 34 | return; 35 | if (kenv->version >= kenv->KVERSION_SPYRO) 36 | r.reflect(spUnk1, "spUnk1"); 37 | else 38 | r.reflect(anotherRenderable, "anotherRenderable"); 39 | r.reflect(lightSet, "lightSet"); 40 | r.reflect(flags, "flags"); 41 | } 42 | -------------------------------------------------------------------------------- /adpcm-xq/adpcm-xq.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Fichiers sources 20 | 21 | 22 | Fichiers sources 23 | 24 | 25 | 26 | 27 | Fichiers d%27en-tête 28 | 29 | 30 | -------------------------------------------------------------------------------- /GroundRenderer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "vecmat.h" 8 | 9 | struct Renderer; 10 | struct RVertexBuffer; 11 | struct RIndexBuffer; 12 | struct CGround; 13 | 14 | struct GroundGeo { 15 | std::vector positions; 16 | std::vector> triangles; 17 | std::vector colors; 18 | size_t numTrisWithoutInfiniteWalls = 0, numTrisWithInfiniteWalls = 0; 19 | 20 | static std::optional generateGroundGeo(CGround* gnd, bool hasInfinites); 21 | }; 22 | 23 | class GroundModel { 24 | private: 25 | Renderer *_gfx; 26 | RVertexBuffer *vertices; 27 | RIndexBuffer *groundIndices; 28 | //RIndexBuffer *wallIndices; 29 | size_t numGroundTriangles, numFinWallTris, numInfWallTris; 30 | std::optional _usedTransform; 31 | 32 | public: 33 | GroundModel(Renderer *gfx, CGround *gnd); 34 | ~GroundModel(); 35 | void draw(bool showInfiniteWalls = false); 36 | const std::optional& usedTransform() const { return _usedTransform; } 37 | }; 38 | 39 | class GroundModelCache { 40 | private: 41 | Renderer *_gfx; 42 | std::map> _cache; 43 | 44 | public: 45 | GroundModelCache(Renderer *gfx) : _gfx(gfx) {} 46 | GroundModel *getModel(CGround *gnd); 47 | void clear() { _cache.clear(); } 48 | }; -------------------------------------------------------------------------------- /DynArray.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | template class DynArray { 6 | private: 7 | T* pointer; 8 | size_t length; 9 | 10 | void freeP() { 11 | if(length) 12 | delete[] pointer; 13 | pointer = nullptr; 14 | length = 0; 15 | } 16 | 17 | void setsize(size_t newlen) { 18 | if(newlen) 19 | pointer = new T[newlen]; 20 | length = newlen; 21 | } 22 | 23 | public: 24 | void resize(size_t newlen) { 25 | freeP(); 26 | setsize(newlen); 27 | } 28 | 29 | size_t size() const { return length; } 30 | T* data() { return pointer; } 31 | const T* data() const { return pointer; } 32 | 33 | T* begin() { return pointer; } 34 | T* end() { return pointer + length; } 35 | const T* begin() const { return pointer; } 36 | const T* end() const { return pointer + length; } 37 | 38 | T& operator[] (size_t index) { return pointer[index]; } 39 | const T& operator[] (size_t index) const { return pointer[index]; } 40 | 41 | DynArray() { pointer = nullptr; length = 0; } 42 | DynArray(size_t len) { setsize(len); } 43 | DynArray(const DynArray &other) { setsize(other.length); memcpy(pointer, other.pointer, length * sizeof(T)); } 44 | DynArray(DynArray &&other) noexcept { pointer = other.pointer; length = other.length; other.pointer = nullptr; other.length = 0; } 45 | void operator=(const DynArray &other) { resize(other.length); memcpy(pointer, other.pointer, length * sizeof(T)); } 46 | void operator=(DynArray &&other) noexcept { freeP(); pointer = other.pointer; length = other.length; other.pointer = nullptr; other.length = 0; } 47 | ~DynArray() { freeP(); } 48 | }; -------------------------------------------------------------------------------- /EditorUI/IGAbout.cpp: -------------------------------------------------------------------------------- 1 | #include "IGAbout.h" 2 | 3 | #include "EditorInterface.h" 4 | #include "EditorWidgets.h" 5 | #include 6 | #include "renderer.h" 7 | #include "File.h" 8 | #include "Image.h" 9 | 10 | void EditorUI::IGAbout(EditorInterface& ui) 11 | { 12 | static bool loaded = false; 13 | static texture_t logo = nullptr; 14 | static int logoWidth, logoHeight; 15 | if (!loaded) { 16 | auto [ptr, len] = GetResourceContent("logo.png"); 17 | Image img = Image::loadFromMemory(ptr, len); 18 | logo = ui.gfx->createTexture(img); 19 | logoWidth = img.width; 20 | logoHeight = img.height; 21 | loaded = true; 22 | } 23 | 24 | ImGui::Image(logo, ImVec2(400.0f, 400.0f * (float)logoHeight / (float)logoWidth)); 25 | #ifdef XEC_APPVEYOR 26 | static const char* version = "Version " XEC_APPVEYOR; 27 | #else 28 | static const char* version = "Development version"; 29 | #endif 30 | ImGui::Text("XXL Editor\n%s\nbuilt on " __DATE__, version); 31 | ImGui::Spacing(); ImGui::Separator(); ImGui::Spacing(); 32 | ImGui::Text("Developed by AdrienTD\nThanks to S.P.Q.R"); 33 | ImGui::Spacing(); ImGui::Separator(); ImGui::Spacing(); 34 | ImGui::TextLinkOpenURL("Wiki", "https://github.com/AdrienTD/XXL-Editor/wiki"); 35 | ImGui::TextUnformatted("for documentation, tutorials, and links to Discord servers"); 36 | ImGui::TextLinkOpenURL("GitHub repo", "https://github.com/AdrienTD/XXL-Editor"); 37 | ImGui::TextUnformatted("for source code and stable releases"); 38 | ImGui::TextLinkOpenURL("AppVeyor", "https://ci.appveyor.com/project/AdrienTD/xxl-editor"); 39 | ImGui::TextUnformatted("for the latest development build"); 40 | } -------------------------------------------------------------------------------- /adpcm-xq/license.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) David Bryant 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * Neither the name of Conifer Software nor the names of its contributors 13 | may be used to endorse or promote products derived from this software 14 | without specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR 20 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /EditorUI/EditorUtils.cpp: -------------------------------------------------------------------------------- 1 | #include "EditorUtils.h" 2 | #include 3 | #include 4 | #include 5 | #include "rwsound.h" 6 | #include "WavDocument.h" 7 | 8 | namespace EditorUI 9 | { 10 | static bool audioInitDone = false; 11 | static SDL_AudioDeviceID audiodevid; 12 | static int audioLastFreq = 0; 13 | static int audioLastNumChannels = 0; 14 | 15 | void InitSnd(int freq, int numChannels, bool byteSwapped) { 16 | if (audioInitDone && audioLastFreq == freq && audioLastNumChannels == numChannels) { 17 | SDL_ClearQueuedAudio(audiodevid); 18 | return; 19 | } 20 | if (audioInitDone) { 21 | SDL_ClearQueuedAudio(audiodevid); 22 | SDL_CloseAudioDevice(audiodevid); 23 | } 24 | SDL_AudioSpec spec, have; 25 | memset(&spec, 0, sizeof(spec)); 26 | spec.freq = freq; 27 | spec.format = byteSwapped ? AUDIO_S16MSB : AUDIO_S16LSB; 28 | spec.channels = numChannels; 29 | spec.samples = 4096; 30 | audiodevid = SDL_OpenAudioDevice(NULL, 0, &spec, &have, 0); 31 | assert(audiodevid); 32 | SDL_PauseAudioDevice(audiodevid, 0); 33 | audioInitDone = true; 34 | audioLastFreq = freq; 35 | audioLastNumChannels = numChannels; 36 | } 37 | 38 | void PlaySnd(const RwSound& snd) { 39 | InitSnd(snd.info.format.sampleRate, 1, snd.info.isBigEndian); 40 | SDL_QueueAudio(audiodevid, snd.data.data.data(), snd.data.data.size()); 41 | } 42 | 43 | void PlaySnd(const WavDocument& wav) { 44 | InitSnd(wav.samplesPerSec, wav.numChannels, false); 45 | SDL_QueueAudio(audiodevid, wav.data.data(), wav.data.size()); 46 | } 47 | 48 | float decode8bitAngle(uint8_t byte) { 49 | return byte * std::numbers::pi_v / 128.0f; 50 | } 51 | } -------------------------------------------------------------------------------- /resources/Encyclopedia.json: -------------------------------------------------------------------------------- 1 | { 2 | "games": { 3 | "1": { 4 | "name": "Asterix XXL 1", 5 | "classInfoFiles": [ 6 | "Properties_Common.json", 7 | "Properties_XXL1.json", 8 | "Events_XXL1.json", 9 | "Beacons_XXL1.json" 10 | ] 11 | }, 12 | "2": { 13 | "name": "Asterix XXL 2", 14 | "classInfoFiles": [ 15 | "Properties_Common.json", 16 | "Properties_XXL2.json", 17 | "Events_XXL2Plus.json", 18 | "Events_XXL2.json", 19 | "Beacons_XXL2.json" 20 | ] 21 | }, 22 | "3": { 23 | "name": "Arthur and Minimoys", 24 | "classInfoFiles": [ 25 | "Properties_Common.json", 26 | "Events_XXL2Plus.json", 27 | "Beacons_Arthur.json" 28 | ] 29 | }, 30 | "4": { 31 | "name": "Asterix at the Olympic Games", 32 | "classInfoFiles": [ 33 | "Properties_Common.json", 34 | "Events_XXL2Plus.json", 35 | "Beacons_Olympic.json" 36 | ] 37 | }, 38 | "5": { 39 | "name": "Spyro Dawn of the Dragon", 40 | "classInfoFiles": [ 41 | "Properties_Common.json", 42 | "Events_XXL2Plus.json", 43 | "Beacons_Other.json" 44 | ] 45 | }, 46 | "6": { 47 | "name": "Alice in Wonderland", 48 | "classInfoFiles": [ 49 | "Properties_Common.json", 50 | "Events_XXL2Plus.json", 51 | "Beacons_Other.json" 52 | ] 53 | }, 54 | "7": { 55 | "name": "How to train your Dragon", 56 | "classInfoFiles": [ 57 | "Properties_Common.json", 58 | "Events_XXL2Plus.json", 59 | "Beacons_Other.json" 60 | ] 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /rwrenderer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "renderer.h" 8 | 9 | struct Renderer; 10 | struct CTextureDictionary; 11 | struct RwGeometry; 12 | struct CKAnyGeometry; 13 | 14 | struct ProTexDict { 15 | Renderer *_gfx; 16 | ProTexDict *_next = nullptr; 17 | std::unordered_map dict; 18 | static uint32_t globalVersion; 19 | 20 | ProTexDict(Renderer *gfx) : _gfx(gfx) {} 21 | ProTexDict(Renderer *gfx, CTextureDictionary *ctd); 22 | ProTexDict(ProTexDict &&ptd) = default; 23 | ~ProTexDict(); 24 | 25 | std::pair find(const std::string &name); 26 | void reset(CTextureDictionary *ctd); 27 | }; 28 | 29 | struct ProGeometry { 30 | Renderer *_gfx; 31 | ProTexDict *_proTexDict; 32 | std::unique_ptr vbuf; 33 | std::unique_ptr ibuf; 34 | size_t numTris; 35 | std::string textureName; 36 | 37 | texture_t ptdTexId = nullptr; 38 | uint32_t ptdVersion = 0; 39 | 40 | //ProGeometry(Renderer *gfx, RVertexBuffer *vbuf, RIndexBuffer *ibuf, size_t numTris, const std::string &textureName, ProTexDict *proTexDict) : 41 | // gfx(gfx), vbuf(vbuf), ibuf(ibuf), numTris(numTris), textureName(textureName), proTexDict(proTexDict); 42 | 43 | ProGeometry(Renderer *gfx, RwGeometry *geo, ProTexDict *proTexDict); 44 | 45 | void draw(bool showTextures = true); 46 | }; 47 | 48 | struct ProGeoCache { 49 | Renderer *_gfx; 50 | std::unordered_map> dict; 51 | 52 | ProGeoCache(Renderer *gfx); 53 | 54 | ProGeometry* getPro(RwGeometry* geo, ProTexDict* proTexDict); 55 | ProGeometry* getPro(const std::shared_ptr& geo, ProTexDict* proTexDict) { return getPro(geo.get(), proTexDict); } 56 | void clear() { dict.clear(); } 57 | }; -------------------------------------------------------------------------------- /EditorUI/ImGuiMemberListener.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "KEnvironment.h" 4 | #include "CKUtils.h" 5 | 6 | namespace EditorUI 7 | { 8 | struct EditorInterface; 9 | 10 | // Creates ImGui editing widgets for every member in a member-reflected object 11 | struct ImGuiMemberListener : NamedMemberListener { 12 | KEnvironment& kenv; EditorInterface& ui; 13 | MemberFlags currentFlags = MemberFlags::MF_NONE; 14 | std::stack scopeExpanded; 15 | 16 | ImGuiMemberListener(KEnvironment& kenv, EditorInterface& ui) : kenv(kenv), ui(ui) {} 17 | 18 | bool icon(const char* label, const char* desc = nullptr); 19 | 20 | template void flagsEditor(const char* name, T& value); 21 | 22 | void reflect(uint8_t& ref, const char* name) override; 23 | void reflect(uint16_t& ref, const char* name) override; 24 | void reflect(uint32_t& ref, const char* name) override; 25 | void reflect(int8_t& ref, const char* name) override; 26 | void reflect(int16_t& ref, const char* name) override; 27 | void reflect(int32_t& ref, const char* name) override; 28 | void reflect(float& ref, const char* name) override; 29 | void reflectAnyRef(kanyobjref& ref, int clfid, const char* name) override; 30 | void reflect(Vector3& ref, const char* name) override; 31 | void reflect(Matrix& ref, const char* name) override; 32 | void reflect(EventNode& ref, const char* name, CKObject* user) override; 33 | void reflect(MarkerIndex& ref, const char* name) override; 34 | void reflectPostRefTuple(uint32_t& tuple, const char* name) override; 35 | void reflect(std::string& ref, const char* name) override; 36 | 37 | void setNextFlags(MemberFlags flags) override; 38 | 39 | void enterArray(const char* name) override; 40 | void leaveArray() override; 41 | 42 | void enterStruct(const char* name) override; 43 | void leaveStruct() override; 44 | 45 | void compositionEditor(CKObject* obj, int clfid, const char* name); 46 | }; 47 | } -------------------------------------------------------------------------------- /CoreClasses/CKGeometry.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "KObject.h" 4 | #include 5 | #include 6 | #include 7 | #include "vecmat.h" 8 | #include 9 | #include "IKRenderable.h" 10 | 11 | struct RwMiniClump; 12 | struct CMaterial; 13 | 14 | struct CKAnyGeometry : CKSubclass { 15 | // Common : 16 | kobjref nextGeo; 17 | //uint32_t flags; 18 | std::shared_ptr clump; 19 | 20 | // XXL1 : 21 | std::vector> costumes; 22 | //kobjref sameGeo = this; 23 | uint32_t flags2 = 6; 24 | std::array unkarea; std::string unkstring; 25 | uint32_t unkloner; 26 | 27 | // XXL2+ : 28 | //kobjref unkobj1; uint32_t spUnk1 = 0; 29 | //kobjref lightSet; 30 | kobjref material; 31 | uint32_t color = 0xFFFFFFFF; 32 | kobjref duplicateGeo; 33 | 34 | // XXL1/2 Remaster: 35 | std::string hdKifPath, hdMatName; 36 | int32_t hdUnk1 = -1; 37 | 38 | // Arthur/OG+ : 39 | kobjref ogUnkObj; 40 | uint8_t ogLastByte; 41 | 42 | // Spyro+: 43 | uint8_t spLastByte2, spLastByte3, spLastByte4; 44 | 45 | // Alice+: 46 | std::array alBytes = { 0,0,0,0,0 }; 47 | 48 | void deserialize(KEnvironment* kenv, File *file, size_t length) override; 49 | void serialize(KEnvironment* kenv, File *file) override; 50 | }; 51 | 52 | struct CKParticleGeometry : CKSubclass { 53 | // XXL1 : 54 | uint32_t pgHead1, pgHead2, pgHead3; 55 | std::array pgSphere; 56 | std::vector pgPoints; 57 | 58 | // XXL2: 59 | uint32_t x2Head1; 60 | std::string x2TexName; 61 | 62 | // Common : 63 | void *extra = nullptr; size_t extraSize = 0; 64 | 65 | void deserialize(KEnvironment* kenv, File *file, size_t length) override; 66 | void serialize(KEnvironment* kenv, File *file) override; 67 | }; 68 | 69 | struct CKGeometry : CKSubclass {}; 70 | struct CKSkinGeometry : CKSubclass {}; -------------------------------------------------------------------------------- /EditorUI/IGMarkerEditor.cpp: -------------------------------------------------------------------------------- 1 | #include "IGMarkerEditor.h" 2 | #include "EditorInterface.h" 3 | #include "EditorWidgets.h" 4 | 5 | #include "KEnvironment.h" 6 | #include "CoreClasses/CKService.h" 7 | 8 | #include 9 | 10 | void EditorUI::IGMarkerEditor(EditorInterface& ui) 11 | { 12 | auto& kenv = ui.kenv; 13 | CKSrvMarker* srvMarker = kenv.levelObjects.getFirst(); 14 | if (!srvMarker) return; 15 | ImGui::Columns(2); 16 | ImGui::BeginChild("MarkerTree"); 17 | int lx = 0; 18 | for (auto& list : srvMarker->lists) { 19 | ImGui::SetNextItemOpen(true, ImGuiCond_Once); 20 | if (ImGui::TreeNode(&list, "List %i", lx)) { 21 | if (ImGui::Button("Add")) { 22 | auto& marker = list.emplace_back(); 23 | marker.position = ui.cursorPosition; 24 | } 25 | int mx = 0; 26 | for (auto& marker : list) { 27 | ImGui::PushID(&marker); 28 | if (ImGui::Selectable("##MarkerEntry", ui.selectedMarkerIndex == mx)) { 29 | ui.selectedMarkerIndex = mx; 30 | } 31 | ImGui::SameLine(); 32 | ImGui::Text("%3i: %s (%u)", mx, marker.name.c_str(), marker.val3); 33 | ImGui::PopID(); 34 | mx++; 35 | } 36 | ImGui::TreePop(); 37 | } 38 | lx++; 39 | } 40 | ImGui::EndChild(); 41 | ImGui::NextColumn(); 42 | ImGui::BeginChild("MarkerInfo"); 43 | if (!srvMarker->lists.empty() && ui.selectedMarkerIndex >= 0 && ui.selectedMarkerIndex < srvMarker->lists[0].size()) { 44 | CKSrvMarker::Marker& marker = srvMarker->lists[0][ui.selectedMarkerIndex]; 45 | if (ImGui::Button("Place camera there")) { 46 | ui.camera.position = marker.position - ui.camera.direction * 5.0f; 47 | } 48 | IGStringInput("Name", marker.name); 49 | ImGui::DragFloat3("Position", &marker.position.x, 0.1f); 50 | ImGui::InputScalar("Orientation 1", ImGuiDataType_U8, &marker.orientation1); 51 | ImGui::InputScalar("Orientation 2", ImGuiDataType_U8, &marker.orientation2); 52 | ImGui::InputScalar("Val3", ImGuiDataType_U16, &marker.val3); 53 | } 54 | ImGui::EndChild(); 55 | ImGui::Columns(); 56 | } 57 | -------------------------------------------------------------------------------- /KObject.cpp: -------------------------------------------------------------------------------- 1 | #include "KObject.h" 2 | #include "File.h" 3 | #include 4 | 5 | std::unordered_map CKObject::refCounts; 6 | std::unordered_map CKObject::objIdMap; 7 | std::set> CKUnknown::hits; 8 | 9 | bool CKUnknown::isSubclassOfID(uint32_t fid) 10 | { 11 | return false; 12 | } 13 | 14 | int CKUnknown::getClassCategory() 15 | { 16 | return clCategory; 17 | } 18 | 19 | int CKUnknown::getClassID() 20 | { 21 | return clId; 22 | } 23 | 24 | const char * CKUnknown::getClassName() 25 | { 26 | return "?"; 27 | } 28 | 29 | std::span CKUnknown::getClassHierarchy() 30 | { 31 | return std::span(); 32 | } 33 | 34 | void CKUnknown::deserialize(KEnvironment* kenv, File * file, size_t length) { 35 | this->mem.resize(length); 36 | this->offset = (uint32_t)file->tell(); 37 | if (length > 0) { 38 | file->read(this->mem.data(), this->mem.size()); 39 | } 40 | } 41 | 42 | void CKUnknown::serialize(KEnvironment* kenv, File * file) { 43 | if (this->mem.size() > 0) { 44 | file->write(this->mem.data(), this->mem.size()); 45 | } 46 | } 47 | 48 | void CKUnknown::deserializeLvlSpecific(KEnvironment * kenv, File * file, size_t length) 49 | { 50 | this->lsMem.resize(length); 51 | this->lvlSpecificOffset = (uint32_t)file->tell(); 52 | if (length > 0) { 53 | file->read(this->lsMem.data(), this->lsMem.size()); 54 | } 55 | } 56 | 57 | void CKUnknown::serializeLvlSpecific(KEnvironment * kenv, File * file) 58 | { 59 | if (this->lsMem.size() > 0) { 60 | file->write(this->lsMem.data(), this->lsMem.size()); 61 | } 62 | } 63 | 64 | CKUnknown::CKUnknown(const CKUnknown& another) = default; 65 | 66 | void CKObject::deserialize(KEnvironment * kenv, File * file, size_t length) 67 | { 68 | printf("Deserialization unimplemented, skipping\n"); 69 | file->seek(length, SEEK_CUR); 70 | } 71 | 72 | void CKObject::serialize(KEnvironment* kenv, File * file) 73 | { 74 | printf("Serialization unimplemented, writing random stuff\n"); 75 | const char *stuff = "Unimplemented, please fix this! ;)"; 76 | file->write(stuff, strlen(stuff)); 77 | } 78 | -------------------------------------------------------------------------------- /Shape.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "vecmat.h" 4 | #include 5 | #include 6 | #include 7 | 8 | struct File; 9 | 10 | struct BoundingSphere { 11 | Vector3 center; 12 | float radius; 13 | 14 | BoundingSphere() : center(Vector3(0, 0, 0)), radius(0) {} 15 | BoundingSphere(const Vector3 ¢er, float radius) : center(center), radius(radius) {} 16 | 17 | bool containsPoint(const Vector3& point) const; 18 | bool intersectsWithSphere(const BoundingSphere& other) const; 19 | float distanceToPoint(const Vector3& point) const; 20 | float distanceToSphere(const BoundingSphere& other) const; 21 | 22 | void merge(const BoundingSphere &other); 23 | void mergePoint(const Vector3 &point); 24 | 25 | void deserialize(File *file, bool withSquare = false); 26 | void serialize(File *file, bool withSquare = false); 27 | }; 28 | 29 | struct AABoundingBox { 30 | Vector3 highCorner, lowCorner; 31 | 32 | AABoundingBox() : highCorner(-vecinf), lowCorner(vecinf) {} 33 | AABoundingBox(const Vector3 &point) : highCorner(point), lowCorner(point) {} 34 | AABoundingBox(const Vector3 &highCorner, const Vector3 &lowCorner) : highCorner(highCorner), lowCorner(lowCorner) {} 35 | 36 | void mergePoint(const Vector3 &point); 37 | void merge(const AABoundingBox &box); 38 | bool containsPoint(const Vector3& point) const; 39 | std::optional intersectionWith(const AABoundingBox& box) const; 40 | 41 | void deserialize(File *file); 42 | void serialize(File *file); 43 | void deserializeLC(File *file); 44 | void serializeLC(File *file); 45 | 46 | private: 47 | static constexpr auto fltinf = std::numeric_limits::infinity(); 48 | static constexpr auto vecinf = Vector3(fltinf, fltinf, fltinf); 49 | }; 50 | 51 | struct AACylinder { 52 | Vector3 center; 53 | float radius, height; 54 | 55 | void deserialize(File *file); 56 | void serialize(File *file); 57 | }; 58 | 59 | struct AARectangle { 60 | Vector3 center; 61 | float length1 = 1.0f, length2 = 1.0f; 62 | uint8_t direction = 0; 63 | 64 | AARectangle() = default; 65 | AARectangle(Vector3 center) : center(center) {} 66 | }; 67 | -------------------------------------------------------------------------------- /EditorUI/EditorWidgets.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "KObject.h" 4 | #include "CKUtils.h" 5 | #include "Events.h" 6 | 7 | struct Window; 8 | 9 | namespace EditorUI 10 | { 11 | struct EditorInterface; 12 | 13 | bool IGObjectSelector(EditorInterface& ui, const char* name, kanyobjref& ptr, uint32_t clfid = 0xFFFFFFFF); 14 | inline bool IGObjectSelectorRef(EditorInterface& ui, const char* name, kanyobjref& ref) { return IGObjectSelector(ui, name, ref, 0xFFFFFFFF); }; 15 | inline bool IGObjectSelectorRef(EditorInterface& ui, const char* name, kobjref& ref) { return IGObjectSelector(ui, name, ref, 0xFFFFFFFF); }; 16 | template bool IGObjectSelectorRef(EditorInterface& ui, const char* name, kobjref& ref) { return IGObjectSelector(ui, name, ref, T::FULL_ID); }; 17 | 18 | bool IGObjectSelector(EditorInterface& ui, const char* name, KAnyPostponedRef& postref, uint32_t clfid = 0xFFFFFFFF); 19 | inline bool IGObjectSelectorRef(EditorInterface& ui, const char* name, KPostponedRef& postref) { return IGObjectSelector(ui, name, postref, 0xFFFFFFFF); } 20 | template bool IGObjectSelectorRef(EditorInterface& ui, const char* name, KPostponedRef& postref) { return IGObjectSelector(ui, name, postref, T::FULL_ID); } 21 | 22 | using EventNodePayload = std::pair; 23 | void IGEventSelector(EditorInterface& ui, const char* name, EventNode& ref); 24 | void IGEventSelector(EditorInterface& ui, const char* name, EventNodeX1& ref); 25 | void IGEventSelector(EditorInterface& ui, const char* name, EventNodeX2& ref); 26 | void IGMarkerSelector(EditorInterface& ui, const char* name, MarkerIndex& ref); 27 | void IGObjectDragDropSource(EditorInterface& ui, CKObject* obj); 28 | bool IGEventMessageSelector(EditorInterface& ui, const char* label, uint16_t& message, CKObject* kobj, bool isCallback = false); 29 | bool IGEventMessageSelector(EditorInterface& ui, const char* label, uint16_t& message, int fid, bool isCallback = false); 30 | 31 | void IGObjectNameInput(const char* label, CKObject* obj, KEnvironment& kenv); 32 | void IGStringInput(const char* label, std::string& str); 33 | bool IGU32Color(const char* name, uint32_t& color); 34 | } -------------------------------------------------------------------------------- /KLocalObject.cpp: -------------------------------------------------------------------------------- 1 | #include "KLocalObject.h" 2 | #include "File.h" 3 | #include 4 | 5 | void KUnknownLocalObject::deserialize(KEnvironment * kenv, File * file, size_t length) 6 | { 7 | memsize = length; 8 | if (length != 0) { 9 | memptr = malloc(length); 10 | file->read(memptr, length); 11 | } 12 | } 13 | 14 | void KUnknownLocalObject::serialize(KEnvironment * kenv, File * file) 15 | { 16 | if (memsize) 17 | file->write(memptr, memsize); 18 | } 19 | 20 | KLocalObject * KUnknownLocalObject::clone() 21 | { 22 | KUnknownLocalObject *clone = new KUnknownLocalObject(this->cls_fid); 23 | clone->memptr = malloc(this->memsize); 24 | clone->memsize = this->memsize; 25 | memcpy(clone->memptr, this->memptr, this->memsize); 26 | return clone; 27 | } 28 | 29 | void KLocalPack::deserialize(KEnvironment * kenv, File * file) 30 | { 31 | uint32_t numObjects = file->readUint32(); 32 | objects.reserve(numObjects); 33 | for (uint32_t i = 0; i < numObjects; i++) { 34 | uint32_t clcat = file->readUint32(); 35 | uint32_t clid = file->readUint32(); 36 | uint32_t nextoff = file->readUint32(); 37 | int cls_fid = clcat | (clid << 6); 38 | KLocalObject *locObj; 39 | auto factory = factories.find(cls_fid); 40 | if (factory == factories.end()) { 41 | locObj = new KUnknownLocalObject(cls_fid); 42 | } else { 43 | locObj = factory->second(); 44 | } 45 | if (cls_fid != kclassToNotDeserialize) { 46 | locObj->deserialize(kenv, file, nextoff - file->tell()); 47 | assert(file->tell() == nextoff); 48 | } 49 | else { 50 | file->seek(nextoff, SEEK_SET); 51 | // and leave it default-constructed 52 | } 53 | objects.emplace_back(locObj); 54 | } 55 | } 56 | 57 | void KLocalPack::serialize(KEnvironment * kenv, File * file) 58 | { 59 | file->writeUint32(objects.size()); 60 | for (const auto &locObj : objects) { 61 | int fid = locObj->getFullID(); 62 | file->writeUint32(fid & 63); 63 | file->writeUint32(fid >> 6); 64 | file->writeUint32(0); 65 | size_t spos = file->tell(); 66 | locObj->serialize(kenv, file); 67 | size_t npos = file->tell(); 68 | file->seek(spos - 4, SEEK_SET); 69 | file->writeUint32(npos); 70 | file->seek(npos, SEEK_SET); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /resources/Beacons_XXL1.json: -------------------------------------------------------------------------------- 1 | { 2 | "beacons": { 3 | "3": "Wooden Crate", 4 | "4": "Metal Crate", 5 | "6": "Helmet", 6 | "7": "Golden Helmet", 7 | "8": "Potion", 8 | "9": "Shield", 9 | "10": "Ham", 10 | "11": "x3 Multiplier", 11 | "12": "x10 Multiplier", 12 | "13": "Laurel", 13 | "14": { 14 | "name": "Boar", 15 | "orientable": true, 16 | "params": { 17 | "8-12": "Timeout (seconds)" 18 | }, 19 | "defaultParams": "1F00" 20 | }, 21 | "15": { 22 | "name": "Water flow", 23 | "orientable": true, 24 | "params": { 25 | "8-15": "Intensity" 26 | } 27 | }, 28 | "16": { 29 | "name": "Merchant", 30 | "params": { 31 | "0": "2 Health Points", 32 | "1": "3 Health Points", 33 | "2": "5 Health Points", 34 | "3": "2/5 Shield", 35 | "4": "3/5 Shield", 36 | "5": "Shield", 37 | "6": "Club Combo", 38 | "7": "Power Hammer Combo", 39 | "8": "Twister Combo", 40 | "9": "Twister Fusion Combo", 41 | "10": "Mole Combo", 42 | "11": "Rage Combo", 43 | "12-14": "Orientation" 44 | }, 45 | "defaultParams": "0022" 46 | }, 47 | "17": "Retro Coin", 48 | "18": "Remaster Coin", 49 | "21": { 50 | "name": "Save point", 51 | "params": { 52 | "0-7": "Map ID", 53 | "8-15": "Respawn ID" 54 | } 55 | }, 56 | "22": { 57 | "name": "Respawn point", 58 | "params": { 59 | "0-7": "Respawn ID" 60 | } 61 | }, 62 | "23": { 63 | "name": "Hero respawn point", 64 | "orientable": true, 65 | "params": { 66 | "8-9": { 67 | "name": "Hero", 68 | "0": "Asterix", 69 | "1": "Obelix", 70 | "2": "Dogmatix", 71 | "3": "Camera" 72 | } 73 | } 74 | }, 75 | "26": { 76 | "name": "Freeze Crate 1", 77 | "defaultParams": "0001" 78 | }, 79 | "27": { 80 | "name": "Freeze Crate 3", 81 | "defaultParams": "0001" 82 | }, 83 | "28": { 84 | "name": "Freeze Crate 5", 85 | "defaultParams": "0001" 86 | } 87 | } 88 | } -------------------------------------------------------------------------------- /CKLocalObjectSubs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "rw.h" 5 | #include "KLocalObject.h" 6 | #include "DynArray.h" 7 | 8 | struct Loc_CKGraphic : KLocalObjectSub<0, 2> { 9 | struct CKGTexture { 10 | std::string name; 11 | RwImage img; 12 | }; 13 | std::vector textures; 14 | 15 | void deserialize(KEnvironment* kenv, File *file, size_t length) override; 16 | void serialize(KEnvironment* kenv, File *file) override; 17 | KLocalObject *clone() override { return new Loc_CKGraphic(*this); } 18 | }; 19 | 20 | struct Loc_CLocManager : KLocalObjectSub<12, 59> { 21 | std::vector> trcStrings; 22 | std::vector stdStrings; 23 | uint16_t numLanguages; 24 | std::vector langStrIndices, langIDs, langArIndices; 25 | uint32_t spUnk0 = 0; 26 | 27 | void deserialize(KEnvironment* kenv, File *file, size_t length) override; 28 | void serialize(KEnvironment* kenv, File *file) override; 29 | KLocalObject *clone() override { return new Loc_CLocManager(*this); } 30 | }; 31 | 32 | struct Loc_CManager2d : KLocalObjectSub<13, 16> { 33 | struct Font { 34 | uint32_t fontId; 35 | RwFont2D rwFont; 36 | std::string x2Name; 37 | }; 38 | 39 | RwPITexDict piTexDict; 40 | std::vector fonts; 41 | 42 | bool empty = true; 43 | 44 | void deserialize(KEnvironment* kenv, File *file, size_t length) override; 45 | void serialize(KEnvironment* kenv, File *file) override; 46 | KLocalObject *clone() override { return new Loc_CManager2d(*this); } 47 | }; 48 | 49 | struct Loc_CKSrvSekensor : KLocalObjectSub<1, 10> { 50 | struct LocalizedLine { 51 | float duration = 0.0f; 52 | // Arthur+: 53 | float oneFloat = 1.0f; 54 | uint8_t someByte = 0; 55 | }; 56 | struct LocalizedSekens { 57 | float totalTime = 0.0f; 58 | //uint8_t numLines = 0; 59 | uint8_t numVoiceLines = 0; 60 | std::vector locLines; 61 | // Arthur+: 62 | uint32_t arSekensIndex = 0; 63 | uint32_t arUnkValue = 0; // removed in OG 64 | }; 65 | std::vector locSekens; 66 | 67 | void deserialize(KEnvironment* kenv, File* file, size_t length) override; 68 | void serialize(KEnvironment* kenv, File* file) override; 69 | KLocalObject* clone() override { return new Loc_CKSrvSekensor(*this); } 70 | }; -------------------------------------------------------------------------------- /EditorUI/IGObjectInspector.cpp: -------------------------------------------------------------------------------- 1 | #include "IGObjectInspector.h" 2 | 3 | #include "EditorInterface.h" 4 | #include "EditorWidgets.h" 5 | #include "ImGuiMemberListener.h" 6 | #include "DictionaryEditors.h" 7 | 8 | #include "KEnvironment.h" 9 | #include "CoreClasses/CKManager.h" 10 | #include "CoreClasses/CKService.h" 11 | #include "CoreClasses/CKHook.h" 12 | #include "CoreClasses/CKGroup.h" 13 | #include "CoreClasses/CKComponent.h" 14 | #include "CoreClasses/CKCamera.h" 15 | #include "CoreClasses/CKCinematicNode.h" 16 | #include "CoreClasses/CKDictionary.h" 17 | #include "CoreClasses/CKLogic.h" 18 | #include "CoreClasses/CKGraphical.h" 19 | 20 | #include 21 | 22 | namespace 23 | { 24 | template void EI_ReflectAnyReflectableObject(const Func& f, CKObject* obj) 25 | { 26 | if (auto* s = obj->dyncast()) 27 | f(s); 28 | else if constexpr (sizeof...(Rest) > 0) 29 | EI_ReflectAnyReflectableObject(f, obj); 30 | } 31 | } 32 | 33 | void EditorUI::IGObjectInspector(EditorInterface& ui) 34 | { 35 | auto& kenv = ui.kenv; 36 | kobjref comboObject = ui.selectedInspectorObjectRef.get(); 37 | IGObjectSelectorRef(ui, "Object", comboObject); 38 | if (comboObject.get() != ui.selectedInspectorObjectRef.get()) 39 | ui.selectedInspectorObjectRef = comboObject.get(); 40 | if (CKObject* obj = ui.selectedInspectorObjectRef.get()) { 41 | IGObjectNameInput("Name", obj, kenv); 42 | ImGui::Separator(); 43 | ImGui::BeginChild("ObjReflection"); 44 | ImGuiMemberListener ml{ kenv, ui }; 45 | ml.setPropertyInfoList(ui.g_encyclo, obj); 46 | auto f = [&](auto s) {s->virtualReflectMembers(ml, &kenv); }; 47 | EI_ReflectAnyReflectableObject(f, obj); 50 | if (auto* s = obj->dyncast()) { 51 | s->reflectLevel(ml, &kenv); 52 | } 53 | if (auto* animDict = obj->dyncast()) { 54 | AnimDictEditor(ui, animDict, false); 55 | } 56 | if (auto* sndDict = obj->dyncast()) { 57 | SoundDictIDEditor(ui, sndDict, false); 58 | } 59 | ImGui::EndChild(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /WavDocument.cpp: -------------------------------------------------------------------------------- 1 | #include "WavDocument.h" 2 | #include 3 | #include "File.h" 4 | 5 | void WavDocument::read(File * file) 6 | { 7 | uint32_t riffTag = file->readUint32(); 8 | assert(riffTag == 'FFIR'); 9 | uint32_t riffSize = file->readUint32(); 10 | uint32_t riffFormat = file->readUint32(); 11 | assert(riffFormat == 'EVAW'); 12 | uint32_t fmtTag = file->readUint32(); 13 | assert(fmtTag == ' tmf'); 14 | uint32_t fmtSize = file->readUint32(); 15 | assert(fmtSize >= 0x10); 16 | formatTag = file->readUint16(); 17 | //assert(formatTag == 1); // WAVE_FORMAT_PCM 18 | numChannels = file->readUint16(); 19 | samplesPerSec = file->readUint32(); 20 | avgBytesPerSec = file->readUint32(); 21 | blockAlign = file->readUint16(); 22 | pcmBitsPerSample = file->readUint16(); 23 | file->seek(fmtSize - 0x10, SEEK_CUR); 24 | uint32_t dataTag = file->readUint32(); 25 | assert(dataTag == 'atad'); 26 | uint32_t dataSize = file->readUint32(); 27 | data.resize(dataSize); 28 | file->read(data.data(), dataSize); 29 | } 30 | 31 | void WavDocument::write(File * file) 32 | { 33 | file->writeUint32('FFIR'); 34 | file->writeUint32(12 + 16 + 8 + data.size()); 35 | file->writeUint32('EVAW'); 36 | file->writeUint32(' tmf'); 37 | file->writeUint32(0x10); 38 | file->writeUint16(formatTag); 39 | file->writeUint16(numChannels); 40 | file->writeUint32(samplesPerSec); 41 | file->writeUint32(avgBytesPerSec); 42 | file->writeUint16(blockAlign); 43 | file->writeUint16(pcmBitsPerSample); 44 | file->writeUint32('atad'); 45 | file->writeUint32(data.size()); 46 | file->write(data.data(), data.size()); 47 | } 48 | 49 | void WavSampleReader::nextSample() 50 | { 51 | _pnt += _wav->blockAlign; 52 | } 53 | 54 | float WavSampleReader::getSample(int channelIndex) 55 | { 56 | float fs = 0.0f; 57 | switch (_wav->formatTag) { 58 | case 1: 59 | if (_wav->pcmBitsPerSample == 8) 60 | fs = reinterpret_cast(_pnt)[channelIndex] / 128.0f - 1.0f; 61 | else if (_wav->pcmBitsPerSample == 16) 62 | fs = reinterpret_cast(_pnt)[channelIndex] / 32768.0f; 63 | break; 64 | case 3: 65 | if (_wav->pcmBitsPerSample == 32) 66 | fs = reinterpret_cast(_pnt)[channelIndex]; 67 | break; 68 | } 69 | return fs; 70 | } 71 | 72 | bool WavSampleReader::available() 73 | { 74 | return (size_t)(_pnt - _wav->data.data()) < _wav->data.size(); 75 | } 76 | -------------------------------------------------------------------------------- /CoreClasses/CKDictionary.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "KObject.h" 4 | #include "rw.h" 5 | #include "rwsound.h" 6 | #include "CKUtils.h" 7 | 8 | struct CKDictionary : CKCategory<9> { 9 | 10 | }; 11 | 12 | struct CAnimationDictionary : CKSubclass { 13 | uint32_t numAnims = 0, numSets = 1; 14 | std::vector animIndices; 15 | 16 | // Arthur+: 17 | int32_t arSector = 0; 18 | int8_t arUnk = 0; 19 | 20 | void deserialize(KEnvironment* kenv, File *file, size_t length) override; 21 | void serialize(KEnvironment* kenv, File *file) override; 22 | }; 23 | 24 | struct CTextureDictionary : CKSubclass { 25 | //struct Texture { 26 | // char name[33]; 27 | // uint32_t unk1, unk2, unk3; 28 | // RwImage image; 29 | //}; 30 | 31 | //std::vector textures; 32 | RwPITexDict piDict; 33 | //RwNTTexDict nativeDict; // for consoles 34 | 35 | void deserialize(KEnvironment* kenv, File *file, size_t length) override; 36 | void serialize(KEnvironment* kenv, File *file) override; 37 | }; 38 | 39 | struct CKSoundDictionary : CKSubclass { 40 | uint8_t inactive = 0xFD; 41 | //uint32_t numSounds; 42 | struct Sound { 43 | uint32_t id1; float unk2, unk3; uint8_t unk4; 44 | float unk5; uint16_t sampleRate; uint32_t unk7; 45 | uint8_t unk8, unk9, unkA; 46 | uint32_t id2; 47 | kobjref waveObj; // Arthur+ 48 | std::string hdPath; // Romaster 49 | }; 50 | std::vector sounds; 51 | RwSoundDictionary rwSoundDict; 52 | 53 | void deserialize(KEnvironment* kenv, File *file, size_t length) override; 54 | void serialize(KEnvironment* kenv, File *file) override; 55 | }; 56 | 57 | struct CKSoundDictionaryID : CKSubclass { 58 | struct SoundEntry { 59 | bool active = false; 60 | uint32_t id = 0, flags = 0; 61 | kobjref obj; Vector3 refalt; 62 | float volume = 1.0f, speed = 1.0f, replayAfterMin = 0.0f, replayAfterMax = 0.0f; 63 | Vector3 boxHigh = { 15.0f, 15.0f, 15.0f }, boxLow = { -15.0f, -15.0f, -15.0f }; 64 | uint8_t playInLoop = 0; 65 | }; 66 | std::vector soundEntries; 67 | 68 | // XXL2+: 69 | std::vector> x2Sounds; 70 | uint32_t x2Sector = 0; 71 | 72 | void deserialize(KEnvironment* kenv, File *file, size_t length) override; 73 | void serialize(KEnvironment* kenv, File *file) override; 74 | void onLevelLoaded(KEnvironment* kenv) override; 75 | }; -------------------------------------------------------------------------------- /EditorUI/IGObjectList.cpp: -------------------------------------------------------------------------------- 1 | #include "IGObjectList.h" 2 | 3 | #include "EditorInterface.h" 4 | #include "EditorWidgets.h" 5 | 6 | #include "KEnvironment.h" 7 | 8 | #include 9 | 10 | void EditorUI::IGObjectList(EditorInterface& ui) 11 | { 12 | auto& kenv = ui.kenv; 13 | 14 | static const char* catnames[15] = { "Managers", "Services", "Hooks", 15 | "Hook Lives", "Groups", "Group Lives", "Components", "Camera", 16 | "Cinematic blocs", "Dictionaries", "Geometries", "Scene nodes", 17 | "Logic stuff", "Graphical stuff", "Errors" 18 | }; 19 | auto handleObjTreeNode = [&ui](CKObject* obj) { 20 | if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) { 21 | ui.selectedInspectorObjectRef = obj; 22 | ui.wndShowObjInspector = true; 23 | } 24 | IGObjectDragDropSource(ui, obj); 25 | }; 26 | auto enumObjList = [&ui, &kenv, &handleObjTreeNode](KObjectList& objlist) { 27 | for (int i = 0; i < 15; i++) { 28 | if (ImGui::TreeNode(catnames[i])) { 29 | for (auto& cl : objlist.categories[i].type) { 30 | if (!cl.objects.empty()) { 31 | CKObject* first = cl.objects[0]; 32 | if (ImGui::TreeNode(&cl, "%s (%i, %i), %zu objects", first->getClassName(), first->getClassCategory(), first->getClassID(), cl.objects.size())) { 33 | int n = 0; 34 | for (CKObject* obj : cl.objects) { 35 | bool b = ImGui::TreeNodeEx(obj, ImGuiTreeNodeFlags_Leaf, "%i, refCount=%i, %s", n, obj->getRefCount(), kenv.getObjectName(obj)); 36 | handleObjTreeNode(obj); 37 | if (b) 38 | ImGui::TreePop(); 39 | n++; 40 | } 41 | ImGui::TreePop(); 42 | } 43 | } 44 | } 45 | ImGui::TreePop(); 46 | } 47 | } 48 | }; 49 | if (ImGui::TreeNode("Global (GAME)")) { 50 | for (CKObject* obj : kenv.globalObjects) { 51 | bool b = ImGui::TreeNodeEx(obj, ImGuiTreeNodeFlags_Leaf, "%s (%i, %i), refCount=%i, %s", obj->getClassName(), obj->getClassCategory(), obj->getClassID(), obj->getRefCount(), kenv.getObjectName(obj)); 52 | handleObjTreeNode(obj); 53 | if (b) 54 | ImGui::TreePop(); 55 | } 56 | ImGui::TreePop(); 57 | } 58 | if (ImGui::TreeNode("Level (LVL)")) { 59 | enumObjList(kenv.levelObjects); 60 | ImGui::TreePop(); 61 | } 62 | int i = 0; 63 | for (auto& str : kenv.sectorObjects) { 64 | if (ImGui::TreeNode(&str, "Sector %i (STR %02i)", i + 1, i)) { 65 | enumObjList(str); 66 | ImGui::TreePop(); 67 | } 68 | i++; 69 | } 70 | } -------------------------------------------------------------------------------- /KLocalObject.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | struct KEnvironment; 9 | struct File; 10 | 11 | struct KLocalObject { 12 | virtual ~KLocalObject() {} 13 | virtual int getFullID() = 0; 14 | virtual void deserialize(KEnvironment *kenv, File *file, size_t length) = 0; 15 | virtual void serialize(KEnvironment *kenv, File *file) = 0; 16 | virtual KLocalObject *clone() = 0; 17 | }; 18 | 19 | template struct KLocalObjectSub : KLocalObject { 20 | static const int CATEGORY = CLCAT; 21 | static const int CLASS_ID = CLID; 22 | static const int FULL_ID = CLCAT | (CLID << 6); 23 | int getFullID() override { return FULL_ID; } 24 | }; 25 | 26 | struct KLocalPack 27 | { 28 | std::vector> objects; 29 | std::map factories; 30 | int kclassToNotDeserialize = -1; // hack to prevent fonts from CManager2d duplicated for every level+language to be read (6*12=72 times for OG!!!) 31 | 32 | void deserialize(KEnvironment *kenv, File *file); 33 | void serialize(KEnvironment *kenv, File *file); 34 | template void addFactory() { factories[T::FULL_ID] = []() -> KLocalObject* { return new T; }; } 35 | template T* get() { 36 | auto it = std::find_if(objects.begin(), objects.end(), [](const std::unique_ptr &ref) {return ref->getFullID() == T::FULL_ID; }); 37 | if (it != objects.end()) 38 | return dynamic_cast(it->get()); 39 | return nullptr; 40 | } 41 | 42 | KLocalPack() = default; 43 | KLocalPack(const KLocalPack &pack) : factories(pack.factories) { 44 | for(auto &obj : pack.objects) 45 | this->objects.emplace_back(obj->clone()); 46 | } 47 | KLocalPack(KLocalPack &&pack) = default; 48 | KLocalPack &operator=(KLocalPack &pack) { 49 | this->objects.clear(); 50 | for (auto &obj : pack.objects) 51 | this->objects.emplace_back(obj->clone()); 52 | factories = pack.factories; 53 | return *this; 54 | } 55 | KLocalPack &operator=(KLocalPack &&pack) = default; 56 | }; 57 | 58 | struct KUnknownLocalObject : KLocalObject { 59 | int cls_fid = -1; 60 | void *memptr = nullptr; size_t memsize = 0; 61 | 62 | KUnknownLocalObject(int cls_fid) : cls_fid(cls_fid) {} 63 | 64 | int getFullID() override { return cls_fid; } 65 | void deserialize(KEnvironment *kenv, File *file, size_t length) override; 66 | void serialize(KEnvironment *kenv, File *file) override; 67 | KLocalObject *clone() override; 68 | }; -------------------------------------------------------------------------------- /CoreClasses/CKHook.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "KObject.h" 4 | #include "CKUtils.h" 5 | #include "Events.h" 6 | 7 | struct CKHookLife; 8 | struct CKSceneNode; 9 | 10 | struct CKHook : CKMRSubclass>, 0> { 11 | kobjref next; // next hook in a CKGroup 12 | uint32_t unk1 = 0; 13 | kobjref life; 14 | KPostponedRef node; 15 | 16 | // XXL2+: 17 | kobjref x2nextLife; // next life in a CKBundle active/inactive hook life list 18 | uint32_t x2UnkA, x2Sector = 0; 19 | 20 | // Addendum: 21 | int activeSector = -2; 22 | 23 | void reflectMembers2(MemberListener& r, KEnvironment* kenv); 24 | void onLevelLoaded(KEnvironment *kenv) override; 25 | int getAddendumVersion() override; 26 | void deserializeAddendum(KEnvironment* kenv, File* file, int version) override; 27 | void serializeAddendum(KEnvironment* kenv, File* file) override; 28 | 29 | virtual void update() {} 30 | }; 31 | 32 | struct CKHookLife : CKCategory<3> { 33 | kobjref hook; 34 | kobjref nextLife; 35 | uint32_t unk1 = 0; 36 | 37 | void deserialize(KEnvironment* kenv, File *file, size_t length) override; 38 | void serialize(KEnvironment* kenv, File *file) override; 39 | }; 40 | 41 | // TO REMOVE 42 | template using CKHookSubclass = CKMRSubclass; 43 | 44 | // ===== Common hooks ===== 45 | 46 | struct CKGrpBonusPool; 47 | struct CKCrateCpnt; 48 | struct CDynamicGround; 49 | 50 | struct CKHkCrate : CKHookSubclass { 51 | kobjref ckhcUnk0; 52 | kobjref ckhcUnk1; 53 | void reflectMembers2(MemberListener& r, KEnvironment* kenv); 54 | }; 55 | struct CKHkBasicBonus : CKHookSubclass { 56 | kobjref nextBonus; 57 | kobjref pool; 58 | kobjref cpnt; 59 | kobjref hero; 60 | std::array somenums; 61 | 62 | void reflectMembers2(MemberListener& r, KEnvironment* kenv); 63 | }; 64 | struct CKHkLight : CKHookSubclass { 65 | kobjref lightGrpLight; 66 | EventNode lightEvtSeq1; 67 | EventNode lightEvtSeq2; 68 | EventNode lightEvtSeq3; 69 | EventNode lightEvtSeq4; 70 | void reflectMembers2(MemberListener& r, KEnvironment* kenv); 71 | }; 72 | 73 | struct CKHkSkyLife : CKSubclass { 74 | uint32_t skyColor, cloudColor; 75 | 76 | void deserialize(KEnvironment* kenv, File* file, size_t length) override; 77 | void serialize(KEnvironment* kenv, File* file) override; 78 | }; 79 | -------------------------------------------------------------------------------- /renderer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "vecmat.h" 4 | #include 5 | 6 | struct Window; 7 | struct Image; 8 | 9 | typedef void* texture_t; 10 | 11 | struct RVertex { 12 | union { 13 | struct { float x, y, z; }; 14 | float pos[3]; 15 | }; 16 | uint32_t color; 17 | union { 18 | struct { float u, v; }; 19 | float texcoord[2]; 20 | }; 21 | RVertex() {} 22 | RVertex(float _x, float _y, float _z, uint32_t _color, float _u, float _v) : x(_x), y(_y), z(_z), color(_color), u(_u), v(_v) {} 23 | }; 24 | 25 | struct RVertexBuffer 26 | { 27 | virtual ~RVertexBuffer() {} 28 | virtual RVertex *lock() = 0; 29 | virtual void unlock() = 0; 30 | }; 31 | 32 | struct RIndexBuffer 33 | { 34 | virtual ~RIndexBuffer() {} 35 | virtual uint16_t *lock() = 0; 36 | virtual void unlock() = 0; 37 | }; 38 | 39 | struct Renderer { 40 | // Frame start/end 41 | virtual void beginFrame() = 0; 42 | virtual void endFrame() = 0; 43 | virtual void setSize(int width, int height) = 0; 44 | virtual void clearFrame(bool clearColors, bool clearDepth, uint32_t color = 0) = 0; 45 | 46 | // State changes 47 | virtual void setTransformMatrix(const Matrix &matrix) = 0; 48 | virtual void bindTexture(int stage, texture_t texture) = 0; 49 | virtual void unbindTexture(int stage) = 0; 50 | virtual void enableScissor() = 0; 51 | virtual void disableScissor() = 0; 52 | virtual void setScissorRect(int x, int y, int w, int h) = 0; 53 | virtual void setBlendColor(uint32_t color) = 0; 54 | virtual void enableAlphaClip() = 0; 55 | virtual void disableAlphaClip() = 0; 56 | 57 | // Texture management 58 | virtual texture_t createTexture(const Image &image) = 0; 59 | virtual void deleteTexture(texture_t texture) = 0; 60 | 61 | // Buffer drawing 62 | virtual RVertexBuffer *createVertexBuffer(int nv) = 0; 63 | virtual RIndexBuffer *createIndexBuffer(int ni) = 0; 64 | virtual void setVertexBuffer(RVertexBuffer *_rv) = 0; 65 | virtual void setIndexBuffer(RIndexBuffer *_ri) = 0; 66 | virtual void drawBuffer(int first, int count, int vertexBase = 0) = 0; 67 | 68 | // Form Drawing 69 | virtual void initFormDrawing() = 0; 70 | virtual void drawRect(float x, float y, float w, float h, uint32_t c = 0xFFFFFFFF) = 0; 71 | virtual void fillRect(float x, float y, float w, float h, uint32_t c = 0xFFFFFFFF, float u = 0.0f, float v = 0.0f, float o = 1.0f, float p = 1.0f) = 0; 72 | virtual void drawLine3D(const Vector3 &start, const Vector3 &end, uint32_t color = 0xFFFFFFFF) = 0; 73 | 74 | // Model drawing 75 | virtual void initModelDrawing() = 0; 76 | }; 77 | 78 | Renderer *CreateRendererOGL1(Window *window); 79 | Renderer *CreateRendererD3D9(Window *window); -------------------------------------------------------------------------------- /EditorUI/IGCounterEditor.cpp: -------------------------------------------------------------------------------- 1 | #include "IGCounterEditor.h" 2 | 3 | #include "EditorInterface.h" 4 | #include "EditorWidgets.h" 5 | #include "ImGuiMemberListener.h" 6 | 7 | #include "KEnvironment.h" 8 | #include "CoreClasses/CKService.h" 9 | #include "CoreClasses/CKLogic.h" 10 | 11 | #include 12 | 13 | void EditorUI::IGCounterEditor(EditorInterface& ui) 14 | { 15 | static KWeakRef selectedCounter; 16 | 17 | auto& kenv = ui.kenv; 18 | CKSrvCounter* srvCounter = kenv.levelObjects.getFirst(); 19 | if (ImGui::BeginTable("CountersTable", 2, ImGuiTableFlags_Resizable | ImGuiTableFlags_NoHostExtendY, ImGui::GetContentRegionAvail())) { 20 | ImGui::TableNextRow(); 21 | ImGui::TableNextColumn(); 22 | 23 | if (ImGui::Button("New integer")) { 24 | kenv.levelObjects.getClassType().instantiation = KInstantiation::LevelUnique; 25 | srvCounter->integerCounters.emplace_back(kenv.createAndInitObject()); 26 | } 27 | ImGui::SameLine(); 28 | if (ImGui::Button("New timer")) { 29 | kenv.levelObjects.getClassType().instantiation = KInstantiation::LevelUnique; 30 | srvCounter->timeCounters.emplace_back(kenv.createAndInitObject()); 31 | } 32 | ImGui::BeginChild("CounterList"); 33 | for (auto& ref : srvCounter->integerCounters) { 34 | CKIntegerCounter* intCounter = ref.get(); 35 | ImGui::PushID(intCounter); 36 | if (ImGui::Selectable("##sel", selectedCounter == intCounter)) 37 | selectedCounter = intCounter; 38 | ImGui::SameLine(); 39 | ImGui::Text("Integer %s", kenv.getObjectName(intCounter)); 40 | ImGui::PopID(); 41 | } 42 | ImGui::Separator(); 43 | for (auto& ref : srvCounter->timeCounters) { 44 | CKTimeCounter* timeCounter = ref.get(); 45 | ImGui::PushID(timeCounter); 46 | if (ImGui::Selectable("##sel", selectedCounter == timeCounter)) 47 | selectedCounter = timeCounter; 48 | ImGui::SameLine(); 49 | ImGui::Text("Timer %s", kenv.getObjectName(timeCounter)); 50 | ImGui::PopID(); 51 | } 52 | ImGui::EndChild(); 53 | 54 | ImGui::TableNextColumn(); 55 | 56 | if (selectedCounter) { 57 | IGObjectNameInput("Name", selectedCounter.get(), kenv); 58 | ImGuiMemberListener ml{ kenv, ui }; 59 | ml.setPropertyInfoList(ui.g_encyclo, selectedCounter.get()); 60 | if (CKIntegerCounter* intCounter = selectedCounter->dyncast()) { 61 | intCounter->reflectMembers2(ml, &kenv); 62 | } 63 | else if (CKTimeCounter* timeCounter = selectedCounter->dyncast()) { 64 | timeCounter->reflectMembers2(ml, &kenv); 65 | } 66 | } 67 | 68 | ImGui::EndTable(); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Duplicator.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CKUtils.h" 4 | #include 5 | #include 6 | 7 | struct KEnvironment; 8 | struct CKObject; 9 | struct CKHook; 10 | struct CKGroup; 11 | struct CKSceneNode; 12 | namespace EditorUI { struct EditorInterface; } 13 | namespace std::filesystem { class path; } 14 | 15 | namespace KFab { 16 | void saveKFab(KEnvironment& kfab, CKObject* mainObj, const std::filesystem::path& path); 17 | CKObject* loadKFab(KEnvironment& kfab, const std::filesystem::path& path); 18 | KEnvironment makeSimilarKEnv(const KEnvironment& kenv); 19 | } 20 | 21 | // Duplicates hooks, groups and related objects. 22 | // Can be used to clone objects in a same level, or import/export an object to a file. 23 | // Uses member reflection on the hook/group to recursively duplicate used objects. 24 | // Works with some types of hooks and groups, though others might need special treatment. 25 | struct Duplicator : MemberListener { 26 | public: 27 | Duplicator(KEnvironment& kenv, EditorUI::EditorInterface* ui) : kenv(kenv), ui(ui) {} 28 | void doClone(CKObject* object); 29 | void doExport(CKObject* object, const std::filesystem::path& path); 30 | void doImport(const std::filesystem::path& path, CKGroup* parentGroup); 31 | 32 | private: 33 | KEnvironment& kenv; 34 | KEnvironment* srcEnv; KEnvironment* destEnv; 35 | EditorUI::EditorInterface* ui; 36 | std::map cloneMap; 37 | std::function cloneFunction; 38 | MemberFlags currentFlags = MemberFlags::MF_NONE; 39 | 40 | template T* cloneWrap(T* obj, int sector = -1) { 41 | return (T*)cloneFunction(obj, sector); 42 | } 43 | 44 | CKSceneNode* cloneNode(CKSceneNode* original, bool recursive); 45 | static CKGroup* findGroup(CKHook* hook, CKGroup* root); 46 | 47 | virtual void reflect(uint8_t& ref, const char* name); 48 | virtual void reflect(uint16_t& ref, const char* name); 49 | virtual void reflect(uint32_t& ref, const char* name); 50 | virtual void reflect(float& ref, const char* name); 51 | virtual void reflectAnyRef(kanyobjref& ref, int clfid, const char* name); 52 | virtual void reflect(Vector3& ref, const char* name); 53 | virtual void reflect(EventNode& ref, const char* name, CKObject* user); 54 | virtual void reflect(MarkerIndex& ref, const char* name); 55 | virtual void reflect(std::string& ref, const char* name); 56 | virtual void setNextFlags(MemberFlags flags) override; 57 | 58 | CKHook* cloneHook(CKHook* hook, bool reflectMembers = true); 59 | CKGroup* cloneGroup(CKGroup* group); 60 | 61 | CKObject* doCommon(CKObject* object); 62 | CKObject* doTransfer(CKObject* object, KEnvironment* srcEnv, KEnvironment* destEnv); 63 | }; -------------------------------------------------------------------------------- /Events.cpp: -------------------------------------------------------------------------------- 1 | #include "Events.h" 2 | #include "File.h" 3 | #include "KEnvironment.h" 4 | #include "CoreClasses/CKService.h" 5 | #include "CoreClasses/CKLogic.h" 6 | 7 | void EventNodeX1::write(KEnvironment * kenv, File * file) const { 8 | CKSrvEvent* srvEvent = kenv->levelObjects.getFirst(); 9 | assert(srvEvent); 10 | 11 | int16_t actualSeqIndex; uint8_t actualBit; 12 | auto& ids = srvEvent->evtSeqIDs; 13 | auto it = std::find(ids.begin(), ids.end(), seqIndex); 14 | if (it != ids.end()) { 15 | actualSeqIndex = (int16_t)(it - ids.begin()); 16 | actualBit = bit; 17 | } 18 | else { 19 | actualSeqIndex = -1; 20 | actualBit = 0; 21 | } 22 | 23 | uint16_t enc = (actualSeqIndex << 3) | actualBit; 24 | file->writeUint16(enc); 25 | } 26 | 27 | void EventNodeX1::read(KEnvironment * kenv, File * file, CKObject *user) { 28 | int16_t enc = (int16_t)file->readUint16(); 29 | bit = enc & 7; 30 | seqIndex = enc >> 3; 31 | if (seqIndex != -1 && kenv->hasClass()) { 32 | if (CKSrvEvent *srvEvent = kenv->levelObjects.getFirst()) { 33 | srvEvent->sequences[seqIndex].users.push_back(user); 34 | srvEvent->sequences[seqIndex].userFound = true; 35 | } 36 | } 37 | } 38 | 39 | void MarkerIndex::write(KEnvironment* kenv, File* file) const 40 | { 41 | file->writeInt32(index); 42 | if (kenv->version >= KEnvironment::KVERSION_ARTHUR) 43 | file->writeInt32(arSecondIndex); 44 | } 45 | 46 | void MarkerIndex::read(KEnvironment* kenv, File* file) 47 | { 48 | index = file->readInt32(); 49 | if (kenv->version >= KEnvironment::KVERSION_ARTHUR) 50 | arSecondIndex = file->readInt32(); 51 | } 52 | 53 | void EventNodeX2::write(KEnvironment* kenv, File* file) 54 | { 55 | file->writeUint32((uint32_t)datas.size()); 56 | for (auto& ref : datas) 57 | kenv->writeObjID(file, ref.get()); 58 | } 59 | 60 | void EventNodeX2::read(KEnvironment* kenv, File* file, CKObject* user) 61 | { 62 | datas.resize(file->readUint32()); 63 | for (auto& ref : datas) 64 | ref = KWeakRef(kenv->readObjPnt(file)->cast()); 65 | } 66 | 67 | void EventNodeX2::clean() 68 | { 69 | auto it = std::remove_if(datas.begin(), datas.end(), [](KWeakRef& ref) {return !ref; }); 70 | datas.erase(it, datas.end()); 71 | } 72 | 73 | void EventNode::write(KEnvironment* kenv, File* file) 74 | { 75 | if (kenv->version < KEnvironment::KVERSION_XXL2) 76 | enx1.write(kenv, file); 77 | else 78 | enx2.write(kenv, file); 79 | } 80 | 81 | void EventNode::read(KEnvironment* kenv, File* file, CKObject* user) 82 | { 83 | if (kenv->version < KEnvironment::KVERSION_XXL2) 84 | enx1.read(kenv, file, user); 85 | else 86 | enx2.read(kenv, file, user); 87 | } 88 | -------------------------------------------------------------------------------- /EditorUI/PropFlagsEditor.cpp: -------------------------------------------------------------------------------- 1 | #include "PropFlagsEditor.h" 2 | #include 3 | #include 4 | #include 5 | 6 | bool PropFlagsEditor(unsigned int& flagsValue, const nlohmann::json& flagsInfo) { 7 | bool modified = false; 8 | 9 | std::vector> flagsList; 10 | flagsList.reserve(flagsInfo.size()); 11 | for (const auto& [key, jsobj] : flagsInfo.items()) { 12 | auto sep = key.find('-'); 13 | int bitStartIndex = 0, bitEndIndex = 0; 14 | if (sep == key.npos) { 15 | std::from_chars(key.data(), key.data() + key.size(), bitStartIndex); 16 | bitEndIndex = bitStartIndex; 17 | } 18 | else { 19 | std::from_chars(key.data(), key.data() + sep, bitStartIndex); 20 | std::from_chars(key.data() + sep + 1, key.data() + key.size(), bitEndIndex); 21 | } 22 | flagsList.emplace_back(bitStartIndex, bitEndIndex, &jsobj); 23 | } 24 | std::ranges::sort(flagsList, {}, [](const auto& key) { return std::get<0>(key); }); 25 | 26 | for (const auto& [bitStartIndex, bitEndIndex, jsptr] : flagsList) { 27 | const auto& jsobj = *jsptr; 28 | unsigned int mask = ((1 << (bitEndIndex - bitStartIndex + 1)) - 1) << bitStartIndex; 29 | if (jsobj.is_string()) { 30 | const auto& name = jsobj.get_ref(); 31 | if (bitStartIndex == bitEndIndex) { 32 | modified |= ImGui::CheckboxFlags(name.c_str(), &flagsValue, 1 << bitStartIndex); 33 | } 34 | else { 35 | unsigned int v = (flagsValue & mask) >> bitStartIndex; 36 | ImGui::SetNextItemWidth(48.0f); 37 | bool b = ImGui::InputScalar(name.c_str(), ImGuiDataType_U32, &v); 38 | if (b) { 39 | modified = true; 40 | flagsValue = (flagsValue & ~mask) | ((v << bitStartIndex) & mask); 41 | } 42 | } 43 | } 44 | else if (jsobj.is_object()) { 45 | unsigned int v = (flagsValue & mask) >> bitStartIndex; 46 | const auto& name = jsobj.at("name").get_ref(); 47 | std::string preview = std::to_string(v); 48 | if (auto it = jsobj.find(preview); it != jsobj.end()) 49 | preview = it->get_ref(); 50 | bool b = false; 51 | if (ImGui::BeginCombo(name.c_str(), preview.c_str())) { 52 | b |= ImGui::InputScalar("Value", ImGuiDataType_U32, &v); 53 | for (auto& [ek, ev] : jsobj.items()) { 54 | if (ek != "name") { 55 | if (ImGui::Selectable(ev.get_ref().c_str())) { 56 | v = std::stoi(ek); 57 | b = true; 58 | } 59 | } 60 | } 61 | ImGui::EndCombo(); 62 | } 63 | if (b) { 64 | modified = true; 65 | flagsValue = (flagsValue & ~mask) | ((v << bitStartIndex) & mask); 66 | } 67 | 68 | } 69 | } 70 | return modified; 71 | } 72 | -------------------------------------------------------------------------------- /EditorUI/IGLineEditor.cpp: -------------------------------------------------------------------------------- 1 | #include "IGLineEditor.h" 2 | #include "EditorInterface.h" 3 | #include "EditorWidgets.h" 4 | 5 | #include "KEnvironment.h" 6 | #include "CoreClasses/CKLogic.h" 7 | 8 | #include 9 | 10 | void EditorUI::IGLineEditor(EditorInterface& ui) 11 | { 12 | static KWeakRef lineObject; 13 | kobjref lineTempRef = lineObject.get(); 14 | IGObjectSelectorRef(ui, "Line", lineTempRef); 15 | lineObject = lineTempRef.get(); 16 | if (!lineObject) return; 17 | if (CKLine* line = lineObject->dyncast()) { 18 | bool update = false; 19 | for (size_t i = 0; i < line->points.size(); ++i) { 20 | ImGui::PushID((int)i); 21 | ImGui::DragFloat3("##LinePoint", &line->points[i].x, 0.1f); 22 | ImGui::SameLine(); 23 | if (ImGui::Button("D")) { 24 | line->points.insert(line->points.begin() + i, line->points[i]); 25 | update = true; 26 | } 27 | if (ImGui::IsItemHovered()) ImGui::SetTooltip("Duplicate"); 28 | ImGui::SameLine(); 29 | if (ImGui::Button("X")) { 30 | line->points.erase(line->points.begin() + i); 31 | update = true; 32 | } 33 | if (ImGui::IsItemHovered()) ImGui::SetTooltip("Remove"); 34 | ImGui::PopID(); 35 | } 36 | if (update) { 37 | line->numSegments = (uint8_t)(line->points.size() - 1); 38 | line->segmentLengths.resize(line->numSegments); 39 | float total = 0.0f; 40 | for (size_t i = 0; i < (size_t)line->numSegments; ++i) { 41 | line->segmentLengths[i] = (line->points[i + 1] - line->points[i]).len3(); 42 | total += line->segmentLengths[i]; 43 | } 44 | line->totalLength = total; 45 | } 46 | } 47 | if (CKFlaggedPath* path = lineObject->dyncast()) { 48 | IGObjectSelectorRef(ui, "Path's line", path->line); 49 | for (size_t i = 0; i < path->numPoints; ++i) { 50 | ImGui::PushID((int)i); 51 | ImGui::SetNextItemWidth(64.0f); 52 | ImGui::DragFloat("##PathElemValue", &path->pntValues[i], 0.1f); 53 | ImGui::SameLine(); 54 | ImGui::SetNextItemWidth(128.0f); 55 | IGEventSelector(ui, "", path->pntEvents[i]); 56 | ImGui::SameLine(); 57 | if (ImGui::Button("D")) { 58 | path->pntValues.insert(path->pntValues.begin() + i, path->pntValues[i]); 59 | path->pntEvents.insert(path->pntEvents.begin() + i, path->pntEvents[i]); 60 | path->numPoints += 1; 61 | } 62 | if (ImGui::IsItemHovered()) ImGui::SetTooltip("Duplicate"); 63 | ImGui::SameLine(); 64 | if (ImGui::Button("X")) { 65 | path->pntValues.erase(path->pntValues.begin() + i); 66 | path->pntEvents.erase(path->pntEvents.begin() + i); 67 | path->numPoints -= 1; 68 | --i; 69 | } 70 | if (ImGui::IsItemHovered()) ImGui::SetTooltip("Remove"); 71 | ImGui::PopID(); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /adpcm-xq/adpcm-lib.h: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////// 2 | // **** ADPCM-XQ **** // 3 | // Xtreme Quality ADPCM Encoder/Decoder // 4 | // Copyright (c) 2024 David Bryant. // 5 | // All Rights Reserved. // 6 | // Distributed under the BSD Software License (see license.txt) // 7 | //////////////////////////////////////////////////////////////////////////// 8 | 9 | #ifndef ADPCMLIB_H_ 10 | #define ADPCMLIB_H_ 11 | 12 | #define NOISE_SHAPING_OFF 0 // flat noise (no shaping) 13 | #define NOISE_SHAPING_STATIC 0x100 // static 1st-order shaping (configurable, highpass default) 14 | #define NOISE_SHAPING_DYNAMIC 0x200 // dynamically tilted noise based on signal 15 | 16 | #define LOOKAHEAD_DEPTH 0x0ff // depth of search 17 | #define LOOKAHEAD_EXHAUSTIVE 0x800 // full breadth of search (all branches taken) 18 | #define LOOKAHEAD_NO_BRANCHING 0x400 // no branches taken (internal use only!) 19 | 20 | #if defined(_MSC_VER) && _MSC_VER < 1600 21 | typedef unsigned __int64 uint64_t; 22 | typedef unsigned __int32 uint32_t; 23 | typedef unsigned __int16 uint16_t; 24 | typedef unsigned __int8 uint8_t; 25 | typedef __int64 int64_t; 26 | typedef __int32 int32_t; 27 | typedef __int16 int16_t; 28 | typedef __int8 int8_t; 29 | #else 30 | #include 31 | #endif 32 | 33 | #ifdef __cplusplus 34 | extern "C" { 35 | #endif 36 | 37 | /* adpcm-lib.c */ 38 | 39 | int adpcm_sample_count_to_block_size (int sample_count, int num_chans, int bps); 40 | int adpcm_block_size_to_sample_count (int block_size, int num_chans, int bps); 41 | int adpcm_align_block_size (int block_size, int num_chans, int bps, int round_up); 42 | void *adpcm_create_context (int num_channels, int sample_rate, int lookahead, int noise_shaping); 43 | void adpcm_set_shaping_weight (void *p, double shaping_weight); 44 | int adpcm_encode_block_ex (void *p, uint8_t *outbuf, size_t *outbufsize, const int16_t *inbuf, int inbufcount, int bps); 45 | int adpcm_encode_block (void *p, uint8_t *outbuf, size_t *outbufsize, const int16_t *inbuf, int inbufcount); 46 | int adpcm_decode_block_ex (int16_t *outbuf, const uint8_t *inbuf, size_t inbufsize, int channels, int bps); 47 | int adpcm_decode_block (int16_t *outbuf, const uint8_t *inbuf, size_t inbufsize, int channels); 48 | void adpcm_free_context (void *p); 49 | 50 | /* adpcm-dns.c */ 51 | 52 | void generate_dns_values (const int16_t *samples, int sample_count, int num_chans, int sample_rate, 53 | int16_t *values, int16_t min_value, int16_t last_value); 54 | 55 | #ifdef __cplusplus 56 | } 57 | #endif 58 | 59 | 60 | #endif /* ADPCMLIB_H_ */ 61 | -------------------------------------------------------------------------------- /imgui/imgui.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Fichiers d%27en-tête 20 | 21 | 22 | Fichiers d%27en-tête 23 | 24 | 25 | Fichiers d%27en-tête 26 | 27 | 28 | Fichiers d%27en-tête 29 | 30 | 31 | Fichiers d%27en-tête 32 | 33 | 34 | Fichiers d%27en-tête 35 | 36 | 37 | Fichiers d%27en-tête 38 | 39 | 40 | Fichiers d%27en-tête 41 | 42 | 43 | Fichiers d%27en-tête 44 | 45 | 46 | Fichiers d%27en-tête 47 | 48 | 49 | 50 | 51 | Fichiers sources 52 | 53 | 54 | Fichiers sources 55 | 56 | 57 | Fichiers sources 58 | 59 | 60 | Fichiers sources 61 | 62 | 63 | Fichiers sources 64 | 65 | 66 | Fichiers sources 67 | 68 | 69 | Fichiers sources 70 | 71 | 72 | -------------------------------------------------------------------------------- /XXL-Editor.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.11.35208.52 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "XXL-Editor", "XXL-Editor.vcxproj", "{1DD045C4-CE0C-4058-9C28-A3E94EDC0C4B}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "imgui", "imgui\imgui.vcxproj", "{0801C4CF-F476-470C-9AE7-92F509C34705}" 9 | EndProject 10 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "adpcm-xq", "adpcm-xq\adpcm-xq.vcxproj", "{05BCB6DC-4785-41DE-9527-F60D940DB9E1}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|x64 = Debug|x64 15 | Debug|x86 = Debug|x86 16 | Release|x64 = Release|x64 17 | Release|x86 = Release|x86 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {1DD045C4-CE0C-4058-9C28-A3E94EDC0C4B}.Debug|x64.ActiveCfg = Debug|x64 21 | {1DD045C4-CE0C-4058-9C28-A3E94EDC0C4B}.Debug|x64.Build.0 = Debug|x64 22 | {1DD045C4-CE0C-4058-9C28-A3E94EDC0C4B}.Debug|x86.ActiveCfg = Debug|Win32 23 | {1DD045C4-CE0C-4058-9C28-A3E94EDC0C4B}.Debug|x86.Build.0 = Debug|Win32 24 | {1DD045C4-CE0C-4058-9C28-A3E94EDC0C4B}.Release|x64.ActiveCfg = Release|x64 25 | {1DD045C4-CE0C-4058-9C28-A3E94EDC0C4B}.Release|x64.Build.0 = Release|x64 26 | {1DD045C4-CE0C-4058-9C28-A3E94EDC0C4B}.Release|x86.ActiveCfg = Release|Win32 27 | {1DD045C4-CE0C-4058-9C28-A3E94EDC0C4B}.Release|x86.Build.0 = Release|Win32 28 | {0801C4CF-F476-470C-9AE7-92F509C34705}.Debug|x64.ActiveCfg = Debug|x64 29 | {0801C4CF-F476-470C-9AE7-92F509C34705}.Debug|x64.Build.0 = Debug|x64 30 | {0801C4CF-F476-470C-9AE7-92F509C34705}.Debug|x86.ActiveCfg = Debug|Win32 31 | {0801C4CF-F476-470C-9AE7-92F509C34705}.Debug|x86.Build.0 = Debug|Win32 32 | {0801C4CF-F476-470C-9AE7-92F509C34705}.Release|x64.ActiveCfg = Release|x64 33 | {0801C4CF-F476-470C-9AE7-92F509C34705}.Release|x64.Build.0 = Release|x64 34 | {0801C4CF-F476-470C-9AE7-92F509C34705}.Release|x86.ActiveCfg = Release|Win32 35 | {0801C4CF-F476-470C-9AE7-92F509C34705}.Release|x86.Build.0 = Release|Win32 36 | {05BCB6DC-4785-41DE-9527-F60D940DB9E1}.Debug|x64.ActiveCfg = Debug|x64 37 | {05BCB6DC-4785-41DE-9527-F60D940DB9E1}.Debug|x64.Build.0 = Debug|x64 38 | {05BCB6DC-4785-41DE-9527-F60D940DB9E1}.Debug|x86.ActiveCfg = Debug|Win32 39 | {05BCB6DC-4785-41DE-9527-F60D940DB9E1}.Debug|x86.Build.0 = Debug|Win32 40 | {05BCB6DC-4785-41DE-9527-F60D940DB9E1}.Release|x64.ActiveCfg = Release|x64 41 | {05BCB6DC-4785-41DE-9527-F60D940DB9E1}.Release|x64.Build.0 = Release|x64 42 | {05BCB6DC-4785-41DE-9527-F60D940DB9E1}.Release|x86.ActiveCfg = Release|Win32 43 | {05BCB6DC-4785-41DE-9527-F60D940DB9E1}.Release|x86.Build.0 = Release|Win32 44 | EndGlobalSection 45 | GlobalSection(SolutionProperties) = preSolution 46 | HideSolutionNode = FALSE 47 | EndGlobalSection 48 | GlobalSection(ExtensibilityGlobals) = postSolution 49 | SolutionGuid = {4A9DD886-F69D-4F63-B6F4-241D4534A926} 50 | EndGlobalSection 51 | EndGlobal 52 | -------------------------------------------------------------------------------- /CoreClasses/CKComponent.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "KObject.h" 4 | #include 5 | #include "vecmat.h" 6 | #include "CKUtils.h" 7 | 8 | struct CKSceneNode; 9 | struct CKSoundDictionaryID; 10 | 11 | struct CKComponent : CKMemberReflectable> { 12 | 13 | }; 14 | 15 | struct CKCrateCpnt : CKMRSubclass { 16 | struct CameraQuakeDatas { // TODO: Move to own class CKCameraQuakeDatas 17 | std::vector data1, data2; 18 | float fnFloat; 19 | }; 20 | kobjref group; 21 | kobjref particleNode; 22 | kobjref soundIds; 23 | kobjref projectiles; 24 | kobjref crateNode; 25 | // -- XXL2+: -- 26 | std::array x2CamQuakeDatas; 27 | float x2UnkFlt; 28 | // ---- 29 | std::array unk1; 30 | uint8_t unk7; 31 | std::array pack1; 32 | std::array pack2; 33 | std::array bonuses; 34 | uint16_t unk8; 35 | uint8_t unk9; 36 | 37 | void deserialize(KEnvironment* kenv, File* file, size_t length) override; 38 | void serialize(KEnvironment* kenv, File* file) override; 39 | void reflectMembers2(MemberListener& r, KEnvironment* kenv); 40 | }; 41 | 42 | struct CKEnemyCpnt : CKMRSubclass { 43 | // Some names proposed by Spork 44 | uint32_t flags = 0x3007ea9; 45 | uint8_t health = 3, damage = 1; 46 | float unk1 = 0.3f; 47 | float walkAnimSpeed = 7.0f; 48 | float coverAnimSpeed = 2.7f; 49 | float speed1 = 12.0f; 50 | float runSpeed1 = 9.0f; 51 | float speed2 = 2.5f; 52 | float speed3 = 24.0f; 53 | float runSpeed2 = 12.0f; 54 | float speed4 = 8.0f; 55 | float speed5 = 6.283185f; 56 | float unkf1 = 3.14f; 57 | float unkf2 = 6.28f; 58 | float unkf3 = 0.05f; 59 | float coverRange = 0.5f; 60 | float unkf5 = 0.392699f; 61 | float attackCooldown = 2.0f; 62 | float unkf7 = 3.0f; 63 | float unkf8 = 2.0f; 64 | float unkf9 = 1.0f; 65 | float targeting = 1.0f; 66 | float unkLast = 1.0f; 67 | 68 | // New Romaster Values: 69 | uint8_t ecRoma1 = 3; 70 | uint8_t ecRoma2 = 1; 71 | float ecRoma3 = 0.1f; 72 | float ecRoma4 = 0.0f; 73 | float ecRoma5 = 0.1f; 74 | float ecRoma6 = 0.0f; 75 | uint8_t ecRoma7 = 8; 76 | 77 | void reflectMembers2(MemberListener& r, KEnvironment* kenv); 78 | }; 79 | 80 | struct CKShadowCpnt : CKMRSubclass { 81 | std::array scpValues; 82 | std::array scpBytes; 83 | void reflectMembers2(MemberListener& r, KEnvironment* kenv); 84 | }; 85 | 86 | struct CKBonusCpnt : CKMRSubclass { 87 | float ckbcUnk0; 88 | kobjref ckbcUnk1; 89 | float ckbcUnk2; 90 | float ckbcUnk3; 91 | float ckbcUnk4; 92 | float ckbcUnk5; 93 | float ckbcUnk6; 94 | float ckbcUnk7; 95 | float ckbcUnk8; 96 | float ckbcUnk9; 97 | float ckbcUnk10; 98 | float ckbcUnk11; 99 | float ckbcUnk12; 100 | float ckbcUnk13; 101 | uint8_t ckbcUnk14; 102 | void reflectMembers2(MemberListener& r, KEnvironment* kenv); 103 | }; 104 | -------------------------------------------------------------------------------- /GameLauncher.cpp: -------------------------------------------------------------------------------- 1 | #include "GameLauncher.h" 2 | #define WIN32_LEAN_AND_MEAN 3 | #include 4 | #include 5 | #include 6 | 7 | bool GameLauncher::openGame() 8 | { 9 | STARTUPINFOW startInfo; 10 | PROCESS_INFORMATION procInfo; 11 | ZeroMemory(&startInfo, sizeof(startInfo)); 12 | ZeroMemory(&procInfo, sizeof(procInfo)); 13 | startInfo.cb = sizeof(startInfo); 14 | namespace fs = std::filesystem; 15 | if (!CreateProcessW(fs::u8path(modulePath).c_str(), NULL, NULL, NULL, FALSE, 0, NULL, fs::u8path(gamePath).c_str(), &startInfo, &procInfo)) { 16 | return false; 17 | } 18 | this->processHandle = procInfo.hProcess; 19 | this->threadHandle = procInfo.hThread; 20 | return true; 21 | } 22 | 23 | void GameLauncher::closeGame() 24 | { 25 | if (processHandle) { 26 | CloseHandle(processHandle); 27 | CloseHandle(threadHandle); 28 | processHandle = nullptr; 29 | threadHandle = nullptr; 30 | } 31 | } 32 | 33 | bool GameLauncher::isGameRunning() 34 | { 35 | DWORD exitCode; 36 | if(processHandle) 37 | if (GetExitCodeProcess(processHandle, &exitCode)) 38 | if (exitCode == STILL_ACTIVE) 39 | return true; 40 | return false; 41 | } 42 | 43 | static BOOL CALLBACK EnumCallback(HWND hWnd, LPARAM lParam) 44 | { 45 | GameLauncher *gl = (GameLauncher*)lParam; 46 | DWORD pid; 47 | GetWindowThreadProcessId(hWnd, &pid); 48 | if (pid == GetProcessId(gl->processHandle)) { 49 | //char wndName[32]; 50 | //if (GetClassNameA(hWnd, wndName, sizeof(wndName))) { 51 | // if (!strcmp(wndName, "Asterix & Obelix XXL")) { 52 | gl->windowHandle = hWnd; 53 | return FALSE; 54 | // } 55 | //} 56 | } 57 | return TRUE; 58 | } 59 | 60 | bool GameLauncher::loadLevel(uint32_t lvlNum) 61 | { 62 | static const uint32_t yellowPagePtr[8] = { 0, 0x6621F4, 0x663D04, 0x72D944, 0x765BF8, 0, 0, 0 }; 63 | static const uint32_t ypGameMgrOffset[8] = { 0, 0x8c, 0x80, 0x88, 0x8c, 0, 0, 0 }; 64 | static const uint32_t gmLevelOffset[8] = { 0, 8, 0x70, 0x158, 0x1A4, 0, 0, 0 }; 65 | if (yellowPagePtr[version] == 0) return false; 66 | 67 | if (!isGameRunning()) { 68 | closeGame(); 69 | bool b = openGame(); 70 | if (!b) return false; 71 | } 72 | uint32_t adYellowPages = 0, adGameManager = 0; 73 | while (!adYellowPages) { 74 | BOOL b = ReadProcessMemory(processHandle, (void*)yellowPagePtr[version], &adYellowPages, 4, NULL); 75 | if (!b) return false; 76 | Sleep(250); 77 | } 78 | //if (firstTime) Sleep(1000); 79 | //printf("%08X ", adYellowPages); 80 | while (!adGameManager) { 81 | BOOL b = ReadProcessMemory(processHandle, (void*)(adYellowPages + ypGameMgrOffset[version]), &adGameManager, 4, NULL); 82 | if (!b) return false; 83 | Sleep(250); 84 | } 85 | //printf("%08X ", adGameManager); 86 | BOOL b = WriteProcessMemory(processHandle, (void*)(adGameManager + gmLevelOffset[version]), &lvlNum, 4, NULL); 87 | if (!b) return false; 88 | 89 | // Bring window in front 90 | windowHandle = nullptr; 91 | EnumWindows(EnumCallback, (LPARAM)this); 92 | if (windowHandle) { 93 | HWND wnd = (HWND)windowHandle; 94 | BringWindowToTop(wnd); 95 | OpenIcon(wnd); 96 | } 97 | return true; 98 | } 99 | -------------------------------------------------------------------------------- /resources/Properties_Common.json: -------------------------------------------------------------------------------- 1 | { 2 | "classes": [ 3 | { 4 | "name": "CKSoundManager", 5 | "category": 0, 6 | "id": 3, 7 | "properties": { 8 | "sizeFor_ksndmgrDings": "Num Tunes", 9 | "ksndmgrDings": "Tune" 10 | } 11 | }, 12 | { 13 | "category": 8, 14 | "id": 22, 15 | "name": "CKDisplayPictureCinematicBloc", 16 | "properties": { 17 | "ckdpcbColor": { 18 | "name": "Color", 19 | "type": "color" 20 | } 21 | } 22 | }, 23 | { 24 | "category": 8, 25 | "id": 25, 26 | "name": "CKStartEventCinematicBloc", 27 | "properties": { 28 | "seEvtNode": { 29 | "name": "Event trigger", 30 | "elbName": "Déclencheur d'évènement" 31 | } 32 | } 33 | }, 34 | { 35 | "name": "CGround", 36 | "category": 12, 37 | "id": 18, 38 | "properties": { 39 | "param1": { 40 | "name": "param1", 41 | "bitFlags": { 42 | "1": "Sliding" 43 | } 44 | }, 45 | "param2": { 46 | "name": "param2", 47 | "bitFlags": { 48 | "0": "Active", 49 | "1": "Water (param = Y coord of water surface)", 50 | "3": "Ceiling" 51 | } 52 | } 53 | } 54 | }, 55 | { 56 | "category": 12, 57 | "id": 37, 58 | "name": "CKCinematicScene", 59 | "properties": { 60 | "csBarsColor": { 61 | "name": "Bars color", 62 | "type": "color" 63 | } 64 | } 65 | }, 66 | { 67 | "category": 12, 68 | "id": 135, 69 | "name": "CKTimeCounter", 70 | "properties": { 71 | "event1": { 72 | "name": "Max time reached", 73 | "elbName": "Valeur max atteinte" 74 | }, 75 | "event2": { 76 | "name": "Max loop reached", 77 | "elbName": "Loop max atteint" 78 | } 79 | } 80 | }, 81 | { 82 | "category": 12, 83 | "id": 136, 84 | "name": "CKIntegerCounter", 85 | "properties": { 86 | "event1": { 87 | "name": "Max reached", 88 | "elbName": "Valeur max atteinte" 89 | }, 90 | "event2": { 91 | "name": "Min reached", 92 | "elbName": "Valeur min atteinte" 93 | }, 94 | "event3": { 95 | "name": "Max loop reached", 96 | "elbName": "Loop max atteint" 97 | } 98 | } 99 | }, 100 | { 101 | "category": 12, 102 | "id": 160, 103 | "name": "CKDetectorEvent", 104 | "properties": { 105 | "deOnEnter": { 106 | "name": "Entrance", 107 | "elbName": "Entrée" 108 | }, 109 | "deOnExit": { 110 | "name": "Exit", 111 | "elbName": "Sortie" 112 | }, 113 | "deOnPresence": { 114 | "name": "Presence", 115 | "elbName": "Présence" 116 | } 117 | } 118 | } 119 | ] 120 | } -------------------------------------------------------------------------------- /resources/Events_XXL2Plus.json: -------------------------------------------------------------------------------- 1 | { 2 | "classes": [ 3 | { 4 | "category": 12, 5 | "id": 135, 6 | "name": "CKTimeCounter", 7 | "events": [ 8 | { 9 | "id": "2103", 10 | "name": "Reset loop count to 0", 11 | "dataType": "none" 12 | }, 13 | { 14 | "id": "2104", 15 | "name": "Reset all", 16 | "dataType": "none" 17 | }, 18 | { 19 | "id": "2105", 20 | "name": "Start timer from current time", 21 | "dataType": "none" 22 | }, 23 | { 24 | "id": "2106", 25 | "name": "Start timer from loop start", 26 | "dataType": "none" 27 | }, 28 | { 29 | "id": "2107", 30 | "name": "Start timer from beginning", 31 | "dataType": "none" 32 | }, 33 | { 34 | "id": "2108", 35 | "name": "Stop timer", 36 | "dataType": "none" 37 | }, 38 | { 39 | "id": "2109", 40 | "name": "Stop timer and reset to loop start", 41 | "dataType": "none" 42 | }, 43 | { 44 | "id": "210A", 45 | "name": "Stop timer and reset to beginning", 46 | "dataType": "none" 47 | }, 48 | { 49 | "id": "210B", 50 | "name": "Add time", 51 | "dataType": "float", 52 | "parameter": "Seconds" 53 | }, 54 | { 55 | "id": "210C", 56 | "name": "Subtract time", 57 | "dataType": "float", 58 | "parameter": "Seconds" 59 | } 60 | ] 61 | }, 62 | { 63 | "category": 12, 64 | "id": 136, 65 | "name": "CKIntegerCounter", 66 | "events": [ 67 | { 68 | "id": "2100", 69 | "name": "Set to start value", 70 | "dataType": "none" 71 | }, 72 | { 73 | "id": "2101", 74 | "name": "Set to minimum value", 75 | "dataType": "none" 76 | }, 77 | { 78 | "id": "2102", 79 | "name": "Set to maximum value", 80 | "dataType": "none" 81 | }, 82 | { 83 | "id": "2103", 84 | "name": "Reset loop count to 0", 85 | "dataType": "none" 86 | }, 87 | { 88 | "id": "2104", 89 | "name": "Reset all", 90 | "dataType": "none" 91 | }, 92 | { 93 | "id": "210B", 94 | "name": "Add value", 95 | "dataType": "int", 96 | "parameter": "Value" 97 | }, 98 | { 99 | "id": "210C", 100 | "name": "Subtract value", 101 | "dataType": "int", 102 | "parameter": "Value" 103 | } 104 | ] 105 | }, 106 | { 107 | "category": 12, 108 | "id": 205, 109 | "name": "CKGameModule", 110 | "events": [ 111 | { 112 | "id": "0200", 113 | "name": "Set completion flag", 114 | "dataType": "bool", 115 | "parameter": "Completed" 116 | } 117 | ] 118 | } 119 | ] 120 | } -------------------------------------------------------------------------------- /File.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | struct File { 9 | virtual void read(void *out, size_t length) = 0; 10 | virtual void write(const void *out, size_t length) = 0; 11 | virtual void seek(size_t pos, int mode) = 0; 12 | virtual size_t tell() = 0; 13 | virtual ~File() {} 14 | 15 | inline uint8_t readUint8() { uint8_t res; read(&res, 1); return res; } 16 | inline uint16_t readUint16() { uint16_t res; read(&res, 2); return res; } 17 | inline uint32_t readUint32() { uint32_t res; read(&res, 4); return res; } 18 | inline int8_t readInt8() { int8_t res; read(&res, 1); return res; } 19 | inline int16_t readInt16() { int16_t res; read(&res, 2); return res; } 20 | inline int32_t readInt32() { int32_t res; read(&res, 4); return res; } 21 | inline float readFloat() { float res; read(&res, 4); return res; } 22 | inline std::string readString(int numChars) { 23 | std::string s; 24 | s.reserve(numChars); 25 | for (int i = 0; i < numChars; i++) 26 | s.push_back(readUint8()); 27 | return s; 28 | } 29 | inline std::string readStringZ() { 30 | std::string s; 31 | while (uint8_t ch = readUint8()) 32 | s.push_back(ch); 33 | return s; 34 | } 35 | template std::string readSizedString() { T len; read(&len, sizeof(T)); if (len == (T)-1) len = 0; return readString(len); } 36 | 37 | inline void writeUint8(uint8_t val) { write(&val, 1); } 38 | inline void writeUint16(uint16_t val) { write(&val, 2); } 39 | inline void writeUint32(uint32_t val) { write(&val, 4); } 40 | inline void writeInt8(int8_t val) { write(&val, 1); } 41 | inline void writeInt16(int16_t val) { write(&val, 2); } 42 | inline void writeInt32(int32_t val) { write(&val, 4); } 43 | inline void writeFloat(float val) { write(&val, 4); } 44 | inline void writeString(const std::string &str) { write(str.data(), str.size()); } 45 | template void writeSizedString(const std::string &str) { T len = (T)str.size(); write(&len, sizeof(T)); writeString(str); } 46 | }; 47 | 48 | struct IOFile : File { 49 | FILE *file = nullptr; 50 | void read(void *out, size_t length) override; 51 | void write(const void *out, size_t length) override; 52 | void seek(size_t pos, int mode) override; 53 | size_t tell() override; 54 | void close(); 55 | void open(const char* name, const char* mode); 56 | void open(const wchar_t *name, const char *mode); 57 | IOFile() : file(nullptr) {} 58 | IOFile(const char* name, const char* mode) { open(name, mode); } 59 | IOFile(const wchar_t* name, const char* mode) { open(name, mode); } 60 | IOFile(IOFile &&af) { file = af.file; af.file = nullptr; } 61 | ~IOFile() { close(); } 62 | }; 63 | 64 | struct MemFile : File { 65 | uint8_t *_startptr = nullptr; 66 | uint8_t *_curptr = nullptr; 67 | void read(void *out, size_t length) override; 68 | void write(const void *out, size_t length) override; 69 | void seek(size_t pos, int mode) override; 70 | size_t tell() override; 71 | MemFile(void *ptr) : _startptr((uint8_t*)ptr), _curptr((uint8_t*)ptr) {}; 72 | }; 73 | 74 | File * GetResourceFile(const char *resName); 75 | std::pair GetResourceContent(const char* resName); 76 | 77 | struct KEnvironment; 78 | std::unique_ptr GetGzipFile(const wchar_t* path, const char* mode, KEnvironment& kenv); -------------------------------------------------------------------------------- /rwrenderer.cpp: -------------------------------------------------------------------------------- 1 | #include "rwrenderer.h" 2 | #include "CoreClasses/CKDictionary.h" 3 | 4 | uint32_t ProTexDict::globalVersion = 0; 5 | 6 | ProTexDict::ProTexDict(Renderer * gfx, CTextureDictionary * ctd) { 7 | _gfx = gfx; 8 | reset(ctd); 9 | } 10 | 11 | ProTexDict::~ProTexDict() { 12 | for (auto &e : dict) { 13 | _gfx->deleteTexture(e.second); 14 | } 15 | } 16 | 17 | std::pair ProTexDict::find(const std::string & name) { 18 | auto it = dict.find(name); 19 | if (it != dict.end()) 20 | return std::make_pair(true, it->second); 21 | if (_next) 22 | return _next->find(name); 23 | return std::make_pair(false, (texture_t)0); 24 | } 25 | 26 | void ProTexDict::reset(CTextureDictionary * ctd) 27 | { 28 | globalVersion++; 29 | for (auto &e : dict) { 30 | _gfx->deleteTexture(e.second); 31 | } 32 | dict.clear(); 33 | for (auto &tex : ctd->piDict.textures) { 34 | dict[tex.texture.name.c_str()] = _gfx->createTexture(tex.images[0].image); 35 | } 36 | } 37 | 38 | ProGeometry::ProGeometry(Renderer * gfx, RwGeometry * geo, ProTexDict * proTexDict) 39 | { 40 | _gfx = gfx; 41 | _proTexDict = proTexDict; 42 | numTris = geo->tris.size(); 43 | vbuf.reset(gfx->createVertexBuffer(geo->numVerts)); 44 | ibuf.reset(gfx->createIndexBuffer(geo->tris.size() * 3)); 45 | RVertex *verts = vbuf->lock(); 46 | for (size_t i = 0; i < geo->numVerts; i++) { 47 | verts[i].x = geo->verts[i].x; 48 | verts[i].y = geo->verts[i].y; 49 | verts[i].z = geo->verts[i].z; 50 | if (!geo->colors.empty()) 51 | verts[i].color = geo->colors[i]; 52 | else 53 | verts[i].color = 0xFFFFFFFF; 54 | if (!geo->texSets.empty() && !geo->texSets[0].empty()) { 55 | auto &guv = geo->texSets[0][i]; 56 | verts[i].u = guv[0]; 57 | verts[i].v = guv[1]; 58 | } 59 | else { 60 | verts[i].u = 0.0f; 61 | verts[i].v = 0.0f; 62 | } 63 | } 64 | vbuf->unlock(); 65 | uint16_t *index = ibuf->lock(); 66 | for (auto &tri : geo->tris) { 67 | for (uint16_t ix : tri.indices) 68 | *(index++) = ix; 69 | } 70 | ibuf->unlock(); 71 | 72 | auto &matvec = geo->materialList.materials; 73 | if (!matvec.empty()) 74 | if (matvec[0].isTextured) 75 | textureName = matvec[0].texture.name; 76 | } 77 | 78 | void ProGeometry::draw(bool showTextures) { 79 | if (numTris == 0) return; 80 | _gfx->setVertexBuffer(vbuf.get()); 81 | _gfx->setIndexBuffer(ibuf.get()); 82 | texture_t texid = nullptr; 83 | if (ptdVersion == ProTexDict::globalVersion) 84 | texid = ptdTexId; 85 | else { 86 | auto mt = _proTexDict->find(textureName); 87 | if (mt.first) 88 | texid = mt.second; 89 | ptdTexId = texid; 90 | ptdVersion = ProTexDict::globalVersion; 91 | } 92 | if (texid && showTextures) 93 | _gfx->bindTexture(0, texid); 94 | else 95 | _gfx->unbindTexture(0); 96 | _gfx->drawBuffer(0, numTris * 3); 97 | } 98 | 99 | ProGeoCache::ProGeoCache(Renderer * gfx) 100 | { 101 | _gfx = gfx; 102 | } 103 | 104 | ProGeometry * ProGeoCache::getPro(RwGeometry *geo, ProTexDict *proTexDict) 105 | { 106 | auto it = dict.find(geo); 107 | if (it != dict.end()) 108 | return it->second.get(); 109 | ProGeometry *prog = new ProGeometry(_gfx, geo, proTexDict); 110 | dict[geo] = std::unique_ptr(prog); 111 | return prog; 112 | } 113 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # XXL Editor 2 | 3 | Level editor for games based on ELB's Kal engine, such as the Asterix & Obelix XXL series. 4 | 5 | ![Latest screenshot](docs/screenshot03.jpg) 6 | 7 | The level editor is still in early development, thus some features might be missing or buggy, depending on the game used. 8 | 9 | Licensed under the MIT License. 10 | 11 | Only works on Windows (Vista and later), though efforts are done to make ports to other operating systems easy in the future. 12 | 13 | To download the editor, go to the [Releases page](https://github.com/AdrienTD/XXL-Editor/releases). 14 | 15 | Check the [Wiki on this repository](https://github.com/AdrienTD/XXL-Editor/wiki) for guides on how to use the editor. 16 | 17 | ## Features 18 | 19 | Stuff that can be (partially) viewed and modified in the editor include: 20 | 21 | * Scene nodes 22 | * Beacons (locations for bonuses and crates) 23 | * Enemy squads and choreographies 24 | * Ground collision 25 | * Pathfinding graph 26 | * Trigger shapes 27 | * Events 28 | * Textures 29 | * Sounds 30 | * Model import/export in Renderware DFF format 31 | * Localized text for XXL1 32 | 33 | ## Supported games 34 | 35 | The games that are currently supported by the XXL Editor include: 36 | 37 | * A&O XXL 1 (2003) 38 | * A&O XXL 2: Mission Las Vegum (2005) 39 | * Asterix at the Olympic Games (2007) 40 | * A&O XXL 2 HD (2018) 41 | * A&O XXL Romastered (2020) 42 | 43 | Support for other games using the Kal engine, such as Arthur and Spyro DotD, has been looked into and certain features are partially supported, 44 | but way more limited compared to the XXL series, as the focus of this project is on Asterix XXL for now. 45 | 46 | Some features can be supported or unsupported depending on the game and platform used. 47 | XXL1 is the most supported game, where you can even manipulate object behaviour, event scripts, enemies, etc. 48 | 49 | Also, it is preferrable to mod the PC versions of the games, as you can see and edit 3D models, textures and sounds, whereas on console 50 | this might not be the case (the models are invisible, you can only change position of bonuses displayed as red blinking spheres instead of their models, ...). 51 | 52 | Adding support for consoles is difficult since the Renderware Texture and Geometry chunks are often stored in a native format specific to the console 53 | which is not necessarily well documented and requires knowledge of the console's architecture. 54 | 55 | Support for models/textures has been added for GameCube and Wii platforms, though there are limitations 56 | (lossy DXT1 texture compression, broken rigging when importing an animated model, ...). 57 | 58 | The Remasters of both XXL 1 and XXL 2 are also supported. While the editor does load the original models/textures/sounds stored in the KWN files, 59 | modifying them there won't have any effect since the remaster replaces them with converted assets in the Cooking folder for the new graphics engine. 60 | 61 | ## Compiling Requirements 62 | * Visual Studio 2017/2019 (other versions untested) 63 | * [vcpkg](https://github.com/Microsoft/vcpkg) with the following installed packages: inih, sdl2, stb, libsquish, nlohmann-json 64 | * Open the VS solution file, and as long as [vcpkg integration](https://docs.microsoft.com/en-us/cpp/build/integrate-vcpkg) is applied, you should be able to compile. 65 | 66 | ## Thanks 67 | * Thanks to S.P.Q.R. for some code addition and names for events and squad actions. 68 | 69 | ## Libraries used: 70 | * [Dear Imgui](https://github.com/ocornut/imgui) 71 | * [ImGuizmo](https://github.com/CedricGuillemet/ImGuizmo) 72 | * [inih](https://github.com/benhoyt/inih) 73 | * [SDL 2](https://www.libsdl.org) 74 | * [stb](https://github.com/nothings/stb) 75 | * [libsquish](https://sourceforge.net/projects/libsquish/) 76 | * [JSON for Modern C++](https://github.com/nlohmann/json) 77 | * [\{fmt\}](https://github.com/fmtlib/fmt) 78 | * [adpcm-xq](https://github.com/dbry/adpcm-xq) 79 | 80 | ## Other Screenshots (old): 81 | ![Screenshot 1](docs/screenshot01.jpg) 82 | ![Screenshot 2](docs/screenshot02.jpg) -------------------------------------------------------------------------------- /File.cpp: -------------------------------------------------------------------------------- 1 | #include "File.h" 2 | #include 3 | #define WIN32_LEAN_AND_MEAN 4 | #include 5 | #include "zlib.h" 6 | #include "KEnvironment.h" 7 | #include "FatalError.h" 8 | 9 | void IOFile::read(void * out, size_t length) { 10 | fread(out, length, 1, file); 11 | } 12 | 13 | void IOFile::write(const void * out, size_t length) { 14 | fwrite(out, length, 1, file); 15 | } 16 | 17 | void IOFile::seek(size_t pos, int mode) { 18 | fseek(file, pos, mode); 19 | } 20 | 21 | size_t IOFile::tell() { 22 | return ftell(file); 23 | } 24 | 25 | void IOFile::close() { 26 | if (file) { 27 | fclose(file); 28 | file = nullptr; 29 | } 30 | } 31 | 32 | void IOFile::open(const char * name, const char * mode) { 33 | if (file) close(); 34 | fopen_s(&file, name, mode); 35 | if (!file) FatalErrorCFormat(L"Failed to open the file %S", name); 36 | } 37 | 38 | void IOFile::open(const wchar_t* name, const char* mode) 39 | { 40 | if (file) close(); 41 | _wfopen_s(&file, name, std::wstring(mode, mode+strlen(mode)).c_str()); 42 | if (!file) FatalErrorCFormat(L"Failed to open the file %s", name); 43 | } 44 | 45 | void MemFile::read(void * out, size_t length) 46 | { 47 | memcpy(out, _curptr, length); 48 | _curptr += length; 49 | } 50 | 51 | void MemFile::write(const void * out, size_t length) 52 | { 53 | memcpy(_curptr, out, length); 54 | _curptr += length; 55 | } 56 | 57 | void MemFile::seek(size_t pos, int mode) 58 | { 59 | if (mode == SEEK_SET) 60 | _curptr = _startptr + pos; 61 | else if (mode == SEEK_CUR) 62 | _curptr += pos; 63 | } 64 | 65 | size_t MemFile::tell() 66 | { 67 | return _curptr - _startptr; 68 | } 69 | 70 | File * GetResourceFile(const char * resName) 71 | { 72 | auto [ptr, size] = GetResourceContent(resName); 73 | return new MemFile(ptr); 74 | } 75 | 76 | std::pair GetResourceContent(const char* resName) 77 | { 78 | static bool firstTime = true; 79 | static bool useExternalDir = false; 80 | static const wchar_t* extDirName = L"xec_resources"; 81 | if (firstTime) { 82 | DWORD attr = GetFileAttributesW(extDirName); 83 | if (attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY)) { 84 | useExternalDir = true; 85 | } 86 | firstTime = false; 87 | } 88 | // First check for file presence in customized resources directory 89 | if (useExternalDir) { 90 | wchar_t path[MAX_PATH]; 91 | swprintf_s(path, L"%s\\%S", extDirName, resName); 92 | FILE* file = nullptr; 93 | _wfopen_s(&file, path, L"rb"); 94 | if (file) { 95 | fseek(file, 0, SEEK_END); 96 | size_t len = ftell(file); 97 | fseek(file, 0, SEEK_SET); 98 | 99 | static void* buffer = nullptr; 100 | if (buffer) free(buffer); 101 | if (buffer = malloc(len)) { 102 | fread(buffer, len, 1, file); 103 | fclose(file); 104 | return { buffer, len }; 105 | } 106 | 107 | fclose(file); 108 | } 109 | } 110 | // Else check in the executable's resources 111 | HMODULE hmod = GetModuleHandleA(NULL); 112 | HRSRC rs = FindResourceA(hmod, resName, "DATA"); 113 | if (!rs) return { nullptr, 0 }; 114 | HGLOBAL gl = LoadResource(hmod, rs); 115 | if (!gl) return { nullptr, 0 }; 116 | return { LockResource(gl), SizeofResource(hmod, rs) }; 117 | } 118 | 119 | std::unique_ptr GetGzipFile(const wchar_t* path, const char* mode, KEnvironment& kenv) 120 | { 121 | struct GzipFile : File { 122 | gzFile _gzf; 123 | GzipFile(const wchar_t* path, const char* mode) { 124 | _gzf = gzopen_w(path, mode); 125 | if (!_gzf) FatalErrorCFormat(L"Failed to open the gzip-compressed file %s", path); 126 | } 127 | ~GzipFile() { 128 | gzclose(_gzf); 129 | } 130 | virtual void read(void* out, size_t length) override { gzread(_gzf, out, length); } 131 | virtual void write(const void* out, size_t length) override { gzwrite(_gzf, out, length); } 132 | virtual void seek(size_t pos, int mode) override { gzseek(_gzf, pos, mode); } 133 | virtual size_t tell() override { return gztell(_gzf); } 134 | }; 135 | if (kenv.version >= KEnvironment::KVERSION_ALICE) { 136 | return std::make_unique(path, mode); 137 | } 138 | else { 139 | return std::make_unique(path, mode); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /CoreClasses/CKManager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "KObject.h" 4 | #include "CKService.h" 5 | #include 6 | 7 | struct CKSound; 8 | struct CTextureDictionary; 9 | 10 | struct CKManager : CKCategory<0> {}; 11 | 12 | struct CKReflectableManager : CKMRSubclass, 0xBADB01> { 13 | void reflectMembers2(MemberListener &r, KEnvironment* kenv) {} 14 | }; 15 | 16 | struct CKServiceManager : CKSubclass { 17 | std::vector> services; 18 | 19 | template T *addService(KEnvironment *kenv) { 20 | static_assert(T::CATEGORY == CKService::CATEGORY, "T must be a service."); 21 | T *srv = kenv->createAndInitObject(); 22 | services.emplace_back(srv); 23 | return srv; 24 | } 25 | 26 | void deserialize(KEnvironment* kenv, File *file, size_t length) override; 27 | void serialize(KEnvironment* kenv, File *file) override; 28 | }; 29 | 30 | struct CKGraphic : CKMRSubclass { 31 | kobjref kgfcSgRootNode; 32 | std::array kgfcUnk1 = { 1.0f,1.0f,1.0f,1.0f }; 33 | std::array kgfcUnk2 = { 1.0f,1.0f,1.0f,1.0f }; 34 | uint32_t kgfcUnk3 = 0xFF0000FF; 35 | float kgfcUnk4 = 27.0f; 36 | uint32_t kgfcUnk5 = 1; 37 | uint8_t kgfcRomasterValue = 1; 38 | void reflectMembers2(MemberListener &r, KEnvironment *kenv); 39 | }; 40 | 41 | struct CKGraphicX2 : CKMRSubclass { 42 | kobjref kgfcSgRootNode; 43 | kobjref kgfcSgRootNode2; // OG 44 | int32_t ckgUnk1; 45 | float ckgUnk2; 46 | int32_t ckgUnk3; 47 | //int32_t ckgUnk4; 48 | std::vector> ckgGfxManagers; 49 | struct VideoReplacement { 50 | std::string texName; 51 | uint8_t ckgUnk17; 52 | int32_t ckgUnk18; 53 | int32_t ckgUnk19; 54 | int32_t ckgUnk20; 55 | float ckgUnk21; 56 | int32_t ogvrUnk1; 57 | int32_t ogvrUnk2; 58 | }; 59 | std::vector ckgVideoReplacements; 60 | kobjref ckgTexDict; 61 | std::array ogHdFloats; 62 | void reflectMembers2(MemberListener& r, KEnvironment* kenv); 63 | }; 64 | 65 | struct CKSoundManager : CKMRSubclass { 66 | struct Tune { 67 | std::string remasterPath; // Remaster only 68 | float duration = 0.0f; 69 | uint32_t arValue1 = 0, arValue2 = 0; 70 | }; 71 | // uint32_t sizeFor_ksndmgrSndDicts; 72 | std::vector ksndmgrSndDicts; 73 | kobjref ksndmgrSndDictID; 74 | uint8_t ksndmgrUnk3 = 1; 75 | uint8_t ksndmgrUnk4 = 1; 76 | uint8_t ksndmgrX2UnkByte = 0; // XXL2+ 77 | Vector3 ksndmgrX2UnkVector; // XXL2+ 78 | float ksndmgrUnk5 = 1.0f; 79 | float ksndmgrUnk6 = 1.0f; 80 | float ksndmgrUnk7 = 0; 81 | float ksndmgrUnk8 = 1.0f; 82 | // uint32_t sizeFor_ksndmgrDings; 83 | std::vector ksndmgrDings; 84 | float ksndmgrUnk11 = 0.7f; 85 | float ksndmgrUnk12 = 0.3f; 86 | float ksndmgrUnk13 = 1.5f; 87 | float ksndmgrUnk14 = 1.0f; 88 | uint32_t ksndmgrSampleRate = 22050; // XXL1 only 89 | std::vector> ksndmgrSoundObjects; // XXL2+ 90 | 91 | // OG+: 92 | std::vector> ogSoundOutputEffects; 93 | std::vector> ogSoundOutputEffects2; 94 | std::array ogFloatValues; 95 | 96 | // Global (GAME) variables (Arthur+): 97 | std::array arGlobFloatValues1; // OG remove one of both 98 | std::array arGlobFloatValues2; 99 | // OG+: 100 | std::vector> ogGlobStreamWaves; 101 | struct StreamType { 102 | std::string strtName; 103 | std::array strtValues; 104 | }; 105 | std::vector ogGlobStreamTypes; 106 | 107 | void reflectMembers2(MemberListener &r, KEnvironment *kenv); 108 | void deserializeGlobal(KEnvironment* kenv, File* file, size_t length) override; 109 | }; 110 | 111 | struct CKInput : CKSubclass { 112 | // TODO 113 | std::vector data; 114 | void deserialize(KEnvironment* kenv, File* file, size_t length) {} 115 | void serialize(KEnvironment* kenv, File* file) {} 116 | void deserializeGlobal(KEnvironment* kenv, File* file, size_t length) override; 117 | void serializeGlobal(KEnvironment* kenv, File* file) override; 118 | }; -------------------------------------------------------------------------------- /imguiimpl.cpp: -------------------------------------------------------------------------------- 1 | #include "imguiimpl.h" 2 | #include "imgui/imgui.h" 3 | #include "File.h" 4 | #include "renderer.h" 5 | #include "Image.h" 6 | #include "window.h" 7 | #include 8 | #include 9 | #include 10 | 11 | 12 | uint32_t g_imguiLastTime; 13 | const char* g_imguiFontFile = "resources/UbuntuMono-R.ttf"; 14 | float g_imguiFontSize = 14; 15 | 16 | #define BGRA_TO_RGBA(x) ( (((x)&0xFF)<<16) | (((x)&0xFF0000)>>16) | ((x)&0xFF00FF00) ) 17 | 18 | void ImGuiImpl_RenderDrawLists(ImDrawData *dr, Renderer *renderer) 19 | { 20 | ImGuiIO &io = ImGui::GetIO(); 21 | //if(winMinimized) return; 22 | 23 | renderer->initFormDrawing(); 24 | renderer->enableScissor(); 25 | 26 | for(int i = 0; i < dr->CmdListsCount; i++) 27 | { 28 | ImDrawList *cl = dr->CmdLists[i]; 29 | 30 | RVertexBuffer *vb = renderer->createVertexBuffer(cl->VtxBuffer.size()); 31 | RIndexBuffer *ib = renderer->createIndexBuffer(cl->IdxBuffer.size()); 32 | RVertex *vm = vb->lock(); 33 | for(int j = 0; j < cl->VtxBuffer.size(); j++) 34 | { 35 | ImDrawVert *a = &cl->VtxBuffer[j]; 36 | vm[j].x = a->pos.x; 37 | vm[j].y = a->pos.y; 38 | vm[j].z = 0; 39 | vm[j].color = a->col; 40 | vm[j].u = a->uv.x; 41 | vm[j].v = a->uv.y; 42 | } 43 | vb->unlock(); 44 | uint16_t *im = ib->lock(); 45 | for(int j = 0; j < cl->IdxBuffer.size(); j++) 46 | im[j] = cl->IdxBuffer[j]; 47 | ib->unlock(); 48 | renderer->setVertexBuffer(vb); 49 | renderer->setIndexBuffer(ib); 50 | 51 | for(int j = 0; j < cl->CmdBuffer.size(); j++) 52 | { 53 | ImDrawCmd *cmd = &cl->CmdBuffer[j]; 54 | renderer->bindTexture(0, (texture_t)cmd->TextureId); 55 | renderer->setScissorRect( 56 | (int)cmd->ClipRect.x, (int)cmd->ClipRect.y, 57 | (int)(cmd->ClipRect.z - cmd->ClipRect.x), 58 | (int)(cmd->ClipRect.w - cmd->ClipRect.y) 59 | ); 60 | renderer->drawBuffer(cmd->IdxOffset, cmd->ElemCount, cmd->VtxOffset); 61 | } 62 | 63 | delete vb; delete ib; 64 | } 65 | 66 | renderer->disableScissor(); 67 | } 68 | 69 | void ImGuiImpl_CreateFontsTexture(Renderer *gfx) 70 | { 71 | ImGuiIO &io = ImGui::GetIO(); 72 | uint8_t *pix; int w, h, bpp; 73 | ImFontGlyphRangesBuilder glyphes; 74 | ImVector ranges; 75 | static const ImWchar myranges[] = { 76 | 0x0100, 0x024F, // Latin Extended 77 | 0x0370, 0x03FF, // Greek and Coptic 78 | 0x2000, 0x206F, // General Punctuation 79 | 0 80 | }; 81 | glyphes.AddRanges(io.Fonts->GetGlyphRangesDefault()); 82 | glyphes.AddRanges(io.Fonts->GetGlyphRangesCyrillic()); 83 | glyphes.AddRanges(myranges); 84 | glyphes.BuildRanges(&ranges); 85 | ImFontConfig config; 86 | config.OversampleH = 1; 87 | config.OversampleV = 1; 88 | config.RasterizerMultiply = 1.4f; 89 | //if(g_imguiFontFile) 90 | // io.Fonts->AddFontFromFileTTF(g_imguiFontFile, g_imguiFontSize, &config, ranges.Data); 91 | auto [fntptr, fntsize] = GetResourceContent("UbuntuMono-R.ttf"); 92 | io.Fonts->AddFontFromMemoryTTF(fntptr, (int)fntsize, 14, &config, ranges.Data); 93 | io.Fonts->GetTexDataAsRGBA32(&pix, &w, &h, &bpp); 94 | Image bm; 95 | bm.width = w; bm.height = h; bm.bpp = 32; bm.pitch = w * 4; 96 | bm.pixels.resize(w*h * 4); 97 | memcpy(bm.pixels.data(), pix, w*h * 4); 98 | texture_t t = gfx->createTexture(bm); 99 | io.Fonts->TexID = (void*)t; 100 | } 101 | 102 | void ImGuiImpl_Init(Window *window) 103 | { 104 | ImGui::CreateContext(); 105 | //imguienabled = true; 106 | ImGuiIO &io = ImGui::GetIO(); 107 | 108 | io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; 109 | 110 | #ifdef _WIN32 111 | SDL_SysWMinfo syswm; 112 | SDL_GetWindowWMInfo(window->getSDLWindow(), &syswm); 113 | HWND hWindow = syswm.info.win.window; 114 | //io.ImeWindowHandle = hWindow; // FIXME 115 | #endif 116 | 117 | g_imguiLastTime = SDL_GetTicks(); 118 | } 119 | 120 | void ImGuiImpl_NewFrame(Window *window) 121 | { 122 | ImGuiIO &io = ImGui::GetIO(); 123 | io.DisplaySize = ImVec2((float)window->getWidth(), (float)window->getHeight()); 124 | 125 | uint32_t newtime = SDL_GetTicks(); 126 | io.DeltaTime = (float)(newtime - g_imguiLastTime) / 1000.f; 127 | if (io.DeltaTime == 0.0f) 128 | io.DeltaTime = 1e-40f; 129 | g_imguiLastTime = newtime; 130 | 131 | ImGui::NewFrame(); 132 | } 133 | 134 | void ImGuiImpl_Render(Renderer *gfx) 135 | { 136 | ImGui::Render(); 137 | ImGuiImpl_RenderDrawLists(ImGui::GetDrawData(), gfx); 138 | } 139 | -------------------------------------------------------------------------------- /EditorUI/IGCloneEditor.cpp: -------------------------------------------------------------------------------- 1 | #include "IGCloneEditor.h" 2 | 3 | #include "EditorInterface.h" 4 | 5 | #include "KEnvironment.h" 6 | #include "CoreClasses/CKGraphical.h" 7 | 8 | #include "GeoUtils.h" 9 | #include "GuiUtils.h" 10 | 11 | #include 12 | 13 | void EditorUI::IGCloneEditor(EditorInterface& ui) 14 | { 15 | using namespace GuiUtils; 16 | auto& kenv = ui.kenv; 17 | 18 | CCloneManager* cloneMgr = kenv.levelObjects.getFirst(); 19 | if (!cloneMgr) return; 20 | if (cloneMgr->_numClones == 0) return; 21 | 22 | ImGui::DragFloat3("Preview pos", &ui.selgeoPos.x, 0.1f); 23 | if (ImGui::Button("Move preview to front")) 24 | ui.selgeoPos = ui.camera.position + ui.camera.direction * 3; 25 | 26 | if (!ui.selClones.empty()) { 27 | ImGui::SameLine(); 28 | if (ImGui::Button("Import DFF")) { 29 | auto filepath = OpenDialogBox(ui.g_window, "Renderware Clump\0*.DFF\0\0", "dff"); 30 | if (!filepath.empty()) { 31 | std::unique_ptr impClump = GeoUtils::LoadDFF(filepath); 32 | std::vector> impGeos = impClump->geoList.geometries[0]->splitByMaterial(); 33 | 34 | std::vector newIndices; 35 | for (size_t p = 0; p < impGeos.size(); ++p) { 36 | if (p < ui.selClones.size()) { 37 | const uint32_t bingIndex = ui.selClones[p]; 38 | auto& tdGeo = cloneMgr->_teamDict._bings[ui.selClones[p]]._clump.atomic.geometry; 39 | tdGeo = std::move(impGeos[p]); 40 | newIndices.push_back(bingIndex); 41 | } 42 | else { 43 | const int prevBingSomeNum = ui.selClones.empty() ? 1 : cloneMgr->_teamDict._bings[ui.selClones[0]]._someNum; 44 | const uint32_t bingIndex = (uint32_t)cloneMgr->_teamDict._bings.size(); 45 | auto& bing = cloneMgr->_teamDict._bings.emplace_back(); 46 | bing._someNum = prevBingSomeNum; 47 | bing._clump.atomic.flags = 5; 48 | bing._clump.atomic.unused = 0; 49 | bing._clump.atomic.geometry = std::move(impGeos[p]); 50 | newIndices.push_back(bingIndex); 51 | } 52 | } 53 | ui.selGeometry = nullptr; 54 | 55 | for (auto& dong : cloneMgr->_team.dongs) { 56 | if (dong.bongs == ui.selClones) { 57 | dong.bongs = newIndices; 58 | } 59 | } 60 | ui.selClones = newIndices; 61 | 62 | ui.progeocache.clear(); 63 | ui.prepareLevelGfx(); 64 | } 65 | } 66 | ImGui::SameLine(); 67 | if (ImGui::Button("Export DFF")) { 68 | auto filepath = SaveDialogBox(ui.g_window, "Renderware Clump\0*.DFF\0\0", "dff"); 69 | if (!filepath.empty()) { 70 | RwTeam::Dong* seldong = nullptr; 71 | for (auto& dong : cloneMgr->_team.dongs) 72 | if (dong.bongs == ui.selClones) 73 | seldong = &dong; 74 | 75 | if (!seldong) { 76 | MsgBox(ui.g_window, "Sorry, I couldn't find back the team entry with the selected team dict indices :(", 16); 77 | } 78 | else { 79 | RwFrameList* framelist = &seldong->clump.frameList; 80 | RwExtHAnim* hanim = (RwExtHAnim*)framelist->extensions[1].find(0x11E); // null if not found 81 | 82 | // merge clone geos 83 | auto sharedMergedGeo = std::make_shared(); 84 | RwGeometry& mergedGeo = *sharedMergedGeo; bool first = true; 85 | for (auto td : seldong->bongs) { 86 | const RwGeometry& tdgeo = *cloneMgr->_teamDict._bings[td]._clump.atomic.geometry.get(); 87 | if (first) { 88 | mergedGeo = tdgeo; 89 | first = false; 90 | } 91 | else { 92 | mergedGeo.merge(tdgeo); 93 | } 94 | } 95 | 96 | RwClump clump = GeoUtils::CreateClumpFromGeo(sharedMergedGeo, hanim); 97 | IOFile out(filepath.c_str(), "wb"); 98 | clump.serialize(&out); 99 | out.close(); 100 | } 101 | } 102 | } 103 | } 104 | 105 | ImGui::BeginChild("CloneList"); 106 | for (auto& clone : ui.cloneSet) { 107 | std::string lol; 108 | for (size_t i = 0; i < clone.size(); i++) { 109 | uint32_t de = clone[i]; 110 | std::string texname = "?"; 111 | const auto& matlist = cloneMgr->_teamDict._bings[de]._clump.atomic.geometry->materialList.materials; 112 | if (!matlist.empty()) 113 | texname = matlist[0].texture.name; 114 | if (i != 0) lol.append(", "); 115 | lol.append(texname); 116 | } 117 | ImGui::PushID(&clone); 118 | if (ImGui::Selectable(lol.c_str(), ui.selClones == clone)) 119 | ui.selClones = clone; 120 | ImGui::PopID(); 121 | } 122 | ImGui::EndChild(); 123 | } 124 | -------------------------------------------------------------------------------- /Image.cpp: -------------------------------------------------------------------------------- 1 | #include "Image.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | Image Image::convertToRGBA32() const 10 | { 11 | if (bpp == 32) 12 | return Image(*this); 13 | Image img; 14 | img.width = width; 15 | img.height = height; 16 | img.bpp = 32; 17 | img.pitch = width * 4; 18 | img.pixels.resize(img.pitch * img.height); 19 | if (bpp <= 8) { 20 | assert(width == pitch); 21 | uint8_t* oldpix = (uint8_t*)pixels.data(); 22 | uint32_t* newpix = (uint32_t*)img.pixels.data(); 23 | size_t oldsize = pitch * height; 24 | for (size_t i = 0; i < oldsize; i++) 25 | newpix[i] = palette[oldpix[i]]; 26 | } 27 | else if (bpp == Format::ImageFormat_DXT1) { 28 | squish::DecompressImage(img.pixels.data(), width, height, pixels.data(), squish::kDxt1); 29 | } 30 | else if (bpp == Format::ImageFormat_DXT2) { 31 | squish::DecompressImage(img.pixels.data(), width, height, pixels.data(), squish::kDxt3); 32 | } 33 | else if (bpp == Format::ImageFormat_DXT4) { 34 | squish::DecompressImage(img.pixels.data(), width, height, pixels.data(), squish::kDxt5); 35 | } 36 | else { 37 | assert(false && "unsupported format for RGBA32 conversion"); 38 | } 39 | return img; 40 | } 41 | 42 | std::optional Image::palettize() const 43 | { 44 | // convert image to 32bpp if it was in another format 45 | std::optional cvtImage; 46 | if (this->bpp != Format::ImageFormat_RGBA8888) { 47 | cvtImage = this->convertToRGBA32(); 48 | } 49 | const Image& image32 = cvtImage ? *cvtImage : *this; 50 | 51 | int numUniqueColors = 0; 52 | std::unordered_map colorIndexMap; 53 | 54 | const size_t numPixels = image32.width * image32.height; 55 | DynArray palPixels(numPixels); 56 | 57 | for (size_t i = 0; i < numPixels; ++i) { 58 | uint32_t color = *(uint32_t*)(image32.pixels.data() + 4 * i); 59 | auto [iter, inserted] = colorIndexMap.try_emplace(color, (uint8_t)numUniqueColors); 60 | if (inserted) { 61 | numUniqueColors += 1; 62 | if (numUniqueColors > 256) 63 | return std::nullopt; 64 | } 65 | palPixels[i] = iter->second; 66 | } 67 | 68 | DynArray palette((numUniqueColors <= 16) ? 16 : 256); 69 | for (auto [color, index] : colorIndexMap) { 70 | palette[index] = color; 71 | } 72 | std::fill(palette.begin() + numUniqueColors, palette.end(), 0); 73 | 74 | Image image8; 75 | image8.width = image32.width; 76 | image8.height = image32.height; 77 | image8.bpp = (numUniqueColors <= 16) ? 4 : 8; 78 | image8.pitch = image8.width; 79 | image8.pixels = std::move(palPixels); 80 | image8.palette = std::move(palette); 81 | return image8; 82 | } 83 | 84 | Image Image::loadFromFile(const char* filename) 85 | { 86 | Image img; 87 | int sizx, sizy, origBpp; 88 | void* pix = stbi_load(filename, &sizx, &sizy, &origBpp, 4); 89 | assert(pix && "Failed to load image file\n"); 90 | img.width = sizx; 91 | img.height = sizy; 92 | img.bpp = 32; 93 | img.pitch = img.width * 4; 94 | img.pixels.resize(img.pitch * img.height); 95 | memcpy(img.pixels.data(), pix, img.pixels.size()); 96 | stbi_image_free(pix); 97 | return img; 98 | } 99 | 100 | Image Image::loadFromFile(const wchar_t* filename) 101 | { 102 | FILE* file; 103 | _wfopen_s(&file, filename, L"rb"); 104 | Image img = loadFromFile(file); 105 | fclose(file); 106 | return img; 107 | } 108 | 109 | Image Image::loadFromFile(FILE* file) 110 | { 111 | Image img; 112 | int sizx, sizy, origBpp; 113 | void* pix = stbi_load_from_file(file, &sizx, &sizy, &origBpp, 4); 114 | assert(pix && "Failed to load image file\n"); 115 | img.width = sizx; 116 | img.height = sizy; 117 | img.bpp = 32; 118 | img.pitch = img.width * 4; 119 | img.pixels.resize(img.pitch * img.height); 120 | memcpy(img.pixels.data(), pix, img.pixels.size()); 121 | stbi_image_free(pix); 122 | return img; 123 | } 124 | 125 | Image Image::loadFromMemory(void* ptr, size_t len) { 126 | Image img; 127 | int sizx, sizy, origBpp; 128 | void* pix = stbi_load_from_memory((uint8_t*)ptr, (int)len, &sizx, &sizy, &origBpp, 4); 129 | assert(pix && "Failed to load image from memory\n"); 130 | img.width = sizx; 131 | img.height = sizy; 132 | img.bpp = 32; 133 | img.pitch = img.width * 4; 134 | img.pixels.resize(img.pitch * img.height); 135 | memcpy(img.pixels.data(), pix, img.pixels.size()); 136 | stbi_image_free(pix); 137 | return img; 138 | } 139 | -------------------------------------------------------------------------------- /GamePatcherKClasses.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "KObject.h" 4 | #include 5 | #include 6 | #include 7 | #include "CKPartlyUnknown.h" 8 | #include "vecmat.h" 9 | 10 | struct File; 11 | 12 | namespace GamePatcher { 13 | extern File* g_drmValues; 14 | 15 | struct CKHook : CKCategory<2> {}; 16 | struct CKComponent : CKCategory<6> {}; 17 | struct CKLogic : CKCategory<12> {}; 18 | 19 | struct CKGroup : CKCategory<4> { 20 | kobjref nextGroup; 21 | kobjref parentGroup; 22 | kobjref life; 23 | kobjref bundle; 24 | uint32_t unk2 = 0; 25 | kobjref childGroup; 26 | kobjref childHook; 27 | 28 | void deserialize(KEnvironment* kenv, File* file, size_t length) override; 29 | void serialize(KEnvironment* kenv, File* file) override; 30 | }; 31 | 32 | struct CKAsterixGameManager : CKSubclass { 33 | std::array part1; 34 | std::array part2; 35 | std::string texture; 36 | std::array part3; 37 | 38 | void deserialize(KEnvironment* kenv, File* file, size_t length) override; 39 | void serialize(KEnvironment* kenv, File* file) override; 40 | }; 41 | 42 | struct CKEnemyCoreCpnt : CKSubclass { 43 | uint32_t firstVal; 44 | uint8_t health; 45 | 46 | void deserialize(KEnvironment* kenv, File* file, size_t length) override; 47 | void serialize(KEnvironment* kenv, File* file) override; 48 | }; 49 | 50 | struct CKEnemyCpnt : CKPartlyUnknown {}; 51 | struct CKSquadEnemyCpnt : CKPartlyUnknown {}; 52 | struct CKSeizableEnemyCpnt : CKPartlyUnknown {}; 53 | struct CKSquadSeizableEnemyCpnt : CKPartlyUnknown {}; 54 | struct CKBasicEnemyCpnt : CKPartlyUnknown {}; 55 | struct CKBasicEnemyLeaderCpnt : CKPartlyUnknown {}; 56 | struct CKJumpingRomanCpnt : CKPartlyUnknown {}; 57 | struct CKRomanArcherCpnt : CKPartlyUnknown {}; 58 | struct CKRocketRomanCpnt : CKPartlyUnknown {}; 59 | struct CKJetPackRomanCpnt : CKPartlyUnknown {}; 60 | struct CKMobileTowerCpnt : CKPartlyUnknown {}; 61 | struct CKTriangularTurtleCpnt : CKPartlyUnknown {}; 62 | struct CKSquareTurtleCpnt : CKPartlyUnknown {}; 63 | struct CKDonutTurtleCpnt : CKPartlyUnknown {}; 64 | struct CKPyramidalTurtleCpnt : CKPartlyUnknown {}; 65 | 66 | struct CKGrpBaseSquad : CKSubclass { 67 | uint32_t bsUnk1; 68 | kobjref msgAction; 69 | 70 | void deserialize(KEnvironment* kenv, File* file, size_t length) override; 71 | void serialize(KEnvironment* kenv, File* file) override; 72 | }; 73 | 74 | struct CKGrpSquad : CKSubclass { 75 | std::array mat1, mat2; 76 | float sqUnk1; 77 | Vector3 sqUnk2; 78 | std::array, 4> refs; 79 | std::array sqUnk3, sqUnk4; 80 | uint32_t sqUnk5; 81 | //uint32_t numChoreographies; 82 | std::vector> choreographies; 83 | //uint32_t numChoreoKeys; 84 | std::vector> choreoKeys; 85 | struct Bing { 86 | uint32_t a; uint8_t b; 87 | }; 88 | std::vector bings, dings; 89 | std::vector fings; 90 | std::array sqUnk6; 91 | uint16_t sqUnk7; 92 | uint8_t sqUnk8; 93 | struct PoolEntry { 94 | kobjref pool; 95 | kobjref cpnt; 96 | uint8_t u1; 97 | uint16_t numEnemies; //(DRM!!!) 98 | uint8_t u2; 99 | kobjref u3; 100 | }; 101 | std::vector pools; 102 | uint16_t sqUnkA; 103 | float sqUnkB; 104 | uint16_t sqUnkC; 105 | 106 | void deserialize(KEnvironment* kenv, File* file, size_t length) override; 107 | void serialize(KEnvironment* kenv, File* file) override; 108 | }; 109 | 110 | struct CKGrpSquadEnemy : CKSubclass { 111 | float seUnk1, seUnk2; 112 | 113 | void deserialize(KEnvironment* kenv, File* file, size_t length) override; 114 | void serialize(KEnvironment* kenv, File* file) override; 115 | }; 116 | 117 | struct CKGrpSquadJetPack : CKPartlyUnknown {}; 118 | 119 | struct CKHkTorchCore : CKSubclass { 120 | std::array part1; 121 | float timer; 122 | 123 | void deserialize(KEnvironment* kenv, File* file, size_t length) override; 124 | void serialize(KEnvironment* kenv, File* file) override; 125 | }; 126 | 127 | struct CKHkTorch : CKPartlyUnknown {}; 128 | }; -------------------------------------------------------------------------------- /Shape.cpp: -------------------------------------------------------------------------------- 1 | #include "Shape.h" 2 | #include "File.h" 3 | #include 4 | 5 | bool BoundingSphere::containsPoint(const Vector3& point) const 6 | { 7 | return (point - center).len3() <= radius; 8 | } 9 | 10 | bool BoundingSphere::intersectsWithSphere(const BoundingSphere& other) const 11 | { 12 | return (other.center - center).len3() <= radius + other.radius; 13 | } 14 | 15 | float BoundingSphere::distanceToPoint(const Vector3& point) const 16 | { 17 | return std::max(0.0f, (point - center).len3() - radius); 18 | } 19 | 20 | float BoundingSphere::distanceToSphere(const BoundingSphere& other) const 21 | { 22 | return std::max(0.0f, (other.center - center).len3() - radius - other.radius); 23 | } 24 | 25 | void BoundingSphere::merge(const BoundingSphere & other) 26 | { 27 | if (center == other.center) { 28 | if (other.radius > radius) 29 | radius = other.radius; 30 | } 31 | else { 32 | Vector3 n_t2o = (other.center - center).normal(); 33 | Vector3 ext_t = center - n_t2o * radius; 34 | Vector3 ext_o = other.center + n_t2o * other.radius; 35 | if ((ext_o - center).len3() <= radius) 36 | ; 37 | else if ((ext_t - other.center).len3() <= other.radius) { 38 | center = other.center; 39 | radius = other.radius; 40 | } 41 | else { 42 | center = (ext_t + ext_o) * 0.5f; 43 | radius = 0.5f * (ext_o - ext_t).len3(); 44 | } 45 | } 46 | } 47 | 48 | void BoundingSphere::mergePoint(const Vector3 & point) 49 | { 50 | if ((point - center).len3() <= radius) 51 | return; 52 | Vector3 c2p = (point - center).normal(); 53 | Vector3 ext = center - c2p * radius; 54 | center = (ext + point) * 0.5f; 55 | radius = (point - center).len3(); 56 | } 57 | 58 | void BoundingSphere::deserialize(File * file, bool withSquare) 59 | { 60 | for (float &c : center) 61 | c = file->readFloat(); 62 | radius = file->readFloat(); 63 | if (withSquare) 64 | file->readFloat(); 65 | } 66 | 67 | void BoundingSphere::serialize(File * file, bool withSquare) 68 | { 69 | for (float &c : center) 70 | file->writeFloat(c); 71 | file->writeFloat(radius); 72 | if (withSquare) 73 | file->writeFloat(radius*radius); 74 | } 75 | 76 | void AABoundingBox::mergePoint(const Vector3 & point) 77 | { 78 | if (point.x > highCorner.x) highCorner.x = point.x; 79 | if (point.y > highCorner.y) highCorner.y = point.y; 80 | if (point.z > highCorner.z) highCorner.z = point.z; 81 | if (point.x < lowCorner.x) lowCorner.x = point.x; 82 | if (point.y < lowCorner.y) lowCorner.y = point.y; 83 | if (point.z < lowCorner.z) lowCorner.z = point.z; 84 | } 85 | 86 | void AABoundingBox::merge(const AABoundingBox & box) 87 | { 88 | mergePoint(box.highCorner); 89 | mergePoint(box.lowCorner); 90 | } 91 | 92 | bool AABoundingBox::containsPoint(const Vector3& point) const 93 | { 94 | return point.x >= lowCorner.x && point.x <= highCorner.x && 95 | point.y >= lowCorner.y && point.y <= highCorner.y && 96 | point.z >= lowCorner.z && point.z <= highCorner.z; 97 | } 98 | 99 | std::optional AABoundingBox::intersectionWith(const AABoundingBox& box) const 100 | { 101 | AABoundingBox intersection; 102 | for (int i = 0; i < 3; ++i) { 103 | intersection.lowCorner.coord[i] = std::max(lowCorner.coord[i], box.lowCorner.coord[i]); 104 | intersection.highCorner.coord[i] = std::min(highCorner.coord[i], box.highCorner.coord[i]); 105 | if (intersection.lowCorner.coord[i] >= intersection.highCorner.coord[i]) { 106 | return std::nullopt; 107 | } 108 | } 109 | return intersection; 110 | } 111 | 112 | void AABoundingBox::deserialize(File * file) 113 | { 114 | for (float &f : highCorner) 115 | f = file->readFloat(); 116 | for (float &f : lowCorner) 117 | f = file->readFloat(); 118 | } 119 | 120 | void AABoundingBox::serialize(File * file) 121 | { 122 | for (float &f : highCorner) 123 | file->writeFloat(f); 124 | for (float &f : lowCorner) 125 | file->writeFloat(f); 126 | } 127 | 128 | void AABoundingBox::deserializeLC(File* file) 129 | { 130 | for (float& f : lowCorner) 131 | f = file->readFloat(); 132 | for (float& f : highCorner) 133 | f = file->readFloat(); 134 | } 135 | 136 | void AABoundingBox::serializeLC(File* file) 137 | { 138 | for (float& f : lowCorner) 139 | file->writeFloat(f); 140 | for (float& f : highCorner) 141 | file->writeFloat(f); 142 | } 143 | 144 | void AACylinder::deserialize(File * file) 145 | { 146 | for (float &f : center) 147 | f = file->readFloat(); 148 | radius = file->readFloat(); 149 | height = file->readFloat(); 150 | } 151 | 152 | void AACylinder::serialize(File * file) 153 | { 154 | for (float &f : center) 155 | file->writeFloat(f); 156 | file->writeFloat(radius); 157 | file->writeFloat(height); 158 | } 159 | -------------------------------------------------------------------------------- /rwsound.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "rw.h" // for stuff like RwsHeader, might need to move this into something like "rwcore.h" because we don't need graphics there 4 | #include "DynArray.h" 5 | #include 6 | #include 7 | #include 8 | 9 | struct File; 10 | 11 | template struct RwStruct { 12 | static const uint32_t tagID = N; 13 | }; 14 | 15 | static constexpr uint32_t NONNULL_POINTER = 0xFFFFFFFF; // a pointer value that can be anything but 0! 16 | 17 | static constexpr std::array RWA_FORMAT_ID_PCM = { 18 | 0x17, 0xD2, 0x1B, 0xD0, 0x87, 0x35, 0xED, 0x4E, 0xB9, 0xD9, 0xB8, 0xE8, 0x6E, 0xA9, 0xB9, 0x95 19 | }; 20 | static constexpr std::array RWA_FORMAT_ID_XBOX_ADPCM = { 21 | 0x93, 0x65, 0x38, 0xEF, 0x11, 0xB6, 0x2D, 0x43, 0x95, 0x7F, 0xA7, 0x1A, 0xDE, 0x44, 0x22, 0x7A 22 | }; 23 | static constexpr std::array RWA_FORMAT_ID_GCN_ADPCM = { 24 | 0xB0, 0x15, 0x62, 0xF8, 0xD5, 0x31, 0x29, 0x4C, 0xBD, 0x37, 0xCD, 0xBF, 0x9B, 0xD1, 0x0C, 0x53 25 | }; 26 | 27 | struct RwaWaveFormat { 28 | uint32_t sampleRate = 0; 29 | uint32_t ptrDataTypeUuid = NONNULL_POINTER; 30 | uint32_t dataSize = 0; 31 | uint8_t bitsPerSample = 0; 32 | uint8_t numChannels = 0; 33 | uint8_t padding1 = 0; 34 | uint8_t padding2 = 0; 35 | uint32_t ptrMiscData = 0; 36 | uint32_t miscDataSize = 0; 37 | uint8_t flags = 0; 38 | uint8_t reserved = 0; 39 | uint8_t padding3 = 0; 40 | uint8_t padding4 = 0; 41 | std::array uuid; 42 | 43 | void deserialize(File* file, bool isBigEndian); 44 | void serialize(File* file, bool isBigEndian) const; 45 | }; 46 | 47 | struct RwSoundData : RwStruct<0x804> { 48 | DynArray additionalEncodingInfo; 49 | DynArray data; 50 | 51 | void deserialize(File *file, uint32_t size); 52 | void serialize(File *file); 53 | }; 54 | 55 | struct RwSoundInfo : RwStruct<0x803> { 56 | uint32_t unk1; 57 | RwaWaveFormat format; 58 | RwaWaveFormat targetFormat; 59 | std::array rest; 60 | DynArray name; 61 | //std::string name; 62 | bool isBigEndian = false; 63 | 64 | void deserialize(File *file, uint32_t size); 65 | void serialize(File *file); 66 | }; 67 | 68 | struct RwSound : RwStruct<0x802> { 69 | RwSoundInfo info; 70 | RwSoundData data; 71 | 72 | void deserialize(File *file); 73 | void serialize(File *file); 74 | }; 75 | 76 | struct RwSoundList : RwStruct<0x80C> { 77 | std::vector sounds; 78 | bool isBigEndian = false; 79 | 80 | void deserialize(File *file); 81 | void serialize(File *file); 82 | }; 83 | 84 | struct RwSoundDictionaryInfo : RwStruct<0x80A> { 85 | DynArray bytes; 86 | 87 | void deserialize(File *file, uint32_t size); 88 | void serialize(File *file); 89 | }; 90 | 91 | struct RwSoundDictionary : RwStruct<0x809> { 92 | RwSoundDictionaryInfo info; 93 | RwSoundList list; 94 | 95 | void deserialize(File *file); 96 | void serialize(File *file); 97 | }; 98 | 99 | struct RwStreamInfo : RwStruct<0x80E> { 100 | uint32_t head_a = 0x14, head_b = 0x10, head_c = 0x24, head_d = 7; 101 | 102 | uint32_t str_a = NONNULL_POINTER, 103 | str_b = NONNULL_POINTER, 104 | str_c = 0, 105 | numSegments = 0; 106 | uint32_t str_e = NONNULL_POINTER, 107 | numSubstreams = 1, 108 | str_g = NONNULL_POINTER, 109 | basicSectorSize = 0; 110 | uint32_t streamSectorSize = 0, 111 | basicSectorSize2 = 0, 112 | streamDataOffset = 0; 113 | std::array streamUuid = { 0 }; 114 | std::array streamName = { 0 }; 115 | 116 | struct Segment { 117 | uint32_t segVal_1a = NONNULL_POINTER, segVal_1b = NONNULL_POINTER, segVal_1c = 0, segVal_1d = NONNULL_POINTER; 118 | uint32_t numMarkers = 0, ptrMarkers = 0, dataAlignedSize = 0, dataOffset = 0; 119 | uint32_t dataSize = 0; 120 | std::array uuid = { 0 }; 121 | std::array name = { 0 }; 122 | }; 123 | std::vector segments; 124 | 125 | uint32_t fin_a = NONNULL_POINTER, 126 | fin_b = NONNULL_POINTER, 127 | fin_c = 0, 128 | samplesPerFrame = 0; 129 | uint32_t subSectorSize = 0, 130 | fin_f = NONNULL_POINTER; 131 | uint16_t channelInterleaveSize = 0; 132 | uint16_t audioFrameSize = 0; 133 | uint16_t repeatChannels = 0; 134 | uint8_t padding1 = 0; 135 | uint8_t padding2 = 0; 136 | uint32_t subSectorUsedSize = 0, 137 | subSectorOffset = 0; 138 | RwaWaveFormat waveFormat; 139 | uint32_t fin_v = 0; 140 | std::array subUuid = { 0 }; 141 | std::array subName = { 0 }; 142 | 143 | void deserialize(File* file); 144 | void serialize(File* file); 145 | }; 146 | 147 | struct RwStream : RwStruct<0x80D> { 148 | RwStreamInfo info; 149 | std::vector data; 150 | 151 | void deserialize(File* file); 152 | void serialize(File* file); 153 | }; 154 | -------------------------------------------------------------------------------- /CoreClasses/CKComponent.cpp: -------------------------------------------------------------------------------- 1 | #include "CKComponent.h" 2 | #include "File.h" 3 | #include "KEnvironment.h" 4 | #include "CKNode.h" 5 | #include "CKDictionary.h" 6 | 7 | void CKCrateCpnt::deserialize(KEnvironment * kenv, File * file, size_t length) 8 | { 9 | group = kenv->readObjRef(file); 10 | particleNode = kenv->readObjRef(file); 11 | soundIds = kenv->readObjRef(file); 12 | projectiles = kenv->readObjRef(file); 13 | crateNode = kenv->readObjRef(file); 14 | 15 | if (kenv->version >= kenv->KVERSION_XXL2) { 16 | for (auto &cqd : x2CamQuakeDatas) { 17 | uint32_t sz = file->readUint32(); 18 | cqd.data1.resize(sz * 2); 19 | for (float &f : cqd.data1) 20 | f = file->readFloat(); 21 | sz = file->readUint32(); 22 | cqd.data2.resize(sz * 2); 23 | for (float &f : cqd.data2) 24 | f = file->readFloat(); 25 | cqd.fnFloat = file->readFloat(); 26 | } 27 | x2UnkFlt = file->readFloat(); 28 | } 29 | 30 | for (float &f : unk1) f = file->readFloat(); 31 | unk7 = file->readUint8(); 32 | for (float &f : pack1) f = file->readFloat(); 33 | for (float &f : pack2) f = file->readFloat(); 34 | for (int &f : bonuses) f = file->readUint32(); 35 | unk8 = file->readUint16(); 36 | unk9 = file->readUint8(); 37 | } 38 | 39 | void CKCrateCpnt::serialize(KEnvironment * kenv, File * file) 40 | { 41 | kenv->writeObjRef(file, group); 42 | kenv->writeObjRef(file, particleNode); 43 | kenv->writeObjRef(file, soundIds); 44 | kenv->writeObjRef(file, projectiles); 45 | kenv->writeObjRef(file, crateNode); 46 | 47 | if (kenv->version >= kenv->KVERSION_XXL2) { 48 | for (auto &cqd : x2CamQuakeDatas) { 49 | uint32_t cnt = cqd.data1.size() / 2; 50 | file->writeUint32(cnt); 51 | for (float &f : cqd.data1) 52 | file->writeFloat(f); 53 | cnt = cqd.data2.size() / 2; 54 | file->writeUint32(cnt); 55 | for (float &f : cqd.data2) 56 | file->writeFloat(f); 57 | file->writeFloat(cqd.fnFloat); 58 | } 59 | file->writeFloat(x2UnkFlt); 60 | } 61 | 62 | for (float &f : unk1) file->writeFloat(f); 63 | file->writeUint8(unk7); 64 | for (float &f : pack1) file->writeFloat(f); 65 | for (float &f : pack2) file->writeFloat(f); 66 | for (int &f : bonuses) file->writeUint32(f); 67 | file->writeUint16(unk8); 68 | file->writeUint8(unk9); 69 | } 70 | 71 | void CKCrateCpnt::reflectMembers2(MemberListener& r, KEnvironment* kenv) 72 | { 73 | r.reflect(group, "group"); 74 | r.reflect(particleNode, "particleNode"); 75 | r.reflect(soundIds, "soundIds"); 76 | r.reflect(projectiles, "projectiles"); 77 | r.reflect(crateNode, "crateNode"); 78 | 79 | // TODO: XXL2 80 | 81 | r.reflect(unk1, "unk1"); 82 | r.reflect(unk7, "unk7"); 83 | r.reflect(pack1, "pack1"); 84 | r.reflect(pack2, "pack2"); 85 | r.reflect(bonuses, "bonuses"); 86 | r.reflect(unk8, "unk8"); 87 | r.reflect(unk9, "unk9"); 88 | } 89 | 90 | #define RREFLECT(r, var) r.reflect(var, #var) 91 | 92 | void CKEnemyCpnt::reflectMembers2(MemberListener& r, KEnvironment* kenv) 93 | { 94 | RREFLECT(r, flags); 95 | RREFLECT(r, health); 96 | RREFLECT(r, damage); 97 | RREFLECT(r, unk1); 98 | RREFLECT(r, walkAnimSpeed); 99 | RREFLECT(r, coverAnimSpeed); 100 | RREFLECT(r, speed1); 101 | RREFLECT(r, runSpeed1); 102 | RREFLECT(r, speed2); 103 | RREFLECT(r, speed3); 104 | RREFLECT(r, runSpeed2); 105 | RREFLECT(r, speed4); 106 | RREFLECT(r, speed5); 107 | RREFLECT(r, unkf1); 108 | RREFLECT(r, unkf2); 109 | RREFLECT(r, unkf3); 110 | RREFLECT(r, coverRange); 111 | RREFLECT(r, unkf5); 112 | RREFLECT(r, attackCooldown); 113 | RREFLECT(r, unkf7); 114 | RREFLECT(r, unkf8); 115 | RREFLECT(r, unkf9); 116 | RREFLECT(r, targeting); 117 | RREFLECT(r, unkLast); 118 | if (kenv->isRemaster) { 119 | RREFLECT(r, ecRoma1); 120 | RREFLECT(r, ecRoma2); 121 | RREFLECT(r, ecRoma3); 122 | RREFLECT(r, ecRoma4); 123 | RREFLECT(r, ecRoma5); 124 | RREFLECT(r, ecRoma6); 125 | RREFLECT(r, ecRoma7); 126 | } 127 | } 128 | 129 | void CKShadowCpnt::reflectMembers2(MemberListener& r, KEnvironment* kenv) 130 | { 131 | r.reflect(scpValues, "scpValues"); 132 | r.reflect(scpBytes, "scpBytes"); 133 | } 134 | 135 | void CKBonusCpnt::reflectMembers2(MemberListener& r, KEnvironment* kenv) { 136 | r.reflect(ckbcUnk0, "ckbcUnk0"); 137 | r.reflect(ckbcUnk1, "ckbcUnk1"); 138 | r.reflect(ckbcUnk2, "ckbcUnk2"); 139 | r.reflect(ckbcUnk3, "ckbcUnk3"); 140 | r.reflect(ckbcUnk4, "ckbcUnk4"); 141 | r.reflect(ckbcUnk5, "ckbcUnk5"); 142 | r.reflect(ckbcUnk6, "ckbcUnk6"); 143 | r.reflect(ckbcUnk7, "ckbcUnk7"); 144 | r.reflect(ckbcUnk8, "ckbcUnk8"); 145 | r.reflect(ckbcUnk9, "ckbcUnk9"); 146 | r.reflect(ckbcUnk10, "ckbcUnk10"); 147 | r.reflect(ckbcUnk11, "ckbcUnk11"); 148 | r.reflect(ckbcUnk12, "ckbcUnk12"); 149 | r.reflect(ckbcUnk13, "ckbcUnk13"); 150 | r.reflect(ckbcUnk14, "ckbcUnk14"); 151 | }; 152 | -------------------------------------------------------------------------------- /resources/UbuntuMono-LICENCE.txt: -------------------------------------------------------------------------------- 1 | ------------------------------- 2 | UBUNTU FONT LICENCE Version 1.0 3 | ------------------------------- 4 | 5 | PREAMBLE 6 | This licence allows the licensed fonts to be used, studied, modified and 7 | redistributed freely. The fonts, including any derivative works, can be 8 | bundled, embedded, and redistributed provided the terms of this licence 9 | are met. The fonts and derivatives, however, cannot be released under 10 | any other licence. The requirement for fonts to remain under this 11 | licence does not require any document created using the fonts or their 12 | derivatives to be published under this licence, as long as the primary 13 | purpose of the document is not to be a vehicle for the distribution of 14 | the fonts. 15 | 16 | DEFINITIONS 17 | "Font Software" refers to the set of files released by the Copyright 18 | Holder(s) under this licence and clearly marked as such. This may 19 | include source files, build scripts and documentation. 20 | 21 | "Original Version" refers to the collection of Font Software components 22 | as received under this licence. 23 | 24 | "Modified Version" refers to any derivative made by adding to, deleting, 25 | or substituting -- in part or in whole -- any of the components of the 26 | Original Version, by changing formats or by porting the Font Software to 27 | a new environment. 28 | 29 | "Copyright Holder(s)" refers to all individuals and companies who have a 30 | copyright ownership of the Font Software. 31 | 32 | "Substantially Changed" refers to Modified Versions which can be easily 33 | identified as dissimilar to the Font Software by users of the Font 34 | Software comparing the Original Version with the Modified Version. 35 | 36 | To "Propagate" a work means to do anything with it that, without 37 | permission, would make you directly or secondarily liable for 38 | infringement under applicable copyright law, except executing it on a 39 | computer or modifying a private copy. Propagation includes copying, 40 | distribution (with or without modification and with or without charging 41 | a redistribution fee), making available to the public, and in some 42 | countries other activities as well. 43 | 44 | PERMISSION & CONDITIONS 45 | This licence does not grant any rights under trademark law and all such 46 | rights are reserved. 47 | 48 | Permission is hereby granted, free of charge, to any person obtaining a 49 | copy of the Font Software, to propagate the Font Software, subject to 50 | the below conditions: 51 | 52 | 1) Each copy of the Font Software must contain the above copyright 53 | notice and this licence. These can be included either as stand-alone 54 | text files, human-readable headers or in the appropriate machine- 55 | readable metadata fields within text or binary files as long as those 56 | fields can be easily viewed by the user. 57 | 58 | 2) The font name complies with the following: 59 | (a) The Original Version must retain its name, unmodified. 60 | (b) Modified Versions which are Substantially Changed must be renamed to 61 | avoid use of the name of the Original Version or similar names entirely. 62 | (c) Modified Versions which are not Substantially Changed must be 63 | renamed to both (i) retain the name of the Original Version and (ii) add 64 | additional naming elements to distinguish the Modified Version from the 65 | Original Version. The name of such Modified Versions must be the name of 66 | the Original Version, with "derivative X" where X represents the name of 67 | the new work, appended to that name. 68 | 69 | 3) The name(s) of the Copyright Holder(s) and any contributor to the 70 | Font Software shall not be used to promote, endorse or advertise any 71 | Modified Version, except (i) as required by this licence, (ii) to 72 | acknowledge the contribution(s) of the Copyright Holder(s) or (iii) with 73 | their explicit written permission. 74 | 75 | 4) The Font Software, modified or unmodified, in part or in whole, must 76 | be distributed entirely under this licence, and must not be distributed 77 | under any other licence. The requirement for fonts to remain under this 78 | licence does not affect any document created using the Font Software, 79 | except any version of the Font Software extracted from a document 80 | created using the Font Software may only be distributed under this 81 | licence. 82 | 83 | TERMINATION 84 | This licence becomes null and void if any of the above conditions are 85 | not met. 86 | 87 | DISCLAIMER 88 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 89 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 90 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF 91 | COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 92 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 93 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 94 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 95 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER 96 | DEALINGS IN THE FONT SOFTWARE. 97 | -------------------------------------------------------------------------------- /CoreClasses/CKCamera.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "KObject.h" 4 | #include "CKUtils.h" 5 | #include "CKPartlyUnknown.h" 6 | #include "vecmat.h" 7 | 8 | struct CKSceneNode; 9 | struct CKCameraFogDatas; 10 | 11 | struct CKCameraBase : CKMemberReflectable, 0>> { 12 | uint32_t kcamUnk0 = 0; 13 | uint32_t kcamUnk1 = 257; 14 | float kcamFarDistance = 100.0f, kcamUnk3 = 0.1f; 15 | float kcamFarDistance_dup = kcamFarDistance, kcamUnk3_dup = kcamUnk3; // duplicates for Romaster 16 | float kcamUnk4 = 0.1f; 17 | 18 | float kcamFOV = 70.0f; 19 | Vector3 kcamPosition = { 0.0f, 0.0f, 0.0f }, 20 | kcamLookAt = { 0.0f, 0.0f, 1.0f }, 21 | kcamUpVector = { 0.0f, 1.0f, 0.0f }; 22 | float kcamFOV_dup = kcamFOV; 23 | Vector3 kcamPosition_dup = kcamPosition, kcamLookAt_dup = kcamLookAt, kcamUpVector_dup = kcamUpVector; 24 | 25 | // XXL2+: 26 | uint32_t kcamX2Unk0 = 0x60; 27 | uint8_t kcamX2Unk1 = 2, kcamX2Unk2 = 2, kcamX2Unk3 = 0; 28 | uint8_t kcamX2Unk4 = 0; 29 | 30 | // Arthur+: 31 | kobjref ogFogData; 32 | uint8_t ogUnk1 = 0; 33 | int32_t ogUnk2a = 1; float ogUnk3a = 0.0f; float ogUnk4a = 0.0f; 34 | int8_t ogUnk2b = 1; // OG 35 | 36 | // OG360+: 37 | int32_t ogHdUnk1 = 0; 38 | kobjref ogHdUnk2; 39 | 40 | kobjref kcamNextCam; 41 | 42 | void reflectMembers2(MemberListener &r, KEnvironment *kenv); 43 | void init(KEnvironment* kenv) override; 44 | }; 45 | 46 | struct CKCamera : CKMRSubclass { 47 | float kcamX2DerUnk0 = 0.0f; 48 | float kcamOgUnk1 = 0.0f; 49 | Vector3 kcamOgUnk2 = { 0.0f, 0.0f, 1.0f }; 50 | void reflectMembers2(MemberListener& r, KEnvironment* kenv); 51 | }; 52 | 53 | struct CKCameraRigidTrack : CKMRSubclass { 54 | // XXL1 55 | kobjref kcrtUnk1; 56 | std::array kcrtUnk2; 57 | 58 | // XXL2+: 59 | Vector3 kcrtX2Vec1; 60 | Vector3 kcrtX2Vec2; 61 | float kcrtX2Flt1; 62 | float kcrtOgFlt2; 63 | 64 | void reflectMembers2(MemberListener& r, KEnvironment* kenv); 65 | }; 66 | 67 | struct CKCameraClassicTrack : CKMRSubclass { 68 | uint32_t kclscamUnk0; 69 | std::array kclscamUnk1; 70 | std::array kclscamUnk2; 71 | std::array kclscamUnk3; 72 | float kclscamUnk4; 73 | 74 | // Romaster only: 75 | uint8_t kclscamUnk5; 76 | Vector3 kclscamUnk6; 77 | 78 | void reflectMembers2(MemberListener &r, KEnvironment *kenv); 79 | }; 80 | 81 | struct CKCameraPathTrack : CKMRSubclass { 82 | kobjref kcptSpline; 83 | Vector3 kcptUnk2; 84 | uint8_t kcptUnk5 = 0xDC; 85 | 86 | std::vector> kcptX2FltArray; 87 | Vector3 kcptX2UnkVector; 88 | 89 | void reflectMembers2(MemberListener& r, KEnvironment* kenv); 90 | }; 91 | 92 | struct CKCameraFixTrack : CKMRSubclass { 93 | float cftUnk1 = 0; 94 | uint32_t cftRoma1 = 0, cftRoma2 = 0; // romaster only 95 | float cftX2Unk2 = 0.0f; 96 | void reflectMembers2(MemberListener &r, KEnvironment *kenv); 97 | }; 98 | 99 | struct CKCameraAxisTrack : CKMRSubclass { 100 | uint8_t catUnk1; 101 | Vector3 catUnk2; 102 | KPostponedRef catNode; 103 | float catX2Unk = 0.0f; 104 | 105 | // Arthur: 106 | uint32_t arUnk1; float arUnk2, arUnk3, arUnk4; 107 | 108 | void reflectMembers2(MemberListener& r, KEnvironment* kenv); 109 | void onLevelLoaded(KEnvironment* kenv) override; 110 | }; 111 | 112 | struct CKCameraSpyTrack : CKMRSubclass { 113 | std::array kcamspyValues; 114 | void reflectMembers2(MemberListener& r, KEnvironment* kenv); 115 | }; 116 | 117 | struct CKCameraPassivePathTrack : CKMRSubclass { 118 | kobjref kcpptSpline; 119 | float kcpptUnk1; 120 | float kcpptUnk2; 121 | float kcpptUnk3; 122 | float kcpptUnk4; 123 | float kcpptUnk5; 124 | float kcpptUnk6; 125 | 126 | // XXL2+: 127 | std::array kcpptX2Values; 128 | uint16_t kcpptUnk7 = 3; 129 | // OG+: 130 | kobjref kcpptSecondSpline; 131 | float kcpptOgUnk1 = 0.0f; 132 | float kcpptOgUnk2 = 100.0f; 133 | 134 | void reflectMembers2(MemberListener& r, KEnvironment* kenv); 135 | }; 136 | 137 | struct CKCameraBalistTrack : CKMRSubclass { 138 | float cambalUnk1 = 2.0f, cambalUnk2 = 2.5f, cambalUnk3 = -10.0f, cambalUnk4 = 0.0f; 139 | float arUnk1 = 0.0f, arUnk2 = 0.0f; 140 | void reflectMembers2(MemberListener& r, KEnvironment* kenv); 141 | }; 142 | 143 | struct CKCameraClassicTrack2 : CKMRSubclass { 144 | std::array values; 145 | float arValue; 146 | std::array ogValues; 147 | void reflectMembers2(MemberListener& r, KEnvironment* kenv); 148 | }; 149 | 150 | struct CKCameraFirstPersonTrack : CKMRSubclass { 151 | float cfptUnk1 = 1.6f, cfptUnk2 = 90.0f, cfptUnk3 = 90.0f; 152 | void reflectMembers2(MemberListener& r, KEnvironment* kenv); 153 | }; 154 | -------------------------------------------------------------------------------- /vecmat.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | struct Matrix; 6 | struct Vector3; 7 | 8 | // 4x4 matrix structure 9 | struct /*alignas(16)*/ Matrix 10 | { 11 | union { 12 | float v[16]; 13 | float m[4][4]; 14 | struct { 15 | float _11, _12, _13, _14, _21, _22, _23, _24, 16 | _31, _32, _33, _34, _41, _42, _43, _44; 17 | }; 18 | }; 19 | Matrix operator*(const Matrix &a) const 20 | { 21 | return multiplyMatrices(*this, a); 22 | } 23 | Matrix &operator*=(const Matrix &a) 24 | { 25 | *this = multiplyMatrices(*this, a); 26 | return *this; 27 | } 28 | bool operator==(const Matrix &a) const { 29 | for (int i = 0; i < 16; i++) 30 | if (v[i] != a.v[i]) 31 | return false; 32 | return true; 33 | } 34 | bool operator!=(const Matrix &a) const { return !(*this == a); } 35 | 36 | Vector3 getTranslationVector() const; 37 | Vector3 getScalingVector() const; 38 | Matrix getInverse4x3() const; 39 | Matrix getInverse4x4() const; 40 | void setTranslation(const Vector3& translation); 41 | 42 | static Matrix getIdentity() 43 | { 44 | Matrix mat; 45 | for (int i = 0; i < 4; i++) 46 | for (int j = 0; j < 4; j++) 47 | mat.m[i][j] = (i == j) ? 1.0f : 0.0f; 48 | return mat; 49 | } 50 | static Matrix getZeroMatrix() 51 | { 52 | Matrix mat; 53 | for (int i = 0; i < 16; i++) 54 | mat.v[i] = 0.0f; 55 | return mat; 56 | } 57 | static Matrix getTranslationMatrix(const Vector3 &translation); 58 | static Matrix getRotationXMatrix(float radians); 59 | static Matrix getRotationYMatrix(float radians); 60 | static Matrix getRotationZMatrix(float radians); 61 | static Matrix getScaleMatrix(const Vector3 &scale); 62 | static Matrix getRHOrthoMatrix(float w, float h, float zn, float zf); 63 | static Matrix getRHPerspectiveMatrix(float fovy, float aspect, float zn, float zf); 64 | static Matrix getRHLookAtViewMatrix(const Vector3 &eye, const Vector3 &at, const Vector3 &up); 65 | static Matrix multiplyMatrices(const Matrix &a, const Matrix &b); 66 | }; 67 | 68 | // Three-dimensional vector 69 | struct /*alignas(16)*/ Vector3 70 | { 71 | union { 72 | struct { float x, y, z; }; // , w; // w used to align size. 73 | float coord[3]; 74 | }; 75 | constexpr Vector3() : x(0.0f), y(0.0f), z(0.0f) {} 76 | constexpr Vector3(float a, float b) : x(a), y(b), z(0.0f) {} 77 | constexpr Vector3(float a, float b, float c) : x(a), y(b), z(c) {} 78 | 79 | Vector3 operator+(const Vector3 &a) const {return Vector3(x + a.x, y + a.y, z + a.z);} 80 | Vector3 operator-(const Vector3 &a) const {return Vector3(x - a.x, y - a.y, z - a.z);} 81 | Vector3 operator*(const Vector3 &a) const {return Vector3(x * a.x, y * a.y, z * a.z);} 82 | Vector3 operator/(const Vector3 &a) const {return Vector3(x / a.x, y / a.y, z / a.z);} 83 | Vector3 operator+(float a) const {return Vector3(x + a, y + a, z + a);} 84 | Vector3 operator-(float a) const {return Vector3(x - a, y - a, z - a);} 85 | Vector3 operator*(float a) const {return Vector3(x * a, y * a, z * a);} 86 | Vector3 operator/(float a) const {return Vector3(x / a, y / a, z / a);} 87 | Vector3 operator-() const {return Vector3(-x, -y, -z);} 88 | 89 | Vector3 &operator+=(const Vector3 &a) {x += a.x; y += a.y; z += a.z; return *this;} 90 | Vector3 &operator-=(const Vector3 &a) {x -= a.x; y -= a.y; z -= a.z; return *this;} 91 | Vector3 &operator*=(const Vector3 &a) {x *= a.x; y *= a.y; z *= a.z; return *this;} 92 | Vector3 &operator/=(const Vector3 &a) {x /= a.x; y /= a.y; z /= a.z; return *this;} 93 | Vector3 &operator+=(float a) {x += a; y += a; z += a; return *this;} 94 | Vector3 &operator-=(float a) {x -= a; y -= a; z -= a; return *this;} 95 | Vector3 &operator*=(float a) {x *= a; y *= a; z *= a; return *this;} 96 | Vector3 &operator/=(float a) {x /= a; y /= a; z /= a; return *this;} 97 | 98 | bool operator==(const Vector3 &a) const {return (x==a.x) && (y==a.y) && (z==a.z);} 99 | bool operator!=(const Vector3 &a) const {return !( (x==a.x) && (y==a.y) && (z==a.z) );} 100 | bool operator<(const Vector3 &a) const { return (x != a.x) ? (x < a.x) : ((y != a.y) ? (y < a.y) : (z < a.z)); } 101 | 102 | //void print() const {printf("(%f, %f, %f)\n", x, y, z);} 103 | float len2xy() const {return sqrt(x*x + y*y);} 104 | float sqlen2xy() const {return x*x + y*y;} 105 | float len2xz() const {return sqrt(x*x + z*z);} 106 | float sqlen2xz() const {return x*x + z*z;} 107 | float len3() const {return sqrt(x*x + y*y + z*z);} 108 | float sqlen3() const {return x*x + y*y + z*z;} 109 | Vector3 normal() const {float l = len3(); if(l != 0.0f) return Vector3(x/l, y/l, z/l); else return Vector3(0,0,0);} 110 | Vector3 normal2xz() const {float l = len2xz(); if(l != 0.0f) return Vector3(x/l, 0, z/l); else return Vector3(0,0,0);} 111 | float dot(const Vector3 &a) const {return a.x * x + a.y * y + a.z * z;} 112 | float dot2xz(const Vector3 &a) const {return a.x * x + a.z * z;} 113 | Vector3 cross(const Vector3 &v) const {return Vector3(y*v.z - z*v.y, z*v.x - x*v.z, x*v.y - y*v.x);} 114 | Vector3 transform(const Matrix &m) const; 115 | 116 | float *begin() { return coord; } 117 | const float *begin() const { return coord; } 118 | float *end() { return coord + 3; } 119 | const float *end() const { return coord + 3; } 120 | }; 121 | -------------------------------------------------------------------------------- /EditorUI/IGCollisionEditor.cpp: -------------------------------------------------------------------------------- 1 | #include "IGCollisionEditor.h" 2 | 3 | #include "EditorInterface.h" 4 | #include "EditorWidgets.h" 5 | #include "ImGuiMemberListener.h" 6 | 7 | #include "KEnvironment.h" 8 | #include "CoreClasses/CKService.h" 9 | #include "CoreClasses/CKNode.h" 10 | 11 | #include 12 | 13 | void EditorUI::IGCollisionEditor(EditorInterface& ui) 14 | { 15 | auto& kenv = ui.kenv; 16 | CKSrvCollision* srvcoll = kenv.levelObjects.getFirst(); 17 | if (!srvcoll) 18 | return; 19 | ImGuiMemberListener igml{ kenv, ui }; 20 | igml.setPropertyInfoList(ui.g_encyclo, srvcoll); 21 | MemberListener* ml = &igml; 22 | if (ImGui::BeginTabBar("ColliTabBar")) { 23 | if (ImGui::BeginTabItem("Head")) { 24 | //ml->reflect(srvcoll->numWhat, "numWhat"); 25 | ml->reflect(srvcoll->numProjectilesUsed, "numProjectilesUsed"); 26 | //ml->reflect(srvcoll->unk1, "unk1"); 27 | //ml->reflect(srvcoll->unk2, "unk2"); 28 | ml->reflect(srvcoll->activeList, "activeList"); 29 | ml->reflect(srvcoll->inactiveList, "inactiveList"); 30 | ImGui::EndTabItem(); 31 | } 32 | if (ImGui::BeginTabItem("projectileTargets")) { 33 | int i = 0; 34 | for (auto& vec : srvcoll->projectileTargets) { 35 | int j = 0; 36 | for (auto& ref : vec) { 37 | std::string str = std::to_string(i) + ',' + std::to_string(j); 38 | IGObjectSelectorRef(ui, str.c_str(), ref); 39 | j++; 40 | } 41 | ImGui::Separator(); 42 | i++; 43 | } 44 | ImGui::EndTabItem(); 45 | } 46 | if (ImGui::BeginTabItem("collidingLifes")) { 47 | int i = 0; 48 | for (auto& ref : srvcoll->collidingLifes) 49 | IGObjectSelectorRef(ui, std::to_string(i++).c_str(), ref); 50 | ImGui::EndTabItem(); 51 | } 52 | if (ImGui::BeginTabItem("collisionTests")) { 53 | static size_t selectedBing = -1; 54 | auto getActorName = [&kenv, srvcoll](uint16_t id) -> const char* { 55 | if (id != 0xFFFF && srvcoll->collidingLifes[id]) { 56 | if (kenv.version >= kenv.KVERSION_XXL2) 57 | return kenv.getObjectName(srvcoll->collidingLifes[id].get()); 58 | else 59 | return srvcoll->collidingLifes[id]->getClassName(); 60 | } 61 | return "/"; 62 | }; 63 | ImGui::Columns(2); 64 | static std::vector bingTags; 65 | if (ImGui::Button("Tag")) { 66 | bingTags = std::vector(srvcoll->collisionTests.size(), 0); 67 | std::vector nodes = { srvcoll->activeList }; 68 | std::vector nodes2; 69 | bingTags[srvcoll->activeList] |= 1; 70 | while (!nodes.empty()) { 71 | nodes2.clear(); 72 | for (int n : nodes) { 73 | for (int a : srvcoll->collisionTests[n].aa) { 74 | if (a != 0xFFFF && (bingTags[a] & 1) == 0) { 75 | bingTags[a] |= 1; 76 | nodes2.push_back(a); 77 | } 78 | } 79 | } 80 | std::swap(nodes, nodes2); 81 | } 82 | nodes = { srvcoll->inactiveList }; 83 | nodes2.clear(); 84 | bingTags[srvcoll->inactiveList] |= 2; 85 | while (!nodes.empty()) { 86 | nodes2.clear(); 87 | for (int n : nodes) { 88 | for (int a : {srvcoll->collisionTests[n].aa[0], srvcoll->collisionTests[n].aa[1]}) { 89 | if (a != 0xFFFF && (bingTags[a] & 2) == 0) { 90 | bingTags[a] |= 2; 91 | nodes2.push_back(a); 92 | } 93 | } 94 | } 95 | std::swap(nodes, nodes2); 96 | } 97 | } 98 | ImGui::BeginChild("CollBingList"); 99 | int b = 0; 100 | for (auto& bing : srvcoll->collisionTests) { 101 | ImGui::PushID(b); 102 | if (ImGui::Selectable("##sel", b == selectedBing)) 103 | selectedBing = b; 104 | if (ImGui::IsItemVisible()) { 105 | ImGui::SameLine(); 106 | int tag = (b < bingTags.size()) ? bingTags[b] : 7; 107 | ImGui::Text("%i:%i, %s : %s", b, tag, getActorName(bing.lifeIndex1), getActorName(bing.lifeIndex2)); 108 | } 109 | ImGui::PopID(); 110 | b++; 111 | } 112 | ImGui::EndChild(); 113 | ImGui::NextColumn(); 114 | ImGui::BeginChild("CollBingInfo"); 115 | if (selectedBing < srvcoll->collisionTests.size()) { 116 | auto& bing = srvcoll->collisionTests[selectedBing]; 117 | ImGui::InputScalar("flags", ImGuiDataType_U16, &bing.flags, nullptr, nullptr, "%04X"); 118 | ml->reflect(bing.shapeNode1, "shapeNode1"); 119 | ml->reflect(bing.shapeNode2, "shapeNode2"); 120 | ml->reflect(bing.lifeIndex1, "lifeIndex1"); 121 | ImGui::Text("%s", getActorName(bing.lifeIndex1)); 122 | ml->reflect(bing.lifeIndex2, "lifeIndex2"); 123 | ImGui::Text("%s", getActorName(bing.lifeIndex2)); 124 | ml->reflect(bing.priority, "priority"); 125 | ml->reflect(bing.aa, "aa"); 126 | ImGui::Separator(); 127 | if (bing.shapeNode1) { 128 | ml->reflect(bing.shapeNode1->cast()->unk1, "obj1_unk1"); 129 | ml->reflect(bing.shapeNode1->cast()->unk2, "obj1_unk2"); 130 | } 131 | if (bing.shapeNode2) { 132 | ml->reflect(bing.shapeNode2->cast()->unk1, "obj2_unk1"); 133 | ml->reflect(bing.shapeNode2->cast()->unk2, "obj2_unk2"); 134 | } 135 | } 136 | ImGui::EndChild(); 137 | ImGui::Columns(); 138 | ImGui::EndTabItem(); 139 | } 140 | ImGui::EndTabBar(); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /rwext.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "rw.h" 4 | //#include "File.h" 5 | #include 6 | #include 7 | #include "vecmat.h" 8 | 9 | struct File; 10 | struct RwsHeader; 11 | 12 | struct RwExtension { 13 | virtual ~RwExtension() {} 14 | virtual uint32_t getType() = 0; 15 | virtual void deserialize(File *file, const RwsHeader &header, void *parent) = 0; 16 | virtual void serialize(File *file) = 0; 17 | virtual std::unique_ptr clone() = 0; 18 | }; 19 | 20 | struct RwExtUnknown : RwExtension { 21 | void *_ptr = nullptr; 22 | uint32_t _length = 0; 23 | uint32_t _type; 24 | 25 | ~RwExtUnknown(); 26 | uint32_t getType() override; 27 | void deserialize(File *file, const RwsHeader &header, void *parent) override; 28 | void serialize(File *file) override; 29 | std::unique_ptr clone() override; 30 | }; 31 | 32 | struct RwExtHAnim : RwExtension { 33 | struct Bone { 34 | uint32_t nodeId, nodeIndex, flags; 35 | }; 36 | uint32_t version, nodeId; // , numNodes; 37 | uint32_t flags, keyFrameSize; 38 | std::vector bones; 39 | 40 | uint32_t getType() override; 41 | void deserialize(File *file, const RwsHeader &header, void *parent) override; 42 | void serialize(File *file) override; 43 | std::unique_ptr clone() override; 44 | }; 45 | 46 | struct RwExtSkin : RwExtension { 47 | uint8_t numBones, numUsedBones, maxWeightPerVertex; 48 | std::vector usedBones; 49 | std::vector> vertexIndices; 50 | std::vector> vertexWeights; 51 | std::vector matrices; 52 | 53 | bool isSplit; 54 | uint32_t boneLimit; // , numMeshes, numRLE; 55 | std::vector boneIndexRemap; 56 | std::vector> boneGroups; 57 | std::vector> boneGroupIndices; 58 | 59 | std::vector nativeData; // for consoles 60 | 61 | uint32_t getType() override; 62 | void deserialize(File *file, const RwsHeader &header, void *parent) override; 63 | void serialize(File *file) override; 64 | std::unique_ptr clone() override; 65 | 66 | void merge(const RwExtSkin &other); 67 | }; 68 | 69 | struct RwExtBinMesh : RwExtension { 70 | uint32_t flags = 0, /*numMeshes = 0,*/ totalIndices = 0; 71 | struct Mesh { 72 | uint32_t material; 73 | std::vector indices; 74 | }; 75 | std::vector meshes; 76 | bool isNative = false; 77 | 78 | uint32_t getType() override; 79 | void deserialize(File* file, const RwsHeader& header, void* parent) override; 80 | void serialize(File* file) override; 81 | std::unique_ptr clone() override; 82 | }; 83 | 84 | struct RwExtNativeData : RwExtension { 85 | std::vector data; 86 | uint32_t getType() override; 87 | void deserialize(File* file, const RwsHeader& header, void* parent) override; 88 | void serialize(File* file) override; 89 | std::unique_ptr clone() override; 90 | }; 91 | 92 | struct RwExtMaterialEffectsPLG_Material : RwExtension { 93 | // https://gtamods.com/wiki/Material_Effects_PLG_(RW_Section) 94 | // Effects often used in ELB games: null 0, env 2, dual 4, uv 5 (bumps are never used) 95 | // When both dual (4) and uv (5) are used (6), first effect is 5 then second effect is 4 96 | // 5 is seen alone, but not 4 (I mean, if multi texture doesn't animate, could as well just use one single texture) 97 | 98 | struct NullEffect { 99 | static constexpr int32_t ID = 0; 100 | void read(File* file) {} 101 | void write(File* file) {} 102 | }; 103 | struct BumpMapEffect { 104 | static constexpr int32_t ID = 1; 105 | float intensity = 0.0f; 106 | uint32_t hasBumpMap = 0; 107 | RwTexture bumpMap; 108 | uint32_t hasHeightMap = 0; 109 | RwTexture heightMap; 110 | void read(File* file); 111 | void write(File* file); 112 | }; 113 | struct EnvironmentEffect { 114 | static constexpr int32_t ID = 2; 115 | float reflectionCoeff = 1.0f; 116 | uint32_t fbac = 1; 117 | uint32_t hasEnvMap = 0; 118 | RwTexture envMap; 119 | void read(File* file); 120 | void write(File* file); 121 | }; 122 | struct DualTextureEffect { 123 | static constexpr int32_t ID = 4; 124 | uint32_t srcBlendMode = 0; 125 | uint32_t destBlendMode = 0; 126 | uint32_t hasTexture = 0; 127 | RwTexture texture; 128 | void read(File* file); 129 | void write(File* file); 130 | }; 131 | struct UVTransformationEffect { 132 | static constexpr int32_t ID = 5; 133 | void read(File* file) {} 134 | void write(File* file) {} 135 | }; 136 | 137 | uint32_t type = 0; 138 | std::variant firstEffect; 139 | std::variant secondEffect; 140 | 141 | uint32_t getType() override; 142 | void deserialize(File* file, const RwsHeader& header, void* parent) override; 143 | void serialize(File* file) override; 144 | std::unique_ptr clone() override; 145 | }; 146 | 147 | struct RwExtMaterialEffectsPLG_Atomic : RwExtension { 148 | uint32_t matFxEnabled = 1; 149 | uint32_t getType() override; 150 | void deserialize(File* file, const RwsHeader& header, void* parent) override; 151 | void serialize(File* file) override; 152 | std::unique_ptr clone() override; 153 | }; 154 | 155 | std::unique_ptr RwExtCreate(uint32_t type, uint32_t parentType); -------------------------------------------------------------------------------- /window.cpp: -------------------------------------------------------------------------------- 1 | #include "window.h" 2 | #include 3 | #include "imgui/imgui.h" 4 | #include 5 | 6 | #ifdef _WIN32 7 | #define WIN32_LEAN_AND_MEAN 8 | #include 9 | #include 10 | #endif 11 | 12 | Window::Window() 13 | { 14 | _sw = SDL_CreateWindow("XXL Editor", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, _width=1280, _height=720, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE); 15 | } 16 | 17 | static int igMouseIndex(int sdl) { 18 | switch (sdl) { 19 | case SDL_BUTTON_LEFT: return 0; 20 | case SDL_BUTTON_RIGHT: return 1; 21 | case SDL_BUTTON_MIDDLE: return 2; 22 | } 23 | return -1; 24 | } 25 | 26 | static ImGuiKey SdlToImGuiKey(SDL_Keycode code) 27 | { 28 | switch (code) { 29 | case SDLK_TAB: return ImGuiKey_Tab; 30 | case SDLK_LEFT: return ImGuiKey_LeftArrow; 31 | case SDLK_RIGHT: return ImGuiKey_RightArrow; 32 | case SDLK_UP: return ImGuiKey_UpArrow; 33 | case SDLK_DOWN: return ImGuiKey_DownArrow; 34 | case SDLK_PAGEUP: return ImGuiKey_PageUp; 35 | case SDLK_PAGEDOWN: return ImGuiKey_PageDown; 36 | case SDLK_HOME: return ImGuiKey_Home; 37 | case SDLK_END: return ImGuiKey_End; 38 | case SDLK_DELETE: return ImGuiKey_Delete; 39 | case SDLK_BACKSPACE: return ImGuiKey_Backspace; 40 | case SDLK_SPACE: return ImGuiKey_Space; 41 | case SDLK_RETURN: return ImGuiKey_Enter; 42 | case SDLK_ESCAPE: return ImGuiKey_Escape; 43 | case SDLK_a: return ImGuiKey_A; 44 | case SDLK_c: return ImGuiKey_C; 45 | case SDLK_v: return ImGuiKey_V; 46 | case SDLK_x: return ImGuiKey_X; 47 | case SDLK_y: return ImGuiKey_Y; 48 | case SDLK_z: return ImGuiKey_Z; 49 | } 50 | return ImGuiKey_None; 51 | } 52 | 53 | static void HandleImGui(const SDL_Event &event) 54 | { 55 | ImGuiIO &igio = ImGui::GetIO(); 56 | switch (event.type) { 57 | case SDL_MOUSEMOTION: 58 | igio.MousePos = ImVec2((float)event.motion.x, (float)event.motion.y); 59 | break; 60 | case SDL_MOUSEBUTTONDOWN: { 61 | int x = igMouseIndex(event.button.button); 62 | if (x != -1) 63 | igio.MouseDown[x] = true; 64 | break; 65 | } 66 | case SDL_MOUSEBUTTONUP: { 67 | int x = igMouseIndex(event.button.button); 68 | if (x != -1) 69 | igio.MouseDown[x] = false; 70 | break; 71 | } 72 | case SDL_MOUSEWHEEL: 73 | igio.MouseWheel += event.wheel.y; 74 | break; 75 | case SDL_KEYDOWN: 76 | case SDL_KEYUP: { 77 | auto kmod = event.key.keysym.mod; 78 | igio.AddKeyEvent(ImGuiKey_ModCtrl, kmod & KMOD_CTRL); 79 | igio.AddKeyEvent(ImGuiKey_ModShift, kmod & KMOD_SHIFT); 80 | igio.AddKeyEvent(ImGuiKey_ModAlt, kmod & KMOD_ALT); 81 | igio.AddKeyEvent(SdlToImGuiKey(event.key.keysym.sym), event.type == SDL_KEYDOWN); 82 | break; 83 | } 84 | case SDL_TEXTINPUT: 85 | igio.AddInputCharactersUTF8(event.text.text); 86 | break; 87 | } 88 | } 89 | 90 | void * Window::getNativeWindow() 91 | { 92 | SDL_SysWMinfo syswm; 93 | SDL_VERSION(&syswm.version); 94 | SDL_GetWindowWMInfo(getSDLWindow(), &syswm); 95 | HWND hWindow = syswm.info.win.window; 96 | return (void*)hWindow; 97 | } 98 | 99 | void Window::handle() 100 | { 101 | SDL_Event event; 102 | bool imguion = ImGui::GetCurrentContext() != nullptr; 103 | for (int i = 0; i < SDL_NUM_SCANCODES; i++) 104 | _keyPressed[i] = false; 105 | _mouseWheel = 0; 106 | for (bool &b : _mousePressed) 107 | b = false; 108 | 109 | bool igWantsMouse = false, igWantsKeyboard = false; 110 | if (imguion) { 111 | ImGuiIO &io = ImGui::GetIO(); 112 | igWantsMouse = io.WantCaptureMouse; 113 | igWantsKeyboard = io.WantCaptureKeyboard; 114 | } 115 | 116 | SDL_Keymod kmod = SDL_GetModState(); 117 | _ctrlPressed = kmod & KMOD_CTRL; 118 | _shiftPressed = kmod & KMOD_SHIFT; 119 | _altPressed = kmod & KMOD_ALT; 120 | 121 | while (SDL_PollEvent(&event)) { 122 | if (imguion) 123 | HandleImGui(event); 124 | switch (event.type) { 125 | case SDL_QUIT: 126 | _quit = true; 127 | break; 128 | case SDL_KEYDOWN: 129 | if (igWantsKeyboard) break; 130 | _keyDown[event.key.keysym.scancode] = true; 131 | _keyPressed[event.key.keysym.scancode] = true; 132 | break; 133 | case SDL_KEYUP: 134 | _keyDown[event.key.keysym.scancode] = false; 135 | break; 136 | case SDL_WINDOWEVENT: 137 | //printf("window event!!!\n"); 138 | switch (event.window.event) { 139 | case SDL_WINDOWEVENT_SIZE_CHANGED: 140 | _width = event.window.data1; 141 | _height = event.window.data2; 142 | //printf("Resized! %i %i\n", _width, _height); 143 | //if (g_gfxRenderer) 144 | // g_gfxRenderer->Reset(); 145 | break; 146 | } 147 | break; 148 | case SDL_MOUSEBUTTONDOWN: 149 | if (!igWantsMouse) 150 | _mouseDown[event.button.button] = true; 151 | break; 152 | case SDL_MOUSEBUTTONUP: 153 | _mouseDown[event.button.button] = false; 154 | if (!igWantsMouse) 155 | _mousePressed[event.button.button] = true; 156 | break; 157 | case SDL_MOUSEMOTION: 158 | _mouseX = event.motion.x; 159 | _mouseY = event.motion.y; 160 | break; 161 | case SDL_MOUSEWHEEL: 162 | if (!igWantsMouse) 163 | _mouseWheel += event.wheel.y; 164 | //printf("mouse wheel %i\n", g_mouseWheel); 165 | break; 166 | } 167 | } 168 | 169 | // Pause when the window is minimized, until it is visible again. 170 | while (SDL_GetWindowFlags(_sw) & SDL_WINDOW_MINIMIZED) { 171 | SDL_WaitEvent(&event); 172 | if (event.type == SDL_QUIT) { 173 | _quit = true; 174 | break; 175 | } 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /Encyclopedia.cpp: -------------------------------------------------------------------------------- 1 | #include "Encyclopedia.h" 2 | #include 3 | #include "File.h" 4 | 5 | #include 6 | #include 7 | #include "GuiUtils.h" 8 | 9 | Encyclopedia::Encyclopedia() = default; 10 | Encyclopedia::~Encyclopedia() = default; 11 | 12 | void Encyclopedia::setKVersion(int ver) 13 | { 14 | if (kversion != ver) { 15 | clear(); 16 | kversion = ver; 17 | } 18 | } 19 | 20 | void Encyclopedia::load() 21 | { 22 | if (loaded) return; 23 | clear(); 24 | 25 | try { 26 | auto [enPtr, enSize] = GetResourceContent("Encyclopedia.json"); 27 | if (enPtr && enSize > 0) { 28 | nlohmann::json jsRoot = nlohmann::json::parse(static_cast(enPtr), static_cast(enPtr) + enSize); 29 | auto& jsGame = jsRoot.at("games").at(std::to_string(kversion)); 30 | auto& jsFileList = jsGame.at("classInfoFiles"); 31 | for (auto& file : jsFileList) 32 | loadFile(file.get_ref()); 33 | } 34 | } 35 | catch (const std::exception& ex) { 36 | if (window) 37 | GuiUtils::MsgBox(window, ex.what(), 16); 38 | } 39 | loaded = true; 40 | } 41 | 42 | void Encyclopedia::clear() 43 | { 44 | kclasses.clear(); 45 | eventSets.clear(); 46 | loaded = false; 47 | } 48 | 49 | const nlohmann::json* Encyclopedia::getClassJson(int clsFullID) 50 | { 51 | load(); 52 | auto it = kclasses.find(clsFullID); 53 | if (it != kclasses.end()) 54 | return &it->second; 55 | else 56 | return nullptr; 57 | } 58 | 59 | const nlohmann::json* Encyclopedia::getEventJson(int fid, int event) 60 | { 61 | load(); 62 | if (auto it = kclasses.find(fid); it != kclasses.end()) { 63 | if (auto isit = it->second.find("events"); isit != it->second.end()) { 64 | for (auto& ev : isit.value()) { 65 | auto [idStart, idEnd] = decodeRange(ev.at("id").get_ref()); 66 | if (idStart <= event && event <= idEnd) { 67 | return &ev; 68 | } 69 | } 70 | } 71 | if (auto isit = it->second.find("includeSets"); isit != it->second.end()) { 72 | for (auto& setname : isit.value()) { 73 | if (auto esit = eventSets.find(setname.get_ref()); esit != eventSets.end()) { 74 | for (auto& ev : esit->second.at("events")) { 75 | auto [idStart, idEnd] = decodeRange(ev.at("id").get_ref()); 76 | if (idStart <= event && event <= idEnd) { 77 | return &ev; 78 | } 79 | } 80 | } 81 | } 82 | } 83 | } 84 | return nullptr; 85 | } 86 | 87 | const nlohmann::json* Encyclopedia::getEventJson(std::span fids, int event) 88 | { 89 | for (const int fid : fids) { 90 | auto* jsEvent = getEventJson(fid, event); 91 | if (jsEvent) 92 | return jsEvent; 93 | } 94 | return nullptr; 95 | } 96 | 97 | std::string Encyclopedia::getEventName(const nlohmann::json* ev, int event) 98 | { 99 | if (ev) 100 | return fmt::format("{:04X}: {}", event, ev->at("name").get_ref()); 101 | return fmt::format("{:04X}: Unknown action", event); 102 | } 103 | 104 | void Encyclopedia::loadFile(const std::string& filename) 105 | { 106 | auto [enPtr, enSize] = GetResourceContent(filename.c_str()); 107 | assert(enPtr && enSize > 0); 108 | nlohmann::json jsRoot = nlohmann::json::parse(static_cast(enPtr), static_cast(enPtr) + enSize); 109 | 110 | // Classes 111 | if (auto itClassList = jsRoot.find("classes"); itClassList != jsRoot.end()) { 112 | for (auto& jsClass : *itClassList) { 113 | int clcat = jsClass.at("category").get(); 114 | int clid = jsClass.at("id").get(); 115 | int fullid = clcat | (clid << 6); 116 | auto& enClass = kclasses[fullid]; 117 | enClass.update(jsClass, true); 118 | } 119 | } 120 | 121 | // Event sets 122 | if (auto itEventSets = jsRoot.find("eventSets"); itEventSets != jsRoot.end()) { 123 | for (auto& elem : *itEventSets) { 124 | eventSets.insert_or_assign(elem.at("name").get(), std::move(elem)); 125 | } 126 | } 127 | 128 | // Beacons 129 | if (auto itBeaconInfo = jsRoot.find("beacons"); itBeaconInfo != jsRoot.end()) { 130 | for (auto& [key, elem] : itBeaconInfo->items()) { 131 | beaconInfos.insert_or_assign(std::stoi(key), std::move(elem)); 132 | } 133 | } 134 | } 135 | 136 | std::pair Encyclopedia::decodeRange(std::string_view sv) { 137 | int a; 138 | assert(sv.size() == 4 || sv.size() == 9); 139 | std::from_chars(sv.data(), sv.data() + 4, a, 16); 140 | if (sv.size() == 9 && sv[4] == '-') { 141 | int b; 142 | std::from_chars(sv.data() + 5, sv.data() + 9, b, 16); 143 | return { a, b }; 144 | } 145 | return { a, a }; 146 | } 147 | 148 | const nlohmann::json* Encyclopedia::getBeaconJson(int beaconTypeId) 149 | { 150 | load(); 151 | auto it = beaconInfos.find(beaconTypeId); 152 | if (it != beaconInfos.end()) 153 | return &it->second; 154 | else 155 | return nullptr; 156 | } 157 | 158 | const std::string& Encyclopedia::getBeaconName(int beaconTypeId) 159 | { 160 | static const std::string unknownBeaconName = "?"; 161 | const auto* jsBeacon = getBeaconJson(beaconTypeId); 162 | if (jsBeacon) { 163 | if (jsBeacon->is_string()) { 164 | return jsBeacon->get_ref(); 165 | } 166 | if (jsBeacon->is_object()) { 167 | if (auto it = jsBeacon->find("name"); it != jsBeacon->end()) { 168 | if (it->is_string()) { 169 | return it->get_ref(); 170 | } 171 | } 172 | } 173 | } 174 | return unknownBeaconName; 175 | } 176 | -------------------------------------------------------------------------------- /EditorUI/IGSoundEditor.cpp: -------------------------------------------------------------------------------- 1 | #include "IGSoundEditor.h" 2 | 3 | #include "EditorInterface.h" 4 | #include "EditorUtils.h" 5 | #include "GuiUtils.h" 6 | 7 | #include "KEnvironment.h" 8 | #include "CoreClasses/CKService.h" 9 | #include "CoreClasses/CKDictionary.h" 10 | 11 | #include "rwsound.h" 12 | #include "WavDocument.h" 13 | 14 | #include 15 | 16 | void EditorUI::IGSoundEditor(EditorInterface& ui) 17 | { 18 | using namespace GuiUtils; 19 | auto& kenv = ui.kenv; 20 | 21 | static auto massByteSwap = [](void* data, size_t numBytes) { 22 | assert((numBytes & 1) == 0); 23 | size_t numShorts = numBytes / 2; 24 | uint16_t* data16 = (uint16_t*)data; 25 | for (size_t i = 0; i < numShorts; ++i) 26 | data16[i] = ((data16[i] & 255) << 8) | (data16[i] >> 8); 27 | }; 28 | static auto exportSound = [](RwSound& snd, const std::filesystem::path& path, KEnvironment& kenv) { 29 | WavDocument wav; 30 | wav.formatTag = 1; 31 | wav.numChannels = 1; 32 | wav.samplesPerSec = snd.info.format.sampleRate; 33 | wav.avgBytesPerSec = wav.samplesPerSec * 2; 34 | wav.pcmBitsPerSample = 16; 35 | wav.blockAlign = ((wav.pcmBitsPerSample + 7) / 8) * wav.numChannels; 36 | wav.data = snd.data.data; 37 | if (snd.info.isBigEndian) { 38 | massByteSwap(wav.data.data(), wav.data.size()); 39 | } 40 | IOFile out = IOFile(path.c_str(), "wb"); 41 | wav.write(&out); 42 | }; 43 | auto enumDict = [&](CKSoundDictionary* sndDict, int strnum) { 44 | if (sndDict->sounds.empty() || sndDict->rwSoundDict.list.sounds.empty()) 45 | return; 46 | const bool formatSupported = sndDict->rwSoundDict.list.sounds[0].info.format.uuid == RWA_FORMAT_ID_PCM; 47 | if (ImGui::TreeNode(sndDict, (strnum == 0) ? "Level" : "Sector %i", strnum)) { 48 | if (formatSupported && ImGui::Button("Export all")) { 49 | auto dirpath = GuiUtils::SelectFolderDialogBox(ui.g_window, "Export all the sounds to folder:"); 50 | if (!dirpath.empty()) { 51 | for (auto& snd : sndDict->rwSoundDict.list.sounds) { 52 | const char* np = strrchr((const char*)snd.info.name.data(), '\\'); 53 | np = np ? np + 1 : (const char*)snd.info.name.data(); 54 | exportSound(snd, dirpath / np, kenv); 55 | } 56 | } 57 | } 58 | for (int sndid = 0; sndid < (int)sndDict->sounds.size(); sndid++) { 59 | auto& snd = sndDict->rwSoundDict.list.sounds[sndid]; 60 | ImGui::PushID(sndid); 61 | ImGui::AlignTextToFramePadding(); 62 | ImGui::BeginGroup(); 63 | ImGui::Text("%3i", sndid); 64 | if (formatSupported) { 65 | ImGui::SameLine(); 66 | if (ImGui::ArrowButton("PlaySound", ImGuiDir_Right)) 67 | PlaySnd(snd); 68 | if (ImGui::IsItemHovered()) ImGui::SetTooltip("Play"); 69 | ImGui::SameLine(); 70 | if (ImGui::Button("I")) { 71 | auto filepath = OpenDialogBox(ui.g_window, "WAV audio file\0*.WAV\0\0", "wav"); 72 | if (!filepath.empty()) { 73 | IOFile wf = IOFile(filepath.c_str(), "rb"); 74 | WavDocument wav; 75 | wav.read(&wf); 76 | WavSampleReader wsr(&wav); 77 | if (wav.formatTag == 1 || wav.formatTag == 3) { 78 | if (wav.numChannels != 1) { 79 | MsgBox(ui.g_window, "The WAV contains multiple channels (e.g. stereo).\nOnly the first channel will be imported.", 48); 80 | } 81 | 82 | size_t numSamples = wav.getNumSamples(); 83 | auto& ndata = snd.data.data; 84 | ndata.resize(numSamples * 2); 85 | int16_t* pnt = (int16_t*)ndata.data(); 86 | for (size_t i = 0; i < numSamples; i++) { 87 | *(pnt++) = (int16_t)(wsr.getSample(0) * 32767); 88 | wsr.nextSample(); 89 | } 90 | if (snd.info.isBigEndian) 91 | massByteSwap(ndata.data(), ndata.size()); 92 | 93 | for (auto* format : { &snd.info.format, &snd.info.targetFormat }) { 94 | format->sampleRate = wav.samplesPerSec; 95 | format->dataSize = ndata.size(); 96 | } 97 | sndDict->sounds[sndid].sampleRate = wav.samplesPerSec; 98 | } 99 | else { 100 | MsgBox(ui.g_window, "The WAV file doesn't contain uncompressed mono 8/16-bit PCM wave data.\nPlease convert it to this format first.", 48); 101 | } 102 | } 103 | } 104 | if (ImGui::IsItemHovered()) ImGui::SetTooltip("Import"); 105 | ImGui::SameLine(); 106 | if (ImGui::Button("E")) { 107 | const char* name = strrchr((const char*)snd.info.name.data(), '\\'); 108 | if (name) name++; 109 | auto filepath = SaveDialogBox(ui.g_window, "WAV audio file\0*.WAV\0\0", "wav", name); 110 | if (!filepath.empty()) { 111 | exportSound(snd, filepath, kenv); 112 | } 113 | } 114 | if (ImGui::IsItemHovered()) ImGui::SetTooltip("Export"); 115 | } 116 | ImGui::SameLine(); 117 | const char* name = (const char*)snd.info.name.data(); 118 | ImGui::Text("%s", latinToUtf8(name).c_str()); 119 | //ImGui::Text("%u %u", snd.info.dings[0].sampleRate, snd.info.dings[1].sampleRate); 120 | ImGui::EndGroup(); 121 | if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceAllowNullID)) { 122 | int32_t fullIndex = (strnum << 24) | sndid; 123 | ImGui::SetDragDropPayload("SoundIndex", &fullIndex, sizeof(fullIndex)); 124 | ImGui::Text("Sound Sector %i ID %i", strnum, sndid); 125 | ImGui::EndDragDropSource(); 126 | } 127 | ImGui::PopID(); 128 | } 129 | ImGui::TreePop(); 130 | } 131 | }; 132 | enumDict(kenv.levelObjects.getFirst(), 0); 133 | for (int i = 0; i < (int)kenv.numSectors; i++) 134 | enumDict(kenv.sectorObjects[i].getFirst(), i + 1); 135 | } 136 | -------------------------------------------------------------------------------- /GroundRenderer.cpp: -------------------------------------------------------------------------------- 1 | #include "GroundRenderer.h" 2 | #include "renderer.h" 3 | #include "CoreClasses/CKLogic.h" 4 | #include "CoreClasses/CKNode.h" 5 | 6 | std::optional GroundGeo::generateGroundGeo(CGround* gnd, bool hasInfinites) 7 | { 8 | static constexpr Vector3 lightDir = { 1.0f, 1.5f, 2.0f }; 9 | 10 | std::optional transform; 11 | if (auto* dynGround = gnd->dyncast()) { 12 | transform = dynGround->getTransform(); 13 | } 14 | 15 | GroundGeo ggeo; 16 | uint16_t startIndex = 0; 17 | for (auto& tri : gnd->triangles) { 18 | std::array tv; 19 | for (int i = 0; i < 3; i++) 20 | tv[i] = gnd->vertices[tri.indices[2 - i]]; 21 | if (transform) { 22 | for (auto& vec : tv) 23 | vec = vec.transform(*transform); 24 | } 25 | Vector3 crs = (tv[2] - tv[0]).cross(tv[1] - tv[0]).normal(); 26 | float dp = crs.dot(lightDir.normal()) + 1.0f; 27 | uint8_t ll = (uint8_t)(dp * 255.0f); 28 | uint32_t clr = 0xFF000000 + ll * 0x010101; 29 | 30 | ggeo.positions.insert(ggeo.positions.end(), tv.begin(), tv.end()); 31 | ggeo.colors.insert(ggeo.colors.end(), 3, clr); 32 | ggeo.triangles.push_back({ startIndex, (uint16_t)(startIndex + 1), (uint16_t)(startIndex + 2) }); 33 | startIndex += 3; 34 | } 35 | auto onWall = [&](const std::array& baseIndices, const std::array& heights) { 36 | bool flip = heights[0] < 0.0f && heights[1] < 0.0f; 37 | 38 | std::array tv; 39 | for (int i = 0; i < 2; i++) { 40 | tv[i] = gnd->vertices[baseIndices[i]]; 41 | tv[2 + i] = gnd->vertices[baseIndices[i]]; 42 | } 43 | if (transform) { 44 | for (auto& vec : tv) 45 | vec = vec.transform(*transform); 46 | } 47 | for (int i = 0; i < 2; i++) 48 | tv[2 + i] += Vector3{ 0.0f, heights[i], 0.0f }; 49 | Vector3 crs = (tv[2] - tv[0]).cross(tv[1] - tv[0]).normal(); 50 | float dp = crs.dot(lightDir.normal()) + 1.0f; 51 | uint8_t ll = (uint8_t)(dp * 255.0f); 52 | uint32_t clr = 0xFFFF0000 + ll * 0x000100; 53 | 54 | ggeo.positions.insert(ggeo.positions.end(), tv.begin(), tv.end()); 55 | ggeo.colors.insert(ggeo.colors.end(), 4, clr); 56 | 57 | std::array tri = { startIndex, (uint16_t)(startIndex + 1), (uint16_t)(startIndex + 2) }; 58 | if (flip) std::swap(tri[1], tri[2]); 59 | ggeo.triangles.push_back(std::move(tri)); 60 | 61 | tri = { (uint16_t)(startIndex + 2), (uint16_t)(startIndex + 1), (uint16_t)(startIndex + 3) }; 62 | if (flip) std::swap(tri[1], tri[2]); 63 | ggeo.triangles.push_back(std::move(tri)); 64 | 65 | startIndex += 4; 66 | }; 67 | for (auto& wall : gnd->finiteWalls) { 68 | onWall(wall.baseIndices, wall.heights); 69 | } 70 | ggeo.numTrisWithoutInfiniteWalls = ggeo.triangles.size(); 71 | if (hasInfinites) { 72 | for (auto& wall : gnd->infiniteWalls) { 73 | static const std::array infiniteHeights = { 100.0f , 100.0f }; 74 | onWall(wall.baseIndices, infiniteHeights); 75 | } 76 | } 77 | ggeo.numTrisWithInfiniteWalls = ggeo.triangles.size(); 78 | 79 | if (ggeo.triangles.empty() || ggeo.positions.empty()) 80 | return {}; 81 | 82 | return std::move(ggeo); 83 | } 84 | 85 | GroundModel::GroundModel(Renderer * gfx, CGround * gnd) 86 | { 87 | _gfx = gfx; 88 | 89 | auto ggeo = GroundGeo::generateGroundGeo(gnd, true); 90 | 91 | vertices = gfx->createVertexBuffer((int)ggeo->positions.size()); 92 | RVertex *rv = vertices->lock(); 93 | for (size_t i = 0; i < ggeo->positions.size(); ++i) { 94 | rv[i].x = ggeo->positions[i].x; 95 | rv[i].y = ggeo->positions[i].y; 96 | rv[i].z = ggeo->positions[i].z; 97 | rv[i].color = ggeo->colors[i]; 98 | rv[i].u = 0.0f; 99 | rv[i].v = 0.0f; 100 | } 101 | vertices->unlock(); 102 | numGroundTriangles = gnd->triangles.size(); 103 | numFinWallTris = gnd->finiteWalls.size() * 2; 104 | numInfWallTris = gnd->infiniteWalls.size() * 2; 105 | assert(ggeo->triangles.size() == numGroundTriangles + numFinWallTris + numInfWallTris); 106 | groundIndices = gfx->createIndexBuffer((numGroundTriangles + numFinWallTris + numInfWallTris) * 3); 107 | uint16_t *gi = groundIndices->lock(); 108 | for (size_t i = 0; i < ggeo->triangles.size(); ++i) { 109 | *gi++ = ggeo->triangles[i][0]; 110 | *gi++ = ggeo->triangles[i][1]; 111 | *gi++ = ggeo->triangles[i][2]; 112 | } 113 | groundIndices->unlock(); 114 | 115 | if (auto* dynGround = gnd->dyncast()) { 116 | _usedTransform = dynGround->getTransform(); 117 | } 118 | else if (gnd->editing) { 119 | _usedTransform = gnd->editing->transform; 120 | } 121 | } 122 | 123 | GroundModel::~GroundModel() 124 | { 125 | delete vertices; 126 | delete groundIndices; 127 | } 128 | 129 | void GroundModel::draw(bool showInfiniteWalls) 130 | { 131 | _gfx->setVertexBuffer(vertices); 132 | _gfx->setIndexBuffer(groundIndices); 133 | _gfx->drawBuffer(0, 3*(numGroundTriangles + numFinWallTris + (showInfiniteWalls ? numInfWallTris : 0)) ); 134 | } 135 | 136 | GroundModel * GroundModelCache::getModel(CGround * gnd) 137 | { 138 | auto it = _cache.find(gnd); 139 | if (it == _cache.end()) { 140 | return _cache.emplace(std::make_pair(gnd, std::make_unique(_gfx, gnd))).first->second.get(); 141 | } 142 | else { 143 | if (auto* dynGround = gnd->dyncast()) { 144 | if (dynGround->getTransform() != it->second->usedTransform()) { 145 | it->second = std::make_unique(_gfx, gnd); 146 | } 147 | } 148 | else if (gnd->editing) { 149 | if (gnd->editing->transform != it->second->usedTransform()) { 150 | it->second = std::make_unique(_gfx, gnd); 151 | } 152 | } 153 | return it->second.get(); 154 | } 155 | } 156 | --------------------------------------------------------------------------------