diff --git a/.gitignore b/.gitignore index 4654bfd0..5be4c69e 100644 --- a/.gitignore +++ b/.gitignore @@ -108,4 +108,6 @@ CMakeUserPresets.json # External projects *-prefix/ -# End of https://www.toptal.com/developers/gitignore/api/cmake,clion+all \ No newline at end of file +# End of https://www.toptal.com/developers/gitignore/api/cmake,clion+all + +cartridges/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 469b70dc..6834e363 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,14 +1,18 @@ -cmake_minimum_required(VERSION 3.14) +cmake_minimum_required(VERSION 3.21) -project(wx_wherigo LANGUAGES CXX) +project(wxWherigo LANGUAGES CXX) include(FetchContent) -set(CMAKE_CXX_STANDARD 20) +set(CMAKE_C_STANDARD 23) +set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(wxBUILD_SHARED OFF) +add_subdirectory(libs/lua-5.1.4) +add_subdirectory(libs/cartridge) + message(STATUS "Fetching wxWidgets...") FetchContent_Declare( @@ -22,16 +26,45 @@ FetchContent_MakeAvailable(wxWidgets) message(STATUS "Configure project....") set(SRCS - src/main.cpp - src/MyFrame.cpp - src/WxWherigo.cpp + main/src/main.cpp + main/src/cApp.cpp + main/src/ui/cFrame.cpp ) -include_directories(include) -add_executable(${PROJECT_NAME} ${SRCS}) +if (APPLE) + add_executable(${PROJECT_NAME} MACOSX_BUNDLE ${SRCS}) + set_target_properties(${PROJECT_NAME} PROPERTIES + BUNDLE True + MACOSX_BUNDLE_GUI_IDENTIFIER dev.mars3142.${PROJECT_NAME} + MACOSX_BUNDLE_BUNDLE_NAME ${PROJECT_NAME} + MACOSX_BUNDLE_BUNDLE_VERSION "0.1" + MACOSX_BUNDLE_SHORT_VERSION_STRING "0.1" + #MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/cmake/customtemplate.plist.in + INSTALL_RPATH "@executable_path" + ) +else () + add_executable(${PROJECT_NAME} ${SRCS}) +endif () + +# Haupt-Includes +include_directories(main/include) + target_link_libraries(${PROJECT_NAME} PRIVATE wxcore wxnet wxbase + lua + cartridge + storage ) + +# Kopiere die .dylib-Dateien ins Bundle +if (APPLE) + add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + $ + $ + "$/Contents/MacOS/" + ) +endif () diff --git a/License.md b/License.md new file mode 100644 index 00000000..7ff7fab3 --- /dev/null +++ b/License.md @@ -0,0 +1,9 @@ +# MIT License + +Copyright (c) 2026 by Peter Siegmund (mars3142) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Readme.md b/Readme.md new file mode 100644 index 00000000..081036ac --- /dev/null +++ b/Readme.md @@ -0,0 +1,31 @@ +# Wherigo Simulator + +This project is a desktop application for simulating and running Wherigo geocaches. It allows you to play Wherigo cartridges directly on your computer, making it possible to experience geocaching adventures without a GPS device or mobile app. + +## Features +- Load and execute Wherigo cartridges (.gwc files) +- Simulate geocaching locations and actions +- Desktop interface for easy navigation and interaction +- Support for media and cartridge data + +## Requirements +- C++23 or newer +- wxWidgets (for GUI) +- Lua 5.1.4 (for cartridge execution) + +## Getting Started +1. Clone this repository. +2. Build the project using CMake and your preferred compiler. +3. Download a Wherigo cartridge (PocketPC format) from [wherigo.com](https://wherigo.com) +4. Open it in the application. + +## Project Structure +- `main/` - Application entry point and UI +- `libs/cartridge/` - Cartridge parsing and media handling +- `libs/lua-5.1.4/` - Lua interpreter + +## License +This project is licensed under the MIT License. See `License.md` for details. + +## Author +Peter Siegmund (mars3142) diff --git a/libs/cartridge/CMakeLists.txt b/libs/cartridge/CMakeLists.txt new file mode 100644 index 00000000..a4a00fe6 --- /dev/null +++ b/libs/cartridge/CMakeLists.txt @@ -0,0 +1,30 @@ +set(CARTRIDGE_VERSION_MAJOR 1) +set(CARTRIDGE_VERSION_MINOR 0) +set(CARTRIDGE_VERSION_PATCH 0) +set(CARTRIDGE_VERSION "${CARTRIDGE_VERSION_MAJOR}.${CARTRIDGE_VERSION_MINOR}.${CARTRIDGE_VERSION_PATCH}") + +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/version.h.in + ${CMAKE_CURRENT_BINARY_DIR}/include/cartridge/version.h + @ONLY +) + +set(CARTRIDGE_SRC + src/binary_reader.cpp + src/cartridge.cpp + src/lat_lng.cpp + src/media.cpp + src/parser.cpp +) + +add_library(cartridge SHARED ${CARTRIDGE_SRC}) +set_target_properties(cartridge PROPERTIES + VERSION ${CARTRIDGE_VERSION} + SOVERSION ${CARTRIDGE_VERSION_MAJOR} +) + +target_include_directories(cartridge + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_BINARY_DIR}/include +) diff --git a/libs/cartridge/include/cartridge/binary_reader.h b/libs/cartridge/include/cartridge/binary_reader.h new file mode 100644 index 00000000..ba3e24e9 --- /dev/null +++ b/libs/cartridge/include/cartridge/binary_reader.h @@ -0,0 +1,72 @@ +// MIT License +// Copyright (c) 2026 by Peter Siegmund (mars3142) +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once +#include +#include +#include + +namespace cartridge { + +enum class SeekOrigin { Begin, Current, End }; + +enum class Endian { Little, Big }; + +class BinaryReader { +public: + explicit BinaryReader(const std::vector &data, + Endian endian = Endian::Little); + + void seek(int offset, SeekOrigin origin); + uint8_t getByte(); + int16_t getShort(); + uint16_t getUShort(); + int32_t getLong(); + uint32_t getULong(); + double getDouble(); + std::string getASCIIZ(); + [[nodiscard]] const std::vector& data() const { return _data; } + +private: + template T readInt(size_t size); + static bool isSystemLittleEndian(); + static void swapBytes(uint8_t *data, size_t size); + + const std::vector &_data; + Endian _endian; + size_t _index; +}; + +template T BinaryReader::readInt(const size_t size) { + if (_index + size > _data.size()) + throw std::out_of_range("readInt out of range"); + T value = 0; + if (_endian == Endian::Little) { + for (size_t i = 0; i < size; ++i) { + value |= static_cast(_data[_index + i]) << (8 * i); + } + } else { + for (size_t i = 0; i < size; ++i) { + value = (value << 8) | _data[_index + i]; + } + } + _index += size; + return value; +} + +} // namespace cartridge diff --git a/libs/cartridge/include/cartridge/cartridge.h b/libs/cartridge/include/cartridge/cartridge.h new file mode 100644 index 00000000..ceec5fcf --- /dev/null +++ b/libs/cartridge/include/cartridge/cartridge.h @@ -0,0 +1,94 @@ +// MIT License +// Copyright (c) 2026 by Peter Siegmund (mars3142) +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include "cartridge/binary_reader.h" +#include "cartridge/lat_lng.h" +#include "cartridge/media.h" +#include +#include +#include +#include + +namespace cartridge { + +class Cartridge { +public: + // Factory method to create Cartridge from BinaryReader + static std::unique_ptr create(BinaryReader &reader); + + // Getters + [[nodiscard]] double altitude() const; + [[nodiscard]] const std::string &author() const; + [[nodiscard]] const LatLng &latLng() const; + [[nodiscard]] const std::string &cartridgeName() const; + [[nodiscard]] const std::string &cartridgeDesc() const; + [[nodiscard]] const std::string &cartridgeGuid() const; + [[nodiscard]] const std::string &typeOfCartridge() const; + [[nodiscard]] const std::string &playerName() const; + [[nodiscard]] const std::string &startLocationDesc() const; + [[nodiscard]] const std::string &version() const; + [[nodiscard]] const std::string &company() const; + [[nodiscard]] const std::string &recommendDevice() const; + [[nodiscard]] const std::string &completionCode() const; + + // Media methods + std::unique_ptr getMedia(int objectId); + [[nodiscard]] int mediaCount() const; + std::unique_ptr splashScreen(); + std::unique_ptr smallIcon(); + std::unique_ptr luac(); + + // Comparison operators + bool operator==(const Cartridge &other) const; + bool operator!=(const Cartridge &other) const; + + Cartridge(std::string cartridgeGuid, double altitude, std::string author, + std::string cartridgeDesc, std::string cartridgeName, + std::string company, std::string completionCode, LatLng latLng, + std::string playerName, std::string recommendDevice, + int smallIconId, int splashScreenId, std::string startLocationDesc, + std::string typeOfCartridge, std::string version, + std::map references, std::vector bytes); + +private: + // Fields + std::string m_cartridgeGuid; + double m_altitude; + std::string m_author; + std::string m_cartridgeDesc; + std::string m_cartridgeName; + std::string m_company; + std::string m_completionCode; + LatLng m_latLng; + std::string m_playerName; + std::string m_recommendDevice; + int m_smallIconId; + int m_splashScreenId; + std::string m_startLocationDesc; + std::string m_typeOfCartridge; + std::string m_version; + std::map m_references; + std::vector m_bytes; + + int m_lastObject = -1; + std::unique_ptr m_lastMedia; +}; + +} // namespace cartridge diff --git a/libs/cartridge/include/cartridge/lat_lng.h b/libs/cartridge/include/cartridge/lat_lng.h new file mode 100644 index 00000000..d5311a96 --- /dev/null +++ b/libs/cartridge/include/cartridge/lat_lng.h @@ -0,0 +1,36 @@ +// MIT License +// Copyright (c) 2026 by Peter Siegmund (mars3142) +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include + +namespace cartridge { +class LatLng { + double m_latitude; + double m_longitude; + + static std::string _format(double value, const std::string& suffix); + + public: + LatLng(double latitude, double longitude); + [[nodiscard]] std::string latitude() const; + [[nodiscard]] std::string longitude() const; + [[nodiscard]] std::string toString() const; +}; +} diff --git a/libs/cartridge/include/cartridge/media.h b/libs/cartridge/include/cartridge/media.h new file mode 100644 index 00000000..9a4adb50 --- /dev/null +++ b/libs/cartridge/include/cartridge/media.h @@ -0,0 +1,66 @@ +// MIT License +// Copyright (c) 2026 by Peter Siegmund (mars3142) +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include "cartridge/binary_reader.h" +#include +#include +#include + +namespace cartridge { + +enum class ObjectType { + Deleted = -1, + Luac = 0, + Bmp = 1, + Png = 2, + Jpg = 3, + Gif = 4, + Wav = 17, + Mp3 = 18, + Fdl = 19, + Snd = 20, + Ogg = 21, + Swf = 33, + Txt = 49, + Invalid = -9999 +}; + +class Media { +public: + Media(std::string objectType, const std::vector &data); + + static std::unique_ptr create(BinaryReader &reader, int objectId, + int address); + + bool operator==(const Media &other) const; + [[nodiscard]] std::size_t hash() const; + + [[nodiscard]] const std::vector &getData() const { return data; } + [[nodiscard]] const std::string &getObjectType() const { return objectType; } + +private: + static std::vector readMediaData(BinaryReader &reader); + static std::string getObjectTypeString(int objectType); + + std::string objectType; + std::vector data; +}; + +} // namespace cartridge diff --git a/libs/cartridge/include/cartridge/parser.h b/libs/cartridge/include/cartridge/parser.h new file mode 100644 index 00000000..ff3533a3 --- /dev/null +++ b/libs/cartridge/include/cartridge/parser.h @@ -0,0 +1,24 @@ +// MIT License +// Copyright (c) 2026 by Peter Siegmund (mars3142) +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +#pragma once + +#include +#include +#include + +#include "cartridge/cartridge.h" + +namespace cartridge { + +std::unique_ptr parseData(const std::vector& bytes); + +} // namespace cartridge diff --git a/libs/cartridge/src/binary_reader.cpp b/libs/cartridge/src/binary_reader.cpp new file mode 100644 index 00000000..374f887b --- /dev/null +++ b/libs/cartridge/src/binary_reader.cpp @@ -0,0 +1,91 @@ +// MIT License +// Copyright (c) 2026 by Peter Siegmund (mars3142) +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include "cartridge/binary_reader.h" + +#include + +namespace cartridge { + +BinaryReader::BinaryReader(const std::vector &data, + const Endian endian) + : _data(data), _endian(endian), _index(0) {} + +void BinaryReader::seek(const int offset, const SeekOrigin origin) { + switch (origin) { + case SeekOrigin::Begin: + _index = offset; + break; + case SeekOrigin::Current: + _index += offset; + break; + case SeekOrigin::End: + _index = _data.size() - offset; + break; + } +} + +uint8_t BinaryReader::getByte() { + if (_index >= _data.size()) + throw std::out_of_range("getByte out of range"); + return _data[_index++]; +} + +int16_t BinaryReader::getShort() { return readInt(2); } + +uint16_t BinaryReader::getUShort() { return readInt(2); } + +int32_t BinaryReader::getLong() { return readInt(4); } + +uint32_t BinaryReader::getULong() { return readInt(4); } + +double BinaryReader::getDouble() { + if (_index + 8 > _data.size()) + throw std::out_of_range("getDouble out of range"); + double value; + std::memcpy(&value, &_data[_index], 8); + if ((_endian == Endian::Little) != isSystemLittleEndian()) { + swapBytes(reinterpret_cast(&value), 8); + } + _index += 8; + return value; +} + +std::string BinaryReader::getASCIIZ() { + std::string result; + uint8_t byte = 0; + do { + byte = getByte(); + if (byte != 0) + result += static_cast(byte); + } while (byte != 0); + return result; +} + +bool BinaryReader::isSystemLittleEndian() { + uint16_t num = 1; + return *reinterpret_cast(&num) == 1; +} + +void BinaryReader::swapBytes(uint8_t *data, size_t size) { + for (size_t i = 0; i < size / 2; ++i) { + std::swap(data[i], data[size - 1 - i]); + } +} + +} // namespace cartridge diff --git a/libs/cartridge/src/cartridge.cpp b/libs/cartridge/src/cartridge.cpp new file mode 100644 index 00000000..6e33e179 --- /dev/null +++ b/libs/cartridge/src/cartridge.cpp @@ -0,0 +1,148 @@ +// MIT License +// Copyright (c) 2026 by Peter Siegmund (mars3142) +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include "cartridge/cartridge.h" +#include "cartridge/binary_reader.h" +#include "cartridge/lat_lng.h" +#include "cartridge/media.h" + +namespace cartridge { + +Cartridge::Cartridge(std::string cartridgeGuid, double altitude, + std::string author, std::string cartridgeDesc, + std::string cartridgeName, std::string company, + std::string completionCode, LatLng latLng, + std::string playerName, std::string recommendDevice, + int smallIconId, int splashScreenId, + std::string startLocationDesc, std::string typeOfCartridge, + std::string version, std::map references, + std::vector bytes) + : m_cartridgeGuid(std::move(cartridgeGuid)), m_altitude(altitude), + m_author(std::move(author)), m_cartridgeDesc(std::move(cartridgeDesc)), + m_cartridgeName(std::move(cartridgeName)), m_company(std::move(company)), + m_completionCode(std::move(completionCode)), m_latLng(latLng), + m_playerName(std::move(playerName)), + m_recommendDevice(std::move(recommendDevice)), m_smallIconId(smallIconId), + m_splashScreenId(splashScreenId), + m_startLocationDesc(std::move(startLocationDesc)), + m_typeOfCartridge(std::move(typeOfCartridge)), + m_version(std::move(version)), m_references(std::move(references)), + m_bytes(std::move(bytes)) {} + +std::unique_ptr Cartridge::create(BinaryReader &reader) { + try { + uint16_t count = reader.getUShort(); + std::map references; + for (uint16_t index = 0; index < count; ++index) { + int objectId = reader.getShort(); + int address = reader.getLong(); + references.emplace(objectId, address); + } + + reader.getLong(); // header length + double latitude = reader.getDouble(); + double longitude = reader.getDouble(); + double altitude = reader.getDouble(); + reader.getLong(); // unknown 0 + reader.getLong(); // unknown 1 + int splashScreenId = reader.getShort(); + int smallIconId = reader.getShort(); + std::string typeOfCartridge = reader.getASCIIZ(); + std::string playerName = reader.getASCIIZ(); + reader.getLong(); // unknown 2 + reader.getLong(); // unknown 3 + std::string cartridgeName = reader.getASCIIZ(); + std::string cartridgeGuid = reader.getASCIIZ(); + std::string cartridgeDesc = reader.getASCIIZ(); + std::string startLocationDesc = reader.getASCIIZ(); + std::string version = reader.getASCIIZ(); + std::string author = reader.getASCIIZ(); + std::string company = reader.getASCIIZ(); + std::string recommendedDevice = reader.getASCIIZ(); + reader.getLong(); // unknown 4 + std::string completionCode = reader.getASCIIZ(); + + // Rohdaten aus dem Reader extrahieren + const std::vector &bytes = reader.data(); + + return std::make_unique( + cartridgeGuid, altitude, author, cartridgeDesc, cartridgeName, company, + completionCode, LatLng(latitude, longitude), playerName, + recommendedDevice, smallIconId, splashScreenId, startLocationDesc, + typeOfCartridge, version, references, bytes); + } catch (const std::exception &) { + return nullptr; + } +} + +double Cartridge::altitude() const { return m_altitude; } +const std::string &Cartridge::author() const { return m_author; } +const LatLng &Cartridge::latLng() const { return m_latLng; } +const std::string &Cartridge::cartridgeName() const { return m_cartridgeName; } +const std::string &Cartridge::cartridgeDesc() const { return m_cartridgeDesc; } +const std::string &Cartridge::cartridgeGuid() const { return m_cartridgeGuid; } +const std::string &Cartridge::typeOfCartridge() const { + return m_typeOfCartridge; +} +const std::string &Cartridge::playerName() const { return m_playerName; } +const std::string &Cartridge::startLocationDesc() const { + return m_startLocationDesc; +} +const std::string &Cartridge::version() const { return m_version; } +const std::string &Cartridge::company() const { return m_company; } +const std::string &Cartridge::recommendDevice() const { + return m_recommendDevice; +} +const std::string &Cartridge::completionCode() const { return m_completionCode; } + +std::unique_ptr Cartridge::getMedia(const int objectId) { + if (m_lastObject == objectId && m_lastMedia) { + return std::make_unique(*m_lastMedia); + } + auto it = m_references.find(objectId); + if (it != m_references.end()) { + int address = it->second; + BinaryReader reader(m_bytes, Endian::Little); + auto media = Media::create(reader, objectId, address); + if (media && media->getData().size() < 128000) { + m_lastObject = objectId; + m_lastMedia = std::make_unique(*media); + } + return media; + } + return nullptr; +} + +int Cartridge::mediaCount() const { + return static_cast(m_references.size()); +} + +std::unique_ptr Cartridge::splashScreen() { + return getMedia(m_splashScreenId); +} +std::unique_ptr Cartridge::smallIcon() { return getMedia(m_smallIconId); } +std::unique_ptr Cartridge::luac() { return getMedia(0); } + +bool Cartridge::operator==(const Cartridge &other) const { + return m_cartridgeGuid == other.m_cartridgeGuid; +} +bool Cartridge::operator!=(const Cartridge &other) const { + return !(*this == other); +} + +} // namespace cartridge \ No newline at end of file diff --git a/libs/cartridge/src/lat_lng.cpp b/libs/cartridge/src/lat_lng.cpp new file mode 100644 index 00000000..e619a88c --- /dev/null +++ b/libs/cartridge/src/lat_lng.cpp @@ -0,0 +1,56 @@ +// MIT License +// Copyright (c) 2026 by Peter Siegmund (mars3142) +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include "cartridge/lat_lng.h" + +#include +#include +#include + +namespace cartridge { + +LatLng::LatLng(const double latitude, const double longitude) + : m_latitude(latitude), m_longitude(longitude) {} + +std::string LatLng::latitude() const { return _format(m_latitude, "NS"); } + +std::string LatLng::longitude() const { return _format(m_longitude, "EW"); } + +std::string LatLng::toString() const { + const std::string lat = latitude(); + const std::string lon = longitude(); + if (!lat.empty() && !lon.empty()) { + return lat + " " + lon; + } + return lat + lon; +} + +std::string LatLng::_format(double value, const std::string &suffix) { + const bool isNegative = value < 0; + value = std::abs(value); + const int degrees = static_cast(std::floor(value)); + if (degrees == 360) { + return ""; + } + const double minutes = (value - degrees) * 60.0; + std::ostringstream oss; + oss << (isNegative ? suffix.substr(1, 1) : suffix.substr(0, 1)) << " " + << degrees << "\u00B0 " << std::fixed << std::setprecision(3) << minutes; + return oss.str(); +} +} // namespace cartridge diff --git a/libs/cartridge/src/media.cpp b/libs/cartridge/src/media.cpp new file mode 100644 index 00000000..fc50850f --- /dev/null +++ b/libs/cartridge/src/media.cpp @@ -0,0 +1,107 @@ +// MIT License +// Copyright (c) 2026 by Peter Siegmund (mars3142) +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include "cartridge/media.h" + +#include + +namespace cartridge { + +Media::Media(std::string objectType, const std::vector &data) + : objectType(std::move(objectType)), data(data) {} + +std::unique_ptr Media::create(BinaryReader &reader, const int objectId, + const int address) { + try { + std::vector data; + int objectType = 0; + reader.seek(address, SeekOrigin::Begin); + if (objectId == 0) { + data = readMediaData(reader); + } else { + if (reader.getByte() != 0) { + objectType = reader.getLong(); + data = readMediaData(reader); + } + } + if (!data.empty()) { + return std::make_unique(getObjectTypeString(objectType), data); + } + return nullptr; + } catch ([[maybe_unused]] const std::exception &ex) { + // Logging analog zu Dart: print('Exception: $ex'); + return nullptr; + } +} + +std::vector Media::readMediaData(BinaryReader &reader) { + const int32_t length = reader.getLong(); + std::vector data; + data.reserve(length); + for (int32_t i = 0; i < length; ++i) { + data.push_back(reader.getByte()); + } + return data; +} + +std::string Media::getObjectTypeString(int objectType) { + switch (objectType) { + case -1: + return "deleted"; + case 0: + return "luac"; + case 1: + return "bmp"; + case 2: + return "png"; + case 3: + return "jpg"; + case 4: + return "gif"; + case 17: + return "wav"; + case 18: + return "mp3"; + case 19: + return "fdl"; + case 20: + return "snd"; + case 21: + return "ogg"; + case 33: + return "swf"; + case 49: + return "txt"; + default: + return "invalid (" + std::to_string(objectType) + ")"; + } +} + +bool Media::operator==(const Media &other) const { + return objectType == other.objectType && data == other.data; +} + +std::size_t Media::hash() const { + const std::size_t h1 = std::hash{}(objectType); + std::size_t h2 = 0; + for (const auto b : data) + h2 ^= std::hash{}(b) + 0x9e3779b9 + (h2 << 6) + (h2 >> 2); + return h1 ^ (h2 << 1); +} + +} // namespace cartridge diff --git a/libs/cartridge/src/parser.cpp b/libs/cartridge/src/parser.cpp new file mode 100644 index 00000000..eeb0cfbd --- /dev/null +++ b/libs/cartridge/src/parser.cpp @@ -0,0 +1,44 @@ +// MIT License +// Copyright (c) 2026 by Peter Siegmund (mars3142) +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include "cartridge/parser.h" + +#include +#include +#include +#include + +namespace cartridge { + +std::unique_ptr parseData(const std::vector &bytes) { + try { + constexpr std::array header = {0x02, 0x0a, 0x43, 0x41, + 0x52, 0x54, 0x00}; + BinaryReader reader(bytes, Endian::Little); + for (const unsigned char i : header) { + if (reader.getByte() != i) { + return nullptr; + } + } + return Cartridge::create(reader); + } catch (...) { + return nullptr; + } +} + +} // namespace cartridge diff --git a/libs/cartridge/version.h.in b/libs/cartridge/version.h.in new file mode 100644 index 00000000..7cfe214c --- /dev/null +++ b/libs/cartridge/version.h.in @@ -0,0 +1,7 @@ +#pragma once + +#define CARTRIDGE_VERSION_MAJOR @CARTRIDGE_VERSION_MAJOR@ +#define CARTRIDGE_VERSION_MINOR @CARTRIDGE_VERSION_MINOR@ +#define CARTRIDGE_VERSION_PATCH @CARTRIDGE_VERSION_PATCH@ +#define CARTRIDGE_VERSION "@CARTRIDGE_VERSION@" + diff --git a/libs/lua-5.1.4/CMakeLists.txt b/libs/lua-5.1.4/CMakeLists.txt new file mode 100644 index 00000000..b8807217 --- /dev/null +++ b/libs/lua-5.1.4/CMakeLists.txt @@ -0,0 +1,72 @@ +# CMake build for Lua 5.1.4 +cmake_minimum_required(VERSION 3.21) +project(lua LANGUAGES C) + +set(LUA_SOURCES + src/lapi.c + src/lcode.c + src/ldebug.c + src/ldo.c + src/ldump.c + src/lfunc.c + src/lgc.c + src/llex.c + src/lmem.c + src/loadlib.c + src/lobject.c + src/lopcodes.c + src/lparser.c + src/lstate.c + src/lstring.c + src/ltable.c + src/ltm.c + src/lundump.c + src/lvm.c + src/lzio.c + src/lauxlib.c + src/lbaselib.c + src/ldblib.c + src/liolib.c + src/lmathlib.c + src/loslib.c + src/ltablib.c + src/lstrlib.c + src/linit.c +) + +set(LUA_VERSION_MAJOR 5) +set(LUA_VERSION_MINOR 1) +set(LUA_VERSION_PATCH 4) +set(LUA_VERSION "${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}.${LUA_VERSION_PATCH}") + +add_library(lua SHARED ${LUA_SOURCES}) +set_target_properties(lua PROPERTIES + VERSION ${LUA_VERSION} + SOVERSION ${LUA_VERSION_MAJOR} +) + +add_executable(lua_bin + src/lua.c +) +target_link_libraries(lua_bin PRIVATE lua) +target_include_directories(lua_bin PRIVATE src) + +add_executable(luac_bin + src/luac.c + src/print.c +) +target_link_libraries(luac_bin PRIVATE lua) +target_include_directories(luac_bin PRIVATE src) + +# Header files +set(LUA_HEADERS + ${LUA_SRC_DIR}/lua.h + ${LUA_SRC_DIR}/luaconf.h + ${LUA_SRC_DIR}/lualib.h + ${LUA_SRC_DIR}/lauxlib.h +) + +target_include_directories(lua + PUBLIC + ${LUA_SRC_DIR} +) diff --git a/include/WxWherigo.h b/main/include/cApp.h similarity index 70% rename from include/WxWherigo.h rename to main/include/cApp.h index 767cfef6..4acf55b3 100644 --- a/include/WxWherigo.h +++ b/main/include/cApp.h @@ -2,7 +2,7 @@ #include -class WxWherigo : public wxApp +class cApp : public wxApp { public: bool OnInit() override; diff --git a/include/MyFrame.h b/main/include/ui/cFrame.h similarity index 77% rename from include/MyFrame.h rename to main/include/ui/cFrame.h index 7d4ca4d5..d6b0981f 100644 --- a/include/MyFrame.h +++ b/main/include/ui/cFrame.h @@ -2,10 +2,10 @@ #include -class MyFrame : public wxFrame +class cFrame : public wxMDIParentFrame { public: - MyFrame(); + cFrame(); private: void OnHello(wxCommandEvent& event); diff --git a/main/src/cApp.cpp b/main/src/cApp.cpp new file mode 100644 index 00000000..57285457 --- /dev/null +++ b/main/src/cApp.cpp @@ -0,0 +1,8 @@ +#include "cApp.h" +#include "ui/cFrame.h" + +bool cApp::OnInit() +{ + auto *frame = new cFrame(); + return frame->Show(true); +} diff --git a/main/src/main.cpp b/main/src/main.cpp new file mode 100644 index 00000000..b00b67d1 --- /dev/null +++ b/main/src/main.cpp @@ -0,0 +1,4 @@ +#include "cApp.h" +#include + +wxIMPLEMENT_APP(cApp); diff --git a/main/src/ui/cFrame.cpp b/main/src/ui/cFrame.cpp new file mode 100644 index 00000000..c1f09b8d --- /dev/null +++ b/main/src/ui/cFrame.cpp @@ -0,0 +1,38 @@ +#include "ui/cFrame.h" + +enum { ID_Hello = 1 }; + +cFrame::cFrame() : wxMDIParentFrame(nullptr, wxID_ANY, "Hello World") { + auto *menuFile = new wxMenu; + menuFile->Append(ID_Hello, "&Hello...\tCtrl-H", + "Help string shown in status bar for this menu item"); + menuFile->AppendSeparator(); + menuFile->Append(wxID_EXIT); + + auto *menuHelp = new wxMenu; + menuHelp->Append(wxID_ABOUT); + + auto *menuBar = new wxMenuBar; + menuBar->Append(menuFile, "&File"); + menuBar->Append(menuHelp, "&Help"); + + SetMenuBar(menuBar); + + CreateStatusBar(); + SetStatusText("Welcome to wxWidgets!"); + + Bind(wxEVT_MENU, &cFrame::OnHello, this, ID_Hello); + Bind(wxEVT_MENU, &cFrame::OnAbout, this, wxID_ABOUT); + Bind(wxEVT_MENU, &cFrame::OnExit, this, wxID_EXIT); +} + +void cFrame::OnExit(wxCommandEvent &event) { Close(true); } + +void cFrame::OnAbout(wxCommandEvent &event) { + wxMessageBox("This is a wxWidgets Hello World example", "About Hello World", + wxOK | wxICON_INFORMATION); +} + +void cFrame::OnHello(wxCommandEvent &event) { + wxLogMessage("Hello world from wxWidgets!"); +} diff --git a/src/MyFrame.cpp b/src/MyFrame.cpp deleted file mode 100644 index b8f6c743..00000000 --- a/src/MyFrame.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include "MyFrame.h" - -enum -{ - ID_Hello = 1 -}; - -MyFrame::MyFrame() - : wxFrame(nullptr, wxID_ANY, "Hello World") -{ - auto *menuFile = new wxMenu; - menuFile->Append(ID_Hello, "&Hello...\tCtrl-H", - "Help string shown in status bar for this menu item"); - menuFile->AppendSeparator(); - menuFile->Append(wxID_EXIT); - - auto *menuHelp = new wxMenu; - menuHelp->Append(wxID_ABOUT); - - auto *menuBar = new wxMenuBar; - menuBar->Append(menuFile, "&File"); - menuBar->Append(menuHelp, "&Help"); - - SetMenuBar( menuBar ); - - CreateStatusBar(); - SetStatusText("Welcome to wxWidgets!"); - - Bind(wxEVT_MENU, &MyFrame::OnHello, this, ID_Hello); - Bind(wxEVT_MENU, &MyFrame::OnAbout, this, wxID_ABOUT); - Bind(wxEVT_MENU, &MyFrame::OnExit, this, wxID_EXIT); -} - -void MyFrame::OnExit(wxCommandEvent& event) -{ - Close(true); -} - -void MyFrame::OnAbout(wxCommandEvent& event) -{ - wxMessageBox("This is a wxWidgets Hello World example", - "About Hello World", wxOK | wxICON_INFORMATION); -} - -void MyFrame::OnHello(wxCommandEvent& event) -{ - wxLogMessage("Hello world from wxWidgets!"); -} diff --git a/src/WxWherigo.cpp b/src/WxWherigo.cpp deleted file mode 100644 index 165f0775..00000000 --- a/src/WxWherigo.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include "WxWherigo.h" -#include "MyFrame.h" - -bool WxWherigo::OnInit() -{ - auto *frame = new MyFrame(); - frame->Show(true); - return true; -} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp deleted file mode 100644 index e018d11e..00000000 --- a/src/main.cpp +++ /dev/null @@ -1,4 +0,0 @@ -#include -#include "WxWherigo.h" - -wxIMPLEMENT_APP(WxWherigo);