diff --git a/.gitignore b/.gitignore index 0236496..9c96343 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,15 @@ build .cache .vscode + +__cmrc_Assets +_cmrc +CMakeCache.txt +CMakeFiles +GravityDefied +Makefile +cmake_install.cmake +libAssets.a +saves +a.out + diff --git a/CMakeLists.txt b/CMakeLists.txt index 06f1426..e3eab91 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,6 @@ -cmake_minimum_required(VERSION 3.24) +cmake_minimum_required(VERSION 3.10) project(gravity_defied_cpp C CXX) +set(CMAKE_VERBOSE_MAKEFILE TRUE) unset(ENV{PKG_CONFIG_PATH}) @@ -13,6 +14,7 @@ if(WIN32) endif() find_package(PkgConfig REQUIRED) +find_package(OpenSSL REQUIRED) pkg_search_module(SDL2 REQUIRED IMPORTED_TARGET sdl2) pkg_search_module(SDL2_TTF REQUIRED IMPORTED_TARGET SDL2_ttf) @@ -20,7 +22,10 @@ pkg_search_module(SDL2_IMAGE REQUIRED IMPORTED_TARGET SDL2_image) # Create a sources variable with a link to all cpp files to compile set(SOURCES + src/config.cpp + src/utils/Log.cpp src/utils/Time.cpp + src/utils/Hashing.cpp src/utils/EmbedFileStream.cpp src/main.cpp src/MathF16.cpp @@ -30,12 +35,14 @@ set(SOURCES src/class_10.cpp src/GameLevel.cpp src/LevelLoader.cpp + src/MRGLoader.cpp src/Micro.cpp src/TextRender.cpp src/GameMenu.cpp src/SettingsStringRender.cpp src/MenuManager.cpp src/RecordManager.cpp + src/SettingsManager.cpp src/Timer.cpp src/lcdui/CanvasImpl.cpp src/lcdui/Canvas.cpp @@ -44,8 +51,6 @@ set(SOURCES src/lcdui/Font.cpp src/lcdui/Command.cpp src/lcdui/FontStorage.cpp - src/rms/RecordEnumerationImpl.cpp - src/rms/RecordStore.cpp ) include(cmake/CMakeRC.cmake) @@ -77,7 +82,7 @@ endif() add_executable(GravityDefied ${SOURCES}) target_compile_features(GravityDefied PRIVATE cxx_std_17) -target_compile_options(GravityDefied PRIVATE -Wall -Wextra) +target_compile_options(GravityDefied PRIVATE -Wall -Wextra -fsigned-char) target_include_directories(GravityDefied PRIVATE ${SDL2_INCLUDE_DIRS} @@ -97,12 +102,14 @@ if(WIN32) ${SDL2_STATIC_LIBRARIES} ${SDL2_TTF_STATIC_LIBRARIES} ${SDL2_IMAGE_STATIC_LIBRARIES} + OpenSSL::SSL Assets::Assets) else() target_link_libraries(GravityDefied ${SDL2_LIBRARIES} ${SDL2_TTF_LIBRARIES} ${SDL2_IMAGE_LIBRARIES} + OpenSSL::SSL Assets::Assets) endif() diff --git a/levels.mrg b/levels.mrg new file mode 100644 index 0000000..e495c2a Binary files /dev/null and b/levels.mrg differ diff --git a/src/GameCanvas.cpp b/src/GameCanvas.cpp index 46aec65..21ce061 100644 --- a/src/GameCanvas.cpp +++ b/src/GameCanvas.cpp @@ -1,12 +1,4 @@ #include "GameCanvas.h" -#include "MathF16.h" -#include "GamePhysics.h" -#include "MenuManager.h" -#include "lcdui/Image.h" -#include "lcdui/FontStorage.h" - -#include -#include GameCanvas::GameCanvas(Micro* micro) { @@ -29,7 +21,7 @@ GameCanvas::GameCanvas(Micro* micro) dx = 0; dy = height2; - commandMenu = new Command("Menu", 1, 1); + commandMenu = new Command("Menu", Command::Type::SCREEN, 1); defaultFontWidth00 = defaultFont->stringWidth("00") + 3; } @@ -37,7 +29,11 @@ void GameCanvas::drawSprite(Graphics* g, int spriteNo, int x, int y) { if (spritesImage) { g->setClip(x, y, spriteSizeX[spriteNo], spriteSizeY[spriteNo]); - g->drawImage(spritesImage.get(), x - spriteOffsetX[spriteNo], y - spriteOffsetY[spriteNo], 20); + g->drawImage( + spritesImage.get(), + x - spriteOffsetX[spriteNo], + y - spriteOffsetY[spriteNo], + Graphics::TOP | Graphics::LEFT); g->setClip(0, 0, getWidth(), getHeight()); } } @@ -85,7 +81,7 @@ int GameCanvas::loadSprites(int flags) fenderImage = nullptr; engineImage = nullptr; } - + if (flags & 2) { if (!bodyPartsImages[1]) { bodyPartsImages[1] = std::make_unique("blueleg.png"); @@ -114,7 +110,7 @@ int GameCanvas::loadSprites(int flags) void GameCanvas::method_129() { - method_164(); + resetActions(); } void GameCanvas::setViewPosition(int dx, int dy) @@ -170,7 +166,11 @@ void GameCanvas::renderBodyPart(int x1F16, int y1F16, int x2F16, int y2F16, int x -= bodyPartsSpriteWidth[bodyPartNo] / 2; y -= bodyPartsSpriteHeight[bodyPartNo] / 2; graphics->setClip(x, y, bodyPartsSpriteWidth[bodyPartNo], bodyPartsSpriteHeight[bodyPartNo]); - graphics->drawImage(bodyPartsImages[bodyPartNo].get(), x - bodyPartsSpriteWidth[bodyPartNo] * (spriteNo % 6), y - bodyPartsSpriteHeight[bodyPartNo] * (spriteNo / 6), 20); + graphics->drawImage( + bodyPartsImages[bodyPartNo].get(), + x - bodyPartsSpriteWidth[bodyPartNo] * (spriteNo % 6), + y - bodyPartsSpriteHeight[bodyPartNo] * (spriteNo / 6), + Graphics::TOP | Graphics::LEFT); graphics->setClip(0, 0, width, getHeight()); } } @@ -217,47 +217,24 @@ void GameCanvas::drawHelmet(int x, int y, int angleF16) int var5 = addDx(x) - helmetSpriteWidth / 2; int var6 = addDy(y) - helmetSpriteHeight / 2; graphics->setClip(var5, var6, helmetSpriteWidth, helmetSpriteHeight); - graphics->drawImage(helmetImage.get(), var5 - helmetSpriteWidth * (var4 % 6), var6 - helmetSpriteHeight * (var4 / 6), 20); + graphics->drawImage( + helmetImage.get(), + var5 - helmetSpriteWidth * (var4 % 6), + var6 - helmetSpriteHeight * (var4 / 6), + Graphics::TOP | Graphics::LEFT); graphics->setClip(0, 0, width, getHeight()); } } -void GameCanvas::drawTime(int64_t time10Ms) +void GameCanvas::drawTime(uint64_t time10Ms) { - int seconds = (int)(time10Ms / 100L); - int time10MsPart = (int)(time10Ms % 100L); - if (timeInSeconds != seconds || stringWithTime.empty()) { - std::string zeroPadding; - if (seconds % 60 >= 10) { - zeroPadding = ""; - } else { - zeroPadding = "0"; - } - - stringWithTime = std::to_string(seconds / 60) + ":" + zeroPadding + std::to_string(seconds % 60) + "."; - timeInSeconds = seconds; - } - - if (time10MsToStringCache[time10MsPart].empty()) { - std::string zeroPadding; - if (time10MsPart >= 10) { - zeroPadding = ""; - } else { - zeroPadding = "0"; - } - - time10MsToStringCache[time10MsPart] = zeroPadding + std::to_string(time10Ms % 100L); - } - - if (time10Ms > 3600000L) { - setColor(0, 0, 0); - graphics->drawString("0:00.", width - defaultFontWidth00, height2 - 5, 40); - graphics->drawString("00", width - defaultFontWidth00, height2 - 5, 36); - } else { - setColor(0, 0, 0); - graphics->drawString(stringWithTime, width - defaultFontWidth00, height2 - 5, 40); - graphics->drawString(time10MsToStringCache[time10MsPart], width - defaultFontWidth00, height2 - 5, 36); - } + const std::string timeStr = Time::timeToString(time10Ms); + setColor(0, 0, 0); + graphics->drawString( + timeStr, + width - defaultFontWidth00, + height2 - GlobalSetting::TimerFpsTextOffset, + Graphics::BOTTOM | Graphics::RIGHT); } void GameCanvas::method_150(int var1) @@ -336,7 +313,11 @@ void GameCanvas::renderEngine(int x, int y, int angleF16) int centerY = addDy(y) - engineSpriteHeight / 2; if (engineImage != nullptr) { graphics->setClip(centerX, centerY, engineSpriteWidth, engineSpriteHeight); - graphics->drawImage(engineImage.get(), centerX - engineSpriteWidth * (spriteNo % 6), centerY - engineSpriteHeight * (spriteNo / 6), 20); + graphics->drawImage( + engineImage.get(), + centerX - engineSpriteWidth * (spriteNo % 6), + centerY - engineSpriteHeight * (spriteNo / 6), + Graphics::TOP | Graphics::LEFT); graphics->setClip(0, 0, width, getHeight()); } } @@ -348,7 +329,11 @@ void GameCanvas::renderFender(int x, int y, int angleF16) int centerX = addDx(x) - fenderSpriteWidth / 2; int centerY = addDy(y) - fenderSpriteHeight / 2; graphics->setClip(centerX, centerY, fenderSpriteWidth, fenderSpriteHeight); - graphics->drawImage(fenderImage.get(), centerX - fenderSpriteWidth * (spriteNo % 6), centerY - fenderSpriteHeight * (spriteNo / 6), 20); + graphics->drawImage( + fenderImage.get(), + centerX - fenderSpriteWidth * (spriteNo % 6), + centerY - fenderSpriteHeight * (spriteNo / 6), + Graphics::TOP | Graphics::LEFT); graphics->setClip(0, 0, width, getHeight()); } } @@ -384,7 +369,7 @@ void GameCanvas::setColor(int red, int green, int blue) void GameCanvas::drawGame(Graphics* g) { // synchronized (objectForSyncronization) { - if (Micro::field_249 && !micro->field_242) { + if (micro->gameStarted && !micro->gameDestroyed) { graphics = g; int var3; @@ -393,20 +378,33 @@ void GameCanvas::drawGame(Graphics* g) graphics->setColor(255, 255, 255); graphics->fillRect(0, 0, getWidth(), getHeight()); if (logoImage != nullptr) { - graphics->drawImage(logoImage.get(), getWidth() / 2, getHeight() / 2, 3); - drawSprite(graphics, 16, getWidth() - spriteSizeX[16] - 5, getHeight() - spriteSizeY[16] - 7); - drawSprite(graphics, 17, getWidth() - spriteSizeX[17] - 4, getHeight() - spriteSizeY[17] - spriteSizeY[16] - 9); + graphics->drawImage( + logoImage.get(), + getWidth() / 2, + getHeight() / 2, + logoImage->getWidth() * GlobalSetting::LogoMultiplier, + logoImage->getHeight() * GlobalSetting::LogoMultiplier, + Graphics::HCENTER | Graphics::VCENTER); + drawSprite(graphics, 16, getWidth() - spriteSizeX[16] - 5 - GlobalSetting::BarScreenOffset, getHeight() - spriteSizeY[16] - 7 - GlobalSetting::BarH); + drawSprite(graphics, 17, getWidth() - spriteSizeX[17] - 4 - GlobalSetting::BarScreenOffset, getHeight() - spriteSizeY[17] - spriteSizeY[16] - 9 - GlobalSetting::BarH); } } else { graphics->setColor(255, 255, 255); graphics->fillRect(0, 0, getWidth(), getHeight()); + if (splashImage != nullptr) { - graphics->drawImage(splashImage.get(), getWidth() / 2, getHeight() / 2, 3); + graphics->drawImage( + splashImage.get(), + getWidth() / 2, + getHeight() / 2, + splashImage->getWidth() * GlobalSetting::SplashMultiplier, + splashImage->getHeight() * GlobalSetting::SplashMultiplier, + Graphics::HCENTER | Graphics::VCENTER); } } - var3 = (int)(((int64_t)(Micro::gameLoadingStateStage << 16) << 32) / 655360L >> 16); - method_161(var3, true); + var3 = (((int64_t)Micro::gameLoadingStateStage << 48) / (11L << 16)) >> 16; + drawProgressBar(var3, true); } else { if (height != getHeight()) { updateSizeAndRepaint(); @@ -423,9 +421,17 @@ void GameCanvas::drawGame(Graphics* g) setColor(0, 0, 0); graphics->setFont(font); if (height2 <= 128) { - graphics->drawString(timerMessage, width / 2, 1, 17); + graphics->drawString( + timerMessage, + width / 2, + 1, + Graphics::TOP | Graphics::HCENTER); } else { - graphics->drawString(timerMessage, width / 2, height2 / 4, 33); + graphics->drawString( + timerMessage, + width / 2, + height2 / 4, + Graphics::BOTTOM | Graphics::HCENTER); } if (timerTriggered) { @@ -434,13 +440,18 @@ void GameCanvas::drawGame(Graphics* g) } } - // print fps to screen - // setColor(0, 0, 0); - // graphics->setFont(font); - // graphics->drawString("FPS: " + std::to_string(fps), defaultFontWidth00, height2 - 5, 36); + if (GlobalSetting::ShowFPS) { + setColor(0, 0, 0); + graphics->setFont(font); + graphics->drawString( + "FPS: " + std::to_string(fps), + defaultFontWidth00, + height2 - GlobalSetting::TimerFpsTextOffset, + Graphics::BOTTOM | Graphics::LEFT); + } var3 = gamePhysics->method_52(); - method_161(var3, false); + drawProgressBar(var3, false); } graphics = nullptr; @@ -448,31 +459,46 @@ void GameCanvas::drawGame(Graphics* g) // } } -void GameCanvas::method_161(int var1, bool mode) +// draw progressbar +void GameCanvas::drawProgressBar(int var1, bool mode) { - int h = mode ? height : height2; + const int h = mode ? height : height2; + const int barX = 1; + const int barY = h - GlobalSetting::BarScreenOffset - GlobalSetting::BarH; + + var1 *= (var1 >= 0); + setColor(0, 0, 0); - graphics->fillRect(1, h - 4, width - 2, 3); + graphics->fillRect(barX, barY, width - (2 * barX), GlobalSetting::BarH); + setColor(255, 255, 255); - graphics->fillRect(2, h - 3, (int)((int64_t)((width - 4) << 16) * (int64_t)var1 >> 16) >> 16, 1); + const int loadingBarX = barX + GlobalSetting::LoadingBarPadding; + graphics->fillRect( + loadingBarX, + barY + GlobalSetting::LoadingBarPadding, + (int)((int64_t)((width - 2 * loadingBarX) << 16) * (int64_t)var1 >> 16) >> 16, + GlobalSetting::BarH - (2 * GlobalSetting::LoadingBarPadding)); } -void GameCanvas::method_163(int var1) -{ - field_232 = var1; -} +// void GameCanvas::method_163(int var1) +// { +// field_232 = var1; +// } void GameCanvas::paint(Graphics* graphics) { - // static int64_t time = 0; - // int64_t now = Time::currentTimeMillis(); - // int64_t delta = now - time; - // time = now; - // if (delta != 0) { - // fps = 1000 / delta; - // // std::cout << "FPS: " << fps << std::endl; - // //setWindowTitle(std::string("Gravity Defied. FPS: ") + std::to_string(fps)); - // } + static int64_t time = 0; + + if (GlobalSetting::ShowFPS) { + const int64_t now = Time::currentTimeMillis(); + const int64_t delta = now - time; + time = now; + + if (delta != 0) { + fps = 1000 / delta; + // setWindowTitle(std::string("Gravity Defied. FPS: ") + std::to_string(fps)); + } + } processTimers(); // We need to call this function as often as we can. It might be better to move this call somewhere. if (Micro::isInGameMenu && menuManager != nullptr) { @@ -482,40 +508,31 @@ void GameCanvas::paint(Graphics* graphics) } } -void GameCanvas::method_164() +void GameCanvas::resetActions() { - int var1; - for (var1 = 0; var1 < 10; ++var1) { - activeKeys[var1] = false; - } - - for (var1 = 0; var1 < 7; ++var1) { - activeActions[var1] = false; - } + activeActions[Keys::UP] = false; + activeActions[Keys::DOWN] = false; + activeActions[Keys::LEFT] = false; + activeActions[Keys::RIGHT] = false; + activeActions[Keys::FIRE] = false; + activeActions[Keys::BACK] = false; } void GameCanvas::handleUpdatedInput() { - int var1 = 0; - int var2 = 0; - int var3 = field_232; + int verticalMovement = 0; + int horizontalMovement = 0; - int var4; - for (var4 = 0; var4 < 10; ++var4) { - if (activeKeys[var4]) { - var1 += field_231[var3][var4][0]; - var2 += field_231[var3][var4][1]; + for (const auto& [action, isActive] : activeActions) { + if (!isActive) { + continue; } - } - for (var4 = 0; var4 < 7; ++var4) { - if (activeActions[var4]) { - var1 += field_230[var4][0]; - var2 += field_230[var4][1]; - } + verticalMovement += availableActions[action][0]; + horizontalMovement += availableActions[action][1]; } - gamePhysics->method_30(var1, var2); + gamePhysics->method_30(verticalMovement, horizontalMovement); } void GameCanvas::processTimers() @@ -530,30 +547,15 @@ void GameCanvas::processTimers() } } -void GameCanvas::processKeyPressed(int keyCode) +void GameCanvas::processKeyPressed(const Keys keyCode) { - int action = getGameAction(keyCode); - int numKey; - // KEY_NUM0 - KEY_NUM10 is 48-58 - if ((numKey = keyCode - 48) >= 0 && numKey < 10) { - activeKeys[numKey] = true; - } else if (action >= 0 && action < 7) { - activeActions[action] = true; - } - + activeActions[keyCode] = true; handleUpdatedInput(); } -void GameCanvas::processKeyReleased(int keyCode) +void GameCanvas::processKeyReleased(const Keys keyCode) { - int action = getGameAction(keyCode); - int numKey; - if ((numKey = keyCode - 48) >= 0 && numKey < 10) { - activeKeys[numKey] = false; - } else if (action >= 0 && action < 7) { - activeActions[action] = false; - } - + activeActions[keyCode] = false; handleUpdatedInput(); } @@ -585,17 +587,22 @@ void GameCanvas::method_168(Command* var1, Displayable* var2) } } -void GameCanvas::keyPressed(int var1) +void GameCanvas::keyPressed(const Keys var1) { if (Micro::isInGameMenu && menuManager != nullptr) { menuManager->processKeyCode(var1); + return; } processKeyPressed(var1); } -void GameCanvas::keyReleased(int var1) +void GameCanvas::keyReleased(const Keys var1) { + if (var1 == Keys::BACK) { + pressedEsc(); + } + processKeyReleased(var1); } diff --git a/src/GameCanvas.h b/src/GameCanvas.h index 9ab971c..edd7163 100644 --- a/src/GameCanvas.h +++ b/src/GameCanvas.h @@ -1,8 +1,8 @@ #pragma once #include -#include #include +#include #include "lcdui/Graphics.h" #include "lcdui/Command.h" @@ -11,13 +11,24 @@ #include "Micro.h" #include "Timer.h" +#include "config.h" +#include "MathF16.h" +#include "GamePhysics.h" +#include "MenuManager.h" +#include "lcdui/Image.h" +#include "lcdui/FontStorage.h" +#include "utils/Time.h" +#include "config.h" + +#include +#include class GamePhysics; class MenuManager; class GameCanvas : public Canvas, public CommandListener { private: - void method_164(); + void resetActions(); void handleUpdatedInput(); void processTimers(); @@ -53,20 +64,14 @@ class GameCanvas : public Canvas, public CommandListener { int timerId = 0; std::vector timers; Command* commandMenu; - inline static std::string stringWithTime = ""; - std::vector time10MsToStringCache = std::vector(100); - int timeInSeconds = -1; inline static int flagAnimationTime = 0; inline static int field_226 = 0; const int startFlagAnimationTimeToSpriteNo[4] = { 12, 10, 11, 10 }; const int finishFlagAnumationTimeToSpriteNo[4] = { 14, 13, 15, 13 }; - int field_230[7][2] = { { 0, 0 }, { 1, 0 }, { 0, -1 }, { 0, 0 }, { 0, 0 }, { 0, 1 }, { -1, 0 } }; - int field_231[3][10][2] = { { { 0, 0 }, { 1, -1 }, { 1, 0 }, { 1, 1 }, { 0, -1 }, { -1, 0 }, { 0, 1 }, { -1, -1 }, { -1, 0 }, { -1, 1 } }, { { 0, 0 }, { 1, 0 }, { 0, 0 }, { 0, 0 }, { -1, 0 }, { 0, -1 }, { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 1, 0 }, { 0, -1 }, { 0, 1 }, { -1, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } } }; - int field_232 = 2; - std::vector activeActions = std::vector(7); std::vector activeKeys = std::vector(10); + std::map activeActions = std::map(); - // int fps; + uint16_t fps; public: GameCanvas(Micro* micro); @@ -89,7 +94,7 @@ class GameCanvas : public Canvas, public CommandListener { void fillRect(int x, int y, int w, int h); void drawForthSpriteByCenter(int centerX, int centerY); void drawHelmet(int var1, int var2, int var3); - void drawTime(int64_t time10Ms); + void drawTime(uint64_t time10Ms); void method_150(int var1); static void method_151(); void renderStartFlag(int x, int y); @@ -101,17 +106,17 @@ class GameCanvas : public Canvas, public CommandListener { void clearScreenWithWhite(); void setColor(int red, int green, int blue); void drawGame(Graphics* g); - void method_161(int var1, bool mode); - void method_163(int var1); + void drawProgressBar(int var1, bool mode); + // void method_163(int var1); void paint(Graphics* g); void init(GamePhysics* gamePhysics); - void processKeyPressed(int keyCode); - void processKeyReleased(int keyCode); + void processKeyPressed(const Keys keyCode); + void processKeyReleased(const Keys keyCode); void scheduleGameTimerTask(std::string var1, int delayMs); void setMenuManager(MenuManager* menuManager); void method_168(Command* var1, Displayable* var2); - void keyPressed(int var1); - void keyReleased(int var1); + void keyPressed(const Keys var1); + void keyReleased(const Keys var1); void commandAction(Command* var1, Displayable* var2); void removeMenuCommand(); void addMenuCommand(); diff --git a/src/GameLevel.h b/src/GameLevel.h index 42b9de8..7cf883c 100644 --- a/src/GameLevel.h +++ b/src/GameLevel.h @@ -29,7 +29,6 @@ class GameLevel { std::vector> pointPositions; GameLevel(); - ~GameLevel(); void init(); void method_174(int var1, int var2, int var3, int var4); int getStartPosX(); diff --git a/src/GameMenu.cpp b/src/GameMenu.cpp index b3b4bd1..6140b09 100644 --- a/src/GameMenu.cpp +++ b/src/GameMenu.cpp @@ -11,13 +11,13 @@ GameMenu::GameMenu(std::string var1, Micro* micro, GameMenu* var3, char* inputString) { - field_94 = var1; - field_95 = -1; + clearVector(); + name = var1; this->micro = micro; gameMenu = var3; canvasWidth = micro->gameCanvas->getWidth(); canvasHeight = micro->gameCanvas->getHeight(); - + font = FontStorage::getFont(Font::STYLE_BOLD, Font::SIZE_LARGE); font3 = FontStorage::getFont(Font::STYLE_PLAIN, Font::SIZE_SMALL); @@ -29,252 +29,152 @@ GameMenu::GameMenu(std::string var1, Micro* micro, GameMenu* var3, char* inputSt TextRender::setDefaultFont(font3); TextRender::setMaxArea(canvasWidth, canvasHeight); - field_101 = 1; - if (canvasWidth <= 100) { - xPos = 6; - } else { - xPos = 9; - } + menuOffsetY = 1; - if (canvasHeight <= 100) { - field_94 = ""; - } + xPos = 9; + menuOffsetX = xPos + 7; + menuSpacing = 2; + selectedMenuItemTickSpriteNo = 0; - field_104 = xPos + 7; - field_103 = 2; - field_110 = 0; - if (field_94 != "") { - field_107 = (canvasHeight - (field_101 << 1) - 10 - font->getBaselinePosition()) / (font2->getBaselinePosition() + field_103); - } else { - field_107 = (canvasHeight - (field_101 << 1) - 10) / (font2->getBaselinePosition() + field_103); - } + numberOfItemsToFit = getNumberOfItemToFitOnScreen(); if (inputString) { - field_111 = true; + isTextInput = true; nameCursorPos = 0; xPos = 8; strArr = inputString; } else { - field_111 = false; + isTextInput = false; } - - if (field_107 > 13) { - field_107 = 13; - } -} - -void GameMenu::method_68(int var1) -{ - field_103 = var1; -} - -void GameMenu::method_69(std::string var1) -{ - field_94 = var1; } -void GameMenu::method_70() +void GameMenu::rolloverMenuToBottom() { - if (field_111) { + if (isTextInput) { nameCursorPos = 0; } else { - if (!vector.empty()) { - field_95 = 0; + if (menuItems.empty()) { + return; + } - for (int var1 = 0; var1 < static_cast(vector.size()) && var1 < field_107; ++var1) { - if (vector[var1]->isNotTextRender()) { - field_95 = var1; - break; - } - } + viewItemIdx = 0; - field_105 = 0; - field_106 = vector.size() - 1; - if (field_106 > field_107 - 1) { - field_106 = field_107 - 1; + int itemsToShowWindowStart = 0; + int itemsToShowWindowEnd = 0; + calculateMenuWindowBoundaries(&itemsToShowWindowStart, &itemsToShowWindowEnd); + + for (int i = itemsToShowWindowStart; i <= itemsToShowWindowEnd; ++i) { + if (menuItems[i]->isNotTextRender()) { + selectedItemIdx = i; + viewItemIdx = i; + return; } } } } -void GameMenu::method_71() +void GameMenu::rolloverMenuToTop() { - field_95 = vector.size() - 1; - - for (int var1 = vector.size() - 1; var1 > 0; --var1) { - if (vector[var1]->isNotTextRender()) { - field_95 = var1; - break; - } - } + viewItemIdx = menuItems.size() - 1; - field_105 = vector.size() - field_107; - if (field_105 < 0) { - field_105 = 0; - } + int itemsToShowWindowStart = 0; + int itemsToShowWindowEnd = 0; + calculateMenuWindowBoundaries(&itemsToShowWindowStart, &itemsToShowWindowEnd); - field_106 = vector.size() - 1; - if (field_106 > field_95 + field_107) { - field_106 = field_95 + field_107; + for (int i = itemsToShowWindowEnd; i >= itemsToShowWindowStart; --i) { + if (menuItems[i]->isNotTextRender()) { + selectedItemIdx = i; + viewItemIdx = i; + return; + } } } void GameMenu::addMenuElement(IGameMenuElement* var1) { - int var2 = field_101; - field_107 = 1; - vector.push_back(var1); - if (field_94 != "") { - var2 = font->getBaselinePosition() + 2; - } - - if (canvasHeight < 100) { - ++var2; - } else { - var2 += 4; - } - - for (int var3 = 0; var3 < static_cast(vector.size()) - 1; ++var3) { - if (vector[var3]->isNotTextRender()) { - var2 += font2->getBaselinePosition() + field_103; - } else { - var2 += (TextRender::getBaselinePosition() < GameCanvas::spriteSizeY[5] ? GameCanvas::spriteSizeY[5] : TextRender::getBaselinePosition()) + field_103; - } - - if (var2 > canvasHeight - (field_101 << 1) - 10) { - break; - } - - ++field_107; - } - - if (field_107 > 13) { - field_107 = 13; - } - - method_70(); + menuItems.push_back(var1); + numberOfItemsToFit = getNumberOfItemToFitOnScreen(); + rolloverMenuToBottom(); } void GameMenu::processGameActionDown() { - if (field_111) { + if (isTextInput) { if (strArr[nameCursorPos] == 32) { strArr[nameCursorPos] = 90; return; } --strArr[nameCursorPos]; + if (strArr[nameCursorPos] < 65) { strArr[nameCursorPos] = 32; return; } - } else if (vector.size() != 0) { - if (!(vector[field_95]->isNotTextRender())) { - ++field_106; - field_95 = field_106; - ++field_105; + } else if (menuItems.size() != 0) { + const int lastMenuItemIdx = static_cast(menuItems.size()) - 1; + const int itemsToShowWindowStart = std::min(viewItemIdx + 1, lastMenuItemIdx); + const int itemsToShowWindowEnd = std::min(viewItemIdx + numberOfItemsToFit, lastMenuItemIdx); + ++viewItemIdx; + + if (viewItemIdx > lastMenuItemIdx) { + rolloverMenuToBottom(); return; } - ++field_95; - if (field_95 > static_cast(vector.size()) - 1) { - method_70(); - return; - } - - bool var3 = false; - - int var2; - for (var2 = field_95; var2 <= field_106 + 1; ++var2) { - if (vector[var2]->isNotTextRender()) { - var3 = true; - break; + for (int i = itemsToShowWindowStart; i <= itemsToShowWindowEnd; ++i) { + if (menuItems[i]->isNotTextRender()) { + selectedItemIdx = i; + viewItemIdx = i; + return; } } - if (var3) { - field_95 = var2; - } else if (field_106 < static_cast(vector.size()) - 1) { - ++field_106; - ++field_105; - } else { - --field_95; - } - - if (field_95 > field_106) { - ++field_105; - ++field_106; - if (field_106 > static_cast(vector.size()) - 1) { - field_106 = vector.size() - 1; - } - - field_95 = field_106; - } + selectedItemIdx = itemsToShowWindowEnd; + viewItemIdx = itemsToShowWindowEnd; } } void GameMenu::processGameActionUp() { - if (field_111) { + if (isTextInput) { if (strArr[nameCursorPos] == 32) { strArr[nameCursorPos] = 65; return; } ++strArr[nameCursorPos]; + if (strArr[nameCursorPos] > 90) { strArr[nameCursorPos] = 32; return; } - } else if (vector.size() != 0) { - --field_95; - if (field_95 < 0) { - method_71(); - return; - } - - bool var3 = false; - - int var2; - for (var2 = field_95; var2 >= field_105; --var2) { - if (vector[var2]->isNotTextRender()) { - var3 = true; - break; - } - } - - if (!var3) { - if (field_105 > 0) { - --field_105; - if (static_cast(vector.size()) > field_107 - 1) { - --field_106; - return; - } - } else { - method_71(); - } + } else if (menuItems.size() != 0) { + const int itemsToShowWindowStart = std::max(viewItemIdx - numberOfItemsToFit, 0); + const int itemsToShowWindowEnd = std::max(viewItemIdx - 1, 0); + --viewItemIdx; + if (viewItemIdx < 0) { + rolloverMenuToTop(); return; } - field_95 = var2; - if (field_95 < field_105) { - --field_105; - if (field_105 < 0) { - field_95 = 0; - field_105 = 0; - } - - if (static_cast(vector.size()) > field_107 - 1) { - --field_106; + for (int i = itemsToShowWindowEnd; i >= itemsToShowWindowStart; --i) { + if (menuItems[i]->isNotTextRender()) { + selectedItemIdx = i; + viewItemIdx = i; + return; } } + + selectedItemIdx = itemsToShowWindowStart; + viewItemIdx = itemsToShowWindowStart; } } void GameMenu::processGameActionUpd(int var1) { - if (field_111) { + if (isTextInput) { switch (var1) { case 1: if (nameCursorPos == 2) { @@ -286,23 +186,27 @@ void GameMenu::processGameActionUpd(int var1) return; case 2: ++nameCursorPos; + if (nameCursorPos > 2) { nameCursorPos = 2; return; } + break; case 3: --nameCursorPos; + if (nameCursorPos < 0) { nameCursorPos = 0; } } } else { - if (field_95 != -1) { - for (int var2 = field_95; var2 < static_cast(vector.size()); ++var2) { + if (selectedItemIdx != -1) { + for (int var2 = selectedItemIdx; var2 < static_cast(menuItems.size()); ++var2) { IGameMenuElement* var3; - if ((var3 = vector[var2]) != nullptr && var3->isNotTextRender()) { + + if ((var3 = menuItems[var2]) != nullptr && var3->isNotTextRender()) { var3->menuElemMethod(var1); return; } @@ -315,66 +219,107 @@ void GameMenu::render_76(Graphics* graphics) { int var2; int i; - if (field_111) { + + if (isTextInput) { graphics->setColor(0, 0, 20); graphics->setFont(font); int8_t var7 = 1; - graphics->drawString("Enter Name", xPos, var7, 20); - var2 = var7 + font->getHeight() + (field_103 << 2); + graphics->drawString( + "Enter Name", + xPos, + var7, + Graphics::TOP | Graphics::LEFT); + var2 = var7 + font->getHeight() + (menuSpacing << 2); graphics->setFont(font2); for (i = 0; i < 3; ++i) { - graphics->drawChar((char)strArr[i], xPos + i * font2->charWidth('W') + 1, var2, 17); + graphics->drawChar( + (char)strArr[i], + xPos + i * font2->charWidth('W') + 1, + var2, + Graphics::TOP | Graphics::HCENTER); + if (i == nameCursorPos) { - graphics->drawChar('^', xPos + i * font2->charWidth('W') + 1, var2 + font2->getHeight(), 17); + graphics->drawChar( + '^', + xPos + i * font2->charWidth('W') + 1, + var2 + font2->getHeight(), + Graphics::TOP | Graphics::HCENTER); } } } else { + int itemsToShowWindowStart = 0; + int itemsToShowWindowEnd = 0; + calculateMenuWindowBoundaries(&itemsToShowWindowStart, &itemsToShowWindowEnd); + + Log::write( + Log::LogLevel::Debug, + "%s, selectedItemIdx: %d, viewItemIdx: %d, window start: %d, window end: %d, number of items to fit: %d, menu items count: %d\n", + name.c_str(), + selectedItemIdx, + viewItemIdx, + itemsToShowWindowStart, + itemsToShowWindowEnd, + numberOfItemsToFit, + menuItems.size()); + graphics->setColor(0, 0, 0); - var2 = field_101; - if (field_94 != "") { + var2 = menuOffsetY; + + if (!name.empty()) { graphics->setFont(font); - graphics->drawString(field_94, xPos, var2, 20); + graphics->drawString( + name, + xPos, + var2, + Graphics::TOP | Graphics::LEFT); var2 += font->getBaselinePosition() + 2; } - if (field_105 > 0) { + // scrolling text: draw top range cursor + if (itemsToShowWindowStart > 0) { micro->gameCanvas->drawSprite(graphics, 2, xPos - 3, var2); } - if (canvasHeight < 100) { - ++var2; - } else { - var2 += 4; - } - graphics->setFont(font2); - for (i = field_105; i < field_106 + 1; ++i) { - IGameMenuElement* var4 = vector[i]; + for (i = itemsToShowWindowStart; i <= itemsToShowWindowEnd; ++i) { + IGameMenuElement* var4 = menuItems[i]; graphics->setColor(0, 0, 0); - var4->render(graphics, var2, field_104); - if (i == field_95 && var4->isNotTextRender()) { - int var5 = xPos - micro->gameCanvas->helmetSpriteWidth / 2; - int var6 = var2 + font2->getBaselinePosition() / 2 - micro->gameCanvas->helmetSpriteHeight / 2; - graphics->setClip(var5, var6, micro->gameCanvas->helmetSpriteWidth, micro->gameCanvas->helmetSpriteHeight); - graphics->drawImage(micro->gameCanvas->helmetImage.get(), var5 - micro->gameCanvas->helmetSpriteWidth * (field_110 % 6), var6 - micro->gameCanvas->helmetSpriteHeight * (field_110 / 6), 20); + var4->render(graphics, var2, menuOffsetX); + + // draw helmet icon near selected item + if (i == selectedItemIdx && var4->isNotTextRender()) { + const int selectedMenuItemTickX = xPos - micro->gameCanvas->helmetSpriteWidth / 2; + const int selectedMenuItemTickY = var2 + font2->getBaselinePosition() / 2; // - micro->gameCanvas->helmetSpriteHeight / 2; + graphics->setClip( + selectedMenuItemTickX, + selectedMenuItemTickY, + micro->gameCanvas->helmetSpriteWidth, + micro->gameCanvas->helmetSpriteHeight); + graphics->drawImage( + micro->gameCanvas->helmetImage.get(), + selectedMenuItemTickX - micro->gameCanvas->helmetSpriteWidth * (selectedMenuItemTickSpriteNo % 6), + selectedMenuItemTickY - micro->gameCanvas->helmetSpriteHeight * (selectedMenuItemTickSpriteNo / 6), + Graphics::TOP | Graphics::LEFT); graphics->setClip(0, 0, canvasWidth, canvasHeight); - ++field_110; - if (field_110 > 30) { - field_110 = 0; + ++selectedMenuItemTickSpriteNo; + + if (selectedMenuItemTickSpriteNo > 30) { + selectedMenuItemTickSpriteNo = 0; } } if (var4->isNotTextRender()) { - var2 += font2->getBaselinePosition() + field_103; + var2 += font2->getBaselinePosition() + menuSpacing; } else { - var2 += (TextRender::getBaselinePosition() < GameCanvas::spriteSizeY[5] ? GameCanvas::spriteSizeY[5] : TextRender::getBaselinePosition()) + field_103; + var2 += (TextRender::getBaselinePosition() < GameCanvas::spriteSizeY[5] ? GameCanvas::spriteSizeY[5] : TextRender::getBaselinePosition()) + menuSpacing; } } - if (static_cast(vector.size()) > field_106 && field_106 != static_cast(vector.size()) - 1) { + // scrolling text: draw bottom range cursor + if (static_cast(menuItems.size()) > itemsToShowWindowEnd && itemsToShowWindowEnd != static_cast(menuItems.size()) - 1) { if (GameCanvas::spriteSizeY[3] + var2 > canvasHeight) { micro->gameCanvas->drawSprite(graphics, 3, xPos - 3, canvasHeight - GameCanvas::spriteSizeY[3]); return; @@ -395,38 +340,59 @@ GameMenu* GameMenu::getGameMenu() return gameMenu; } -int GameMenu::method_79() +void GameMenu::clearVector() { - return field_95; + menuItems.clear(); + viewItemIdx = -1; + selectedItemIdx = -1; } -void GameMenu::clearVector() +char* GameMenu::getStrArr() const { - vector.clear(); - field_105 = 0; - field_106 = 0; - field_95 = -1; + return strArr; } -std::string GameMenu::makeString() +void GameMenu::startAtPosition(const int pos) { - return std::string(strArr); + rolloverMenuToBottom(); + viewItemIdx = pos; + selectedItemIdx = pos; } -char* GameMenu::getStrArr() const +int GameMenu::getNumberOfItemToFitOnScreen() const { - return strArr; + int screenHeightForMenuItems = canvasHeight - (menuOffsetY << 1); + + if (!name.empty()) { + screenHeightForMenuItems -= font->getBaselinePosition() + 10; + } + + const int menuItemHeight = font2->getBaselinePosition() + menuSpacing; + return std::floor(screenHeightForMenuItems / menuItemHeight) - 1; } -void GameMenu::method_83(int var1) -{ - method_70(); +void GameMenu::calculateMenuWindowBoundaries(int* itemsToShowWindowStart, int* itemsToShowWindowEnd) { + if (menuItems.empty()) { + return; + } - while (field_95 < var1) { - ++field_95; - if (field_95 > field_106) { - ++field_105; - ++field_106; + if (static_cast(menuItems.size()) <= numberOfItemsToFit) { + *itemsToShowWindowStart = 0; + *itemsToShowWindowEnd = menuItems.size() - 1; + } else { + *itemsToShowWindowStart = viewItemIdx; + *itemsToShowWindowEnd = *itemsToShowWindowStart + numberOfItemsToFit - 1; + + if (*itemsToShowWindowEnd > static_cast((menuItems.size() - 1))) { + *itemsToShowWindowEnd = menuItems.size() - 1; + } + + if (*itemsToShowWindowEnd - *itemsToShowWindowStart < numberOfItemsToFit) { + *itemsToShowWindowStart = *itemsToShowWindowEnd - numberOfItemsToFit + 1; } } + + *itemsToShowWindowStart = std::max(*itemsToShowWindowStart, 0); + *itemsToShowWindowEnd = std::min(*itemsToShowWindowEnd, static_cast(menuItems.size() - 1)); + } diff --git a/src/GameMenu.h b/src/GameMenu.h index fed448e..9c536eb 100644 --- a/src/GameMenu.h +++ b/src/GameMenu.h @@ -13,34 +13,37 @@ class Graphics; class GameMenu { private: GameMenu* gameMenu; - std::string field_94; - int field_95; - std::vector vector; + std::string name; + int selectedItemIdx; + int viewItemIdx; + + std::vector menuItems; Micro* micro; std::shared_ptr font; std::shared_ptr font2; std::shared_ptr font3; - int field_101; - int field_103; - int field_104; - int field_105; - int field_106; - int field_107; + int menuOffsetY; + int menuSpacing; + int menuOffsetX; + // int itemsToShowWindowStart; + // int itemsToShowWindowEnd; + int numberOfItemsToFit; int canvasWidth; int canvasHeight; - int field_110; - bool field_111; + int selectedMenuItemTickSpriteNo; + bool isTextInput; int nameCursorPos; char* strArr; + int getNumberOfItemToFitOnScreen() const; + void calculateMenuWindowBoundaries(int* itemsToShowWindowStart, int* itemsToShowWindowEnd); + public: int xPos; GameMenu(std::string var1, Micro* micro, GameMenu* var3, char* inputString = nullptr); - void method_68(int var1); - void method_69(std::string var1); - void method_70(); - void method_71(); + void rolloverMenuToBottom(); + void rolloverMenuToTop(); void addMenuElement(IGameMenuElement* var1); void processGameActionDown(); void processGameActionUp(); @@ -48,9 +51,8 @@ class GameMenu { void render_76(Graphics* graphics); void setGameMenu(GameMenu* gameMenu); GameMenu* getGameMenu(); - int method_79(); void clearVector(); - std::string makeString(); + // std::string makeString(); char* getStrArr() const; - void method_83(int var1); + void startAtPosition(const int pos); }; diff --git a/src/GamePhysics.cpp b/src/GamePhysics.cpp index d776b18..169a6e7 100644 --- a/src/GamePhysics.cpp +++ b/src/GamePhysics.cpp @@ -3,7 +3,6 @@ #include "LevelLoader.h" #include "class_10.h" #include "MathF16.h" -#include GamePhysics::GamePhysics(LevelLoader* levelLoader) { diff --git a/src/GamePhysics.h b/src/GamePhysics.h index ed62c64..9cf1d5d 100644 --- a/src/GamePhysics.h +++ b/src/GamePhysics.h @@ -1,7 +1,6 @@ #pragma once #include -#include #include "TimerOrMotoPartOrMenuElem.h" class LevelLoader; diff --git a/src/LevelLoader.cpp b/src/LevelLoader.cpp index fa3d73c..f70c490 100644 --- a/src/LevelLoader.cpp +++ b/src/LevelLoader.cpp @@ -1,9 +1,4 @@ #include "LevelLoader.h" -#include "utils/FileStream.h" -#include "utils/EmbedFileStream.h" - -#include -#include int LevelLoader::field_133 = 0; int LevelLoader::field_134 = 0; @@ -18,91 +13,77 @@ const int LevelLoader::field_118 = 1; bool LevelLoader::isEnabledPerspective = true; bool LevelLoader::isEnabledShadows = true; -LevelLoader::LevelLoader(const std::filesystem::path& mrgFilePath) +LevelLoader::LevelLoader(const std::filesystem::path& path) { for (int i = 0; i < 3; ++i) { field_123[i] = (int)((int64_t)((GamePhysics::const175_1_half[i] + 19660) >> 1) * (int64_t)((GamePhysics::const175_1_half[i] + 19660) >> 1) >> 16); field_124[i] = (int)((int64_t)((GamePhysics::const175_1_half[i] - 19660) >> 1) * (int64_t)((GamePhysics::const175_1_half[i] - 19660) >> 1) >> 16); } - if (!mrgFilePath.string().empty()) { - FileStream* fileStream = new FileStream(mrgFilePath, std::ios::in | std::ios::binary); - if (!fileStream->isOpen()) { - throw std::system_error(errno, std::system_category(), "Failed to open " + mrgFilePath.string()); - } - levelFileStream = fileStream; - } else { - EmbedFileStream* embedFileStream = new EmbedFileStream("levels.mrg"); - levelFileStream = static_cast(embedFileStream); + this->mrgFilePath = path; + trackHeaders = MRGLoader::loadLevels(this->mrgFilePath); + loadNextTrack(); +} + +std::string LevelLoader::getName(const int level, const int track) const +{ + const MRGLoader::LevelTracks l = trackHeaders.at(level); + const int tracksCount = static_cast(l.tracks.size()); + + if (level < 3 && track < tracksCount) { + return l.tracks.at(track).trackName; } - loadLevels(); - method_87(); + return "---"; } -LevelLoader::~LevelLoader() +int LevelLoader::getTracksCount(const int level) const { - delete levelFileStream; + const MRGLoader::LevelTracks l = trackHeaders.at(level); + return static_cast(l.tracks.size()); } -void LevelLoader::loadLevels() +std::vector LevelLoader::GetTrackNames(const int level) const { - std::vector var3(40); - std::vector var4(3); - - for (int league = 0; league < 3; ++league) { - levelFileStream->readVariable(&var4[league], true); - levelOffsetInFile[league] = std::vector(var4[league]); - levelNames[league] = std::vector(var4[league]); - - for (int levelNp = 0; levelNp < var4[league]; ++levelNp) { - int var7; - levelFileStream->readVariable(&var7, true); - levelOffsetInFile[league][levelNp] = var7; - - for (int var8 = 0; var8 < 40; ++var8) { - levelFileStream->readVariable(&var3[var8], true); - if (var3[var8] == 0) { - std::string s = std::string(reinterpret_cast(var3.data()), var8); - std::replace(s.begin(), s.end(), '_', ' '); - levelNames[league][levelNp] = s; - break; - } - } - } + const MRGLoader::LevelTracks levelTracks = trackHeaders.at(level); + std::vector trackNames{}; + + for (auto &track: levelTracks.tracks) { + trackNames.push_back(track.trackName); } -} -std::string LevelLoader::getName(int league, int level) -{ - return league < 3 && level < static_cast(levelNames[league].size()) ? levelNames[league][level] : "---"; + return trackNames; } -void LevelLoader::method_87() +void LevelLoader::loadNextTrack() { - method_88(field_125, field_126 + 1); + loadTrack(loadedLevel, loadedTrack + 1); } -int LevelLoader::method_88(int var1, int var2) +int LevelLoader::loadTrack(const int level, const int track) { - field_125 = var1; - field_126 = var2; - if (field_126 >= static_cast(levelNames[field_125].size())) { - field_126 = 0; + Log::write(Log::LogLevel::Debug, "loadTrack %d %d\n", level, track); + loadedLevel = level; + loadedTrack = track; + + const MRGLoader::LevelTracks l = trackHeaders.at(level); + const int tracksCount = static_cast(l.tracks.size()); + + if (loadedTrack >= tracksCount) { + loadedTrack = 0; } - method_89(field_125 + 1, field_126 + 1); - return field_126; -} + const MRGLoader::Track t = l.tracks.at(loadedTrack); + FileStream levelFileStream{mrgFilePath, std::ios::in | std::ios::binary}; + levelFileStream.setPos(t.offset); -void LevelLoader::method_89(int var1, int var2) -{ - levelFileStream->setPos(levelOffsetInFile[var1 - 1][var2 - 1]); if (gameLevel == nullptr) { gameLevel = new GameLevel(); } - gameLevel->load(levelFileStream); + + gameLevel->load(&levelFileStream); method_96(gameLevel); + return loadedTrack; } void LevelLoader::method_90(int var1) @@ -142,6 +123,7 @@ void LevelLoader::method_96(GameLevel* gameLevel) field_131 = INT_MIN; this->gameLevel = gameLevel; int var2 = gameLevel->pointsCount; + if (field_121.empty() || field_132 < var2) { field_132 = var2 < 100 ? 100 : var2; field_121.assign(field_132, std::vector(2)); @@ -155,6 +137,7 @@ void LevelLoader::method_96(GameLevel* gameLevel) for (int var3 = 0; var3 < var2; ++var3) { int var4 = gameLevel->pointPositions[(var3 + 1) % var2][0] - gameLevel->pointPositions[var3][0]; int var5 = gameLevel->pointPositions[(var3 + 1) % var2][1] - gameLevel->pointPositions[var3][1]; + if (var3 != 0 && var3 != var2 - 1) { field_131 = field_131 < gameLevel->pointPositions[var3][0] ? gameLevel->pointPositions[var3][0] : field_131; } @@ -163,6 +146,7 @@ void LevelLoader::method_96(GameLevel* gameLevel) int var8 = GamePhysics::getSmthLikeMaxAbs(var6, var4); field_121[var3][0] = (int)(((int64_t)var6 << 32) / (int64_t)var8 >> 16); field_121[var3][1] = (int)(((int64_t)var4 << 32) / (int64_t)var8 >> 16); + if (gameLevel->startFlagPoint == 0 && gameLevel->pointPositions[var3][0] > gameLevel->startPosX) { gameLevel->startFlagPoint = var3 + 1; } @@ -206,6 +190,7 @@ void LevelLoader::method_100(int var1, int var2, int var3) var1 >>= 1; field_134 = field_134 < gameLevel->pointsCount - 1 ? field_134 : gameLevel->pointsCount - 1; field_133 = field_133 < 0 ? 0 : field_133; + if (var2 > field_136) { while (field_134 < gameLevel->pointsCount - 1 && var2 > gameLevel->pointPositions[++field_134][0]) { } @@ -236,6 +221,7 @@ int LevelLoader::method_101(TimerOrMotoPartOrMenuElem* var1, int var2) int8_t var17 = 2; int var18 = var1->xF16 >> 1; int var19 = var1->yF16 >> 1; + if (isEnabledPerspective) { var19 -= 65536; } @@ -247,6 +233,7 @@ int LevelLoader::method_101(TimerOrMotoPartOrMenuElem* var1, int var2) int var5 = gameLevel->pointPositions[var22][1]; int var6 = gameLevel->pointPositions[var22 + 1][0]; int var7; + if ((var7 = gameLevel->pointPositions[var22 + 1][1]) < var5) { ; } @@ -257,6 +244,7 @@ int LevelLoader::method_101(TimerOrMotoPartOrMenuElem* var1, int var2) int var10 = (int)((int64_t)var8 * (int64_t)var8 >> 16) + (int)((int64_t)var9 * (int64_t)var9 >> 16); int var11 = (int)((int64_t)(var18 - var4) * (int64_t)(-var8) >> 16) + (int)((int64_t)(var19 - var5) * (int64_t)(-var9) >> 16); int var12; + if ((var10 < 0 ? -var10 : var10) >= 3) { var12 = (int)(((int64_t)var11 << 32) / (int64_t)var10 >> 16); } else { @@ -277,6 +265,7 @@ int LevelLoader::method_101(TimerOrMotoPartOrMenuElem* var1, int var2) var9 = var19 - var14; int8_t var3; int64_t var23; + if ((var23 = ((int64_t)var8 * (int64_t)var8 >> 16) + ((int64_t)var9 * (int64_t)var9 >> 16)) < (int64_t)field_123[var2]) { if (var23 >= (int64_t)field_124[var2]) { var3 = 1; @@ -296,6 +285,7 @@ int LevelLoader::method_101(TimerOrMotoPartOrMenuElem* var1, int var2) if (var3 == 1 && (int)((int64_t)field_121[var22][0] * (int64_t)var1->field_382 >> 16) + (int)((int64_t)field_121[var22][1] * (int64_t)var1->field_383 >> 16) < 0) { ++var16; var17 = 1; + if (var16 == 1) { var20 = field_121[var22][0]; var21 = field_121[var22][1]; diff --git a/src/LevelLoader.h b/src/LevelLoader.h index 66c6912..4faad27 100644 --- a/src/LevelLoader.h +++ b/src/LevelLoader.h @@ -5,11 +5,17 @@ #include #include #include +#include +#include "config.h" +#include "utils/FileStream.h" +#include "utils/EmbedFileStream.h" +#include "MRGLoader.h" #include "GamePhysics.h" #include "GameCanvas.h" #include "GameLevel.h" #include "TimerOrMotoPartOrMenuElem.h" +#include "utils/Log.h" #include "utils/FileStream.h" class LevelLoader { @@ -17,7 +23,8 @@ class LevelLoader { std::vector> field_121; int field_123[3]; int field_124[3]; - inline static std::vector> levelOffsetInFile = std::vector>(3); + std::array trackHeaders; + std::filesystem::path mrgFilePath; int field_132 = 0; static int field_133; @@ -25,9 +32,6 @@ class LevelLoader { static int field_135; static int field_136; - FileStream* levelFileStream; - void loadLevels(); - public: static const int field_114; static const int field_115; @@ -37,9 +41,8 @@ class LevelLoader { static bool isEnabledPerspective; static bool isEnabledShadows; GameLevel* gameLevel = nullptr; - int field_125 = 0; - int field_126 = -1; - std::vector> levelNames = std::vector>(3); + int loadedLevel = 0; + int loadedTrack = -1; int field_129; int field_130; int field_131; @@ -47,13 +50,13 @@ class LevelLoader { int field_138; LevelLoader(const std::filesystem::path& mrgFilePath); - ~LevelLoader(); - std::string getName(int league, int level); + std::string getName(const int level, const int track) const; + int getTracksCount(const int level) const; - void method_87(); - int method_88(int var1, int var2); - void method_89(int var1, int var2); + void loadNextTrack(); + int loadTrack(const int level, const int track); + std::vector GetTrackNames(const int level) const; void method_90(int var1); int method_91(); diff --git a/src/MRGLoader.cpp b/src/MRGLoader.cpp new file mode 100644 index 0000000..61bd909 --- /dev/null +++ b/src/MRGLoader.cpp @@ -0,0 +1,116 @@ +#include "MRGLoader.h" + +static MRGLoader::LevelTracks loadLevel(FileStream& levelFileStream, const uint8_t level) +{ + MRGLoader::LevelTracks levelTracks; + levelFileStream.readVariable(&levelTracks.tracksCount, true); + + for (uint32_t trackNo = 0; trackNo < levelTracks.tracksCount; ++trackNo) { + MRGLoader::Track track; + levelFileStream.readVariable(&track.offset, true); + + std::stringstream ss; + char nameCh; + + do { + levelFileStream.readVariable(&nameCh, true); + ss << nameCh; + } while (nameCh > 0); + + track.trackName = ss.str(); + + levelTracks.tracks.push_back(track); + } + + Log::write(Log::LogLevel::Debug, "Level %d, Tracks %d\n", level, levelTracks.tracksCount); + + for (auto& i : levelTracks.tracks) { + Log::write(Log::LogLevel::Debug, "%d %s\n", i.offset, i.trackName.c_str()); + } + + return levelTracks; +} + +std::array MRGLoader::loadLevels(const std::filesystem::path& mrgFilePath) +{ + FileStream levelFileStream(mrgFilePath, std::ios::in | std::ios::binary); + std::array levels; + + for (uint8_t level = 0; level < 3; ++level) { + levels[level] = loadLevel(levelFileStream, level); + } + + return levels; +} + +MRGLoader::TrackInfo MRGLoader::loadTrack(const std::filesystem::path& mrgFilePath, const uint32_t fileOffset) +{ + FileStream levelFileStream(mrgFilePath, std::ios::in | std::ios::binary); + levelFileStream.setPos(fileOffset); + + uint8_t c; + levelFileStream.readVariable(&c, true); + std::cout << (int)c << std::endl; + // if (c == 50) { + // char var3[20]; + // inStream->readVariable(var3, false, 20); + // } + + int32_t pointX, pointY; + int8_t tempDX, tempDY; + + MRGLoader::TrackInfo trackInfo; + levelFileStream.readVariable(&trackInfo.startPosX, true); + levelFileStream.readVariable(&trackInfo.startPosY, true); + levelFileStream.readVariable(&trackInfo.finishPosX, true); + levelFileStream.readVariable(&trackInfo.finishPosY, true); + levelFileStream.readVariable(&trackInfo.pointsCount, true); + levelFileStream.readVariable(&pointX, true); + levelFileStream.readVariable(&pointY, true); + std::cout << trackInfo.pointsCount << std::endl; + std::cout << trackInfo.startPosX << " " << trackInfo.startPosY << std::endl; + std::cout << trackInfo.finishPosX << " " << trackInfo.finishPosY << std::endl; + + // l.startPosX = l.startPosX >> 16 << 3; + // l.startPosY = l.startPosY >> 16 << 3; + // l.finishPosX = l.finishPosX >> 16 << 3; + // l.finishPosY = l.finishPosY >> 16 << 3; + // l.pointX = l.pointX >> 16 << 3; + // l.pointY = l.pointY >> 16 << 3; + + // std::cout << l.pointsCount << std::endl; + // std::cout << l.startPosX << " " << l.startPosY << std::endl; + // std::cout << l.finishPosX << " " << l.finishPosY << std::endl; + // std::cout << l.pointX << " " << l.pointY << std::endl; + + int32_t offsetX = pointX; + int32_t offsetY = pointY; + trackInfo.points.push_back({ .x = offsetX, .y = offsetY }); + + for (uint16_t i = 1; i < trackInfo.pointsCount; ++i) { + levelFileStream.readVariable(&tempDX, true); + + if (tempDX == -1) { + offsetY = 0; + offsetX = 0; + levelFileStream.readVariable(&pointX, true); + levelFileStream.readVariable(&pointY, true); + } else { + pointX = tempDX; + levelFileStream.readVariable(&tempDY, true); + pointY = tempDY; + } + + offsetX += pointX; + offsetY += pointY; + trackInfo.points.push_back({ .x = offsetX, .y = offsetY }); + // addPointSimple(offsetX, offsetY); + } + + for (auto& i : trackInfo.points) { + std::cout << "X: " << i.x << " Y: " << i.y << '\n'; + } + + std::cout << std::endl; + return trackInfo; +} diff --git a/src/MRGLoader.h b/src/MRGLoader.h new file mode 100644 index 0000000..50eee8c --- /dev/null +++ b/src/MRGLoader.h @@ -0,0 +1,41 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "utils/Log.h" +#include "utils/FileStream.h" + +namespace MRGLoader { +struct TrackPoint { + int32_t x; + int32_t y; +}; +struct TrackInfo { + int32_t startPosX; + int32_t startPosY; + int32_t finishPosX; + int32_t finishPosY; + uint16_t pointsCount; + // int32_t pointX; + // int32_t pointY; + std::vector points; +}; +struct Track { + uint32_t offset; + std::string trackName; +}; +struct LevelTracks { + uint32_t tracksCount; + std::vector tracks; +}; + +std::array loadLevels(const std::filesystem::path& mrgFilePath); +MRGLoader::TrackInfo loadTrack(const std::filesystem::path& mrgFilePath, const uint32_t fileOffset); +}; diff --git a/src/MenuManager.cpp b/src/MenuManager.cpp index caff4e0..22cd868 100644 --- a/src/MenuManager.cpp +++ b/src/MenuManager.cpp @@ -1,16 +1,4 @@ #include "MenuManager.h" -#include "rms/RecordStoreException.h" -#include "rms/RecordStoreNotOpenException.h" -#include "rms/RecordStore.h" -#include "lcdui/FontStorage.h" -#include "TextRender.h" -#include "RecordManager.h" -#include "Micro.h" -#include "LevelLoader.h" -#include "GameMenu.h" -#include "SettingsStringRender.h" -#include "utils/Time.h" - MenuManager::MenuManager(Micro* var1) { @@ -20,130 +8,60 @@ MenuManager::MenuManager(Micro* var1) void MenuManager::initPart(int var1) { - int var4; switch (var1) { - case 1: - field_341 = defaultInputString; - field_374 = { "On", "Off" }; - field_375 = { "Keyset 1", "Keyset 2", "Keyset 3" }; - recordManager = new RecordManager(); - field_337 = -1L; - field_338 = -1; - field_339 = -1; - field_340.clear(); - isRecordStoreOpened = false; - field_278 = std::vector(19); - - for (int var11 = 0; var11 < 19; ++var11) { - field_278[var11] = -127; - } + case 1: { + recordManager = std::make_unique(); + trackTimeMs = 0; + trackTimeFormatted.clear(); - try { - recordStore = RecordStore::openRecordStore("GDTRStates", true); - isRecordStoreOpened = true; - return; - } catch (RecordStoreException& var9) { - isRecordStoreOpened = false; - return; - } + return; + } case 2: { - recorcStoreRecordId = -1; + settings = SettingsManager::loadSettings(); - RecordEnumeration* records; - try { - records = recordStore->enumerateRecords(nullptr, nullptr, false); - } catch (RecordStoreNotOpenException& var8) { - return; + if (settings.playerName[0] < 'A' || settings.playerName[0] > 'Z') { + strncpy(settings.playerName, defaultName, PLAYER_NAME_MAX); } - std::vector var3; - if (records->numRecords() > 0) { - try { - var3 = records->nextRecord(); - records->reset(); - recorcStoreRecordId = records->nextRecordId(); - } catch (RecordStoreException& var7) { - return; - } - - if (var3.size() <= 19) { - for (std::size_t i = 0; i < var3.size(); ++i) { - field_278[i] = var3[i]; - } - } - - records->destroy(); - } - - var3 = method_216(16, (int8_t)-1); - if (!var3.empty() && var3[0] != -1) { - for (var4 = 0; var4 < 3; ++var4) { - field_341[var4] = var3[var4]; - } - } - - if (field_341[0] == 82 && field_341[1] == 75 && field_341[2] == 69) { - availableLeagues = 3; - field_344 = 2; - field_342[0] = (int8_t)(micro->levelLoader->levelNames[0].size() - 1); - field_342[1] = (int8_t)(micro->levelLoader->levelNames[1].size() - 1); - field_342[2] = (int8_t)(micro->levelLoader->levelNames[2].size() - 1); + // cheat code: set name to RKE to enable all leagues ??? + if (strncmp(settings.playerName, cheatCode, PLAYER_NAME_MAX) == 0) { + settings.availableLeagues = 3; + settings.availableLevels = 2; + settings.availableTracks[0] = (int8_t)(micro->levelLoader->getTracksCount(0)); + settings.availableTracks[1] = (int8_t)(micro->levelLoader->getTracksCount(1)); + settings.availableTracks[2] = (int8_t)(micro->levelLoader->getTracksCount(2)); return; } - availableLeagues = 0; - field_344 = 1; - field_342[0] = 0; - field_342[1] = 0; - field_342[2] = -1; - } return; - case 3: - isDisablePerspective = method_217(0, isDisablePerspective); - isDisabledShadows = method_217(1, isDisabledShadows); - isDisabledDriverSprite = method_217(2, isDisabledDriverSprite); - isDisabledBikeSprite = method_217(3, isDisabledBikeSprite); - field_367 = method_217(14, field_367); - isDisableLookAhead = method_217(4, isDisableLookAhead); - field_369 = method_217(11, field_369); - field_370 = method_217(10, field_370); - field_371 = method_217(12, field_371); - field_373 = method_217(15, field_373); - field_354 = field_370; - field_355 = field_369; - - if (field_341[0] != 82 || field_341[1] != 75 || field_341[2] != 69) { - availableLeagues = method_217(5, availableLeagues); - field_344 = method_217(6, field_344); - - for (var4 = 0; var4 < 3; ++var4) { - field_342[var4] = method_217(7 + var4, field_342[var4]); - } - } + } + case 3: { + field_354 = settings.selectedLevel; + field_355 = settings.selectedTrack; try { - field_345.at(field_370) = field_369; + field_345.at(settings.selectedLevel) = settings.selectedTrack; } catch (std::exception& var6) { - field_370 = 0; - field_369 = 0; - field_345[field_370] = field_369; + settings.selectedLevel = 0; + settings.selectedTrack = 0; + field_345[settings.selectedLevel] = settings.selectedTrack; } - LevelLoader::isEnabledPerspective = isDisablePerspective == 0; - LevelLoader::isEnabledShadows = isDisabledShadows == 0; - micro->gamePhysics->setEnableLookAhead(isDisableLookAhead == 0); - micro->gameCanvas->method_163(field_367); + LevelLoader::isEnabledPerspective = settings.perspective == 0; + LevelLoader::isEnabledShadows = settings.shadows == 0; + micro->gamePhysics->setEnableLookAhead(settings.lookAhead == 0); micro->gameCanvas->method_124(field_372 == 0); leagueNamesAll4 = { "100cc", "175cc", "220cc", "325cc" }; - levelNames = micro->levelLoader->levelNames; - if (availableLeagues < 3) { + + if (settings.availableLeagues < 3) { this->leagueNames = { "100cc", "175cc", "220cc" }; } else { this->leagueNames = leagueNamesAll4; } - field_360 = field_371; + field_360 = settings.selectedLeague; return; + } case 4: { gameMenuMain = new GameMenu("Main", micro, nullptr); gameMenuPlay = new GameMenu("Play", micro, gameMenuMain); @@ -154,7 +72,7 @@ void MenuManager::initPart(int var1) settingStringGoToMain = new SettingsStringRender("Go to Main", 0, this, std::vector(), false, micro, gameMenuMain, true); settingStringContinue = new SettingsStringRender("Continue", 0, this, std::vector(), false, micro, gameMenuMain, true); settingStringPlayMenu = new SettingsStringRender("Play Menu", 0, this, std::vector(), false, micro, gameMenuMain, true); - + std::shared_ptr boldSmallFont = FontStorage::getFont(Font::STYLE_BOLD, Font::SIZE_SMALL); if (gameMenuAbout->xPos + boldSmallFont->stringWidth("http://www.codebrew.se/") >= getCanvasWidth()) { textRenderCodeBrewLink = new TextRender("www.codebrew.se", micro); @@ -165,11 +83,11 @@ void MenuManager::initPart(int var1) textRenderCodeBrewLink->setFont(boldSmallFont); gameMenuHighscore = new GameMenu("Highscore", micro, gameMenuPlay); gameMenuFinished = new GameMenu("Finished!", micro, gameMenuPlay); - } return; - case 5: + } + case 5: { gameMenuIngame = new GameMenu("Ingame", micro, gameMenuPlay); - gameMenuEnterName = new GameMenu("Enter Name", micro, gameMenuFinished, field_341); + gameMenuEnterName = new GameMenu("Enter Name", micro, gameMenuFinished, settings.playerName); gameMenuConfirmClear = new GameMenu("Confirm Clear", micro, gameMenuOptions); gameMenuConfirmReset = new GameMenu("Confirm Reset", micro, gameMenuConfirmClear); taskPlayMenu = new TimerOrMotoPartOrMenuElem("Play Menu", gameMenuPlay, this); @@ -182,18 +100,18 @@ void MenuManager::initPart(int var1) gameMenuMain->addMenuElement(taskHelp); gameMenuMain->addMenuElement(taskAbout); gameMenuMain->addMenuElement(settingStringExitGame); - settingStringLevel = new SettingsStringRender("Level", field_370, this, field_361, false, micro, gameMenuPlay, false); - settingsStringTrack = new SettingsStringRender("Track", field_345[field_370], this, levelNames[field_370], false, micro, gameMenuPlay, false); - settingsStringLeague = new SettingsStringRender("League", field_371, this, leagueNames, false, micro, gameMenuPlay, false); + settingStringLevel = new SettingsStringRender("Level", settings.selectedLevel, this, levelLabels, false, micro, gameMenuPlay, false); + settingsStringTrack = new SettingsStringRender("Track", field_345[settings.selectedLevel], this, micro->levelLoader->GetTrackNames(settingStringLevel->getCurrentOptionPos()), false, micro, gameMenuPlay, false); + settingsStringLeague = new SettingsStringRender("League", settings.selectedLeague, this, leagueNames, false, micro, gameMenuPlay, false); try { - settingsStringTrack->setAvailableOptions(field_342[field_370]); + settingsStringTrack->setAvailableOptions(settings.availableTracks[settings.selectedLevel]); } catch (std::exception& var5) { settingsStringTrack->setAvailableOptions(0); } - settingStringLevel->setAvailableOptions(field_344); - settingsStringLeague->setAvailableOptions(availableLeagues); + settingStringLevel->setAvailableOptions(settings.availableLevels); + settingsStringLeague->setAvailableOptions(settings.availableLeagues); gameTimerTaskHighscore = new TimerOrMotoPartOrMenuElem("Highscore", gameMenuHighscore, this); gameMenuHighscore->addMenuElement(settingStringBack); taskStart = new SettingsStringRender("Start>", 0, this, std::vector(), false, micro, gameMenuMain, true); @@ -204,20 +122,21 @@ void MenuManager::initPart(int var1) gameMenuPlay->addMenuElement(gameTimerTaskHighscore); gameMenuPlay->addMenuElement(settingStringGoToMain); - perspectiveSetting = new SettingsStringRender("Perspective", isDisablePerspective, this, field_374, true, micro, gameMenuOptions, false); - shadowsSetting = new SettingsStringRender("Shadows", isDisabledShadows, this, field_374, true, micro, gameMenuOptions, false); - driverSpriteSetting = new SettingsStringRender("Driver sprite", isDisabledDriverSprite, this, field_374, true, micro, gameMenuOptions, false); - bikeSpriteSetting = new SettingsStringRender("Bike sprite", isDisabledBikeSprite, this, field_374, true, micro, gameMenuOptions, false); - inputSetting = new SettingsStringRender("Input", field_367, this, field_375, false, micro, gameMenuOptions, false); - lookAheadSetting = new SettingsStringRender("Look ahead", isDisableLookAhead, this, field_374, true, micro, gameMenuOptions, false); + perspectiveSetting = new SettingsStringRender("Perspective", settings.perspective, this, onOffLabels, true, micro, gameMenuOptions, false); + shadowsSetting = new SettingsStringRender("Shadows", settings.shadows, this, onOffLabels, true, micro, gameMenuOptions, false); + driverSpriteSetting = new SettingsStringRender("Driver sprite", settings.driverSprite, this, onOffLabels, true, micro, gameMenuOptions, false); + bikeSpriteSetting = new SettingsStringRender("Bike sprite", settings.bikeSprite, this, onOffLabels, true, micro, gameMenuOptions, false); + // inputSetting = new SettingsStringRender("Input", settings.input, this, inputLabels, false, micro, gameMenuOptions, false); + lookAheadSetting = new SettingsStringRender("Look ahead", settings.lookAhead, this, onOffLabels, true, micro, gameMenuOptions, false); clearHighscoreSetting = new TimerOrMotoPartOrMenuElem("Clear highscore", gameMenuConfirmClear, this); return; - case 6: + } + case 6: { gameMenuOptions->addMenuElement(perspectiveSetting); gameMenuOptions->addMenuElement(shadowsSetting); gameMenuOptions->addMenuElement(driverSpriteSetting); gameMenuOptions->addMenuElement(bikeSpriteSetting); - gameMenuOptions->addMenuElement(inputSetting); + // gameMenuOptions->addMenuElement(inputSetting); gameMenuOptions->addMenuElement(lookAheadSetting); gameMenuOptions->addMenuElement(clearHighscoreSetting); gameMenuOptions->addMenuElement(settingStringBack); @@ -238,18 +157,18 @@ void MenuManager::initPart(int var1) addTextRender(field_317, "Race to the finish line as fast as you can without crashing. By leaning forward and backward you can adjust the rotation of your bike. By landing on both wheels after jumping, your bike won't crash as easily. Beware, the levels tend to get harder and harder..."); field_317->addMenuElement(settingStringBack); gameMenuHelp->addMenuElement(field_318); - field_319 = new GameMenu("Keys", micro, gameMenuHelp); - field_320 = new TimerOrMotoPartOrMenuElem("Keys", field_319, this); - addTextRender(field_319, "- " + field_375[0] + " -"); - addTextRender(field_319, "UP accelerates, DOWN brakes, RIGHT leans forward and LEFT leans backward. 1 accelerates and leans backward. 3 accelerates and leans forward. 7 brakes and leans backward. 9 brakes and leans forward."); - field_319->addMenuElement(field_376.get()); - addTextRender(field_319, "- " + field_375[1] + " -"); - addTextRender(field_319, "1 accelerates, 4 brakes, 6 leans forward and 5 leans backward."); - field_319->addMenuElement(field_376.get()); - addTextRender(field_319, "- " + field_375[2] + " -"); - addTextRender(field_319, "3 accelerates, 6 brakes, 5 leans forward and 4 leans backward."); - field_319->addMenuElement(settingStringBack); - gameMenuHelp->addMenuElement(field_320); + // field_319 = new GameMenu("Keys", micro, gameMenuHelp); + // field_320 = new TimerOrMotoPartOrMenuElem("Keys", field_319, this); + // addTextRender(field_319, "- " + inputLabels[0] + " -"); + // addTextRender(field_319, "UP accelerates, DOWN brakes, RIGHT leans forward and LEFT leans backward. 1 accelerates and leans backward. 3 accelerates and leans forward. 7 brakes and leans backward. 9 brakes and leans forward."); + // field_319->addMenuElement(field_376.get()); + // addTextRender(field_319, "- " + inputLabels[1] + " -"); + // addTextRender(field_319, "1 accelerates, 4 brakes, 6 leans forward and 5 leans backward."); + // field_319->addMenuElement(field_376.get()); + // addTextRender(field_319, "- " + inputLabels[2] + " -"); + // addTextRender(field_319, "3 accelerates, 6 brakes, 5 leans forward and 4 leans backward."); + // field_319->addMenuElement(settingStringBack); + // gameMenuHelp->addMenuElement(field_320); field_321 = new GameMenu("Unlocking", micro, gameMenuHelp); field_322 = new TimerOrMotoPartOrMenuElem("Unlocking", field_321, this); addTextRender(field_321, "By completing the easier levels, new levels will be unlocked. You will also gain access to higher leagues where more advanced bikes with different characteristics are available."); @@ -261,7 +180,8 @@ void MenuManager::initPart(int var1) gameMenuOptionsHighscoreDescription->addMenuElement(settingStringBack); gameMenuHelp->addMenuElement(taskHighscore); return; - case 7: + } + case 7: { gameMenuOptions2 = new GameMenu("Options", micro, gameMenuHelp); field_326 = new TimerOrMotoPartOrMenuElem("Options", gameMenuOptions2, this); @@ -276,9 +196,9 @@ void MenuManager::initPart(int var1) gameMenuOptions2->addMenuElement(field_376.get()); addTextRender(gameMenuOptions2, "Bike Sprite: On / Off"); addTextRender(gameMenuOptions2, "Default: . uses a texture for the bike. uses line graphics."); - gameMenuOptions2->addMenuElement(field_376.get()); - addTextRender(gameMenuOptions2, "Input: Keyset 1,2,3 "); - addTextRender(gameMenuOptions2, "Default: <1>. Determines which type of input should be used when playing. See \"Keys\" in the help menu for more info."); + // gameMenuOptions2->addMenuElement(field_376.get()); + // addTextRender(gameMenuOptions2, "Input: Keyset 1,2,3 "); + // addTextRender(gameMenuOptions2, "Default: <1>. Determines which type of input should be used when playing. See \"Keys\" in the help menu for more info."); gameMenuOptions2->addMenuElement(field_376.get()); addTextRender(gameMenuOptions2, "Look ahead: On/Off"); addTextRender(gameMenuOptions2, "Default: . Turns on and off smart camera movement."); @@ -301,13 +221,14 @@ void MenuManager::initPart(int var1) gameMenuIngame->addMenuElement(taskHelp); gameMenuIngame->addMenuElement(settingStringPlayMenu); field_335 = new SettingsStringRender("Ok", 0, this, std::vector(), false, micro, gameMenuMain, true); - field_336 = new SettingsStringRender("Name - " + std::string(field_341), 0, this, std::vector(), false, micro, gameMenuMain, true); - commandOk = new Command("Ok", 4, 1); - commandBack = new Command("Back", 2, 1); + field_336 = new SettingsStringRender("Name - " + std::string(settings.playerName, PLAYER_NAME_MAX), 0, this, std::vector(), false, micro, gameMenuMain, true); + commandOk = new Command("Ok", Command::Type::OK, 1); + commandBack = new Command("Back", Command::Type::BACK, 1); method_1(gameMenuMain, false); rasterImage = std::make_unique("raster.png"); - + return; + } default: break; } @@ -332,85 +253,77 @@ int MenuManager::getCurrentTrack() return settingsStringTrack->getCurrentOptionPos(); } -bool MenuManager::method_196() +bool MenuManager::isRestartNeeded() { - if (field_357) { - field_357 = false; + if (restartNeeded) { + restartNeeded = false; return true; - } else { - return false; } + + return false; } void MenuManager::method_197() { - recordManager->method_17(settingsStringLeague->getCurrentOptionPos(), field_341, field_337); - recordManager->writeRecordInfo(); - field_356 = false; + recordManager->loadRecordInfo(settingStringLevel->getCurrentOptionPos(), settingsStringTrack->getCurrentOptionPos()); + recordManager->addNewRecord(settingsStringLeague->getCurrentOptionPos(), settings.playerName, trackTimeMs); + recordManager->writeRecordInfo(settingStringLevel->getCurrentOptionPos(), settingsStringTrack->getCurrentOptionPos()); + + currentLevelFinished = false; gameMenuFinished->clearVector(); - gameMenuFinished->addMenuElement(new TextRender("Time: " + field_340, micro)); - std::vector var1 = recordManager->getRecordDescription(settingsStringLeague->getCurrentOptionPos()); + gameMenuFinished->addMenuElement(new TextRender("Time: " + trackTimeFormatted, micro)); + const std::array recordDescriptions = recordManager->getRecordDescription(settingsStringLeague->getCurrentOptionPos()); - for (std::size_t var2 = 0; var2 < var1.size(); ++var2) { - if (var1[var2] != "") { - gameMenuFinished->addMenuElement(new TextRender(std::to_string(var2 + 1) + "." + var1[var2], micro)); + for (uint8_t i = 0; i < recordDescriptions.size(); ++i) { + if (recordDescriptions[i].empty()) { + continue; } + + std::stringstream ss; + ss << (i + 1) << "." << recordDescriptions[i]; + gameMenuFinished->addMenuElement(new TextRender(ss.str(), micro)); } - recordManager->closeRecordStore(); int8_t availableLeagues = -1; + if (settingsStringTrack->getMaxAvailableOptionPos() >= settingsStringTrack->getCurrentOptionPos()) { - settingsStringTrack->setAvailableOptions(settingsStringTrack->getCurrentOptionPos() + 1 < field_342[settingStringLevel->getCurrentOptionPos()] ? field_342[settingStringLevel->getCurrentOptionPos()] : settingsStringTrack->getCurrentOptionPos() + 1); - field_342[settingStringLevel->getCurrentOptionPos()] = (int8_t)settingsStringTrack->getMaxAvailableOptionPos() < field_342[settingStringLevel->getCurrentOptionPos()] ? field_342[settingStringLevel->getCurrentOptionPos()] : (int8_t)settingsStringTrack->getMaxAvailableOptionPos(); + settingsStringTrack->setAvailableOptions(settingsStringTrack->getCurrentOptionPos() + 1 < settings.availableTracks[settingStringLevel->getCurrentOptionPos()] ? settings.availableTracks[settingStringLevel->getCurrentOptionPos()] : settingsStringTrack->getCurrentOptionPos() + 1); + settings.availableTracks[settingStringLevel->getCurrentOptionPos()] = (int8_t)settingsStringTrack->getMaxAvailableOptionPos() < settings.availableTracks[settingStringLevel->getCurrentOptionPos()] ? settings.availableTracks[settingStringLevel->getCurrentOptionPos()] : (int8_t)settingsStringTrack->getMaxAvailableOptionPos(); } if (settingsStringTrack->getCurrentOptionPos() == settingsStringTrack->getMaxOptionPos()) { - field_356 = true; - switch (settingStringLevel->getCurrentOptionPos()) { - case 0: - if (availableLeagues < 1) { - availableLeagues = 1; - availableLeagues = 1; - settingsStringLeague->setAvailableOptions(availableLeagues); - } - break; - case 1: - if (availableLeagues < 2) { - availableLeagues = 2; - availableLeagues = 2; - settingsStringLeague->setAvailableOptions(availableLeagues); - } - break; - case 2: - if (availableLeagues < 3) { - availableLeagues = 3; - availableLeagues = 3; - settingsStringLeague->setOptionsList(leagueNamesAll4); - leagueNames = leagueNamesAll4; - settingsStringLeague->setAvailableOptions(availableLeagues); - } + currentLevelFinished = true; + + availableLeagues = settingStringLevel->getCurrentOptionPos() + 1; + + if (availableLeagues == 3) { + settingsStringLeague->setOptionsList(leagueNamesAll4); + leagueNames = leagueNamesAll4; } + settingsStringLeague->setAvailableOptions(availableLeagues); settingStringLevel->setAvailableOptions(settingStringLevel->getMaxAvailableOptionPos() + 1); - if (field_342[settingStringLevel->getMaxAvailableOptionPos()] == -1) { - field_342[settingStringLevel->getMaxAvailableOptionPos()] = 0; + + if (settings.availableTracks[settingStringLevel->getMaxAvailableOptionPos()] == -1) { + settings.availableTracks[settingStringLevel->getMaxAvailableOptionPos()] = 0; } } - int var3 = getCountOfRecordStoresWithPrefix(settingStringLevel->getCurrentOptionPos()); - addTextRender(gameMenuFinished, var3 + " of " + std::to_string(levelNames[settingStringLevel->getCurrentOptionPos()].size()) + " tracks in " + field_361[settingStringLevel->getCurrentOptionPos()] + " completed."); - if (!field_356) { + const uint32_t numberOfRecords = RecordManager::getNumberOfRecordsForLevel(settingStringLevel->getCurrentOptionPos()); + addTextRender(gameMenuFinished, std::to_string(numberOfRecords) + " of " + std::to_string(micro->levelLoader->getTracksCount(settingStringLevel->getCurrentOptionPos())) + " tracks in " + levelLabels[settingStringLevel->getCurrentOptionPos()] + " completed."); + + if (!currentLevelFinished) { field_333->setText("Restart: " + micro->levelLoader->getName(settingStringLevel->getCurrentOptionPos(), settingsStringTrack->getCurrentOptionPos())); field_334->setText("Next: " + micro->levelLoader->getName(field_354, field_355 + 1)); } else { if (settingStringLevel->getCurrentOptionPos() < settingStringLevel->getMaxOptionPos()) { settingStringLevel->setCurentOptionPos(settingStringLevel->getCurrentOptionPos() + 1); settingsStringTrack->setCurentOptionPos(0); - settingsStringTrack->setAvailableOptions(field_342[settingStringLevel->getCurrentOptionPos()]); + settingsStringTrack->setAvailableOptions(settings.availableTracks[settingStringLevel->getCurrentOptionPos()]); } if (availableLeagues != -1) { - addTextRender(gameMenuFinished, "Congratultions! You have successfully unlocked a new league: " + leagueNames[availableLeagues]); + addTextRender(gameMenuFinished, "Congratulations! You have successfully unlocked a new league: " + leagueNames[availableLeagues]); if (availableLeagues == 3) { gameMenuFinished->addMenuElement(new TextRender("Enjoy...", micro)); } @@ -420,7 +333,7 @@ void MenuManager::method_197() bool var4 = true; for (int var5 = 0; var5 < 3; ++var5) { - if (field_342[var5] != static_cast(micro->levelLoader->levelNames[var5].size() - 1)) { + if (settings.availableTracks[var5] != micro->levelLoader->getTracksCount(var5)) { var4 = false; } } @@ -431,7 +344,7 @@ void MenuManager::method_197() } } - if (!field_356) { + if (!currentLevelFinished) { gameMenuFinished->addMenuElement(field_334); } @@ -463,24 +376,24 @@ void MenuManager::method_201(int var1) case 0: method_1(gameMenuMain, false); micro->gamePhysics->enableGenerateInputAI(); - field_357 = true; + restartNeeded = true; break; case 1: field_354 = settingStringLevel->getCurrentOptionPos(); field_355 = settingsStringTrack->getCurrentOptionPos(); field_333->setText("Restart: " + micro->levelLoader->getName(field_354, field_355)); - field_357 = false; + restartNeeded = false; method_1(gameMenuIngame, false); break; case 2: { - field_362 = Time::currentTimeMillis(); gameMenuFinished->clearVector(); field_354 = settingStringLevel->getCurrentOptionPos(); field_355 = settingsStringTrack->getCurrentOptionPos(); - recordManager->method_8(settingStringLevel->getCurrentOptionPos(), settingsStringTrack->getCurrentOptionPos()); - int var2 = recordManager->getPosOfNewRecord(settingsStringLeague->getCurrentOptionPos(), field_337); - field_340 = timeToString(field_337); - if (var2 >= 0 && var2 <= 2) { + recordManager->loadRecordInfo(settingStringLevel->getCurrentOptionPos(), settingsStringTrack->getCurrentOptionPos()); + const uint8_t var2 = recordManager->getPosOfNewRecord(settingsStringLeague->getCurrentOptionPos(), trackTimeMs); + trackTimeFormatted = Time::timeToString(trackTimeMs); + + if (var2 < RECORD_NO_MAX) { TextRender* var3 = new TextRender("", micro); var3->setDx(GameCanvas::spriteSizeX[5] + 1); switch (var2) { @@ -498,7 +411,7 @@ void MenuManager::method_201(int var1) } gameMenuFinished->addMenuElement(var3); - TextRender* var4 = new TextRender("" + field_340, micro); + TextRender* var4 = new TextRender(trackTimeFormatted, micro); var4->setDx(GameCanvas::spriteSizeX[5] + 1); gameMenuFinished->addMenuElement(var4); gameMenuFinished->addMenuElement(field_335); @@ -521,7 +434,7 @@ void MenuManager::method_201(int var1) micro->gamePhysics->method_53(); micro->gameToMenu(); - while (Micro::isInGameMenu && Micro::field_249 && currentGameMenu != nullptr) { + while (Micro::isInGameMenu && Micro::gameStarted && currentGameMenu != nullptr) { int64_t var20; if (micro->gamePhysics->isGenerateInputAI) { int var9; @@ -570,7 +483,7 @@ void MenuManager::method_201(int var1) micro->timeMs += Time::currentTimeMillis() - currentTimeMillis; micro->gameCanvas->isDrawingTime = true; if (currentGameMenu == nullptr) { - Micro::field_249 = false; + Micro::gameStarted = false; } } @@ -587,60 +500,66 @@ void MenuManager::fillCanvasWithImage(Graphics* graphics) { for (int y = 0; y < getCanvasHeight(); y += rasterImage->getHeight()) { for (int x = 0; x < getCanvasWidth(); x += rasterImage->getWidth()) { - graphics->drawImage(rasterImage.get(), x, y, 20); + graphics->drawImage( + rasterImage.get(), + x, + y, + Graphics::TOP | Graphics::LEFT); } } } -void MenuManager::processNonFireKeyCode(int keyCode) +void MenuManager::processKeyCode(const Keys keyCode) { - if (micro->gameCanvas->getGameAction(keyCode) != 8) { - // if not fire - processKeyCode(keyCode); + if (currentGameMenu == nullptr) { + return; } -} -void MenuManager::processKeyCode(int keyCode) -{ - if (currentGameMenu != nullptr) { - switch (micro->gameCanvas->getGameAction(keyCode)) { - case 1: // UP - currentGameMenu->processGameActionUp(); - return; - case 2: // LEFT - currentGameMenu->processGameActionUpd(3); - if (currentGameMenu == gameMenuHighscore) { - --field_360; - if (field_360 < 0) { - field_360 = 0; - } + switch (keyCode) { + case Keys::UP: { + currentGameMenu->processGameActionUp(); + return; + } + case Keys::LEFT: { + currentGameMenu->processGameActionUpd(3); - method_207(field_360); + if (currentGameMenu == gameMenuHighscore) { + --field_360; + if (field_360 < 0) { + field_360 = 0; } - case 3: - case 4: - case 7: - default: - break; - case 5: // RIGHT - currentGameMenu->processGameActionUpd(2); - if (currentGameMenu == gameMenuHighscore) { - ++field_360; - if (field_360 > settingsStringLeague->getMaxAvailableOptionPos()) { - field_360 = settingsStringLeague->getMaxAvailableOptionPos(); - } - method_207(field_360); - return; + method_207(field_360); + } + + return; + } + case Keys::RIGHT: { + currentGameMenu->processGameActionUpd(2); + + if (currentGameMenu == gameMenuHighscore) { + ++field_360; + if (field_360 > settingsStringLeague->getMaxAvailableOptionPos()) { + field_360 = settingsStringLeague->getMaxAvailableOptionPos(); } - break; - case 6: // DOWN - currentGameMenu->processGameActionDown(); - return; - case 8: // FIRE - currentGameMenu->processGameActionUpd(1); + + method_207(field_360); return; } + + return; + } + case Keys::DOWN: { + currentGameMenu->processGameActionDown(); + return; + } + case Keys::FIRE: { + currentGameMenu->processGameActionUpd(1); + return; + } + default: { + return; + } } } @@ -670,6 +589,7 @@ GameMenu* MenuManager::getGameMenu() void MenuManager::method_1(GameMenu* gm, bool var2) { micro->gameCanvas->removeCommand(commandBack); + if (gm != gameMenuMain && gm != gameMenuFinished && gm != nullptr) { micro->gameCanvas->addCommand(commandBack); } @@ -678,15 +598,16 @@ void MenuManager::method_1(GameMenu* gm, bool var2) field_360 = settingsStringLeague->getCurrentOptionPos(); method_207(field_360); } else if (gm == gameMenuFinished) { - field_341 = gameMenuEnterName->getStrArr(); - field_336->setText("Name - " + std::string(field_341)); + strncpy(settings.playerName, gameMenuEnterName->getStrArr(), PLAYER_NAME_MAX); + field_336->setText("Name - " + std::string(settings.playerName, PLAYER_NAME_MAX)); } else if (gm == gameMenuPlay) { - settingsStringTrack->setOptionsList(micro->levelLoader->levelNames[settingStringLevel->getCurrentOptionPos()]); + settingsStringTrack->setOptionsList(micro->levelLoader->GetTrackNames(settingStringLevel->getCurrentOptionPos())); + if (currentGameMenu == field_299) { field_345[settingStringLevel->getCurrentOptionPos()] = settingsStringTrack->getCurrentOptionPos(); } - settingsStringTrack->setAvailableOptions(field_342[settingStringLevel->getCurrentOptionPos()]); + settingsStringTrack->setAvailableOptions(settings.availableTracks[settingStringLevel->getCurrentOptionPos()]); settingsStringTrack->setCurentOptionPos(field_345[settingStringLevel->getCurrentOptionPos()]); } @@ -695,8 +616,9 @@ void MenuManager::method_1(GameMenu* gm, bool var2) } currentGameMenu = gm; + if (currentGameMenu != nullptr && !var2) { - currentGameMenu->method_70(); + currentGameMenu->rolloverMenuToBottom(); } field_377 = false; @@ -705,29 +627,25 @@ void MenuManager::method_1(GameMenu* gm, bool var2) void MenuManager::method_207(int var1) { gameMenuHighscore->clearVector(); - recordManager->method_8(settingStringLevel->getCurrentOptionPos(), settingsStringTrack->getCurrentOptionPos()); + recordManager->loadRecordInfo(settingStringLevel->getCurrentOptionPos(), settingsStringTrack->getCurrentOptionPos()); gameMenuHighscore->addMenuElement(new TextRender(micro->levelLoader->getName(settingStringLevel->getCurrentOptionPos(), settingsStringTrack->getCurrentOptionPos()), micro)); gameMenuHighscore->addMenuElement(new TextRender("LEAGUE: " + settingsStringLeague->getOptionsList()[var1], micro)); - std::vector var2 = recordManager->getRecordDescription(var1); + const std::array recordDescription = recordManager->getRecordDescription(var1); - for (std::size_t var3 = 0; var3 < var2.size(); ++var3) { - if (var2[var3] != "") { - TextRender* var4 = new TextRender(std::to_string(var3 + 1) + "." + var2[var3], micro); - var4->setDx(GameCanvas::spriteSizeX[5] + 1); - if (var3 == 0) { - var4->setDrawSprite(true, 5); - } else if (var3 == 1) { - var4->setDrawSprite(true, 6); - } else if (var3 == 2) { - var4->setDrawSprite(true, 7); - } - - gameMenuHighscore->addMenuElement(var4); + for (uint8_t i = 0; i < recordDescription.size(); ++i) { + if (recordDescription[i].empty()) { + continue; } + + std::stringstream ss; + ss << (i + 1) << '.' << recordDescription[i]; + TextRender* var4 = new TextRender(ss.str(), micro); + var4->setDx(GameCanvas::spriteSizeX[5] + 1); + var4->setDrawSprite(true, 5 + i); + gameMenuHighscore->addMenuElement(var4); } - recordManager->closeRecordStore(); - if (var2[0] == "") { + if (recordDescription[0].empty()) { gameMenuHighscore->addMenuElement(new TextRender("No Highscores", micro)); } @@ -736,52 +654,31 @@ void MenuManager::method_207(int var1) void MenuManager::saveSmthToRecordStoreAndCloseIt() { - if (isRecordStoreOpened) { - method_208(); - - try { - recordStore->closeRecordStore(); - isRecordStoreOpened = false; - } catch (RecordStoreException& var1) { - } - } - + method_208(); currentGameMenu = nullptr; } void MenuManager::method_208() { - copyThreeBytesFromArr(16, field_341); - - setValue(0, (int8_t)perspectiveSetting->getCurrentOptionPos()); - setValue(1, (int8_t)shadowsSetting->getCurrentOptionPos()); - setValue(2, (int8_t)driverSpriteSetting->getCurrentOptionPos()); - setValue(3, (int8_t)bikeSpriteSetting->getCurrentOptionPos()); - setValue(14, (int8_t)inputSetting->getCurrentOptionPos()); - setValue(4, (int8_t)lookAheadSetting->getCurrentOptionPos()); - setValue(5, (int8_t)settingsStringLeague->getMaxAvailableOptionPos()); - setValue(6, (int8_t)settingStringLevel->getMaxAvailableOptionPos()); - setValue(10, (int8_t)settingStringLevel->getCurrentOptionPos()); - setValue(11, (int8_t)settingsStringTrack->getCurrentOptionPos()); - setValue(12, (int8_t)settingsStringLeague->getCurrentOptionPos()); - - for (int i = 0; i < 3; ++i) { - setValue(7 + i, field_342[i]); - } - - if (recorcStoreRecordId == -1) { - try { - recorcStoreRecordId = recordStore->addRecord(field_278, 0, 19); - } catch (RecordStoreNotOpenException& var2) { - } catch (RecordStoreException& var3) { - } - } else { - try { - recordStore->setRecord(recorcStoreRecordId, field_278, 0, 19); - } catch (RecordStoreNotOpenException& var4) { - } catch (RecordStoreException& var5) { - } - } + settings.perspective = (int8_t)perspectiveSetting->getCurrentOptionPos(); + settings.shadows = (int8_t)shadowsSetting->getCurrentOptionPos(); + settings.driverSprite = (int8_t)driverSpriteSetting->getCurrentOptionPos(); + settings.bikeSprite = (int8_t)bikeSpriteSetting->getCurrentOptionPos(); + settings.lookAhead = (int8_t)lookAheadSetting->getCurrentOptionPos(); + settings.availableLeagues = (int8_t)settingsStringLeague->getMaxAvailableOptionPos(); + settings.availableLevels = (int8_t)settingStringLevel->getMaxAvailableOptionPos(); + // settings.availableEasyTracks = (int8_t)availableTracks[0]; + // settings.availableMediumTracks = (int8_t)availableTracks[1]; + // settings.availableHardTracks = (int8_t)availableTracks[2]; + settings.selectedLevel = (int8_t)settingStringLevel->getCurrentOptionPos(); + settings.selectedTrack = (int8_t)settingsStringTrack->getCurrentOptionPos(); + settings.selectedLeague = (int8_t)settingsStringLeague->getCurrentOptionPos(); + settings.unknown2 = -127; + // settings.input = (int8_t)inputSetting->getCurrentOptionPos(); + settings.input = 0; + settings.unknown3 = -127; + + SettingsManager::saveSettings(settings); } void MenuManager::run() @@ -808,9 +705,9 @@ void MenuManager::processMenu(IGameMenuElement* menuElement) if (menuElement == taskStart) { if (settingStringLevel->getCurrentOptionPos() <= settingStringLevel->getMaxAvailableOptionPos() && settingsStringTrack->getCurrentOptionPos() <= settingsStringTrack->getMaxAvailableOptionPos() && settingsStringLeague->getCurrentOptionPos() <= settingsStringLeague->getMaxAvailableOptionPos()) { micro->gamePhysics->disableGenerateInputAI(); - micro->levelLoader->method_88(settingStringLevel->getCurrentOptionPos(), settingsStringTrack->getCurrentOptionPos()); + micro->levelLoader->loadTrack(settingStringLevel->getCurrentOptionPos(), settingsStringTrack->getCurrentOptionPos()); micro->gamePhysics->setMotoLeague(settingsStringLeague->getCurrentOptionPos()); - field_357 = true; + restartNeeded = true; micro->menuToGame(); } else { showAlert("GDTR", "Complete more tracks to unlock this track/league combo.", nullptr); @@ -832,14 +729,14 @@ void MenuManager::processMenu(IGameMenuElement* menuElement) return; } } else { - if (menuElement == inputSetting) { - if (inputSetting->method_114()) { - inputSetting->setCurentOptionPos(inputSetting->getCurrentOptionPos() + 1); - } + // if (menuElement == inputSetting) { + // if (inputSetting->method_114()) { + // inputSetting->setCurentOptionPos(inputSetting->getCurrentOptionPos() + 1); + // } - micro->gameCanvas->method_163(inputSetting->getCurrentOptionPos()); - return; - } + // micro->gameCanvas->method_163(inputSetting->getCurrentOptionPos()); + // return; + // } if (menuElement == lookAheadSetting) { micro->gamePhysics->setEnableLookAhead(lookAheadSetting->getCurrentOptionPos() == 0); @@ -871,7 +768,7 @@ void MenuManager::processMenu(IGameMenuElement* menuElement) if (menuElement == settingStringPlayMenu) { settingStringLevel->setCurentOptionPos(field_354); - settingsStringTrack->setAvailableOptions(field_342[field_354]); + settingsStringTrack->setAvailableOptions(settings.availableTracks[field_354]); settingsStringTrack->setCurentOptionPos(field_355); method_1(currentGameMenu->getGameMenu(), false); return; @@ -890,23 +787,23 @@ void MenuManager::processMenu(IGameMenuElement* menuElement) if (menuElement == field_333) { if (settingsStringLeague->getCurrentOptionPos() <= settingsStringLeague->getMaxAvailableOptionPos()) { settingStringLevel->setCurentOptionPos(field_354); - settingsStringTrack->setAvailableOptions(field_342[field_354]); + settingsStringTrack->setAvailableOptions(settings.availableTracks[field_354]); settingsStringTrack->setCurentOptionPos(field_355); micro->gamePhysics->setMotoLeague(settingsStringLeague->getCurrentOptionPos()); - field_357 = true; + restartNeeded = true; micro->menuToGame(); return; } } else { if (menuElement == field_334) { - if (!field_356) { + if (!currentLevelFinished) { settingsStringTrack->menuElemMethod(2); } - micro->levelLoader->method_88(settingStringLevel->getCurrentOptionPos(), settingsStringTrack->getCurrentOptionPos()); + micro->levelLoader->loadTrack(settingStringLevel->getCurrentOptionPos(), settingsStringTrack->getCurrentOptionPos()); micro->gamePhysics->setMotoLeague(settingsStringLeague->getCurrentOptionPos()); method_208(); - field_357 = true; + restartNeeded = true; micro->menuToGame(); return; } @@ -918,7 +815,7 @@ void MenuManager::processMenu(IGameMenuElement* menuElement) } if (menuElement == field_336) { - gameMenuEnterName->method_70(); + gameMenuEnterName->rolloverMenuToBottom(); method_1(gameMenuEnterName, false); return; } @@ -930,11 +827,11 @@ void MenuManager::processMenu(IGameMenuElement* menuElement) if (menuElement == settingsStringTrack) { if (settingsStringTrack->method_114()) { - settingsStringTrack->setAvailableOptions(field_342[settingStringLevel->getCurrentOptionPos()]); + settingsStringTrack->setAvailableOptions(settings.availableTracks[settingStringLevel->getCurrentOptionPos()]); settingsStringTrack->init(); field_299 = settingsStringTrack->getGameMenu(); method_1(field_299, false); - field_299->method_83(settingsStringTrack->getCurrentOptionPos()); + field_299->startAtPosition(settingsStringTrack->getCurrentOptionPos()); } field_345[settingStringLevel->getCurrentOptionPos()] = settingsStringTrack->getCurrentOptionPos(); @@ -945,11 +842,11 @@ void MenuManager::processMenu(IGameMenuElement* menuElement) if (settingStringLevel->method_114()) { gameMenuStringLevel = settingStringLevel->getGameMenu(); method_1(gameMenuStringLevel, false); - gameMenuStringLevel->method_83(settingStringLevel->getCurrentOptionPos()); + gameMenuStringLevel->startAtPosition(settingStringLevel->getCurrentOptionPos()); } - settingsStringTrack->setOptionsList(micro->levelLoader->levelNames[settingStringLevel->getCurrentOptionPos()]); - settingsStringTrack->setAvailableOptions(field_342[settingStringLevel->getCurrentOptionPos()]); + settingsStringTrack->setOptionsList(micro->levelLoader->GetTrackNames(settingStringLevel->getCurrentOptionPos())); + settingsStringTrack->setAvailableOptions(settings.availableTracks[settingStringLevel->getCurrentOptionPos()]); settingsStringTrack->setCurentOptionPos(field_345[settingStringLevel->getCurrentOptionPos()]); settingsStringTrack->init(); return; @@ -959,7 +856,7 @@ void MenuManager::processMenu(IGameMenuElement* menuElement) gameMenuLeague = settingsStringLeague->getGameMenu(); settingsStringLeague->setParentGameMenu(currentGameMenu); method_1(gameMenuLeague, false); - gameMenuLeague->method_83(settingsStringLeague->getCurrentOptionPos()); + gameMenuLeague->startAtPosition(settingsStringLeague->getCurrentOptionPos()); } } } @@ -969,6 +866,7 @@ void MenuManager::processMenu(IGameMenuElement* menuElement) int MenuManager::method_210() { int var1 = 0; + if (driverSpriteSetting->getCurrentOptionPos() == 0) { var1 |= 2; } @@ -984,6 +882,7 @@ void MenuManager::method_211(int var1) { bikeSpriteSetting->setCurentOptionPos(1); driverSpriteSetting->setCurentOptionPos(1); + if ((var1 & 1) > 0) { bikeSpriteSetting->setCurentOptionPos(0); } @@ -993,91 +892,9 @@ void MenuManager::method_211(int var1) } } -int MenuManager::method_212() -{ - return settingStringLevel->getCurrentOptionPos(); -} - -int MenuManager::method_213() -{ - return settingsStringTrack->getCurrentOptionPos(); -} - -int MenuManager::method_214() -{ - return settingsStringLeague->getCurrentOptionPos(); -} - -void MenuManager::method_215(int64_t var1) -{ - field_337 = var1; -} - -std::vector MenuManager::method_216(int var1, int8_t var2) -{ - switch (var1) { - case 16: { - std::vector var3 = std::vector(3); - - for (int var4 = 0; var4 < 3; ++var4) { - var3[var4] = field_278[16 + var4]; - } - - if (var3[0] == -127) { - var3[0] = var2; - } - return var3; - } - default: - return std::vector(); - } -} - -int8_t MenuManager::method_217(int var1, int8_t var2) -{ - return field_278[var1] == -127 ? var2 : field_278[var1]; -} - -void MenuManager::copyThreeBytesFromArr(int var1, char* var2) -{ - if (isRecordStoreOpened && var1 == 16) { - for (int i = 0; i < 3; ++i) { - field_278[16 + i] = var2[i]; - } - } -} - -std::string MenuManager::timeToString(int64_t time) -{ - field_338 = (int)(time / 100L); - field_339 = (int)(time % 100L); - std::string timeStr; - if (field_338 / 60 < 10) { - timeStr = " 0" + std::to_string(field_338 / 60); - } else { - timeStr = " " + std::to_string(field_338 / 60); - } - - if (field_338 % 60 < 10) { - timeStr = timeStr + ":0" + std::to_string(field_338 % 60); - } else { - timeStr = timeStr + ":" + std::to_string(field_338 % 60); - } - - if (field_339 < 10) { - timeStr = timeStr + ".0" + std::to_string(field_339); - } else { - timeStr = timeStr + "." + std::to_string(field_339); - } - - return timeStr; -} - -void MenuManager::setValue(int pos, int8_t value) +void MenuManager::setGameTimeMs(const uint64_t timeMs) { - if (isRecordStoreOpened) { - field_278[pos] = value; - } + trackTimeMs = timeMs; } void MenuManager::exit() @@ -1093,14 +910,13 @@ void MenuManager::exit() settingStringLevel->setAvailableOptions(1); settingsStringTrack->setCurentOptionPos(0); - field_341[0] = 65; - field_341[1] = 65; - field_341[2] = 65; - inputSetting->setCurentOptionPos(0); - field_342[0] = 0; - field_342[1] = 0; - field_342[2] = -1; - availableLeagues = 0; + // playerName = defaultName; + // availableTracks[0] = 0; + // availableTracks[1] = 0; + // availableTracks[2] = -1; + // availableLeagues = 0; + + // inputSetting->setCurentOptionPos(0); method_208(); recordManager->deleteRecordStores(); } @@ -1119,21 +935,3 @@ void MenuManager::addOkAndBackCommands() micro->gameCanvas->addCommand(commandOk); } - -int MenuManager::getCountOfRecordStoresWithPrefix(int prefixNumber) -{ - std::vector storeNames = RecordStore::listRecordStores(); - if (recordManager != nullptr && !storeNames.empty()) { - int count = 0; - - for (std::size_t i = 0; i < storeNames.size(); ++i) { - if (storeNames[i].find(std::to_string(prefixNumber), 0) == 0) { - ++count; - } - } - - return count; - } else { - return 0; - } -} diff --git a/src/MenuManager.h b/src/MenuManager.h index 45fae25..c5fc460 100644 --- a/src/MenuManager.h +++ b/src/MenuManager.h @@ -4,26 +4,24 @@ #include #include +#include "lcdui/FontStorage.h" +#include "TextRender.h" +#include "RecordManager.h" +#include "Micro.h" +#include "LevelLoader.h" +#include "GameMenu.h" +#include "SettingsStringRender.h" +#include "utils/Time.h" +#include "config.h" #include "IMenuManager.h" - -class Micro; -class RecordManager; -class Command; -class GameMenu; -class TimerOrMotoPartOrMenuElem; -class SettingsStringRender; -class RecordStore; -class Image; -class TextRender; -class Graphics; -class Displayable; -class IGameMenuElement; +#include "SettingsManager.h" class MenuManager : public IMenuManager { private: - std::vector field_278; + SettingsManager::Settings settings; + Micro* micro; - RecordManager* recordManager; + std::unique_ptr recordManager; Command* commandOk; Command* commandBack; GameMenu* gameMenuMain; @@ -51,7 +49,7 @@ class MenuManager : public IMenuManager { SettingsStringRender* shadowsSetting; SettingsStringRender* driverSpriteSetting; SettingsStringRender* bikeSpriteSetting; - SettingsStringRender* inputSetting; + // SettingsStringRender* inputSetting; SettingsStringRender* lookAheadSetting; TimerOrMotoPartOrMenuElem* clearHighscoreSetting; TimerOrMotoPartOrMenuElem* field_313; @@ -78,57 +76,26 @@ class MenuManager : public IMenuManager { SettingsStringRender* field_334; SettingsStringRender* field_335; SettingsStringRender* field_336; - int64_t field_337; - int field_338; - int field_339; - std::string field_340; - char* field_341; - char field_342[4]; - char defaultInputString[4] = "AAA"; - int8_t availableLeagues = 0; - int8_t field_344 = 0; + uint64_t trackTimeMs; + std::string trackTimeFormatted; std::vector field_345 = { 0, 0, 0 }; - std::vector> levelNames; std::vector leagueNames = std::vector(3); std::vector leagueNamesAll4; - RecordStore* recordStore; - int recorcStoreRecordId = -1; - bool isRecordStoreOpened; std::unique_ptr rasterImage; TextRender* textRenderCodeBrewLink; int field_354 = 0; int field_355 = 0; - bool field_356 = false; - bool field_357 = false; - std::vector field_361 = { "Easy", "Medium", "Pro" }; - int64_t field_362 = 0L; - int8_t isDisablePerspective = 0; - int8_t isDisabledShadows = 0; - int8_t isDisabledDriverSprite = 0; - int8_t isDisabledBikeSprite = 0; - int8_t field_367 = 0; - int8_t isDisableLookAhead = 0; - int8_t field_369 = 0; - int8_t field_370 = 0; - int8_t field_371 = 0; + bool currentLevelFinished = false; + bool restartNeeded = false; int8_t field_372 = 0; - int8_t field_373 = 0; - std::vector field_374; - std::vector field_375; std::unique_ptr field_376; + // Alert alert = nullptr; // TODO void addTextRender(GameMenu* gameMenu, std::string text); void method_197(); void fillCanvasWithImage(Graphics* graphics); - void processNonFireKeyCode(int keyCode); - std::vector method_216(int var1, int8_t var2); - int8_t method_217(int var1, int8_t var2); - void copyThreeBytesFromArr(int var1, char* var2); - std::string timeToString(int64_t time); - void setValue(int pos, int8_t value); void exit(); - int getCountOfRecordStoresWithPrefix(int prefixNumber); public: GameMenu* currentGameMenu; @@ -139,7 +106,7 @@ class MenuManager : public IMenuManager { void initPart(int var1); int getCurrentLevel(); int getCurrentTrack(); - bool method_196(); + bool isRestartNeeded(); void repaint(); int getCanvasHeight(); int getCanvasWidth(); @@ -155,12 +122,9 @@ class MenuManager : public IMenuManager { void processMenu(IGameMenuElement* menuElement); int method_210(); void method_211(int var1); - int method_212(); - int method_213(); - int method_214(); - void method_215(int64_t var1); + void setGameTimeMs(const uint64_t var1); void removeOkAndBackCommands(); void addOkAndBackCommands(); /* synchronized */ void method_202(Graphics* var1); - void processKeyCode(int keyCode); + void processKeyCode(const Keys keyCode); }; diff --git a/src/Micro.cpp b/src/Micro.cpp index 13f1bee..29f65a4 100644 --- a/src/Micro.cpp +++ b/src/Micro.cpp @@ -6,19 +6,15 @@ #include "LevelLoader.h" #include "utils/Time.h" #include "lcdui/CanvasImpl.h" -#include "rms/RecordStore.h" +#include "config.h" -bool Micro::field_249 = false; +bool Micro::gameStarted = false; int Micro::gameLoadingStateStage = 0; Micro::Micro() { } -Micro::~Micro() -{ -} - void Micro::setNumPhysicsLoops(int value) { numPhysicsLoops = value; @@ -45,7 +41,7 @@ int64_t Micro::goLoadingStep() int64_t startTimeMillis = Time::currentTimeMillis(); switch (gameLoadingStateStage) { case 1: - levelLoader = new LevelLoader(mrgFilePath); + levelLoader = new LevelLoader(GlobalSetting::MrgFilePath); break; case 2: gamePhysics = new GamePhysics(levelLoader); @@ -138,27 +134,15 @@ void Micro::restart(bool var1) void Micro::destroyApp(bool var1) { (void)var1; - field_249 = false; - field_242 = true; + gameStarted = false; + gameDestroyed = true; menuManager->saveSmthToRecordStoreAndCloseIt(); } -void Micro::startApp(int argc, char** argv) +void Micro::startApp() { - if (argc > 1) { - std::string argv1(argv[1]); - - if (argv1 == "-h" || argv1 == "--help") { - showHelp(argv[0]); - return; - } - - this->mrgFilePath = argv1; - } - - RecordStore::setRecordStoreDir(argv[0]); - - field_249 = true; + SettingsManager::initSettings(); + gameStarted = true; // if (thread == null) { // thread = new Thread(this); // thread.start(); @@ -176,14 +160,16 @@ void Micro::run() gameCanvas->setCommandListener(gameCanvas); restart(false); menuManager->method_201(0); - if (menuManager->method_196()) { + + if (menuManager->isRestartNeeded()) { restart(true); } int64_t var3 = 0L; - while (field_249) { + while (gameStarted) { int var5; + if (gamePhysics->method_21() != menuManager->method_210()) { var5 = gameCanvas->loadSprites(menuManager->method_210()); gamePhysics->method_22(var5); @@ -194,7 +180,8 @@ void Micro::run() try { if (isInGameMenu) { menuManager->method_201(1); - if (menuManager->method_196()) { + + if (menuManager->isRestartNeeded()) { restart(true); } } @@ -236,6 +223,7 @@ void Micro::run() // } catch (InterruptedException var12) { // } int64_t var7 = 1000L; + if (field_246 > 0L) { var7 = std::min(field_246 - Time::currentTimeMillis(), static_cast(1000)); } @@ -254,13 +242,14 @@ void Micro::run() } goalLoop(); - menuManager->method_215(gameTimeMs / 10L); + menuManager->setGameTimeMs(gameTimeMs / 10L); menuManager->method_201(2); - if (menuManager->method_196()) { + + if (menuManager->isRestartNeeded()) { restart(true); } - if (!field_249) { + if (!gameStarted) { break; } } @@ -268,7 +257,7 @@ void Micro::run() field_248 = var5 != 4; } - var10000 = field_249; + var10000 = gameStarted; } catch (std::exception& var15) { continue; } @@ -280,6 +269,7 @@ void Micro::run() try { gamePhysics->method_53(); int64_t var1; + if ((var1 = Time::currentTimeMillis()) - var3 < 30L) { // try { // synchronized (this) { @@ -305,6 +295,7 @@ void Micro::run() void Micro::goalLoop() { int64_t var4 = 0L; + if (!gamePhysics->field_69) { gameCanvas->scheduleGameTimerTask("Wheelie!", 1000); } else { @@ -330,6 +321,7 @@ void Micro::goalLoop() // return; // } int64_t deltaTime; + if ((deltaTime = timeMs - Time::currentTimeMillis()) > 0L) { Time::sleep(deltaTime); } @@ -340,6 +332,7 @@ void Micro::goalLoop() gamePhysics->method_53(); int64_t var2; + if ((var2 = Time::currentTimeMillis()) - var4 < 30L) { // try { // synchronized (this) { @@ -359,13 +352,4 @@ void Micro::goalLoop() void Micro::setMode(int mode) { gamePhysics->setMode(mode); -} - -void Micro::showHelp(const char* progName) -{ - std::cout << "Usage: " << progName << " " << std::endl - << "Example:" << std::endl - << " " << progName << " levels.mrg # A path to a custom levels file could be specified" << std::endl - << " " << progName << " # When no path is specified, the built-in levels file will be used" << std::endl - << std::endl; } \ No newline at end of file diff --git a/src/Micro.h b/src/Micro.h index 073617c..a89901b 100644 --- a/src/Micro.h +++ b/src/Micro.h @@ -12,28 +12,27 @@ class Micro { private: int64_t goLoadingStep(); void destroyApp(bool var1); - std::filesystem::path mrgFilePath; public: - GameCanvas* gameCanvas; - LevelLoader* levelLoader; - GamePhysics* gamePhysics; - MenuManager* menuManager; - bool field_242 = false; + inline static bool isInGameMenu; + static bool gameStarted; + static int gameLoadingStateStage; + + bool gameDestroyed = false; + bool isInited = false; + bool field_248 = false; int numPhysicsLoops = 2; int64_t timeMs = 0; int64_t gameTimeMs = 0; int64_t field_246 = 0; - bool isInited = false; - bool field_248 = false; - static bool field_249; - inline static bool isInGameMenu; - static int gameLoadingStateStage; + GameCanvas* gameCanvas; + LevelLoader* levelLoader; + GamePhysics* gamePhysics; + MenuManager* menuManager; Micro(); - ~Micro(); - void startApp(int argc, char** argv); + void startApp(); void gameToMenu(); void menuToGame(); diff --git a/src/RecordManager.cpp b/src/RecordManager.cpp index 3a4f57d..0a5b93a 100644 --- a/src/RecordManager.cpp +++ b/src/RecordManager.cpp @@ -1,237 +1,128 @@ #include "RecordManager.h" -#include "rms/RecordStore.h" -#include "rms/RecordStoreException.h" -#include "rms/RecordStoreNotOpenException.h" -#include "rms/InvalidRecordIDException.h" - -void RecordManager::method_8(int var1, int var2) +void RecordManager::loadRecordInfo(const uint8_t level, const uint8_t track) { - resetRecordsTime(); - - try { - str = std::to_string(var1) + std::to_string(var2); - recordStore = RecordStore::openRecordStore(str, true); - } catch (RecordStoreException& var9) { - return; - } - - packedRecordInfoRecordId = -1; - - RecordEnumeration* recordEnum; - try { - recordEnum = recordStore->enumerateRecords(nullptr, nullptr, false); - } catch (RecordStoreNotOpenException& var8) { - return; - } + recordsSaveDataConverter = { .bytes = {} }; - if (recordEnum->numRecords() > 0) { - std::vector var4; - try { - var4 = recordEnum->nextRecord(); - recordEnum->reset(); - packedRecordInfoRecordId = recordEnum->nextRecordId(); - } catch (RecordStoreNotOpenException& var5) { - return; - } catch (InvalidRecordIDException& var6) { - return; - } catch (RecordStoreException& var7) { - return; - } + const std::string trackSaveName = std::to_string(level) + std::to_string(track) + ".dat"; + const std::filesystem::path saveFileName = GlobalSetting::SavesPath / GlobalSetting::SavesPrefix / trackSaveName; + Log::write(Log::LogLevel::Debug, "loadRecordInfo %s\n", saveFileName.c_str()); - loadRecordInfo(var4); - recordEnum->destroy(); - } + FileStream levelFileStream(saveFileName, std::ios::in | std::ios::binary); + levelFileStream.readVariable(&recordsSaveDataConverter.bytes, false, sizeof(RecordsSaveData)); } -int64_t RecordManager::load5BytesAsLong(std::vector var1, int offset) +std::array RecordManager::getRecordDescription(const uint8_t league) const { - int64_t result = 0L; - int64_t mult = 1L; - - for (int var7 = offset; var7 < 5 + offset; ++var7) { - int var8 = (var1[var7] + 256) % 256; - result += mult * (int64_t)var8; - mult *= 256L; - } - - return result; -} + std::array recordsDescription; -void RecordManager::pushLongAs5Bytes(std::vector var1, int var2, int64_t var3) -{ - for (int var5 = var2; var5 < 5 + var2; ++var5) { - var1[var5] = (int8_t)((int)(var3 % 256L)); - var3 /= 256L; - } -} + for (uint8_t i = 0; i < RECORD_NO_MAX; ++i) { + const Record record = recordsSaveDataConverter.recordsSaveData.leagueRecords[league].records[i]; -void RecordManager::loadRecordInfo(std::vector var1) -{ - int offset = 0; - - int league; - int pos; - for (league = 0; league < 4; ++league) { - for (pos = 0; pos < 3; ++pos) { - recordTimeMs[league][pos] = load5BytesAsLong(var1, offset); - offset += 5; + if (record.timeMs != 0L) { + std::stringstream ss; + ss << std::string(record.playerName, PLAYER_NAME_MAX) + << ' ' + << Time::timeToString(record.timeMs); + recordsDescription[i] = ss.str(); + } else { + recordsDescription[i] = ""; } } - for (league = 0; league < LEAGUES_MAX; ++league) { - for (pos = 0; pos < RECORD_NO_MAX; ++pos) { - for (auto i = 0; i < PLAYER_NAME_MAX; ++i) { - recordName[league][pos][i] = var1[offset++]; - } - } - } + return recordsDescription; } -void RecordManager::getLevelInfo(std::vector var1) +void RecordManager::writeRecordInfo(const uint8_t level, const uint8_t track) { - int shift = 0; - - int league; - int recordNo; - for (league = 0; league < 4; ++league) { - for (recordNo = 0; recordNo < 3; ++recordNo) { - pushLongAs5Bytes(var1, shift, recordTimeMs[league][recordNo]); - shift += 5; - } - } + const std::string trackSaveName = std::to_string(level) + std::to_string(track) + ".dat"; + const std::filesystem::path saveFileName = GlobalSetting::SavesPath / GlobalSetting::SavesPrefix / trackSaveName; + Log::write(Log::LogLevel::Debug, "writeRecordInfo %s\n", saveFileName.c_str()); - for (league = 0; league < LEAGUES_MAX; ++league) { - for (recordNo = 0; recordNo < RECORD_NO_MAX; ++recordNo) { - for (auto i = 0; i < PLAYER_NAME_MAX; ++i) { - var1[shift++] = recordName[league][recordNo][i]; - } - } - } + FileStream levelFileStream(saveFileName, std::ios::out | std::ios::binary); + levelFileStream.writeVariable(&recordsSaveDataConverter.bytes, sizeof(RecordsSaveData)); } -void RecordManager::resetRecordsTime() +uint8_t RecordManager::getPosOfNewRecord(const uint8_t league, const uint64_t timeMs) const { - for (int league = 0; league < 4; ++league) { - for (int pos = 0; pos < 3; ++pos) { - recordTimeMs[league][pos] = 0L; - } - } -} + for (uint8_t i = 0; i < RECORD_NO_MAX; ++i) { + const Record record = recordsSaveDataConverter.recordsSaveData.leagueRecords[league].records[i]; -std::vector RecordManager::getRecordDescription(int var1) -{ - std::vector var2 = std::vector(3); - - for (int var3 = 0; var3 < 3; ++var3) { - if (recordTimeMs[var1][var3] != 0L) { - int var4 = (int)recordTimeMs[var1][var3] / 100; - int var5 = (int)recordTimeMs[var1][var3] % 100; - var2[var3] = "" + std::string(recordName[var1][var3]) + " "; - - if (var4 / 60 < 10) { - var2[var3] = var2[var3] + " 0" + std::to_string(var4 / 60); - } else { - var2[var3] = var2[var3] + " " + std::to_string(var4 / 60); - } - - if (var4 % 60 < 10) { - var2[var3] = var2[var3] + ":0" + std::to_string(var4 % 60); - } else { - var2[var3] = var2[var3] + ":" + std::to_string(var4 % 60); - } - - if (var5 < 10) { - var2[var3] = var2[var3] + ".0" + std::to_string(var5); - } else { - var2[var3] = var2[var3] + "." + std::to_string(var5); - } - } else { - var2[var3].clear(); + if (record.timeMs > timeMs || record.timeMs == 0L) { + return i; } } - return var2; + return 3; } -void RecordManager::writeRecordInfo() +void RecordManager::addNewRecord(const uint8_t league, const char* playerName, const uint64_t timeMs) { - getLevelInfo(packedRecordInfo); - if (packedRecordInfoRecordId == -1) { - try { - packedRecordInfoRecordId = recordStore->addRecord(packedRecordInfo, 0, 96); - } catch (RecordStoreNotOpenException& var1) { - } catch (RecordStoreException& var2) { - } - } else { - try { - recordStore->setRecord(packedRecordInfoRecordId, packedRecordInfo, 0, 96); - } catch (RecordStoreNotOpenException& var3) { - } catch (RecordStoreException& var4) { - } + Log::write(Log::LogLevel::Debug, "addNewRecord %d %s %d\n", (int)league, playerName, timeMs); + const uint8_t newRecordPos = getPosOfNewRecord(league, timeMs); + Log::write(Log::LogLevel::Debug, "Record pos %d\n", (int)newRecordPos); + + if (newRecordPos >= 3) { + // out of scope, we save only first 3 records + return; } -} -int RecordManager::getPosOfNewRecord(int league, int64_t timeMs) -{ - for (int i = 0; i < 3; ++i) { - if (recordTimeMs[league][i] > timeMs || recordTimeMs[league][i] == 0L) { - return i; - } + if (newRecordPos < (RECORD_NO_MAX - 1)) { + std::rotate( + std::begin(recordsSaveDataConverter.recordsSaveData.leagueRecords[league].records) + newRecordPos, + std::end(recordsSaveDataConverter.recordsSaveData.leagueRecords[league].records) - 1, + std::end(recordsSaveDataConverter.recordsSaveData.leagueRecords[league].records)); } - return 3; + recordsSaveDataConverter.recordsSaveData.leagueRecords[league].records[newRecordPos] = { + .timeMs = timeMs, + .playerName = {}, + .padding = { 0, 0, 0, 0, 0 } + }; + strncpy(recordsSaveDataConverter.recordsSaveData.leagueRecords[league].records[newRecordPos].playerName, playerName, PLAYER_NAME_MAX); } -void RecordManager::method_17(int league, char* values, int64_t timeMs) +void RecordManager::deleteRecordStores() const { - int newRecordPos; - if ((newRecordPos = getPosOfNewRecord(league, timeMs)) != 3) { - if (timeMs > 16777000L) { - timeMs = 16777000L; // 3 int8_ts, not five, wtf? - } + Log::write(Log::LogLevel::Debug, "deleteRecordStores\n"); + const std::filesystem::path saveFileName = GlobalSetting::SavesPath / GlobalSetting::SavesPrefix; - addNewRecord(league, newRecordPos); - recordTimeMs[league][newRecordPos] = timeMs; + for (const auto& file : std::filesystem::directory_iterator(saveFileName)) { + const std::string filename = file.path().filename().string(); + Log::write(Log::LogLevel::Debug, "Found record %s\n", filename.c_str()); - for (auto i = 0; i < PLAYER_NAME_MAX; ++i) { - recordName[league][newRecordPos][i] = values[i]; + if (GlobalSetting::GlobalSaveFileName == filename) { + Log::write(Log::LogLevel::Debug, "Skipped deletion for %s\n", file.path().c_str()); + continue; } - } -} -void RecordManager::addNewRecord(int gameLevel, int position) -{ - for (auto pos = 2; pos > position; --pos) { - recordTimeMs[gameLevel][pos] = recordTimeMs[gameLevel][pos - 1]; - for (auto i = 0; i < PLAYER_NAME_MAX; ++i) { - recordName[gameLevel][pos][i] = recordName[gameLevel][pos - 1][i]; - } + std::filesystem::remove(file); } } -void RecordManager::deleteRecordStores() +uint32_t RecordManager::getNumberOfRecordsForLevel(const uint8_t level) { - std::vector names = RecordStore::listRecordStores(); - - for (std::size_t i = 0; i < names.size(); ++i) { - if (names[i] != "GDTRStates") { - try { - // RecordStore *var10000 = recordStore; - RecordStore::deleteRecordStore(names[i]); - } catch (RecordStoreException& var3) { - } + Log::write(Log::LogLevel::Debug, "getNumberOfRecordsForLevel %d\n", level); + + const std::string prefix = std::to_string(level); + const std::filesystem::path saveFileName = GlobalSetting::SavesPath / GlobalSetting::SavesPrefix; + + uint32_t count = 0; + + for (const auto& file : std::filesystem::directory_iterator(saveFileName)) { + const std::string filename = file.path().filename().string(); + Log::write(Log::LogLevel::Debug, "Found record %s\n", filename.c_str()); + + if (!file.is_regular_file()) { + Log::write(Log::LogLevel::Debug, "Skipped record %s\n", file.path().c_str()); + continue; } - } -} -void RecordManager::closeRecordStore() -{ - if (recordStore != nullptr) { - try { - recordStore->closeRecordStore(); - return; - } catch (RecordStoreException& var1) { + if (filename.rfind(prefix, 0) == 0) { + Log::write(Log::LogLevel::Debug, "Included record %s\n", file.path().c_str()); + ++count; } } -} + + return count; +} \ No newline at end of file diff --git a/src/RecordManager.h b/src/RecordManager.h index d4cc618..ae7c527 100644 --- a/src/RecordManager.h +++ b/src/RecordManager.h @@ -3,41 +3,48 @@ #include #include #include +#include +#include +#include -class RecordStore; +#include "config.h" +#include "utils/Log.h" +#include "utils/Time.h" +#include "utils/FileStream.h" class RecordManager { public: - enum { - LEAGUES_MAX = 4, - RECORD_NO_MAX = 3, - PLAYER_NAME_MAX = 3, + void loadRecordInfo(const uint8_t level, const uint8_t track); + std::array getRecordDescription(const uint8_t league) const; + uint8_t getPosOfNewRecord(const uint8_t league, const uint64_t timeMs) const; + void writeRecordInfo(const uint8_t level, const uint8_t track); + void addNewRecord(const uint8_t league, const char* playerName, const uint64_t timeMs); + void deleteRecordStores() const; + static uint32_t getNumberOfRecordsForLevel(const uint8_t level); + +private: + struct Record { + uint64_t timeMs; + char playerName[PLAYER_NAME_MAX]; + uint8_t padding[5]; + }; + static_assert(sizeof(Record) == 16); + + struct LeagueRecords { + Record records[RECORD_NO_MAX]; }; + static_assert(sizeof(LeagueRecords) == 48); - inline static const int unused = 3; + struct RecordsSaveData { + LeagueRecords leagueRecords[LEAGUES_MAX]; + }; + static_assert(sizeof(RecordsSaveData) == 192); - void method_8(int var1, int var2); - std::vector getRecordDescription(int var1); - void writeRecordInfo(); - int getPosOfNewRecord(int league, int64_t timeMs); - void method_17(int league, char* values, int64_t timeMs); - void deleteRecordStores(); - void closeRecordStore(); + union RecordsSaveDataConverter { + RecordsSaveData recordsSaveData; + int8_t bytes[sizeof(RecordsSaveData)]; + }; + static_assert(sizeof(RecordsSaveDataConverter) == sizeof(RecordsSaveData)); -private: - std::vector> recordTimeMs = std::vector>(4, std::vector(3)); - // 4: league, 100, 175, 225, 350, - // 3: three best times - char recordName[LEAGUES_MAX][RECORD_NO_MAX][PLAYER_NAME_MAX + 1]; - RecordStore* recordStore = nullptr; - int packedRecordInfoRecordId = -1; - std::vector packedRecordInfo = std::vector(96); - std::string str; - - int64_t load5BytesAsLong(std::vector var1, int offset); - void pushLongAs5Bytes(std::vector var1, int var2, int64_t var3); - void loadRecordInfo(std::vector var1); - void getLevelInfo(std::vector var1); - void resetRecordsTime(); - void addNewRecord(int gameLevel, int position); + RecordsSaveDataConverter recordsSaveDataConverter; }; diff --git a/src/SettingsManager.cpp b/src/SettingsManager.cpp new file mode 100644 index 0000000..ea55a86 --- /dev/null +++ b/src/SettingsManager.cpp @@ -0,0 +1,67 @@ +#include "SettingsManager.h" + +void SettingsManager::initSettings() +{ + std::filesystem::path saveFileName = GlobalSetting::SavesPath; + + if (!std::filesystem::exists(saveFileName)) { + Log::write(Log::LogLevel::Info, "Creating directory %s\n", saveFileName.c_str()); + std::filesystem::create_directory(saveFileName); + } + + saveFileName = saveFileName / GlobalSetting::SavesPrefix; + + if (!std::filesystem::exists(saveFileName)) { + Log::write(Log::LogLevel::Info, "Creating directory %s\n", saveFileName.c_str()); + std::filesystem::create_directory(saveFileName); + } + + saveFileName = saveFileName / GlobalSetting::GlobalSaveFileName; + + if (!std::filesystem::exists(saveFileName)) { + Log::write(Log::LogLevel::Info, "Creating global settings file %s\n", saveFileName.c_str()); + SettingsManager::Settings settings = { + .perspective = 0, + .shadows = 0, + .driverSprite = 0, + .bikeSprite = 0, + .lookAhead = 0, + .availableLeagues = 0, + .availableLevels = 0, + .availableTracks = { 0, 0, -1 }, + .selectedLevel = 0, + .selectedTrack = 0, + .selectedLeague = 0, + .unknown2 = -127, + .input = 0, + .unknown3 = -127, + .playerName = {} + }; + strncpy(settings.playerName, defaultName, PLAYER_NAME_MAX); + saveSettings(settings); + } +} + +SettingsManager::Settings SettingsManager::loadSettings() +{ + const std::filesystem::path saveFileName = GlobalSetting::SavesPath / GlobalSetting::SavesPrefix / GlobalSetting::GlobalSaveFileName; + Log::write(Log::LogLevel::Debug, "loadSettings %s\n", saveFileName.c_str()); + + FileStream levelFileStream(saveFileName, std::ios::in | std::ios::binary); + + SettingsManager::SettingsConverter settingsConverter; + levelFileStream.readVariable(&settingsConverter.bytes, false, sizeof(SettingsManager::Settings)); + + return settingsConverter.settings; +} + +void SettingsManager::saveSettings(SettingsManager::Settings settings) +{ + const std::filesystem::path saveFileName = GlobalSetting::SavesPath / GlobalSetting::SavesPrefix / GlobalSetting::GlobalSaveFileName; + Log::write(Log::LogLevel::Debug, "saveSettings %s\n", saveFileName.c_str()); + + SettingsManager::SettingsConverter settingsConverter = { .settings = settings }; + + FileStream levelFileStream(saveFileName, std::ios::out | std::ios::binary); + levelFileStream.writeVariable(&settingsConverter.bytes, sizeof(SettingsManager::Settings)); +} diff --git a/src/SettingsManager.h b/src/SettingsManager.h new file mode 100644 index 0000000..ae7e2d7 --- /dev/null +++ b/src/SettingsManager.h @@ -0,0 +1,47 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "utils/Log.h" +#include "utils/FileStream.h" + +class SettingsManager { +public: + struct Settings { + int8_t perspective; // 0 + int8_t shadows; // 1 + int8_t driverSprite; // 2 + int8_t bikeSprite; // 3 + int8_t lookAhead; // 4 + int8_t availableLeagues; // 5 + int8_t availableLevels; // 6 + int8_t availableTracks[3]; // 7-9 + int8_t selectedLevel; // 10 + int8_t selectedTrack; // 11 + int8_t selectedLeague; // 12 + int8_t unknown2; // 13 + int8_t input; // 14 + int8_t unknown3; //15 + char playerName[PLAYER_NAME_MAX]; //16-19 + }; + static_assert(sizeof(Settings) == 19); + + union SettingsConverter { + Settings settings; + int8_t bytes[sizeof(Settings)]; + }; + + static void initSettings(); + static SettingsManager::Settings loadSettings(); + static void saveSettings(SettingsManager::Settings settings); +private: + +}; \ No newline at end of file diff --git a/src/SettingsStringRender.cpp b/src/SettingsStringRender.cpp index 9962e23..338fae4 100644 --- a/src/SettingsStringRender.cpp +++ b/src/SettingsStringRender.cpp @@ -180,9 +180,18 @@ void SettingsStringRender::render(Graphics* graphics, int y, int x) { if (useColon) { if (!hasSprite) { - graphics->drawString(text, x, y, 20); + graphics->drawString( + text, + x, + y, + Graphics::TOP | Graphics::LEFT); } else { - graphics->drawString(text, x + GameCanvas::spriteSizeX[8] + 3, y, 20); + graphics->drawString( + text, + x + GameCanvas::spriteSizeX[8] + 3, + y, + Graphics::TOP | Graphics::LEFT); + if (isDrawSprite8) { micro->gameCanvas->drawSprite(graphics, 8, x, y - GameCanvas::spriteSizeY[8] / 2 + graphics->getFont()->getHeight() / 2); } else { @@ -190,15 +199,24 @@ void SettingsStringRender::render(Graphics* graphics, int y, int x) } } } else { - graphics->drawString(text, x, y, 20); + graphics->drawString( + text, + x, + y, + Graphics::TOP | Graphics::LEFT); int shiftedX = x + graphics->getFont()->stringWidth(text); + if (currentOptionPos > maxAvailableOption && !field_146) { micro->gameCanvas->drawSprite(graphics, 8, shiftedX + 1, y - GameCanvas::spriteSizeY[8] / 2 + graphics->getFont()->getHeight() / 2); shiftedX += GameCanvas::spriteSizeX[9] + 1; } shiftedX += 2; - graphics->drawString(selectedOptionName, shiftedX, y, 20); + graphics->drawString( + selectedOptionName, + shiftedX, + y, + Graphics::TOP | Graphics::LEFT); } } diff --git a/src/TextRender.cpp b/src/TextRender.cpp index a4a8a87..783ee02 100644 --- a/src/TextRender.cpp +++ b/src/TextRender.cpp @@ -58,7 +58,12 @@ void TextRender::render(Graphics* graphics, int y, int x) graphics->setFont(font); } - graphics->drawString(text, x + dx, y, 20); + graphics->drawString( + text, + x + dx, + y, + Graphics::TOP | Graphics::LEFT); + if (isDrawSprite) { micro->gameCanvas->drawSprite(graphics, spriteNo, x, y); } diff --git a/src/TimerOrMotoPartOrMenuElem.cpp b/src/TimerOrMotoPartOrMenuElem.cpp index 48e4c68..a774efd 100644 --- a/src/TimerOrMotoPartOrMenuElem.cpp +++ b/src/TimerOrMotoPartOrMenuElem.cpp @@ -65,5 +65,9 @@ void TimerOrMotoPartOrMenuElem::setGameMenu(GameMenu* gameMenu) void TimerOrMotoPartOrMenuElem::render(Graphics* graphics, int y, int x) { - graphics->drawString(text, x, y, 20); + graphics->drawString( + text, + x, + y, + Graphics::TOP | Graphics::LEFT); } \ No newline at end of file diff --git a/src/class_10.cpp b/src/class_10.cpp index a454d49..25648ab 100644 --- a/src/class_10.cpp +++ b/src/class_10.cpp @@ -9,11 +9,6 @@ class_10::class_10() reset(); } -class_10::~class_10() -{ - // -} - void class_10::reset() { field_257 = field_259 = field_260 = 0; diff --git a/src/class_10.h b/src/class_10.h index b72d09f..396f5b5 100644 --- a/src/class_10.h +++ b/src/class_10.h @@ -1,7 +1,6 @@ #pragma once #include -#include #include "TimerOrMotoPartOrMenuElem.h" @@ -15,6 +14,5 @@ class class_10 { std::vector> motoComponents = std::vector>(6); class_10(); - ~class_10(); void reset(); }; \ No newline at end of file diff --git a/src/config.cpp b/src/config.cpp new file mode 100644 index 0000000..b5cea15 --- /dev/null +++ b/src/config.cpp @@ -0,0 +1,18 @@ +#include "config.h" + +bool GlobalSetting::WindowFullscreen = false; +bool GlobalSetting::ShowFPS = false; +uint8_t GlobalSetting::LogoMultiplier = 1; +uint8_t GlobalSetting::SplashMultiplier = 1; +uint8_t GlobalSetting::FontMultiplier = 4; +uint8_t GlobalSetting::TimerFpsTextOffset = 14; +uint8_t GlobalSetting::LoadingBarPadding = 2; +uint8_t GlobalSetting::BarScreenOffset = 2; +uint8_t GlobalSetting::BarH = 10; +uint16_t GlobalSetting::DefaultScreenWidth = 640; +uint16_t GlobalSetting::DefaultScreenHeight = 480; +uint16_t GlobalSetting::DefaultZoomLevel = 100; +std::string GlobalSetting::SavesPrefix = ""; +std::filesystem::path GlobalSetting::SavesPath = "./saves"; +std::filesystem::path GlobalSetting::GlobalSaveFileName = "GDTRStates.dat"; +std::filesystem::path GlobalSetting::MrgFilePath = "levels.mrg"; \ No newline at end of file diff --git a/src/config.h b/src/config.h new file mode 100644 index 0000000..a5c72bf --- /dev/null +++ b/src/config.h @@ -0,0 +1,82 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + + +constexpr int LEAGUES_MAX = 4; +constexpr int RECORD_NO_MAX = 3; +constexpr int PLAYER_NAME_MAX = 3; + + +enum class Keys { + NONE, // = 0, + UP, // = 1, + DOWN, // = 6, + LEFT, // = 2, + RIGHT, // = 5, + FIRE, // = 8, + BACK, // = 9, +}; + +static std::map gameKeyMappings = { + { SDLK_LEFT, Keys::LEFT }, + { SDLK_RIGHT, Keys::RIGHT }, + { SDLK_UP, Keys::UP }, + { SDLK_DOWN, Keys::DOWN }, + + { SDLK_RETURN, Keys::UP }, + { SDLK_ESCAPE, Keys::BACK }, + { SDLK_BACKSPACE, Keys::DOWN }, +}; +static std::map menuKeyMappings = { + { SDLK_LEFT, Keys::LEFT }, + { SDLK_RIGHT, Keys::RIGHT }, + { SDLK_UP, Keys::UP }, + { SDLK_DOWN, Keys::DOWN }, + + { SDLK_RETURN, Keys::FIRE }, + { SDLK_ESCAPE, Keys::BACK }, + { SDLK_BACKSPACE, Keys::BACK }, +}; + +static std::map> availableActions = { + { Keys::UP, { 1, 0 } }, + { Keys::DOWN, { -1, 0 } }, + { Keys::LEFT, { 0, -1 } }, + { Keys::RIGHT, { 0, 1 } }, + // { Keys::FIRE, {1, 0} }, + // { Keys::BACK, {-1, 0} }, +}; +static std::vector onOffLabels = { "On", "Off" }; + +static std::vector inputLabels = { "Keyset 1", "Keyset 2", "Keyset 3" }; +static std::vector levelLabels = { "Easy", "Medium", "Pro" }; + +const char cheatCode[] = "RKE"; +const char defaultName[] = "AAA"; + +struct GlobalSetting { + static bool WindowFullscreen; + static bool ShowFPS; + static uint8_t LogoMultiplier; + static uint8_t SplashMultiplier; + static uint8_t FontMultiplier; + static uint8_t TimerFpsTextOffset; + static uint8_t LoadingBarPadding; + static uint8_t BarScreenOffset; + static uint8_t BarH; + static uint16_t DefaultScreenWidth; + static uint16_t DefaultScreenHeight; + static uint16_t DefaultZoomLevel; + static std::string SavesPrefix; + static std::filesystem::path GlobalSaveFileName; + static std::filesystem::path SavesPath; + static std::filesystem::path MrgFilePath; +}; \ No newline at end of file diff --git a/src/lcdui/Canvas.cpp b/src/lcdui/Canvas.cpp index db42151..12772e5 100644 --- a/src/lcdui/Canvas.cpp +++ b/src/lcdui/Canvas.cpp @@ -1,23 +1,11 @@ #include "Canvas.h" -#include -#include - -#include "CanvasImpl.h" -#include "Graphics.h" -#include "Command.h" -#include "CommandListener.h" - Canvas::Canvas() { impl = std::make_unique(this); graphics = std::make_unique(impl->getRenderer()); } -Canvas::~Canvas() -{ -} - int Canvas::getWidth() { return impl->getWidth(); @@ -56,20 +44,6 @@ void Canvas::serviceRepaints() { } -int Canvas::getGameAction(int keyCode) -{ - switch (keyCode) { - case Keys::UP: - case Keys::DOWN: - case Keys::LEFT: - case Keys::RIGHT: - case Keys::FIRE: - return keyCode; - default: - throw std::runtime_error("getGameAction(" + std::to_string(keyCode) + ") isn't implemented!"); - } -} - void Canvas::removeCommand(Command* command) { currentCommands.erase(command); @@ -85,12 +59,12 @@ void Canvas::setCommandListener(CommandListener* listener) commandListener = listener; } -void Canvas::publicKeyPressed(int keyCode) +void Canvas::publicKeyPressed(const Keys keyCode) { keyPressed(keyCode); } -void Canvas::publicKeyReleased(int keyCode) +void Canvas::publicKeyReleased(const Keys keyCode) { keyReleased(keyCode); } @@ -98,7 +72,7 @@ void Canvas::publicKeyReleased(int keyCode) void Canvas::pressedEsc() { for (const auto& command : currentCommands) { - if (command->type == Command::Type::BACK || currentCommands.size() == 1) { + if (command->type == Command::Type::BACK || (!Micro::isInGameMenu && currentCommands.size() == 1)) { commandListener->commandAction(command, this); return; } diff --git a/src/lcdui/Canvas.h b/src/lcdui/Canvas.h index 3ec7ec1..0e6a783 100644 --- a/src/lcdui/Canvas.h +++ b/src/lcdui/Canvas.h @@ -3,12 +3,18 @@ #include #include #include +#include +#include "CanvasImpl.h" +#include "Graphics.h" +#include "Command.h" +#include "CommandListener.h" #include "Displayable.h" #include "Command.h" +#include "../Micro.h" +#include "../config.h" class CanvasImpl; -class Graphics; class Canvas : public Displayable { private: @@ -19,16 +25,7 @@ class Canvas : public Displayable { std::unordered_set currentCommands; public: - enum Keys { - UP = 1, - DOWN = 6, - LEFT = 2, - RIGHT = 5, - FIRE = 8 - }; - Canvas(); - ~Canvas(); bool isShown() override; int getWidth() override; int getHeight() override; @@ -36,14 +33,13 @@ class Canvas : public Displayable { CanvasImpl* getCanvasImpl(); void repaint(); void serviceRepaints(); - int getGameAction(int keyCode); void removeCommand(Command* command); void addCommand(Command* command); void setCommandListener(CommandListener* listener); - void publicKeyPressed(int keyCode); - void publicKeyReleased(int keyCode); + void publicKeyPressed(const Keys keyCode); + void publicKeyReleased(const Keys keyCode); void pressedEsc(); virtual void paint(Graphics* g) = 0; - virtual void keyPressed(int keyCode) = 0; - virtual void keyReleased(int keyCode) = 0; + virtual void keyPressed(const Keys keyCode) = 0; + virtual void keyReleased(const Keys keyCode) = 0; }; \ No newline at end of file diff --git a/src/lcdui/CanvasImpl.cpp b/src/lcdui/CanvasImpl.cpp index 7f1c807..80df909 100644 --- a/src/lcdui/CanvasImpl.cpp +++ b/src/lcdui/CanvasImpl.cpp @@ -1,17 +1,7 @@ #include "CanvasImpl.h" -#include -#include -#include -#include -#include - -#include "Canvas.h" - -CanvasImpl::CanvasImpl(Canvas* canvas) +CanvasImpl::CanvasImpl(Canvas* canvas) : canvas(canvas) { - this->canvas = canvas; - if (SDL_Init(SDL_INIT_EVERYTHING) != 0) { throw std::runtime_error(SDL_GetError()); } @@ -24,30 +14,51 @@ CanvasImpl::CanvasImpl(Canvas* canvas) throw std::runtime_error(TTF_GetError()); } + uint32_t windowFlags = SDL_WINDOW_SHOWN; + + if (GlobalSetting::WindowFullscreen) { + windowFlags = windowFlags | SDL_WINDOW_FULLSCREEN_DESKTOP; + } + window = SDL_CreateWindow( 0, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, - width, height, - SDL_WINDOW_SHOWN); + GlobalSetting::DefaultScreenWidth, GlobalSetting::DefaultScreenHeight, + windowFlags); if (!window) { throw std::runtime_error(SDL_GetError()); } - renderer = SDL_CreateRenderer( - window, -1, SDL_RENDERER_ACCELERATED); + renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); if (!renderer) { throw std::runtime_error(SDL_GetError()); } + zoomLevel = GlobalSetting::DefaultZoomLevel > 0 ? GlobalSetting::DefaultZoomLevel : 100; + + if(SDL_GetRendererOutputSize(renderer, &windowWidth, &windowHeight) < 0) { + windowWidth=GlobalSetting::DefaultScreenWidth; + windowHeight=GlobalSetting::DefaultScreenHeight; + } + windowWidth=(windowWidth * zoomLevel) / 100; + windowHeight=(windowHeight * zoomLevel) / 100; + + texTarget = SDL_CreateTexture(renderer, SDL_GetWindowPixelFormat(window), + SDL_TEXTUREACCESS_TARGET, windowWidth, windowHeight); + + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); + SDL_RenderClear(renderer); + SDL_SetRenderTarget(renderer, texTarget); SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); SDL_RenderClear(renderer); } CanvasImpl::~CanvasImpl() { + SDL_DestroyTexture(texTarget); SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); SDL_Quit(); @@ -57,17 +68,37 @@ CanvasImpl::~CanvasImpl() void CanvasImpl::repaint() { + SDL_SetRenderTarget(renderer, NULL); + SDL_RenderCopyEx(renderer, texTarget, NULL, NULL, 0, NULL, SDL_FLIP_NONE); SDL_RenderPresent(renderer); + SDL_SetRenderTarget(renderer, texTarget); +} + +int CanvasImpl::getZoom() +{ + if (zoomLevel <= 0) { + return GlobalSetting::DefaultZoomLevel; + } + + return zoomLevel; } int CanvasImpl::getWidth() { - return width; + if (windowWidth < 0) { + return GlobalSetting::DefaultScreenWidth; + } + + return windowWidth; } int CanvasImpl::getHeight() { - return height; + if (windowHeight < 0) { + return GlobalSetting::DefaultScreenHeight; + } + + return windowHeight; } SDL_Renderer* CanvasImpl::getRenderer() @@ -85,23 +116,17 @@ void CanvasImpl::processEvents() exit(0); // IMPROVE This is a super dumb way to finish the game, but it works break; case SDL_KEYDOWN: { - int keyCode = convertKeyCharToKeyCode(e.key.keysym.sym); - // std::cout << "Key pressed: " << keyCode << std::endl; - if (keyCode != 0) { + const Keys keyCode = convertKeyCharToKeyCode(e.key.keysym.sym); + + if (keyCode != Keys::NONE) { canvas->publicKeyPressed(keyCode); } } break; case SDL_KEYUP: { - int sdlCode = e.key.keysym.sym; - int keyCode = convertKeyCharToKeyCode(sdlCode); - // std::cout << "Key released: " << keyCode << std::endl; - if (keyCode != 0) { + const Keys keyCode = convertKeyCharToKeyCode(e.key.keysym.sym); + + if (keyCode != Keys::NONE) { canvas->publicKeyReleased(keyCode); - } else { - if (sdlCode == SDLK_ESCAPE) { - // std::cout << "ESC released" << std::endl; - canvas->pressedEsc(); - } } } break; default: @@ -110,23 +135,20 @@ void CanvasImpl::processEvents() } } -int CanvasImpl::convertKeyCharToKeyCode(SDL_Keycode keyCode) +Keys CanvasImpl::convertKeyCharToKeyCode(const SDL_Keycode keyCode) { - switch (keyCode) { - case SDLK_RETURN: - return Canvas::Keys::FIRE; - case SDLK_LEFT: - return Canvas::Keys::LEFT; - case SDLK_RIGHT: - return Canvas::Keys::RIGHT; - case SDLK_UP: - return Canvas::Keys::UP; - case SDLK_DOWN: - return Canvas::Keys::DOWN; - default: - std::cout << "unknown keyEvent: " << keyCode << std::endl; - return 0; + Log::write(Log::LogLevel::Debug, "KEY %d; Menu: %d\n", keyCode, Micro::isInGameMenu); + + if (Micro::isInGameMenu && menuKeyMappings.count(keyCode) > 0) { + return menuKeyMappings[keyCode]; } + + if (!Micro::isInGameMenu && gameKeyMappings.count(keyCode) > 0) { + return gameKeyMappings[keyCode]; + } + + Log::write(Log::LogLevel::Warning, "unknown keyEvent: %d\n", keyCode); + return Keys::NONE; } void CanvasImpl::setWindowTitle(const std::string& title) diff --git a/src/lcdui/CanvasImpl.h b/src/lcdui/CanvasImpl.h index 90c4763..ba6d8ce 100644 --- a/src/lcdui/CanvasImpl.h +++ b/src/lcdui/CanvasImpl.h @@ -1,30 +1,38 @@ #pragma once #include - #include #include #include +#include +#include + +#include "Canvas.h" +#include "../config.h" +#include "../utils/Log.h" class Canvas; class CanvasImpl { private: + int windowWidth = -1; + int windowHeight = -1; + int zoomLevel = -1; + Canvas* canvas; + SDL_Texture *texTarget; SDL_Window* window; SDL_Renderer* renderer; - const int width = 640; - const int height = 480; - - static int convertKeyCharToKeyCode(SDL_Keycode keyCode); + static Keys convertKeyCharToKeyCode(const SDL_Keycode keyCode); public: CanvasImpl(Canvas* canvas); ~CanvasImpl(); void repaint(); + int getZoom(); int getWidth(); int getHeight(); diff --git a/src/lcdui/Command.cpp b/src/lcdui/Command.cpp index 8cb9f73..29c790d 100644 --- a/src/lcdui/Command.cpp +++ b/src/lcdui/Command.cpp @@ -1,12 +1,8 @@ #include "Command.h" -Command::Command(std::string name, int type, int priority) +Command::Command(const std::string name, const Command::Type type, const int priority) : name(name) , type(type) , priority(priority) { -} - -Command::~Command() -{ } \ No newline at end of file diff --git a/src/lcdui/Command.h b/src/lcdui/Command.h index c5705df..3fe78fb 100644 --- a/src/lcdui/Command.h +++ b/src/lcdui/Command.h @@ -11,7 +11,7 @@ inline void hash_combine(std::size_t& seed, const T& v) class Command { public: - enum Type { + enum class Type { SCREEN = 1, BACK = 2, CANCEL = 3, @@ -40,10 +40,9 @@ class Command { } }; - Command(std::string name, int type, int priority); - ~Command(); + Command(const std::string name, const Command::Type type, const int priority); const std::string name; - const int type; + const Command::Type type; const int priority; }; \ No newline at end of file diff --git a/src/lcdui/Font.cpp b/src/lcdui/Font.cpp index 547c4ee..29f4f43 100644 --- a/src/lcdui/Font.cpp +++ b/src/lcdui/Font.cpp @@ -1,7 +1,5 @@ #include "Font.h" -#include - CMRC_DECLARE(assets); Font::Font(FontStyle style, FontSize pointSize) @@ -66,11 +64,11 @@ int Font::getRealFontSize(FontSize size) { switch (size) { case SIZE_LARGE: - return 32; + return 8 * GlobalSetting::FontMultiplier; case SIZE_MEDIUM: - return 16; + return 4 * GlobalSetting::FontMultiplier; case SIZE_SMALL: - return 12; + return 3 * GlobalSetting::FontMultiplier; default: throw std::runtime_error("unknown font size: " + std::to_string(size)); } diff --git a/src/lcdui/Font.h b/src/lcdui/Font.h index 8d138ba..4ccb52a 100644 --- a/src/lcdui/Font.h +++ b/src/lcdui/Font.h @@ -3,8 +3,10 @@ #include #include #include +#include #include +#include "../config.h" class Font { public: diff --git a/src/lcdui/FontStorage.cpp b/src/lcdui/FontStorage.cpp index 068f8e3..4e6edd5 100644 --- a/src/lcdui/FontStorage.cpp +++ b/src/lcdui/FontStorage.cpp @@ -1,5 +1,3 @@ -#include - #include "Font.h" #include "FontStorage.h" diff --git a/src/lcdui/Graphics.cpp b/src/lcdui/Graphics.cpp index bbdf747..8dd8bfe 100644 --- a/src/lcdui/Graphics.cpp +++ b/src/lcdui/Graphics.cpp @@ -1,5 +1,4 @@ #include "Graphics.h" -#include Graphics::Graphics(SDL_Renderer* renderer) { @@ -243,6 +242,16 @@ void Graphics::drawImage(Image* const image, int x, int y, int anchor) SDL_DestroyTexture(texture); } +void Graphics::drawImage(Image* const image, int x, int y, int customW, int customH, int anchor) +{ + SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, image->getSurface()); + x = getAnchorX(x, customW, anchor); + y = getAnchorY(y, customH, anchor); + SDL_Rect dstRect { x, y, customW, customH }; + SDL_RenderCopy(renderer, texture, 0, &dstRect); + SDL_DestroyTexture(texture); +} + int Graphics::getAnchorX(int x, int size, int anchor) { if ((anchor & LEFT) != 0) { diff --git a/src/lcdui/Graphics.h b/src/lcdui/Graphics.h index 481e68c..44586f5 100644 --- a/src/lcdui/Graphics.h +++ b/src/lcdui/Graphics.h @@ -22,6 +22,8 @@ class Graphics { SDL_Color currentColor; // void _ellipse(int cx, int cy, int xradius, int yradius); void _putpixel(int x, int y); + static int getAnchorX(int x, int size, int anchor); + static int getAnchorY(int y, int size, int anchor); public: enum Anchors { @@ -45,6 +47,5 @@ class Graphics { void drawArc(int x, int y, int w, int h, int startAngle, int arcAngle); void drawLine(int x1, int y1, int x2, int y2); void drawImage(Image* const image, int x, int y, int anchor); - static int getAnchorX(int x, int size, int anchor); - static int getAnchorY(int y, int size, int anchor); + void drawImage(Image* const image, int x, int y, int w, int h, int anchor); }; \ No newline at end of file diff --git a/src/levels.mrg b/src/levels.mrg new file mode 100644 index 0000000..e495c2a Binary files /dev/null and b/src/levels.mrg differ diff --git a/src/main.cpp b/src/main.cpp index ff84b9b..6014f0f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,19 +1,135 @@ -#include #include #include #include +#include +#include "utils/Hashing.h" +#include "utils/Log.h" +#include "config.h" #include "Micro.h" +void showHelp(const char* progName) +{ + Log::write(Log::LogLevel::None, R"usage( +Usage %s [options] + -f, --fullscreen Enable fullscreen + --fps Show FPS + --width INT Set screen width in px (default: 640) + --height INT Set screen height in px (default: 480) + --zoom INT Set render size in percents (default: 100) + -h, --help Show this help screen + -v, --verbose INT Set log level [0-6] (default: 2) + --font-mult Set font multiplier (default: 4) + --logo-mult Set logo multiplier (default: 1) +)usage", progName); +} + +bool parseArguments(int argc, char** argv) +{ + int opt; + static struct option long_options[] = { + { "fullscreen", no_argument, nullptr, 'f' }, + { "verbose", optional_argument, nullptr, 'v' }, + { "help", no_argument, nullptr, 'h' }, + { "width", required_argument, nullptr, 0 }, + { "height", required_argument, nullptr, 0 }, + { "zoom", required_argument, nullptr, 0 }, + { "fps", no_argument, nullptr, 0 }, + { "font-mult", required_argument, nullptr, 0 }, + { "logo-mult", required_argument, nullptr, 0 }, + { nullptr, 0, nullptr, 0 } + }; + + int option_index = 0; + Log::logLevel = Log::LogLevel::Info; + + while ((opt = getopt_long(argc, argv, "fhv:", long_options, &option_index)) != -1) { + switch (opt) { + case 0: { + if (long_options[option_index].flag != 0) + break; + + Log::write(Log::LogLevel::Debug, "Got parameter %d %s %s\n", option_index, long_options[option_index].name, optarg); + + if (strncmp(long_options[option_index].name, "fps", 3) == 0) { + GlobalSetting::ShowFPS = true; + Log::write(Log::LogLevel::None, "Showing FPS\n"); + } else if (strncmp(long_options[option_index].name, "width", 5) == 0) { + GlobalSetting::DefaultScreenWidth = atoi(optarg); + Log::write(Log::LogLevel::None, "Setting width %d\n", GlobalSetting::DefaultScreenWidth); + } else if (strncmp(long_options[option_index].name, "height", 6) == 0) { + GlobalSetting::DefaultScreenHeight = atoi(optarg); + Log::write(Log::LogLevel::None, "Setting height %d\n", GlobalSetting::DefaultScreenHeight); + } else if (strncmp(long_options[option_index].name, "zoom", 4) == 0) { + GlobalSetting::DefaultZoomLevel = atoi(optarg); + Log::write(Log::LogLevel::None, "Setting render size %d%%\n", GlobalSetting::DefaultZoomLevel); + } else if (strncmp(long_options[option_index].name, "font-mult", 9) == 0) { + GlobalSetting::FontMultiplier = atoi(optarg); + Log::write(Log::LogLevel::None, "Setting font multiplier to %d\n", GlobalSetting::FontMultiplier); + } else if (strncmp(long_options[option_index].name, "logo-mult", 9) == 0) { + const int value = atoi(optarg); + GlobalSetting::LogoMultiplier = value; + GlobalSetting::SplashMultiplier = value; + Log::write(Log::LogLevel::None, "Setting logo multiplier to %d\n", GlobalSetting::LogoMultiplier); + } + + break; + } + case 'f': { + Log::write(Log::LogLevel::None, "Setting fullscreen\n"); + GlobalSetting::WindowFullscreen = true; + break; + } + case 'v': { + const uint logLevelFromArg = 6 - atoi(optarg); + + if (logLevelFromArg > static_cast(Log::LogLevel::None)) { + Log::logLevel = Log::LogLevel::All; + } else { + Log::logLevel = static_cast(logLevelFromArg); + } + + Log::write(Log::LogLevel::None, "Setting logs verbosity to %u\n", Log::logLevel); + break; + } + case 'h': + default: { + showHelp(argv[0]); + return false; + } + } + } + + // Handle any remaining arguments + if (optind < argc) { + std::stringstream ss("Remaining arguments: "); + + for (int i = optind; i < argc; i++) { + ss << argv[i] << " "; + } + + Log::write(Log::LogLevel::Error, ss.str().c_str()); + } + + Log::write(Log::LogLevel::Info, "Path: %s\n", GlobalSetting::MrgFilePath.c_str()); + GlobalSetting::SavesPrefix = Hashing::HashFileMD5(GlobalSetting::MrgFilePath.string()); + Log::write(Log::LogLevel::Info, "MRG file hash: %s\n", GlobalSetting::SavesPrefix.c_str()); + return true; +} + int main(int argc, char** argv) { + if (!parseArguments(argc, argv)) { + return EXIT_FAILURE; + } + try { std::unique_ptr micro = std::make_unique(); - micro->startApp(argc, argv); + micro->startApp(); } catch (std::exception& e) { std::cerr << "Exception: " << e.what() << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; -}; +}; \ No newline at end of file diff --git a/src/rms/InvalidRecordIDException.h b/src/rms/InvalidRecordIDException.h deleted file mode 100644 index a4074ef..0000000 --- a/src/rms/InvalidRecordIDException.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once - -class InvalidRecordIDException : public std::exception { -}; \ No newline at end of file diff --git a/src/rms/RecordComparator.h b/src/rms/RecordComparator.h deleted file mode 100644 index 951dce2..0000000 --- a/src/rms/RecordComparator.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once - -class RecordComparator { -}; \ No newline at end of file diff --git a/src/rms/RecordEnumeration.h b/src/rms/RecordEnumeration.h deleted file mode 100644 index 8fc3859..0000000 --- a/src/rms/RecordEnumeration.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include -#include - -class RecordEnumeration { -public: - virtual int numRecords() = 0; - virtual std::vector nextRecord() = 0; - virtual void reset() = 0; - virtual int nextRecordId() = 0; - virtual void destroy() = 0; -}; diff --git a/src/rms/RecordEnumerationImpl.cpp b/src/rms/RecordEnumerationImpl.cpp deleted file mode 100644 index 33880a0..0000000 --- a/src/rms/RecordEnumerationImpl.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include "RecordEnumerationImpl.h" -#include "RecordStoreException.h" - -RecordEnumerationImpl::RecordEnumerationImpl(std::vector> data) -{ - this->data = data; -} - -RecordEnumerationImpl::RecordEnumerationImpl() -{ -} - -RecordEnumerationImpl::~RecordEnumerationImpl() -{ - data.clear(); -} - -int RecordEnumerationImpl::numRecords() -{ - return data.size(); -} - -std::vector RecordEnumerationImpl::nextRecord() -{ - return data[currentPos++]; -} - -int RecordEnumerationImpl::addRecord(std::vector bytes) -{ - data.push_back(bytes); - return data.size() - 1; -} - -void RecordEnumerationImpl::setRecord(int index, std::vector bytes) -{ - if (static_cast(data.size()) <= index) { - throw RecordStoreException(); - } - data[index] = bytes; -} - -void RecordEnumerationImpl::reset() -{ - currentPos = 0; -} - -int RecordEnumerationImpl::nextRecordId() -{ - if (currentPos >= static_cast(data.size())) { - throw RecordStoreException(); - } - return currentPos; -} - -void RecordEnumerationImpl::destroy() -{ - data.clear(); -} - -void RecordEnumerationImpl::serialize(FileStream* outStream) -{ - size_t temp = data.size(); - outStream->writeVariable(¤tPos); - outStream->writeVariable(&(temp = data.size())); - - for (auto i = data.cbegin(); i != data.cend(); i++) { - outStream->writeVariable(&(temp = i->size())); - for (auto j = i->cbegin(); j != i->cend(); j++) { - int8_t buffer; - outStream->writeVariable(&(buffer = *j)); - } - } -} - -void RecordEnumerationImpl::deserialize(FileStream* inStream) -{ - size_t temp; - inStream->readVariable(¤tPos); - inStream->readVariable(&temp); - data.resize(temp); - - for (size_t i = 0; i < data.size(); ++i) { - inStream->readVariable(&temp); - data[i].resize(temp); - for (size_t j = 0; j < data[i].size(); ++j) { - inStream->readVariable(&data[i][j]); - } - } -} diff --git a/src/rms/RecordEnumerationImpl.h b/src/rms/RecordEnumerationImpl.h deleted file mode 100644 index 8db27f1..0000000 --- a/src/rms/RecordEnumerationImpl.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include -#include - -#include "RecordEnumeration.h" -#include "../utils/FileStream.h" - -class RecordEnumerationImpl : public RecordEnumeration { -private: - // static const int64_t serialVersionUID = 123; - int currentPos = 0; - -public: - RecordEnumerationImpl(std::vector> data); - RecordEnumerationImpl(); - ~RecordEnumerationImpl(); - - int numRecords(); - std::vector nextRecord(); - int addRecord(std::vector bytes); - void setRecord(int index, std::vector bytes); - void reset(); - int nextRecordId(); - void destroy(); - - void serialize(FileStream* outStream); - void deserialize(FileStream* inStream); - - std::vector> data; -}; diff --git a/src/rms/RecordFilter.h b/src/rms/RecordFilter.h deleted file mode 100644 index 5e40044..0000000 --- a/src/rms/RecordFilter.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once - -class RecordFilter { -}; \ No newline at end of file diff --git a/src/rms/RecordStore.cpp b/src/rms/RecordStore.cpp deleted file mode 100644 index 8237ee5..0000000 --- a/src/rms/RecordStore.cpp +++ /dev/null @@ -1,140 +0,0 @@ -#include "RecordStore.h" - -#include -#include -#include -#include -#include -#include - -#ifdef WIN32 -#include -#else -#include -#include -#endif - -#include "RecordStoreException.h" -#include "../utils/FileStream.h" -#include "../utils/String.h" - -RecordStore::RecordStore(std::filesystem::path filePath, RecordEnumerationImpl* records) -{ - this->filePath = filePath; - this->records.reset(records); -} - -RecordEnumeration* RecordStore::enumerateRecords(RecordFilter* filter, RecordComparator* comparator, bool keepUpdated) -{ - assert(filter == nullptr); - assert(comparator == nullptr); - assert(!keepUpdated); - log("enumerateRecords()"); - return records.get(); -} - -void RecordStore::closeRecordStore() -{ - // nothing -} - -int RecordStore::addRecord(std::vector arr, int offset, int numBytes) -{ - log("addRecord()"); - assert(static_cast(arr.size()) == numBytes); - assert(offset == 0); - int id = records->addRecord(arr); - save(); - return id; -} - -void RecordStore::setRecord(int recordId, std::vector arr, int offset, int numBytes) -{ - (void)offset; - (void)numBytes; - records->setRecord(recordId, arr); - save(); -} - -void RecordStore::save() -{ - FileStream outStream(filePath, std::ios::out | std::ios::binary); - records->serialize(&outStream); -} - -RecordEnumerationImpl* RecordStore::load(std::filesystem::path filePath) -{ - RecordEnumerationImpl* temp = new RecordEnumerationImpl(); - FileStream inStream(filePath, std::ios::in | std::ios::binary); - temp->deserialize(&inStream); - return temp; -} - -RecordStore* RecordStore::openRecordStore(std::string name, bool createIfNecessary) -{ - if (opened.find(name) == opened.end()) { - opened[name] = createRecordStore(name, createIfNecessary); - } - - return opened[name].get(); -} - -std::unique_ptr RecordStore::createRecordStore(std::string name, bool createIfNecessary) -{ - log("createRecordStore(" + name + ", " + std::to_string(createIfNecessary) + ")"); - std::filesystem::path filePath = recordStoreDir / std::filesystem::path(name); - - if (std::filesystem::exists(filePath)) { - return std::unique_ptr(new RecordStore(filePath, load(filePath))); - } - - if (createIfNecessary) { - std::filesystem::create_directories(filePath.parent_path()); - - std::unique_ptr rs(new RecordStore(filePath, new RecordEnumerationImpl())); - rs->save(); - return rs; - } else { - throw RecordStoreException(); - } -} - -std::vector RecordStore::listRecordStores() -{ - std::vector result; - - for (const auto& entry : recordStoreDir) - result.push_back(entry.filename().string()); - - log("listRecordStores() = {" + String::join(result, ", ") + "}"); - - return result; -} - -void RecordStore::deleteRecordStore(std::string name) -{ - log("deleteRecordStore(" + name + ")"); - throw std::runtime_error("deleteRecordStore is not implemented"); -} - -void RecordStore::log(std::string s) -{ - std::cout << s << std::endl; -} - -void RecordStore::setRecordStoreDir([[maybe_unused]] const char* progName) -{ -#ifdef WIN32 - const char* base = dirname(strdup(progName)); - recordStoreDir = std::filesystem::path(base) / "recordStore"; -#else - const char* homeDir = getenv("HOME"); - if (!homeDir) - homeDir = getpwuid(getuid())->pw_dir; - - if (!homeDir) - throw std::system_error(errno, std::system_category(), "Error getting home directory"); - - recordStoreDir = std::filesystem::path(homeDir) / ".GravityDefied"; -#endif -} diff --git a/src/rms/RecordStore.h b/src/rms/RecordStore.h deleted file mode 100644 index 821c532..0000000 --- a/src/rms/RecordStore.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#include "RecordEnumerationImpl.h" - -class RecordFilter; -class RecordComparator; - -class RecordStore { -private: - RecordStore(std::filesystem::path filePath, RecordEnumerationImpl* records); - void save(); - static RecordEnumerationImpl* load(std::filesystem::path filePath); - static std::unique_ptr createRecordStore(std::string name, bool createIfNecessary); - static void log(std::string s); - - inline static std::filesystem::path recordStoreDir; - inline static std::unordered_map> opened; - std::filesystem::path filePath; - std::unique_ptr records; - -public: - static void setRecordStoreDir(const char* progName); - static RecordStore* openRecordStore(std::string name, bool createIfNecessary); - void closeRecordStore(); - static void deleteRecordStore(std::string name); - static std::vector listRecordStores(); - RecordEnumeration* enumerateRecords(RecordFilter* filter, RecordComparator* comparator, bool keepUpdated); - int addRecord(std::vector arr, int offset, int numBytes); - void setRecord(int recordId, std::vector arr, int offset, int numBytes); -}; \ No newline at end of file diff --git a/src/rms/RecordStoreException.h b/src/rms/RecordStoreException.h deleted file mode 100644 index 51f8d38..0000000 --- a/src/rms/RecordStoreException.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once - -class RecordStoreException : public std::exception { -}; \ No newline at end of file diff --git a/src/rms/RecordStoreNotOpenException.h b/src/rms/RecordStoreNotOpenException.h deleted file mode 100644 index af44314..0000000 --- a/src/rms/RecordStoreNotOpenException.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once - -class RecordStoreNotOpenException : public std::exception { -}; \ No newline at end of file diff --git a/src/tests/test_Graphics.h b/src/tests/test_Graphics.h index 369b6d2..4852021 100644 --- a/src/tests/test_Graphics.h +++ b/src/tests/test_Graphics.h @@ -3,7 +3,6 @@ #include #include #include -#include #include "../lcdui/Graphics.h" #include "../lcdui/Image.h" diff --git a/src/tests/test_rms.cpp b/src/tests/test_rms.cpp deleted file mode 100644 index 49c3a45..0000000 --- a/src/tests/test_rms.cpp +++ /dev/null @@ -1,34 +0,0 @@ -#include "test_rms.h" - -#include -#include -#include -#include - -#include "../rms/RecordStore.h" -#include "../utils/String.h" - -void test_rms() -{ - std::vector records = RecordStore::listRecordStores(); - - RecordStore* rs = RecordStore::openRecordStore("GDTRStat", true); - - const bool create = false; // to test the rms you have to toggle this variable manually - - if (create) { - std::vector v1 = { 12, 54, 25, -23 }; - std::vector v2 = { 1, 17, 45, -23, -100 }; - rs->addRecord(v1, 0, v1.size()); - rs->addRecord(v2, 0, v2.size()); - } else { - RecordEnumeration* re = rs->enumerateRecords(nullptr, nullptr, false); - re->reset(); - - for (int i = 0; i < re->numRecords(); ++i) { - int recordId = re->nextRecordId(); - std::vector record = re->nextRecord(); - std::cout << "Record " << std::to_string(recordId) << ": {" << String::join(record, ", ") << "}" << std::endl; - } - } -} \ No newline at end of file diff --git a/src/tests/test_rms.h b/src/tests/test_rms.h deleted file mode 100644 index 797f070..0000000 --- a/src/tests/test_rms.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -void test_rms(); \ No newline at end of file diff --git a/src/utils/Hashing.cpp b/src/utils/Hashing.cpp new file mode 100644 index 0000000..8d5e1f8 --- /dev/null +++ b/src/utils/Hashing.cpp @@ -0,0 +1,50 @@ +#include "Hashing.h" + +namespace Hashing { +std::string hexStr(const uint8_t* data, int len) +{ + std::stringstream ss; + ss << std::hex << std::setfill('0'); + + for (int i(0); i < len; ++i) + ss << std::setw(2) << (int)data[i]; + + return ss.str(); +} + +std::string HashFileMD5(const std::string& filename) +{ + std::ifstream file(filename, std::ios::binary); + + if (!file) { + throw std::runtime_error("Failed to open file: " + filename); + } + + EVP_MD_CTX* md5Context = EVP_MD_CTX_new(); + EVP_MD_CTX_init(md5Context); + EVP_DigestInit_ex(md5Context, EVP_md5(), nullptr); + + const size_t bufferSize = 4096; + char buffer[bufferSize]; + + while (!file.eof()) { + file.read(buffer, bufferSize); + EVP_DigestUpdate(md5Context, buffer, file.gcount()); + } + + std::array result; + EVP_DigestFinal_ex(md5Context, result.data(), nullptr); + file.close(); + + EVP_MD_CTX_free(md5Context); + + std::stringstream ss; + ss << std::hex << std::setfill('0'); + + for (const uint8_t i : result) { + ss << std::setw(2) << (int)i; + } + + return ss.str(); +} +} diff --git a/src/utils/Hashing.h b/src/utils/Hashing.h new file mode 100644 index 0000000..4c08278 --- /dev/null +++ b/src/utils/Hashing.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include + +namespace Hashing { + std::string HashFileMD5(const std::string& filename); +}; \ No newline at end of file diff --git a/src/utils/Log.cpp b/src/utils/Log.cpp new file mode 100644 index 0000000..035591c --- /dev/null +++ b/src/utils/Log.cpp @@ -0,0 +1,15 @@ +#include "Log.h" + +Log::LogLevel Log::logLevel = Log::LogLevel::Error; + +void Log::write(const Log::LogLevel level, const char* szFormat, ...) +{ + if (level < Log::logLevel) { + return; + } + + va_list args; + va_start(args, szFormat); + vprintf(szFormat, args); + va_end(args); +} \ No newline at end of file diff --git a/src/utils/Log.h b/src/utils/Log.h new file mode 100644 index 0000000..0e5adcb --- /dev/null +++ b/src/utils/Log.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include +#include + +struct Log { + enum class LogLevel { + All = 0, + Debug = 1, + Info = 2, + Warning = 3, + Error = 4, + Fatal = 5, + None = 6 + }; + + static Log::LogLevel logLevel; + static void write(const Log::LogLevel level, const char* szFormat, ...); +}; \ No newline at end of file diff --git a/src/utils/Time.cpp b/src/utils/Time.cpp index d8c2c4d..af1086f 100644 --- a/src/utils/Time.cpp +++ b/src/utils/Time.cpp @@ -1,4 +1,4 @@ -#include "time.h" +#include "Time.h" #include #include @@ -10,7 +10,23 @@ int64_t currentTimeMillis() std::chrono::system_clock::now().time_since_epoch()).count(); } -void sleep(int64_t ms) +std::string timeToString(const uint64_t time) +{ + const uint32_t minutes = (uint32_t)(time / 100L / 60L); + const uint32_t seconds = (uint32_t)(time / 100L % 60L); + const uint32_t subseconds = (uint32_t)(time % 100L); + + std::stringstream ss; + ss << std::setfill('0') + << std::setw(2) << minutes + << ':' + << std::setw(2) << seconds + << '.' + << std::setw(2) << subseconds; + return ss.str(); +} + +void sleep(const uint64_t ms) { SDL_Delay(ms); } diff --git a/src/utils/Time.h b/src/utils/Time.h index 328f3d1..5bc4634 100644 --- a/src/utils/Time.h +++ b/src/utils/Time.h @@ -1,8 +1,14 @@ #pragma once -#include +#include +#include +#include +#include +#include +#include namespace Time { int64_t currentTimeMillis(); -void sleep(int64_t ms); +std::string timeToString(const uint64_t time); +void sleep(const uint64_t ms); }; \ No newline at end of file