├── .github └── workflows │ └── cmake.yml ├── .gitignore ├── CMakeLists.txt ├── CMakeSettings.json ├── LICENSE.md ├── PRIVACY.md ├── README.md ├── cmake └── CMakeLists.txt ├── media ├── promo-tn.jpg ├── promo.jpg ├── wordle-512-rounded.png ├── wordle-512.png ├── wordle-header-w-1024.png └── wordle-w-header-09.png ├── res ├── data.pfs ├── disclaimer.png ├── font.png ├── icon.bmp ├── icon_mask.bmp ├── mockup.png ├── tiles.png ├── tiles.xcf ├── wordle.aifspec ├── wordle.mbm └── wordle.pkg └── src ├── Android.mk ├── game.c ├── game.h ├── main.c ├── osd.c ├── pfs.c ├── stb_image.h ├── stb_sprintf.h ├── utils.c ├── wordle.cpp ├── wordle.rss ├── wordle_application.cpp ├── wordle_application.h ├── wordle_appui.cpp ├── wordle_appui.h ├── wordle_appview.cpp ├── wordle_appview.h ├── wordle_document.cpp ├── wordle_document.h ├── wordlist_de.c ├── wordlist_en.c ├── wordlist_fi.c ├── wordlist_ru.c └── wordlist_utils.c /.github/workflows/cmake.yml: -------------------------------------------------------------------------------- 1 | name: CMake 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | env: 10 | # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) 11 | BUILD_TYPE: Release 12 | 13 | jobs: 14 | build: 15 | # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac. 16 | # You can convert this to a matrix build if you need cross-platform coverage. 17 | # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix 18 | runs-on: ubuntu-latest 19 | 20 | steps: 21 | - uses: actions/checkout@v3 22 | 23 | - name: Install dependencies 24 | run: | 25 | sudo apt-get update -y -qq 26 | sudo apt-get install libsdl2-dev 27 | 28 | - name: Configure CMake 29 | # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. 30 | # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type 31 | run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DBUILD_ON_ALT_PLATFORM=ON ${{github.workspace}} 32 | 33 | - name: Build 34 | # Build your program with the given configuration 35 | run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} 36 | 37 | #- name: Test 38 | #working-directory: ${{github.workspace}}/build 39 | # Execute tests defined by the CMake configuration. 40 | # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail 41 | #run: ctest -C ${{env.BUILD_TYPE}} 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vs 2 | android 3 | build 4 | out 5 | CMakeSettings.json 6 | tmp 7 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.00) 2 | 3 | # Use CMake or Visual Studio to enable these settings. 4 | option(GENERATE_SIS "Generate Symbian SIS installer" OFF) 5 | option(BUILD_ON_ALT_PLATFORM "Build on alternate platform" OFF) 6 | 7 | if(BUILD_ON_ALT_PLATFORM) 8 | include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/CMakeLists.txt) 9 | return() 10 | elseif(DEFINED ENV{NGAGESDK}) 11 | set(NGAGESDK $ENV{NGAGESDK}) 12 | set(CMAKE_TOOLCHAIN_FILE ${NGAGESDK}/cmake/ngage-toolchain.cmake) 13 | else() 14 | message(FATAL_ERROR "The environment variable NGAGESDK needs to be defined.") 15 | endif() 16 | 17 | project(wordle C CXX) 18 | 19 | set(UID1 0x1000007a) # KExecutableImageUidValue, e32uid.h 20 | set(UID2 0x100039ce) # KAppUidValue16, apadef.h 21 | set(UID3 0x1000abcd) # game.exe UID 22 | set(APP_UID 0x1000dcba) # wordle.app UID 23 | 24 | set(GCC_COMN_DEFS -D__SYMBIAN32__ -D__GCC32__ -D__EPOC32__ -D__MARM__ -D__MARM_ARMI__) 25 | set(GCC_MODE_DEFS -DNDEBUG -D_UNICODE) 26 | set(GCC_DEFS ${GCC_COMN_DEFS} ${GCC_MODE_DEFS}) 27 | 28 | set(game_libs 29 | ${EXTRA_LIB}/libSDL.a 30 | ${EPOC_PLATFORM}/gcc/lib/gcc-lib/arm-epoc-pe/2.9-psion-98r2/libgcc.a 31 | ${EPOC_LIB}/egcc.lib 32 | ${EPOC_LIB}/euser.lib 33 | ${EPOC_LIB}/estlib.lib 34 | ${EPOC_LIB}/ws32.lib 35 | ${EPOC_LIB}/hal.lib 36 | ${EPOC_LIB}/efsrv.lib 37 | ${EPOC_LIB}/scdv.lib 38 | ${EPOC_LIB}/gdi.lib) 39 | 40 | set(wordle_libs 41 | ${EPOC_LIB}/euser.lib 42 | ${EPOC_LIB}/apparc.lib 43 | ${EPOC_LIB}/cone.lib 44 | ${EPOC_LIB}/eikcore.lib 45 | ${EPOC_LIB}/avkon.lib) 46 | 47 | set(SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/src") 48 | set(RESOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/res") 49 | 50 | set(game_sources 51 | "${SRC_DIR}/main.c" 52 | "${SRC_DIR}/game.c" 53 | "${SRC_DIR}/osd.c" 54 | "${SRC_DIR}/pfs.c" 55 | "${SRC_DIR}/utils.c" 56 | "${SRC_DIR}/wordlist_de.c" 57 | "${SRC_DIR}/wordlist_en.c" 58 | "${SRC_DIR}/wordlist_fi.c" 59 | "${SRC_DIR}/wordlist_ru.c" 60 | "${SRC_DIR}/wordlist_utils.c") 61 | 62 | set(game_resources 63 | "tiles.png" 64 | "font.png" 65 | "disclaimer.png") 66 | 67 | set(wordle_sources 68 | "${SRC_DIR}/wordle.cpp" 69 | "${SRC_DIR}/wordle_application.cpp" 70 | "${SRC_DIR}/wordle_appui.cpp" 71 | "${SRC_DIR}/wordle_appview.cpp" 72 | "${SRC_DIR}/wordle_document.cpp") 73 | 74 | add_library(game STATIC ${game_sources}) 75 | add_library(wordle STATIC ${wordle_sources}) 76 | 77 | build_exe(game exe ${UID1} ${UID2} ${UID3} "${game_libs}") 78 | build_dll(wordle app ${UID1} ${UID2} ${APP_UID} "${wordle_libs}") 79 | 80 | pack_assets(${RESOURCE_DIR} "${game_resources}") 81 | build_aif(${RESOURCE_DIR} wordle ${APP_UID}) 82 | build_resource(${SRC_DIR} wordle "") 83 | 84 | if(GENERATE_SIS) 85 | build_sis(${RESOURCE_DIR} wordle) 86 | 87 | add_dependencies( 88 | wordle.sis 89 | game.exe 90 | wordle.aif 91 | wordle.app 92 | wordle.rsc) 93 | endif() 94 | 95 | add_dependencies( 96 | game.exe 97 | game) 98 | 99 | target_compile_definitions( 100 | game 101 | PUBLIC 102 | __EXE__ 103 | FUNCTION_NAME=__FUNCTION__ 104 | STBI_NO_THREAD_LOCALS 105 | ${GCC_DEFS} 106 | UID1=${UID1} 107 | UID2=${UID2} 108 | UID3=${UID3}) 109 | 110 | target_compile_options( 111 | game 112 | PUBLIC 113 | -Wall 114 | -O3) 115 | 116 | target_include_directories( 117 | game 118 | PUBLIC 119 | ${SRC_DIR} 120 | ${SDL_INC_DIR}) 121 | 122 | add_dependencies( 123 | wordle.app 124 | wordle) 125 | 126 | target_compile_definitions( 127 | wordle 128 | PUBLIC 129 | __DLL__ 130 | ${GCC_DEFS} 131 | UID1=${UID1} 132 | UID2=${UID2} 133 | UID3=${APP_UID}) 134 | 135 | target_compile_options( 136 | wordle 137 | PUBLIC 138 | -O3) 139 | 140 | target_include_directories( 141 | wordle 142 | PUBLIC 143 | ${SRC_DIR}) 144 | -------------------------------------------------------------------------------- /CMakeSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Release", 5 | "generator": "Ninja", 6 | "configurationType": "Release", 7 | "inheritEnvironments": [ "msvc_x64_x64" ], 8 | "buildRoot": "${projectDir}\\out\\build\\${name}", 9 | "installRoot": "${projectDir}\\out\\install\\${name}", 10 | "cmakeCommandArgs": "", 11 | "buildCommandArgs": "", 12 | "ctestCommandArgs": "", 13 | "variables": [ 14 | { 15 | "name": "GENERATE_SIS", 16 | "value": "True", 17 | "type": "BOOL" 18 | } 19 | ] 20 | } 21 | ] 22 | } -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2022 Michael Fitzmayer 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a 4 | copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included 12 | in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /PRIVACY.md: -------------------------------------------------------------------------------- 1 | # Privacy Policy 2 | 3 | Michael Fitzmayer built NG Wordl as an Open Source app. This SERVICE is 4 | provided by Michael Fitzmayer at no cost and is intended for use as is. 5 | 6 | This page is used to inform visitors regarding my policies with the 7 | collection, use, and disclosure of Personal Information if anyone 8 | decided to use my Service. 9 | 10 | If you choose to use my Service, then you agree to the collection and 11 | use of information in relation to this policy. The Personal Information 12 | that I collect is used for providing and improving the Service. I will 13 | not use or share your information with anyone except as described in 14 | this Privacy Policy. 15 | 16 | The terms used in this Privacy Policy have the same meanings as in our 17 | Terms and Conditions, which is accessible at NG Wordl unless otherwise 18 | defined in this Privacy Policy. 19 | 20 | ## Information Collection and Use 21 | 22 | For a better experience, while using our Service, I may require you to 23 | provide us with certain personally identifiable information. The 24 | information that I request will be retained on your device and is not 25 | collected by me in any way. 26 | 27 | The app does use third party services that may collect information used 28 | to identify you. 29 | 30 | Link to privacy policy of third party service providers used by the app 31 | 32 | * [Google Play Services](https://www.google.com/policies/privacy/) 33 | 34 | ## Log Data 35 | 36 | I want to inform you that whenever you use my Service, in a case of an 37 | error in the app I collect data and information (through third party 38 | products) on your phone called Log Data. This Log Data may include 39 | information such as your device Internet Protocol ("IP") address, device 40 | name, operating system version, the configuration of the app when 41 | utilizing my Service, the time and date of your use of the Service, and 42 | other statistics. 43 | 44 | ## Cookies 45 | 46 | Cookies are files with a small amount of data that are commonly used as 47 | anonymous unique identifiers. These are sent to your browser from the 48 | websites that you visit and are stored on your device's internal memory. 49 | 50 | This Service does not use these "cookies" explicitly. However, the app 51 | may use third party code and libraries that use "cookies" to collect 52 | information and improve their services. You have the option to either 53 | accept or refuse these cookies and know when a cookie is being sent to 54 | your device. If you choose to refuse our cookies, you may not be able to 55 | use some portions of this Service. 56 | 57 | ## Service Providers 58 | 59 | I may employ third-party companies and individuals due to the following 60 | reasons: 61 | 62 | * To facilitate our Service; 63 | * To provide the Service on our behalf; 64 | * To perform Service-related services; or 65 | * To assist us in analyzing how our Service is used. 66 | 67 | I want to inform users of this Service that these third parties have 68 | access to your Personal Information. The reason is to perform the tasks 69 | assigned to them on our behalf. However, they are obligated not to 70 | disclose or use the information for any other purpose. 71 | 72 | ## Security 73 | 74 | I value your trust in providing us your Personal Information, thus we 75 | are striving to use commercially acceptable means of protecting it. But 76 | remember that no method of transmission over the internet, or method of 77 | electronic storage is 100% secure and reliable, and I cannot guarantee 78 | its absolute security. 79 | 80 | ## Links to Other Sites 81 | 82 | This Service may contain links to other sites. If you click on a 83 | third-party link, you will be directed to that site. Note that these 84 | external sites are not operated by me. Therefore, I strongly advise you 85 | to review the Privacy Policy of these websites. I have no control over 86 | and assume no responsibility for the content, privacy policies, or 87 | practices of any third-party sites or services. 88 | 89 | ## Children’s Privacy 90 | 91 | These Services do not address anyone under the age of 13. I do not 92 | knowingly collect personally identifiable information from children 93 | under 13. In the case I discover that a child under 13 has provided me 94 | with personal information, I immediately delete this from our servers. 95 | If you are a parent or guardian and you are aware that your child has 96 | provided us with personal information, please contact me so that I will 97 | be able to do necessary actions. 98 | 99 | ## Changes to This Privacy Policy 100 | 101 | I may update our Privacy Policy from time to time. Thus, you are 102 | advised to review this page periodically for any changes. I will notify 103 | you of any changes by posting the new Privacy Policy on this page. 104 | These changes are effective immediately after they are posted on this 105 | page. 106 | 107 | ## Contact Us 108 | 109 | If you have any questions or suggestions about my Privacy Policy, do not 110 | hesitate to contact me. 111 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Wordle 2 | 3 | [![Codacy Badge](https://app.codacy.com/project/badge/Grade/d91244e94a37417fbd1649ae5f2def6f)](https://www.codacy.com/gh/ngagesdk/wordle/dashboard?utm_source=github.com&utm_medium=referral&utm_content=ngagesdk/wordle&utm_campaign=Badge_Grade) 4 | [![CMake](https://github.com/ngagesdk/wordle/actions/workflows/cmake.yml/badge.svg)](https://github.com/ngagesdk/wordle/actions/workflows/cmake.yml) 5 | 6 | A portable clone of Wordle designed for the Nokia N-Gage. 7 | 8 | [![Wordle](https://raw.githubusercontent.com/ngagesdk/wordle/master/media/promo-tn.jpg)](https://raw.githubusercontent.com/ngagesdk/wordle/master/media/promo.jpg?raw=true "Wordle") 9 | 10 | ## Features 11 | 12 | - Multiple language settings such as English, Russian (СЛОВО), German 13 | (WÖRDL) and Finnish (SANIS). 14 | 15 | - Play the NYT's Daily Word! 16 | 17 | - Enjoy a stress-free game with as many tries as you want in endless 18 | mode. 19 | 20 | - Tailored for the N-Gage, but highly portable as the entire game is 21 | written in C89 and only depends on [SDL 22 | 2.0.x](https://github.com/libsdl-org/SDL). 23 | 24 | - A web version of the game can be found on my website: [N-Gage 25 | Wordle](https://mupf.dev/wordle/). 26 | 27 | - An Android version is available on Google Play: 28 | 29 | [![Get it on Google Play](https://play.google.com/intl/en_us/badges/images/generic/en_badge_web_generic.png)](https://play.google.com/store/apps/details?id=de.mupfelofen.wordle) 30 | 31 | ## Compiling 32 | 33 | First clone the repository: 34 | ```bash 35 | git clone https://github.com/ngagesdk/wordle.git 36 | ``` 37 | 38 | ### N-Gage 39 | 40 | The easiest way to get Wordle up and running is Visual Studio 2022 with 41 | [C++ CMake tools for 42 | Windows](https://docs.microsoft.com/en-us/cpp/build/cmake-projects-in-visual-studio) 43 | installed. If you have installed and set up the [N-Gage 44 | SDK.](https://github.com/ngagesdk/ngage-toolchain) in advance, simply 45 | open the cloned repository via `File -> Open -> Folder`. Everything 46 | else is set up automatically. 47 | 48 | ### Other Platforms 49 | 50 | Wordle can also be compiled for other platforms such as Linux, Windows 51 | or any other platform [supported by 52 | SDL2](https://wiki.libsdl.org/Installation#supported_platforms). For 53 | Windows, I recommend using [MSYS2](https://www.msys2.org/). 54 | 55 | Wordle can be compiled with the included CMake configuration. E.g: 56 | ```bash 57 | mkdir build 58 | cd build 59 | cp ../res/data.pfs . 60 | cmake -DBUILD_ON_ALT_PLATFORM=ON .. 61 | make 62 | ./wordle.exe 63 | ``` 64 | 65 | ## Licence and Credits 66 | 67 | - Special thanks to Josh Wardle for developing this brilliant game in 68 | the first place. 69 | 70 | - Packed file loader by [Daniel 71 | Monteiro](https://montyontherun.itch.io/). 72 | 73 | - stb by Sean Barrett is licensed under "The MIT License". See the file 74 | [LICENSE](https://github.com/nothings/stb/blob/master/LICENSE) for 75 | details. 76 | 77 | - The application menu icon has been designed by geekahedron from the 78 | [Wordleverse](https://discord.com/invite/FdQKzenz) Discord community. 79 | 80 | - The Android [App icon and Feature graphic](media/) by [Dan 81 | Whelan](https://danwhelan.ie) is licensed under a Creative Commons 82 | [Attribution-ShareAlike 4.0 International (CC BY-SA 83 | 4.0)](https://creativecommons.org/licenses/by-sa/4.0/) license. 84 | 85 | - The finnish word list is based on the "nykysuomen sanalista" by 86 | [Kotus](https://kaino.kotus.fi/sanat/nykysuomi/), licensed under a [CC 87 | BY 3.0](https://creativecommons.org/licenses/by/3.0/deed.fi) license. 88 | 89 | - [ASCII Bitmap Font 90 | "cellphone"](https://opengameart.org/content/ascii-bitmap-font-cellphone) 91 | by domsson. 92 | 93 | - This project's source code is, unless stated otherwise, licensed under 94 | the "The MIT License". See the file [LICENSE.md](LICENSE.md) for 95 | details. 96 | -------------------------------------------------------------------------------- /cmake/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | 3 | project(wordle C) 4 | 5 | find_package(SDL2 REQUIRED) 6 | 7 | set(SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/src") 8 | set(RESOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/res") 9 | 10 | if(EMSCRIPTEN) 11 | configure_file(${RESOURCE_DIR}/data.pfs ${CMAKE_CURRENT_BINARY_DIR}/data.pfs COPYONLY) 12 | set(CMAKE_EXECUTABLE_SUFFIX ".html") 13 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s USE_SDL=2") 14 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -s USE_SDL=2") 15 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s USE_SDL=2") 16 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --preload-file data.pfs") 17 | endif() 18 | 19 | include_directories( 20 | ${SDL2_INCLUDE_DIRS} 21 | ${SRC_DIR}) 22 | 23 | set(wordle_sources 24 | "${SRC_DIR}/main.c" 25 | "${SRC_DIR}/game.c" 26 | "${SRC_DIR}/osd.c" 27 | "${SRC_DIR}/pfs.c" 28 | "${SRC_DIR}/utils.c" 29 | "${SRC_DIR}/wordlist_de.c" 30 | "${SRC_DIR}/wordlist_en.c" 31 | "${SRC_DIR}/wordlist_fi.c" 32 | "${SRC_DIR}/wordlist_ru.c" 33 | "${SRC_DIR}/wordlist_utils.c") 34 | 35 | add_executable( 36 | wordle 37 | ${wordle_sources}) 38 | 39 | target_link_libraries( 40 | wordle 41 | ${SDL2_LIBRARIES} 42 | ${SDL_LIBS}) 43 | 44 | target_link_options( 45 | wordle 46 | PUBLIC 47 | -static-libgcc 48 | -static-libstdc++) 49 | 50 | if(UNIX) 51 | target_link_libraries(wordle m) 52 | endif(UNIX) 53 | 54 | target_compile_options( 55 | wordle 56 | PUBLIC 57 | -O3 58 | -Wall 59 | -Wextra 60 | -Wpedantic) 61 | 62 | target_compile_definitions( 63 | wordle 64 | PUBLIC 65 | SDL_MAIN_HANDLED) 66 | -------------------------------------------------------------------------------- /media/promo-tn.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngagesdk/wordle/bd1962baa1e347f7e4344c02296d7e073cbc16d7/media/promo-tn.jpg -------------------------------------------------------------------------------- /media/promo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngagesdk/wordle/bd1962baa1e347f7e4344c02296d7e073cbc16d7/media/promo.jpg -------------------------------------------------------------------------------- /media/wordle-512-rounded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngagesdk/wordle/bd1962baa1e347f7e4344c02296d7e073cbc16d7/media/wordle-512-rounded.png -------------------------------------------------------------------------------- /media/wordle-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngagesdk/wordle/bd1962baa1e347f7e4344c02296d7e073cbc16d7/media/wordle-512.png -------------------------------------------------------------------------------- /media/wordle-header-w-1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngagesdk/wordle/bd1962baa1e347f7e4344c02296d7e073cbc16d7/media/wordle-header-w-1024.png -------------------------------------------------------------------------------- /media/wordle-w-header-09.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngagesdk/wordle/bd1962baa1e347f7e4344c02296d7e073cbc16d7/media/wordle-w-header-09.png -------------------------------------------------------------------------------- /res/data.pfs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngagesdk/wordle/bd1962baa1e347f7e4344c02296d7e073cbc16d7/res/data.pfs -------------------------------------------------------------------------------- /res/disclaimer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngagesdk/wordle/bd1962baa1e347f7e4344c02296d7e073cbc16d7/res/disclaimer.png -------------------------------------------------------------------------------- /res/font.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngagesdk/wordle/bd1962baa1e347f7e4344c02296d7e073cbc16d7/res/font.png -------------------------------------------------------------------------------- /res/icon.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngagesdk/wordle/bd1962baa1e347f7e4344c02296d7e073cbc16d7/res/icon.bmp -------------------------------------------------------------------------------- /res/icon_mask.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngagesdk/wordle/bd1962baa1e347f7e4344c02296d7e073cbc16d7/res/icon_mask.bmp -------------------------------------------------------------------------------- /res/mockup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngagesdk/wordle/bd1962baa1e347f7e4344c02296d7e073cbc16d7/res/mockup.png -------------------------------------------------------------------------------- /res/tiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngagesdk/wordle/bd1962baa1e347f7e4344c02296d7e073cbc16d7/res/tiles.png -------------------------------------------------------------------------------- /res/tiles.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngagesdk/wordle/bd1962baa1e347f7e4344c02296d7e073cbc16d7/res/tiles.xcf -------------------------------------------------------------------------------- /res/wordle.aifspec: -------------------------------------------------------------------------------- 1 | mbmfile=wordle.mbm 2 | ELangEnglish=Wordle 3 | -------------------------------------------------------------------------------- /res/wordle.mbm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngagesdk/wordle/bd1962baa1e347f7e4344c02296d7e073cbc16d7/res/wordle.mbm -------------------------------------------------------------------------------- /res/wordle.pkg: -------------------------------------------------------------------------------- 1 | ; wordle.pkg 2 | ; 3 | ;Language - standard language definitions 4 | &EN 5 | 6 | ; standard SIS file header 7 | #{"Wordle"},(0x1000DCBA),1,3,0 8 | 9 | ;Supports Series 60 v 0.9 10 | (0x101F6F88), 0, 0, 0, {"Series60ProductID"} 11 | 12 | ; 13 | "..\res\data.pfs"-"E:\System\Apps\wordle\data.pfs" 14 | "..\out\build\Release\game.exe"-"E:\System\Apps\wordle\game.exe" 15 | "..\out\build\Release\wordle.aif"-"E:\System\Apps\wordle\wordle.aif" 16 | "..\out\build\Release\wordle.app"-"E:\System\Apps\wordle\wordle.app" 17 | "..\out\build\Release\wordle.rsc"-"E:\System\Apps\wordle\wordle.rsc" 18 | -------------------------------------------------------------------------------- /src/Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(call my-dir) 2 | 3 | include $(CLEAR_VARS) 4 | 5 | LOCAL_MODULE := main 6 | 7 | SDL_PATH := ../SDL 8 | 9 | LOCAL_C_INCLUDES := $(LOCAL_PATH)/$(SDL_PATH)/include 10 | 11 | # Add your application source files here... 12 | LOCAL_SRC_FILES := main.c game.c osd.c pfs.c utils.c wordlist_de.c wordlist_en.c wordlist_fi.c wordlist_ru.c wordlist_utils.c 13 | 14 | LOCAL_SHARED_LIBRARIES := SDL2 15 | 16 | LOCAL_LDLIBS := -lGLESv1_CM -lGLESv2 -lOpenSLES -llog -landroid 17 | 18 | include $(BUILD_SHARED_LIBRARY) 19 | -------------------------------------------------------------------------------- /src/game.c: -------------------------------------------------------------------------------- 1 | /** @file game.c 2 | * 3 | * A clone of Wordle for the Nokie N-Gage. 4 | * 5 | * Copyright (c) 2022, Michael Fitzmayer. All rights reserved. 6 | * SPDX-License-Identifier: MIT 7 | * 8 | **/ 9 | 10 | #include 11 | #include 12 | #include 13 | #include "game.h" 14 | 15 | #define STB_SPRINTF_IMPLEMENTATION 16 | #include "stb_sprintf.h" 17 | 18 | #if defined __SYMBIAN32__ 19 | #define SAVE_FILE "C:\\wordle.sav" 20 | #define DAILY_SAVE_FILE "C:\\daily.sav" 21 | #else 22 | #define SAVE_FILE "wordle.sav" 23 | #define DAILY_SAVE_FILE "daily.sav" 24 | #endif 25 | 26 | #if ! SDL_VERSION_ATLEAST(2, 0, 20) 27 | int SDL_isalpha(int x) { return isalpha(x); } 28 | #define SDL_clamp(x, a, b) (((x) < (a)) ? (a) : (((x) > (b)) ? (b) : (x))) 29 | #endif 30 | 31 | static event_t get_current_event(game_t* core); 32 | static void clear_tiles(SDL_bool clear_state, game_t* core); 33 | static void delete_letter(game_t* core); 34 | static int draw_tiles(game_t* core); 35 | static void get_index_limits(int* lower_limit, int* upper_limit, game_t* core); 36 | static void go_back_to_menu(game_t* core); 37 | static void goto_next_letter(game_t* core); 38 | static SDL_bool has_game_ended(game_t* core); 39 | static void move_rows_up(game_t* core); 40 | static void reset_game(SDL_bool nyt_mode, game_t* core); 41 | static void select_next_letter(const unsigned char start_char, const unsigned char end_char, game_t* core); 42 | static void select_previous_letter(const unsigned char start_char, const unsigned char end_char, game_t* core); 43 | static void select_utf8_letter(const Uint16 *text, game_t* core); 44 | static void show_results(game_t* core); 45 | static void set_zoom_factor(game_t* core); 46 | static void set_render_offset(game_t* core); 47 | static void toggle_fullscreen(game_t* core); 48 | 49 | extern void init_file_reader(const char * dataFilePath); 50 | extern size_t size_of_file(const char * path); 51 | extern Uint8 *load_binary_file_from_path(const char * path); 52 | extern int osd_init(game_t* core); 53 | extern void osd_print(const char* display_text, const int pos_x, const int pos_y, game_t* core); 54 | extern int load_texture_from_file(const char* file_name, SDL_Texture** texture, game_t* core); 55 | extern Uint32 generate_hash(const unsigned char* name); 56 | extern unsigned int xorshift(unsigned int* xs); 57 | extern void set_language(const lang_t language, const SDL_bool set_title_screen, game_t* core); 58 | extern void set_next_language(game_t* core); 59 | extern unsigned int get_nyt_daily_index(void); 60 | extern void get_valid_answer(unsigned char valid_answer[6], game_t* core); 61 | extern SDL_bool is_guess_allowed(const unsigned char* guess, game_t* core); 62 | extern void validate_current_guess(SDL_bool* is_won, game_t* core); 63 | 64 | int game_init(const char* resource_file, const char* title, game_t** core) 65 | { 66 | int status = 0; 67 | Uint32 renderer_flags = SDL_RENDERER_SOFTWARE; 68 | 69 | *core = (game_t*)calloc(1, sizeof(struct game)); 70 | if (NULL == *core) 71 | { 72 | return 1; 73 | } 74 | 75 | if (0 != SDL_Init(SDL_INIT_VIDEO)) 76 | { 77 | return 1; 78 | } 79 | 80 | #ifndef __SYMBIAN32__ 81 | renderer_flags = SDL_RENDERER_ACCELERATED; 82 | # ifndef __ANDROID__ 83 | renderer_flags |= SDL_RENDERER_PRESENTVSYNC; 84 | # endif 85 | #endif 86 | 87 | set_zoom_factor((*core)); 88 | 89 | (*core)->window = SDL_CreateWindow( 90 | title, 91 | SDL_WINDOWPOS_UNDEFINED, 92 | SDL_WINDOWPOS_UNDEFINED, 93 | WINDOW_WIDTH * (*core)->zoom_factor, 94 | WINDOW_HEIGHT * (*core)->zoom_factor, 95 | 0); 96 | 97 | if (NULL == (*core)->window) 98 | { 99 | return 1; 100 | } 101 | 102 | #ifdef __ANDROID__ 103 | toggle_fullscreen((*core)); 104 | #endif 105 | 106 | (*core)->renderer = SDL_CreateRenderer((*core)->window, -1, renderer_flags); 107 | if (NULL == (*core)->renderer) 108 | { 109 | if (NULL == (*core)->renderer) 110 | { 111 | SDL_DestroyWindow((*core)->window); 112 | return 1; 113 | } 114 | } 115 | if (0 != SDL_RenderSetIntegerScale((*core)->renderer, SDL_TRUE)) 116 | { 117 | /* Nothing to do here. */ 118 | } 119 | 120 | init_file_reader(resource_file); 121 | 122 | (*core)->render_target = SDL_CreateTexture( 123 | (*core)->renderer, 124 | SDL_PIXELFORMAT_RGB444, 125 | SDL_TEXTUREACCESS_TARGET, 126 | WINDOW_WIDTH * (*core)->zoom_factor, 127 | WINDOW_HEIGHT * (*core)->zoom_factor); 128 | 129 | if (NULL == (*core)->render_target) 130 | { 131 | return 1; 132 | } 133 | 134 | if (0 > SDL_SetRenderTarget((*core)->renderer, (*core)->render_target)) 135 | { 136 | SDL_DestroyTexture((*core)->render_target); 137 | return 1; 138 | } 139 | SDL_SetRenderDrawColor((*core)->renderer, 0xff, 0xff, 0xff, 0x00); 140 | SDL_RenderClear((*core)->renderer); 141 | 142 | status = load_texture_from_file((const char*)"tiles.png", &(*core)->tile_texture, (*core)); 143 | if (0 != status) 144 | { 145 | return status; 146 | } 147 | 148 | #ifdef __ANDROID__ 149 | status = load_texture_from_file((const char*)"disclaimer.png", &(*core)->disclaimer_texture, (*core)); 150 | if (0 != status) 151 | { 152 | return status; 153 | } 154 | 155 | { 156 | SDL_RWops* save_file = NULL; 157 | char save_file_path[256] = { 0 }; 158 | stbsp_snprintf(save_file_path, 256, "%s/%s", SDL_AndroidGetInternalStoragePath(), SAVE_FILE); 159 | save_file = SDL_RWFromFile(save_file_path, "rb"); 160 | 161 | if (NULL == save_file) 162 | { 163 | (*core)->show_disclaimer = SDL_TRUE; 164 | } 165 | else 166 | { 167 | SDL_RWclose(save_file); 168 | } 169 | } 170 | #endif 171 | 172 | status = osd_init((*core)); 173 | if (0 != status) 174 | { 175 | return status; 176 | } 177 | 178 | set_language(LANG_ENGLISH, SDL_TRUE, (*core)); 179 | srand(time(0)); 180 | 181 | (*core)->seed = (unsigned int)rand(); 182 | (*core)->current_index = 27; 183 | (*core)->show_menu = SDL_TRUE; 184 | (*core)->is_running = SDL_TRUE; 185 | 186 | return status; 187 | } 188 | 189 | SDL_bool game_is_running(game_t* core) 190 | { 191 | if (NULL == core) 192 | { 193 | return SDL_FALSE; 194 | } 195 | else 196 | { 197 | return core->is_running; 198 | } 199 | } 200 | 201 | int game_update(game_t *core) 202 | { 203 | int status = 0; 204 | SDL_bool redraw_tiles = SDL_FALSE; 205 | SDL_Rect dst = { 0, 0, WINDOW_WIDTH * core->zoom_factor, WINDOW_HEIGHT * core->zoom_factor }; 206 | Uint32 delta_time = 0; 207 | event_t event = get_current_event(core); 208 | unsigned char start_char; 209 | unsigned char end_char; 210 | 211 | if (NULL == core) 212 | { 213 | return 1; 214 | } 215 | 216 | if (0 == core->tile[0].letter) 217 | { 218 | redraw_tiles = SDL_TRUE; 219 | core->tile[0].letter = 0; 220 | } 221 | 222 | if (SDL_TRUE == core->show_menu) 223 | { 224 | core->show_stats = SDL_FALSE; 225 | redraw_tiles = SDL_TRUE; 226 | } 227 | 228 | if (EVENT_NONE != event) 229 | { 230 | #ifdef __ANDROID__ 231 | core->show_disclaimer = SDL_FALSE; 232 | #endif 233 | redraw_tiles = SDL_TRUE; 234 | } 235 | 236 | core->time_b = core->time_a; 237 | core->time_a = SDL_GetTicks(); 238 | 239 | if (core->time_a > core->time_b) 240 | { 241 | delta_time = core->time_a - core->time_b; 242 | } 243 | else 244 | { 245 | delta_time = core->time_b - core->time_a; 246 | } 247 | core->time_since_last_frame = delta_time; 248 | 249 | switch (event) 250 | { 251 | default: 252 | case EVENT_NONE: 253 | break; 254 | case EVENT_KEY_0: 255 | if (0 != core->wordlist.special_chars[0]) 256 | { 257 | unsigned char* current_letter = &core->tile[core->current_index].letter; 258 | 259 | start_char = core->wordlist.first_letter; 260 | end_char = core->wordlist.last_letter; 261 | 262 | if (*current_letter >= start_char && *current_letter < end_char) 263 | { 264 | *current_letter = core->wordlist.special_chars[0]; 265 | } 266 | else 267 | { 268 | static unsigned int special_char_index = 0; 269 | 270 | special_char_index += 1; 271 | if (0x00 == core->wordlist.special_chars[special_char_index]) 272 | { 273 | special_char_index = 0; 274 | } 275 | *current_letter = core->wordlist.special_chars[special_char_index]; 276 | } 277 | } 278 | break; 279 | case EVENT_KEY_2: 280 | if (SDL_TRUE == core->wordlist.is_cyrillic) 281 | { 282 | start_char = 0xc0; // А 283 | end_char = 0xc3; // Г 284 | } 285 | else 286 | { 287 | start_char = 'A'; 288 | end_char = 'C'; 289 | } 290 | select_next_letter(start_char, end_char, core); 291 | break; 292 | case EVENT_KEY_3: 293 | if (SDL_TRUE == core->wordlist.is_cyrillic) 294 | { 295 | start_char = 0xc4; // Д 296 | end_char = 0xc7; // З 297 | } 298 | else 299 | { 300 | start_char = 'D'; 301 | end_char = 'F'; 302 | } 303 | select_next_letter(start_char, end_char, core); 304 | break; 305 | case EVENT_KEY_4: 306 | if (SDL_TRUE == core->wordlist.is_cyrillic) 307 | { 308 | start_char = 0xc8; // И 309 | end_char = 0xca; // Л 310 | } 311 | else 312 | { 313 | start_char = 'G'; 314 | end_char = 'I'; 315 | } 316 | select_next_letter(start_char, end_char, core); 317 | break; 318 | case EVENT_KEY_5: 319 | if (SDL_TRUE == core->wordlist.is_cyrillic) 320 | { 321 | start_char = 0xcb; // М 322 | end_char = 0xcf; // П 323 | } 324 | else 325 | { 326 | start_char = 'J'; 327 | end_char = 'L'; 328 | } 329 | select_next_letter(start_char, end_char, core); 330 | break; 331 | case EVENT_KEY_6: 332 | if (SDL_TRUE == core->wordlist.is_cyrillic) 333 | { 334 | start_char = 0xd0; // Р 335 | end_char = 0xd3; // У 336 | } 337 | else 338 | { 339 | start_char = 'M'; 340 | end_char = 'O'; 341 | } 342 | select_next_letter(start_char, end_char, core); 343 | break; 344 | case EVENT_KEY_7: 345 | if (SDL_TRUE == core->wordlist.is_cyrillic) 346 | { 347 | start_char = 0xd4; // Ф 348 | end_char = 0xd7; // Ч 349 | } 350 | else 351 | { 352 | start_char = 'P'; 353 | end_char = 'S'; 354 | } 355 | select_next_letter(start_char, end_char, core); 356 | break; 357 | case EVENT_KEY_8: 358 | if (SDL_TRUE == core->wordlist.is_cyrillic) 359 | { 360 | start_char = 0xd8; // Ш 361 | end_char = 0xdb; // Ы 362 | } 363 | else 364 | { 365 | start_char = 'T'; 366 | end_char = 'V'; 367 | } 368 | select_next_letter(start_char, end_char, core); 369 | break; 370 | case EVENT_KEY_9: 371 | if (SDL_TRUE == core->wordlist.is_cyrillic) 372 | { 373 | start_char = 0xdc; // Ь 374 | end_char = 0xdf; // Я 375 | } 376 | else 377 | { 378 | start_char = 'W'; 379 | end_char = 'Z'; 380 | } 381 | select_next_letter(start_char, end_char, core); 382 | break; 383 | case EVENT_CONFIRM: 384 | if (core->attempt < 6) 385 | { 386 | if (0 == ((core->current_index + 1) % 5)) 387 | { 388 | int index; 389 | int letter_index; 390 | int end_index; 391 | 392 | get_index_limits(&index, &end_index, core); 393 | 394 | for (letter_index = 0; letter_index < 5; letter_index += 1) 395 | { 396 | core->current_guess[letter_index] = core->tile[index].letter; 397 | index += 1; 398 | } 399 | 400 | if (SDL_TRUE == is_guess_allowed(core->current_guess, core)) 401 | { 402 | if (SDL_TRUE == core->nyt_mode) 403 | { 404 | game_save(core); 405 | } 406 | 407 | if (SDL_TRUE == has_game_ended(core)) 408 | { 409 | if (SDL_TRUE == core->nyt_mode) 410 | { 411 | core->nyt_final_attempt = core->attempt + 1; 412 | core->nyt_has_ended = SDL_TRUE; 413 | game_save(core); 414 | } 415 | show_results(core); 416 | } 417 | else 418 | { 419 | if ((SDL_TRUE == core->endless_mode) && (4 == core->attempt)) 420 | { 421 | move_rows_up(core); 422 | core->current_index -= 5; 423 | } 424 | else 425 | { 426 | core->attempt += 1; 427 | } 428 | goto_next_letter(core); 429 | } 430 | } 431 | else 432 | { 433 | // Word not valid: shake row? 434 | } 435 | } 436 | } 437 | else 438 | { 439 | game_save(core); 440 | clear_tiles(SDL_TRUE, core); 441 | core->current_index = 27; 442 | set_language(core->wordlist.language, SDL_TRUE, core); 443 | } 444 | break; 445 | case EVENT_CONFIRM_LETTER: 446 | goto_next_letter(core); 447 | break; 448 | case EVENT_DELETE_LETTER: 449 | delete_letter(core); 450 | switch((core->current_index)) 451 | { 452 | case 0: 453 | case 5: 454 | case 10: 455 | case 15: 456 | case 20: 457 | case 25: 458 | if (0 == core->tile[core->current_index].letter) 459 | { 460 | go_back_to_menu(core); 461 | } 462 | break; 463 | default: 464 | break; 465 | } 466 | break; 467 | case EVENT_NEXT_LETTER: 468 | start_char = core->wordlist.first_letter; 469 | end_char = core->wordlist.last_letter; 470 | select_next_letter(start_char, end_char, core); 471 | break; 472 | case EVENT_PREV_LETTER: 473 | start_char = core->wordlist.first_letter; 474 | end_char = core->wordlist.last_letter; 475 | select_previous_letter(start_char, end_char, core); 476 | break; 477 | case EVENT_TEXTINPUT: 478 | if (SDL_TRUE != core->show_menu) 479 | { 480 | select_utf8_letter((const Uint16*)core->event.text.text, core); 481 | } 482 | break; 483 | case EVENT_CONFIRM_ENDLESS_MODE: 484 | core->endless_mode = SDL_TRUE; 485 | core->nyt_mode = SDL_FALSE; 486 | reset_game(SDL_FALSE, core); 487 | break; 488 | case EVENT_CONFIRM_LOAD_GAME: 489 | core->endless_mode = SDL_FALSE; 490 | core->nyt_mode = SDL_FALSE; 491 | game_load(SDL_FALSE, core); 492 | break; 493 | case EVENT_CONFIRM_NEW_GAME: 494 | core->endless_mode = SDL_FALSE; 495 | core->nyt_mode = SDL_FALSE; 496 | game_save(core); 497 | reset_game(SDL_FALSE, core); 498 | break; 499 | case EVENT_CONFIRM_NYT_MODE: 500 | core->endless_mode = SDL_FALSE; 501 | core->nyt_mode = SDL_TRUE; 502 | game_load(SDL_TRUE, core); 503 | 504 | if (SDL_TRUE == core->nyt_has_ended) 505 | { 506 | show_results(core); 507 | } 508 | break; 509 | case EVENT_CONFIRM_SET_LANG: 510 | core->language_set_once = SDL_TRUE; 511 | set_next_language(core); 512 | break; 513 | case EVENT_MENU_NEXT: 514 | core->current_index += 1; 515 | if (core->current_index > 29) 516 | { 517 | core->current_index = 25; 518 | } 519 | break; 520 | case EVENT_MENU_PREV: 521 | core->current_index -= 1; 522 | if (core->current_index < 25) 523 | { 524 | core->current_index = 29; 525 | } 526 | break; 527 | case EVENT_MENU_SELECT_ENDLESS_MODE: 528 | core->selected_mode = MODE_ENDLESS; 529 | break; 530 | case EVENT_MENU_SELECT_NYT_MODE: 531 | core->selected_mode = MODE_NYT; 532 | break; 533 | case EVENT_MENU_SELECT_NEW_GAME: 534 | core->current_index = 25; 535 | break; 536 | case EVENT_MENU_SELECT_QUIT: 537 | core->current_index = 29; 538 | break; 539 | case EVENT_BACK: 540 | go_back_to_menu(core); 541 | break; 542 | case EVENT_QUIT: 543 | core->is_running = SDL_FALSE; 544 | return 0; 545 | case EVENT_TOGGLE_FS: 546 | toggle_fullscreen(core); 547 | break; 548 | } 549 | 550 | if (SDL_TRUE == redraw_tiles) 551 | { 552 | status = draw_tiles(core); 553 | if (0 != status) 554 | { 555 | return status; 556 | } 557 | 558 | if (SDL_TRUE == core->show_stats) 559 | { 560 | char stats[16] = { 0 }; 561 | int stats_pos_x = 64; 562 | 563 | if (get_nyt_daily_index() >= 1000) 564 | { 565 | stats_pos_x = 57; 566 | } 567 | 568 | stbsp_snprintf(stats, 16, "Wordle %u %1u/6", get_nyt_daily_index(), core->nyt_final_attempt); 569 | osd_print(stats, stats_pos_x, 12, core); 570 | } 571 | } 572 | 573 | if (0 > SDL_SetRenderTarget(core->renderer, NULL)) 574 | { 575 | return 1; 576 | } 577 | 578 | dst.x += core->render_offset_x; 579 | dst.y += core->render_offset_y; 580 | 581 | if (0 > SDL_RenderCopy(core->renderer, core->render_target, NULL, &dst)) 582 | { 583 | return 1; 584 | } 585 | 586 | #ifdef __ANDROID__ 587 | if (SDL_TRUE == core->show_disclaimer) 588 | { 589 | if (0 > SDL_RenderCopy(core->renderer, core->disclaimer_texture, NULL, &dst)) 590 | { 591 | return 1; 592 | } 593 | } 594 | #endif 595 | 596 | SDL_SetRenderDrawColor(core->renderer, 0xff, 0xff, 0xff, 0x00); 597 | SDL_RenderPresent(core->renderer); 598 | SDL_RenderClear(core->renderer); 599 | 600 | return status; 601 | } 602 | 603 | void game_quit(game_t* core) 604 | { 605 | #ifdef __ANDROID__ 606 | if (core->disclaimer_texture) 607 | { 608 | SDL_DestroyTexture(core->disclaimer_texture); 609 | core->disclaimer_texture = NULL; 610 | } 611 | #endif 612 | 613 | if (core->tile_texture) 614 | { 615 | SDL_DestroyTexture(core->tile_texture); 616 | core->tile_texture = NULL; 617 | } 618 | 619 | if (core->render_target) 620 | { 621 | SDL_DestroyTexture(core->render_target); 622 | core->render_target = NULL; 623 | } 624 | 625 | if (core->window) 626 | { 627 | SDL_DestroyWindow(core->window); 628 | } 629 | 630 | if (core->renderer) 631 | { 632 | SDL_DestroyRenderer(core->renderer); 633 | } 634 | 635 | if (core) 636 | { 637 | free(core); 638 | } 639 | 640 | SDL_Quit(); 641 | } 642 | 643 | /* Since a compiler often appends padding bytes to structures and 644 | * because this function does not checks the endianness of the system, 645 | * this function is by definition not portable. The worst that can 646 | * happen, however, is that the resulting save file is not usable across 647 | * different platforms. 648 | */ 649 | void game_save(game_t* core) 650 | { 651 | SDL_RWops* save_file; 652 | int index; 653 | 654 | #ifdef __EMSCRIPTEN__ 655 | return; 656 | #endif 657 | 658 | if (NULL == core) 659 | { 660 | return; 661 | } 662 | 663 | if (SDL_FALSE == core->nyt_mode) 664 | { 665 | save_state_t state; 666 | 667 | for (index = 0; index < 30; index += 1) 668 | { 669 | state.tile[index].letter = core->tile[index].letter; 670 | state.tile[index].letter_index = core->tile[index].letter_index; 671 | state.tile[index].state = core->tile[index].state; 672 | } 673 | 674 | state.version = SAVE_VERSION; 675 | state.current_index = core->current_index; 676 | state.previous_letter = core->previous_letter; 677 | state.valid_answer_index = core->valid_answer_index; 678 | state.attempt = core->attempt; 679 | state.seed = core->seed; 680 | state.language = core->wordlist.language; 681 | 682 | #ifdef __ANDROID__ 683 | { 684 | char save_file_path[256] = { 0 }; 685 | stbsp_snprintf(save_file_path, 256, "%s/%s", SDL_AndroidGetInternalStoragePath(), SAVE_FILE); 686 | save_file = SDL_RWFromFile(save_file_path, "wb"); 687 | } 688 | #else 689 | save_file = SDL_RWFromFile(SAVE_FILE, "wb"); 690 | #endif 691 | if (NULL == save_file) 692 | { 693 | return; 694 | } 695 | 696 | SDL_RWwrite(save_file, &state, sizeof(struct save_state), 1); 697 | SDL_RWclose(save_file); 698 | } 699 | else 700 | { 701 | nyt_save_state_t state; 702 | 703 | for (index = 0; index < 30; index += 1) 704 | { 705 | state.tile[index].letter = core->tile[index].letter; 706 | state.tile[index].letter_index = core->tile[index].letter_index; 707 | state.tile[index].state = core->tile[index].state; 708 | } 709 | 710 | state.current_index = core->current_index; 711 | state.previous_letter = core->previous_letter; 712 | state.valid_answer_index = core->valid_answer_index; 713 | state.has_ended = core->nyt_has_ended; 714 | state.attempt = core->attempt; 715 | state.final_attempt = core->nyt_final_attempt; 716 | 717 | #ifdef __ANDROID__ 718 | { 719 | char save_file_path[256] = { 0 }; 720 | stbsp_snprintf(save_file_path, 256, "%s/%s", SDL_AndroidGetInternalStoragePath(), DAILY_SAVE_FILE); 721 | save_file = SDL_RWFromFile(save_file_path, "wb"); 722 | } 723 | #else 724 | save_file = SDL_RWFromFile(DAILY_SAVE_FILE, "wb"); 725 | #endif 726 | if (NULL == save_file) 727 | { 728 | return; 729 | } 730 | 731 | SDL_RWwrite(save_file, &state, sizeof(struct nyt_save_state), 1); 732 | SDL_RWclose(save_file); 733 | } 734 | } 735 | 736 | void game_load(SDL_bool load_daily, game_t* core) 737 | { 738 | SDL_RWops* save_file; 739 | int index; 740 | 741 | if (NULL == core) 742 | { 743 | return; 744 | } 745 | 746 | #ifdef __EMSCRIPTEN__ 747 | if (SDL_TRUE == load_daily) 748 | { 749 | reset_game(SDL_TRUE, core); 750 | } 751 | else 752 | { 753 | reset_game(SDL_FALSE, core); 754 | } 755 | return; 756 | #endif 757 | 758 | if (SDL_FALSE == load_daily) 759 | { 760 | save_state_t state = { 0 }; 761 | 762 | #ifdef __ANDROID__ 763 | { 764 | char save_file_path[256] = { 0 }; 765 | stbsp_snprintf(save_file_path, 256, "%s/%s", SDL_AndroidGetInternalStoragePath(), SAVE_FILE); 766 | save_file = SDL_RWFromFile(save_file_path, "rb"); 767 | } 768 | #else 769 | save_file = SDL_RWFromFile(SAVE_FILE, "rb"); 770 | #endif 771 | if (NULL == save_file) 772 | { 773 | /* Nothing to do here. */ 774 | } 775 | else 776 | { 777 | if (1 != SDL_RWread(save_file, &state, sizeof(struct save_state), 1)) 778 | { 779 | /* Nothing to do here. */ 780 | } 781 | SDL_RWclose(save_file); 782 | } 783 | 784 | if (SAVE_VERSION != state.version) 785 | { 786 | // Do not load outdated or invalid save states. 787 | return; 788 | } 789 | 790 | core->show_menu = SDL_FALSE; 791 | clear_tiles(SDL_TRUE, core); 792 | 793 | for (index = 0; index < 30; index += 1) 794 | { 795 | core->tile[index].letter = state.tile[index].letter; 796 | core->tile[index].letter_index = state.tile[index].letter_index; 797 | core->tile[index].state = state.tile[index].state; 798 | } 799 | 800 | core->current_index = state.current_index; 801 | core->previous_letter = state.previous_letter; 802 | core->valid_answer_index = state.valid_answer_index; 803 | core->attempt = state.attempt; 804 | core->seed = state.seed; 805 | 806 | set_language(state.language, SDL_FALSE, core); 807 | } 808 | else 809 | { 810 | nyt_save_state_t state = { 0 }; 811 | 812 | #ifdef __ANDROID__ 813 | { 814 | char save_file_path[256] = { 0 }; 815 | stbsp_snprintf(save_file_path, 256, "%s/%s", SDL_AndroidGetInternalStoragePath(), DAILY_SAVE_FILE); 816 | save_file = SDL_RWFromFile(save_file_path, "rb"); 817 | } 818 | #else 819 | save_file = SDL_RWFromFile(DAILY_SAVE_FILE, "rb"); 820 | #endif 821 | if (NULL == save_file) 822 | { 823 | /* Nothing to do here. */ 824 | } 825 | else 826 | { 827 | if (1 != SDL_RWread(save_file, &state, sizeof(struct nyt_save_state), 1)) 828 | { 829 | /* Nothing to do here. */ 830 | } 831 | SDL_RWclose(save_file); 832 | } 833 | 834 | if (state.valid_answer_index != get_nyt_daily_index()) 835 | { 836 | /* New daily world available. */ 837 | reset_game(SDL_TRUE, core); 838 | } 839 | else 840 | { 841 | core->show_menu = SDL_FALSE; 842 | clear_tiles(SDL_TRUE, core); 843 | 844 | for (index = 0; index < 30; index += 1) 845 | { 846 | core->tile[index].letter = state.tile[index].letter; 847 | core->tile[index].letter_index = state.tile[index].letter_index; 848 | core->tile[index].state = state.tile[index].state; 849 | } 850 | 851 | core->current_index = state.current_index; 852 | core->previous_letter = state.previous_letter; 853 | core->valid_answer_index = state.valid_answer_index; 854 | core->attempt = state.attempt; 855 | core->nyt_has_ended = state.has_ended; 856 | core->nyt_final_attempt = state.final_attempt; 857 | 858 | set_language(LANG_ENGLISH, SDL_FALSE, core); 859 | } 860 | } 861 | SDL_StartTextInput(); 862 | } 863 | 864 | static event_t get_current_event(game_t* core) 865 | { 866 | event_t current_event = EVENT_NONE; 867 | 868 | if (NULL == core) 869 | { 870 | return current_event; 871 | } 872 | 873 | if (SDL_PollEvent(&core->event)) 874 | { 875 | switch (core->event.type) 876 | { 877 | case SDL_QUIT: 878 | return EVENT_QUIT; 879 | #ifdef __ANDROID__ 880 | case SDL_FINGERMOTION: 881 | core->swipe_v += core->event.tfinger.dy; 882 | core->swipe_h += core->event.tfinger.dx; 883 | break; 884 | case SDL_FINGERUP: 885 | case SDL_FINGERDOWN: 886 | if ((SDL_FINGERDOWN == core->event.type)) 887 | { 888 | core->touch_down_timestamp = core->event.tfinger.timestamp; 889 | } 890 | if ((SDL_FINGERUP == core->event.type)) 891 | { 892 | core->touch_up_timestamp = core->event.tfinger.timestamp; 893 | } 894 | 895 | if (SDL_TRUE == core->show_menu) 896 | { 897 | Uint32 touch_duration = (core->touch_up_timestamp - core->touch_down_timestamp); 898 | 899 | if (core->swipe_h >= 0.1f) 900 | { 901 | core->swipe_h = 0.f; 902 | return EVENT_MENU_NEXT; 903 | } 904 | else if (core->swipe_h <= -0.1f) 905 | { 906 | core->swipe_h = 0.f; 907 | return EVENT_MENU_PREV; 908 | } 909 | 910 | if (core->swipe_v >= 0.1f) 911 | { 912 | if (27 == core->current_index) 913 | { 914 | core->swipe_v = 0.f; 915 | return EVENT_MENU_SELECT_NYT_MODE; 916 | } 917 | } 918 | else if (core->swipe_v <= -0.1f) 919 | { 920 | if (27 == core->current_index) 921 | { 922 | core->swipe_v = 0.f; 923 | return EVENT_MENU_SELECT_ENDLESS_MODE; 924 | } 925 | } 926 | 927 | if ((touch_duration >= 100) && (touch_duration <= 1000)) 928 | { 929 | switch (core->current_index) 930 | { 931 | case 25: 932 | return EVENT_CONFIRM_NEW_GAME; 933 | case 26: 934 | return EVENT_CONFIRM_LOAD_GAME; 935 | case 27: 936 | switch (core->selected_mode) 937 | { 938 | default: 939 | case MODE_NYT: 940 | return EVENT_CONFIRM_NYT_MODE; 941 | case MODE_ENDLESS: 942 | return EVENT_CONFIRM_ENDLESS_MODE; 943 | } 944 | case 28: 945 | return EVENT_CONFIRM_SET_LANG; 946 | case 29: 947 | return EVENT_QUIT; 948 | default: 949 | break; 950 | } 951 | } 952 | break; 953 | } 954 | else 955 | { 956 | Uint32 touch_duration = (core->touch_up_timestamp - core->touch_down_timestamp); 957 | if ((touch_duration >= 100) && (touch_duration <= 1000)) 958 | { 959 | SDL_StartTextInput(); 960 | } 961 | } 962 | break; 963 | #endif 964 | case SDL_KEYDOWN: 965 | { 966 | #ifndef __SYMBIAN32__ 967 | switch (core->event.key.keysym.sym) 968 | { 969 | case SDLK_F11: 970 | return EVENT_TOGGLE_FS; 971 | } 972 | #endif 973 | if (SDL_TRUE == core->show_menu) 974 | { 975 | switch (core->event.key.keysym.sym) 976 | { 977 | case SDLK_KP_ENTER: 978 | case SDLK_RETURN: 979 | case SDLK_SELECT: 980 | case SDLK_5: 981 | case SDLK_KP_5: 982 | switch (core->current_index) 983 | { 984 | case 25: 985 | return EVENT_CONFIRM_NEW_GAME; 986 | case 26: 987 | return EVENT_CONFIRM_LOAD_GAME; 988 | case 27: 989 | switch (core->selected_mode) 990 | { 991 | default: 992 | case MODE_NYT: 993 | return EVENT_CONFIRM_NYT_MODE; 994 | case MODE_ENDLESS: 995 | return EVENT_CONFIRM_ENDLESS_MODE; 996 | } 997 | case 28: 998 | return EVENT_CONFIRM_SET_LANG; 999 | case 29: 1000 | return EVENT_QUIT; 1001 | default: 1002 | break; 1003 | } 1004 | break; 1005 | case SDLK_LEFT: 1006 | return EVENT_MENU_PREV; 1007 | case SDLK_RIGHT: 1008 | return EVENT_MENU_NEXT; 1009 | case SDLK_UP: 1010 | if (27 == core->current_index) 1011 | { 1012 | return EVENT_MENU_SELECT_ENDLESS_MODE; 1013 | } 1014 | break; 1015 | case SDLK_DOWN: 1016 | if (27 == core->current_index) 1017 | { 1018 | return EVENT_MENU_SELECT_NYT_MODE; 1019 | } 1020 | break; 1021 | #ifdef __SYMBIAN32__ 1022 | case SDLK_SOFTLEFT: 1023 | return EVENT_MENU_SELECT_NEW_GAME; 1024 | case SDLK_SOFTRIGHT: 1025 | return EVENT_MENU_SELECT_QUIT; 1026 | #endif 1027 | case SDLK_AC_BACK: 1028 | case SDLK_ESCAPE: 1029 | SDL_StopTextInput(); 1030 | return EVENT_QUIT; 1031 | } 1032 | } 1033 | else 1034 | { 1035 | // Interpret key-presses while being not in the 1036 | // menu. 1037 | switch (core->event.key.keysym.sym) 1038 | { 1039 | case SDLK_2: 1040 | case SDLK_KP_2: 1041 | return EVENT_KEY_2; 1042 | case SDLK_3: 1043 | case SDLK_KP_3: 1044 | return EVENT_KEY_3; 1045 | case SDLK_4: 1046 | case SDLK_KP_4: 1047 | return EVENT_KEY_4; 1048 | case SDLK_5: 1049 | case SDLK_KP_5: 1050 | return EVENT_KEY_5; 1051 | case SDLK_6: 1052 | case SDLK_KP_6: 1053 | return EVENT_KEY_6; 1054 | case SDLK_7: 1055 | case SDLK_KP_7: 1056 | return EVENT_KEY_7; 1057 | case SDLK_8: 1058 | case SDLK_KP_8: 1059 | return EVENT_KEY_8; 1060 | case SDLK_9: 1061 | case SDLK_KP_9: 1062 | return EVENT_KEY_9; 1063 | case SDLK_0: 1064 | case SDLK_KP_0: 1065 | return EVENT_KEY_0; 1066 | case SDLK_UP: 1067 | return EVENT_NEXT_LETTER; 1068 | case SDLK_DOWN: 1069 | return EVENT_PREV_LETTER; 1070 | case SDLK_KP_ENTER: 1071 | case SDLK_RETURN: 1072 | case SDLK_SELECT: 1073 | return EVENT_CONFIRM; 1074 | case SDLK_BACKSPACE: 1075 | case SDLK_LEFT: 1076 | return EVENT_DELETE_LETTER; 1077 | case SDLK_RIGHT: 1078 | return EVENT_CONFIRM_LETTER; 1079 | case SDLK_AC_BACK: 1080 | case SDLK_ESCAPE: 1081 | #ifdef __SYMBIAN32__ 1082 | case SDLK_SOFTLEFT: 1083 | case SDLK_SOFTRIGHT: 1084 | #endif 1085 | SDL_StopTextInput(); 1086 | return EVENT_BACK; 1087 | } 1088 | } 1089 | break; 1090 | } 1091 | case SDL_TEXTINPUT: 1092 | return EVENT_TEXTINPUT; 1093 | break; 1094 | } 1095 | } 1096 | 1097 | return EVENT_NONE; 1098 | } 1099 | 1100 | static void clear_tiles(SDL_bool clear_state, game_t* core) 1101 | { 1102 | int index; 1103 | 1104 | if (NULL == core) 1105 | { 1106 | return; 1107 | } 1108 | 1109 | for (index = 0; index < 30; index += 1) 1110 | { 1111 | core->tile[index].letter = 0; 1112 | if (SDL_TRUE == clear_state) 1113 | { 1114 | core->tile[index].state = LETTER_SELECT; 1115 | } 1116 | } 1117 | } 1118 | 1119 | static void delete_letter(game_t* core) 1120 | { 1121 | int lower_index_limit = 0; 1122 | int upper_index_limit = 0; 1123 | 1124 | if (NULL == core) 1125 | { 1126 | return; 1127 | } 1128 | 1129 | if (core->attempt >= 6) 1130 | { 1131 | return; 1132 | } 1133 | 1134 | get_index_limits(&lower_index_limit, &upper_index_limit, core); 1135 | 1136 | core->tile[core->current_index].letter = 0; 1137 | core->current_index -= 1; 1138 | core->current_index = SDL_clamp(core->current_index, lower_index_limit, upper_index_limit); 1139 | } 1140 | 1141 | static int draw_tiles(game_t* core) 1142 | { 1143 | SDL_Rect src = { 0, 0, 32, 32 }; 1144 | SDL_Rect dst = { 4 * core->zoom_factor, 2 * core->zoom_factor, 32 * core->zoom_factor, 32 * core->zoom_factor}; 1145 | int index = 0; 1146 | int count = 0; 1147 | 1148 | if (NULL == core) 1149 | { 1150 | return 0; 1151 | } 1152 | 1153 | if (NULL == core->render_target) 1154 | { 1155 | return 0; 1156 | } 1157 | 1158 | if (NULL == core->tile_texture) 1159 | { 1160 | return 0; 1161 | } 1162 | 1163 | if (0 > SDL_SetRenderTarget(core->renderer, core->render_target)) 1164 | { 1165 | return 1; 1166 | } 1167 | SDL_RenderClear(core->renderer); 1168 | 1169 | for (index = 0; index < 30; index += 1) 1170 | { 1171 | static SDL_bool is_ngage = SDL_FALSE; 1172 | 1173 | if ((0 == index) || (0 == (index % 5))) 1174 | { 1175 | const Uint32 hash_ngage = 0x0daa8447; // NGAGE 1176 | unsigned char check_pattern[6] = { 0 }; 1177 | int letter_index; 1178 | 1179 | for (letter_index = 0; letter_index < 5; letter_index += 1) 1180 | { 1181 | check_pattern[letter_index] = core->tile[index + letter_index].letter; 1182 | } 1183 | 1184 | if (hash_ngage == generate_hash(check_pattern)) 1185 | { 1186 | is_ngage = SDL_TRUE; 1187 | } 1188 | } 1189 | 1190 | if (SDL_FALSE == is_ngage) 1191 | { 1192 | switch(core->tile[index].state) 1193 | { 1194 | default: 1195 | case LETTER_SELECT: 1196 | src.x = 0; 1197 | src.y = 0; 1198 | break; 1199 | case CORRECT_LETTER: 1200 | src.x = 0; 1201 | src.y = 96; 1202 | break; 1203 | case WRONG_LETTER: 1204 | src.x = 0; 1205 | src.y = 32; 1206 | break; 1207 | case WRONG_POSITION: 1208 | src.x = 0; 1209 | src.y = 64; 1210 | break; 1211 | } 1212 | 1213 | // Special characters. 1214 | if (SDL_FALSE == core->wordlist.is_cyrillic) 1215 | { 1216 | switch (core->tile[index].letter) 1217 | { 1218 | case 0xc4: // Ä 1219 | src.x = 864; 1220 | break; 1221 | case 0xd6: // Ö 1222 | src.x = 896; 1223 | break; 1224 | case 0xdc: // Ü 1225 | src.x = 928; 1226 | break; 1227 | case 0xdf: // ß 1228 | src.x = 960; 1229 | break; 1230 | } 1231 | } 1232 | 1233 | switch (core->tile[index].letter) 1234 | { 1235 | case 0x00: // Empty tile 1236 | src.x = 0; 1237 | break; 1238 | case 0x2d: // Hyphen 1239 | src.x = 992; 1240 | break; 1241 | case 0x01: // New game icon 1242 | src.x = 0; 1243 | src.y = 160; 1244 | break; 1245 | case 0x02: // Load game icon 1246 | src.x = 0; 1247 | src.y = 128; 1248 | break; 1249 | case 0x03: // Game mode icon 1250 | src.x = 1056; 1251 | 1252 | switch(core->selected_mode) 1253 | { 1254 | default: 1255 | case MODE_NYT: 1256 | src.y = 64; 1257 | break; 1258 | case MODE_ENDLESS: 1259 | src.y = 0; 1260 | break; 1261 | } 1262 | 1263 | if (27 == core->current_index) 1264 | { 1265 | src.y += 32; 1266 | } 1267 | break; 1268 | case 0x04: // Set lang. icon 1269 | src.x = 0; 1270 | src.y = 192; 1271 | break; 1272 | case 0x05: // Quit game icon 1273 | src.x = 0; 1274 | src.y = 224; 1275 | break; 1276 | case 0x06: // Flag icon 1277 | src.x = 1056; 1278 | switch (core->wordlist.language) 1279 | { 1280 | default: 1281 | case LANG_ENGLISH: 1282 | src.y = 128; 1283 | break; 1284 | case LANG_RUSSIAN: 1285 | src.y = 160; 1286 | break; 1287 | case LANG_GERMAN: 1288 | src.y = 192; 1289 | break; 1290 | case LANG_FINNISH: 1291 | src.y = 224; 1292 | break; 1293 | } 1294 | break; 1295 | } 1296 | 1297 | if (SDL_TRUE == core->wordlist.is_cyrillic && 1298 | 0x00 != core->tile[index].letter && 1299 | 0x2d != core->tile[index].letter && 1300 | 0x01 != core->tile[index].letter && 1301 | 0x02 != core->tile[index].letter && 1302 | 0x03 != core->tile[index].letter && 1303 | 0x04 != core->tile[index].letter && 1304 | 0x05 != core->tile[index].letter && 1305 | 0x06 != core->tile[index].letter) 1306 | { 1307 | src.y += 128; 1308 | } 1309 | 1310 | if (core->tile[index].letter >= core->wordlist.first_letter && core->tile[index].letter <= core->wordlist.last_letter) 1311 | { 1312 | src.x = (core->tile[index].letter - core->wordlist.first_letter) * 32; 1313 | src.x += 32; 1314 | } 1315 | } 1316 | else 1317 | { 1318 | src.x = 1024; 1319 | switch(core->tile[index].letter) 1320 | { 1321 | case 'N': 1322 | src.y = 0; 1323 | break; 1324 | case 'G': 1325 | src.y = 32; 1326 | break; 1327 | case 'A': 1328 | src.y = 64; 1329 | break; 1330 | case 'E': 1331 | src.y = 96; 1332 | is_ngage = SDL_FALSE; 1333 | break; 1334 | } 1335 | } 1336 | 1337 | SDL_RenderCopy(core->renderer, core->tile_texture, &src, &dst); 1338 | 1339 | if (index == core->current_index) 1340 | { 1341 | unsigned int frame_count = 0; 1342 | SDL_SetRenderDrawColor(core->renderer, 0xf5, 0x79, 0x3a, 0x00); 1343 | for (frame_count = 0; frame_count < (core->zoom_factor * 2); frame_count += 1) 1344 | { 1345 | SDL_Rect inner_frame = { 1346 | (dst.x + frame_count), 1347 | (dst.y + frame_count), 1348 | (dst.w - (frame_count * 2)), 1349 | (dst.h - (frame_count * 2)) 1350 | }; 1351 | SDL_RenderDrawRect(core->renderer, &inner_frame); 1352 | } 1353 | } 1354 | 1355 | dst.x += (34 * core->zoom_factor); 1356 | count += 1; 1357 | 1358 | if (count > 4) 1359 | { 1360 | count = 0; 1361 | dst.x = (4 * core->zoom_factor); 1362 | dst.y += (34 * core->zoom_factor); 1363 | } 1364 | } 1365 | 1366 | return 0; 1367 | } 1368 | 1369 | static void get_index_limits(int* lower_limit, int* upper_limit, game_t* core) 1370 | { 1371 | if (NULL == core) 1372 | { 1373 | return; 1374 | } 1375 | 1376 | switch (core->attempt) 1377 | { 1378 | default: 1379 | case 0: 1380 | *lower_limit = 0; 1381 | *upper_limit = 4; 1382 | break; 1383 | case 1: 1384 | *lower_limit = 5; 1385 | *upper_limit = 9; 1386 | break; 1387 | case 2: 1388 | *lower_limit = 10; 1389 | *upper_limit = 14; 1390 | break; 1391 | case 3: 1392 | *lower_limit = 15; 1393 | *upper_limit = 19; 1394 | break; 1395 | case 4: 1396 | *lower_limit = 20; 1397 | *upper_limit = 24; 1398 | break; 1399 | case 5: 1400 | *lower_limit = 25; 1401 | *upper_limit = 29; 1402 | break; 1403 | } 1404 | } 1405 | 1406 | static void go_back_to_menu(game_t* core) 1407 | { 1408 | SDL_StopTextInput(); 1409 | game_save(core); 1410 | clear_tiles(SDL_TRUE, core); 1411 | core->current_index = 27; 1412 | set_language(core->wordlist.language, SDL_TRUE, core); 1413 | } 1414 | 1415 | static void goto_next_letter(game_t* core) 1416 | { 1417 | int lower_index_limit = 0; 1418 | int upper_index_limit = 0; 1419 | 1420 | if (NULL == core) 1421 | { 1422 | return; 1423 | } 1424 | 1425 | if (core->attempt >= 6) 1426 | { 1427 | return; 1428 | } 1429 | 1430 | get_index_limits(&lower_index_limit, &upper_index_limit, core); 1431 | 1432 | if (core->tile[core->current_index].letter != 0) 1433 | { 1434 | core->current_index += 1; 1435 | core->current_index = SDL_clamp(core->current_index, lower_index_limit, upper_index_limit); 1436 | core->tile[core->current_index].letter = 0; 1437 | } 1438 | } 1439 | 1440 | static SDL_bool has_game_ended(game_t* core) 1441 | { 1442 | SDL_bool is_won = SDL_FALSE; 1443 | validate_current_guess(&is_won, core); 1444 | 1445 | if ((SDL_TRUE == core->nyt_mode) && (core->nyt_has_ended)) 1446 | { 1447 | return SDL_TRUE; 1448 | } 1449 | 1450 | if ((SDL_TRUE == is_won) || (SDL_FALSE == is_won && 5 == core->attempt)) 1451 | { 1452 | return SDL_TRUE; 1453 | } 1454 | 1455 | return SDL_FALSE; 1456 | } 1457 | 1458 | static void move_rows_up(game_t* core) 1459 | { 1460 | int counter; 1461 | int tile_index; 1462 | for (counter = 0; counter < 5; counter += 1) 1463 | { 1464 | for (tile_index = 1; tile_index < 30; tile_index += 1) 1465 | { 1466 | core->tile[tile_index - 1].letter = core->tile[tile_index].letter; 1467 | core->tile[tile_index - 1].letter_index = core->tile[tile_index].letter_index; 1468 | core->tile[tile_index - 1].state = core->tile[tile_index].state; 1469 | } 1470 | } 1471 | } 1472 | 1473 | static void reset_game(SDL_bool nyt_mode, game_t* core) 1474 | { 1475 | if (NULL == core) 1476 | { 1477 | return; 1478 | } 1479 | 1480 | clear_tiles(SDL_TRUE, core); 1481 | 1482 | core->attempt = 0; 1483 | core->current_index = 0; 1484 | core->show_menu = SDL_FALSE; 1485 | 1486 | if (SDL_TRUE == core->show_stats) 1487 | { 1488 | core->show_stats = SDL_FALSE; 1489 | draw_tiles(core); 1490 | } 1491 | 1492 | if (SDL_FALSE == nyt_mode) 1493 | { 1494 | core->valid_answer_index = xorshift(&core->seed) % core->wordlist.word_count; 1495 | while (core->valid_answer_index > core->wordlist.word_count) 1496 | { 1497 | core->valid_answer_index = xorshift(&core->seed) % core->wordlist.word_count; 1498 | } 1499 | core->nyt_mode = SDL_FALSE; 1500 | } 1501 | else 1502 | { 1503 | set_language(LANG_ENGLISH, SDL_FALSE, core); 1504 | core->valid_answer_index = get_nyt_daily_index(); 1505 | core->nyt_mode = SDL_TRUE; 1506 | } 1507 | SDL_StartTextInput(); 1508 | } 1509 | 1510 | static void select_next_letter(const unsigned char start_char, const unsigned char end_char, game_t* core) 1511 | { 1512 | unsigned char* current_letter = &core->tile[core->current_index].letter; 1513 | 1514 | if (core->attempt >= 6) 1515 | { 1516 | return; 1517 | } 1518 | 1519 | if (*current_letter >= start_char && *current_letter < end_char) 1520 | { 1521 | *current_letter += 1; 1522 | } 1523 | else 1524 | { 1525 | *current_letter = start_char; 1526 | } 1527 | } 1528 | 1529 | static void select_previous_letter(const unsigned char start_char, const unsigned char end_char, game_t* core) 1530 | { 1531 | unsigned char* current_letter = &core->tile[core->current_index].letter; 1532 | 1533 | if (core->attempt >= 6) 1534 | { 1535 | return; 1536 | } 1537 | 1538 | if (0 == *current_letter) 1539 | { 1540 | *current_letter = start_char; 1541 | } 1542 | 1543 | *current_letter -= 1; 1544 | 1545 | if (*current_letter < start_char) 1546 | { 1547 | *current_letter = end_char; 1548 | } 1549 | } 1550 | 1551 | static void select_utf8_letter(const Uint16 *text, game_t* core) 1552 | { 1553 | unsigned char* current_letter = (unsigned char*)&core->tile[core->current_index].letter; 1554 | unsigned char selected_letter = 0; 1555 | SDL_bool is_alpha = SDL_FALSE; 1556 | 1557 | switch(*text) 1558 | { 1559 | case 0xa4c3: // ä 1560 | case 0x84c3: // Ä 1561 | is_alpha = SDL_TRUE; 1562 | selected_letter = 0xc4; 1563 | break; 1564 | case 0xb6c3: // ö 1565 | case 0x96c3: // Ö 1566 | is_alpha = SDL_TRUE; 1567 | selected_letter = 0xd6; 1568 | break; 1569 | case 0xbcc3: // ü 1570 | case 0x9cc3: // Ü 1571 | is_alpha = SDL_TRUE; 1572 | selected_letter = 0xdc; 1573 | break; 1574 | case 0x9fc3: // ß 1575 | is_alpha = SDL_TRUE; 1576 | selected_letter = 0xdf; 1577 | break; 1578 | case 0xb0d0: // а 1579 | case 0x90d0: // А 1580 | is_alpha = SDL_TRUE; 1581 | selected_letter = 0xc0; 1582 | break; 1583 | case 0xb1d0: // б 1584 | case 0x91d0: // Б 1585 | is_alpha = SDL_TRUE; 1586 | selected_letter = 0xc1; 1587 | break; 1588 | case 0xb2d0: // в 1589 | case 0x92d0: // В 1590 | is_alpha = SDL_TRUE; 1591 | selected_letter = 0xc2; 1592 | break; 1593 | case 0xb3d0: // г 1594 | case 0x93d0: // Г 1595 | is_alpha = SDL_TRUE; 1596 | selected_letter = 0xc3; 1597 | break; 1598 | case 0xb4d0: // д 1599 | case 0x94d0: // Д 1600 | is_alpha = SDL_TRUE; 1601 | selected_letter = 0xc4; 1602 | break; 1603 | case 0xb5d0: // е 1604 | case 0x95d0: // Е 1605 | is_alpha = SDL_TRUE; 1606 | selected_letter = 0xc5; 1607 | break; 1608 | case 0xb6d0: // ж 1609 | case 0x96d0: // Ж 1610 | is_alpha = SDL_TRUE; 1611 | selected_letter = 0xc6; 1612 | break; 1613 | case 0xb7d0: // з 1614 | case 0x97d0: // З 1615 | is_alpha = SDL_TRUE; 1616 | selected_letter = 0xc7; 1617 | break; 1618 | case 0xb8d0: // и 1619 | case 0x98d0: // И 1620 | is_alpha = SDL_TRUE; 1621 | selected_letter = 0xc8; 1622 | break; 1623 | case 0xb9d0: // й 1624 | case 0x99d0: // Й 1625 | is_alpha = SDL_TRUE; 1626 | selected_letter = 0xc9; 1627 | break; 1628 | case 0xbad0: // к 1629 | case 0x9ad0: // К 1630 | is_alpha = SDL_TRUE; 1631 | selected_letter = 0xca; 1632 | break; 1633 | case 0xbbd0: // л 1634 | case 0x9bd0: // Л 1635 | is_alpha = SDL_TRUE; 1636 | selected_letter = 0xcb; 1637 | break; 1638 | case 0xbcd0: // м 1639 | case 0x9cd0: // М 1640 | is_alpha = SDL_TRUE; 1641 | selected_letter = 0xcc; 1642 | break; 1643 | case 0xbdd0: // н 1644 | case 0x9dd0: // Н 1645 | is_alpha = SDL_TRUE; 1646 | selected_letter = 0xcd; 1647 | break; 1648 | case 0xbed0: // о 1649 | case 0x9ed0: // О 1650 | is_alpha = SDL_TRUE; 1651 | selected_letter = 0xce; 1652 | break; 1653 | case 0xbfd0: // п 1654 | case 0x9fd0: // П 1655 | is_alpha = SDL_TRUE; 1656 | selected_letter = 0xcf; 1657 | break; 1658 | case 0x80d1: // р 1659 | case 0xa0d0: // Р 1660 | is_alpha = SDL_TRUE; 1661 | selected_letter = 0xd0; 1662 | break; 1663 | case 0x81d1: // с 1664 | case 0xa1d0: // С 1665 | is_alpha = SDL_TRUE; 1666 | selected_letter = 0xd1; 1667 | break; 1668 | case 0x82d1: // т 1669 | case 0xa2d0: // Т 1670 | is_alpha = SDL_TRUE; 1671 | selected_letter = 0xd2; 1672 | break; 1673 | case 0x83d1: // у 1674 | case 0xa3d0: // У 1675 | is_alpha = SDL_TRUE; 1676 | selected_letter = 0xd3; 1677 | break; 1678 | case 0x84d1: // ф 1679 | case 0xa4d0: // Ф 1680 | is_alpha = SDL_TRUE; 1681 | selected_letter = 0xd4; 1682 | break; 1683 | case 0x85d1: // х 1684 | case 0xa5d0: // Х 1685 | is_alpha = SDL_TRUE; 1686 | selected_letter = 0xd5; 1687 | break; 1688 | case 0x86d1: // ц 1689 | case 0xa6d0: // Ц 1690 | is_alpha = SDL_TRUE; 1691 | selected_letter = 0xd6; 1692 | break; 1693 | case 0x87d1: // ч 1694 | case 0xa7d0: // Ч 1695 | is_alpha = SDL_TRUE; 1696 | selected_letter = 0xd7; 1697 | break; 1698 | case 0x88d1: // ш 1699 | case 0xa8d0: // Ш 1700 | is_alpha = SDL_TRUE; 1701 | selected_letter = 0xd8; 1702 | break; 1703 | case 0x89d1: // щ 1704 | case 0xa9d0: // Щ 1705 | is_alpha = SDL_TRUE; 1706 | selected_letter = 0xd9; 1707 | break; 1708 | case 0x8ad1: // ъ 1709 | case 0xaad0: // Ъ 1710 | is_alpha = SDL_TRUE; 1711 | selected_letter = 0xda; 1712 | break; 1713 | case 0x8bd1: // ы 1714 | case 0xabd0: // Ы 1715 | is_alpha = SDL_TRUE; 1716 | selected_letter = 0xdb; 1717 | break; 1718 | case 0x8cd1: // ь 1719 | case 0xacd0: // Ь 1720 | is_alpha = SDL_TRUE; 1721 | selected_letter = 0xdc; 1722 | break; 1723 | case 0x8dd1: // э 1724 | case 0xadd0: // Э 1725 | is_alpha = SDL_TRUE; 1726 | selected_letter = 0xdd; 1727 | break; 1728 | case 0x8ed1: // ю 1729 | case 0xaed0: // Ю 1730 | is_alpha = SDL_TRUE; 1731 | selected_letter = 0xde; 1732 | break; 1733 | case 0x8fd1: // я 1734 | case 0xafd0: // Я 1735 | is_alpha = SDL_TRUE; 1736 | selected_letter = 0xdf; 1737 | break; 1738 | case 0x2d: // - 1739 | is_alpha = SDL_TRUE; 1740 | selected_letter = 0x2d; 1741 | break; 1742 | default: 1743 | if (SDL_isalpha(*text)) 1744 | { 1745 | is_alpha = SDL_TRUE; 1746 | selected_letter = (unsigned char)SDL_toupper(*text); 1747 | } 1748 | break; 1749 | } 1750 | 1751 | if (SDL_TRUE == core->wordlist.is_cyrillic) 1752 | { 1753 | if (((selected_letter < 0xc0) || (selected_letter > 0xdf)) && (selected_letter != 0x2d)) 1754 | { 1755 | // Only accept cyrillic letters and hypen in cyrillic 1756 | // wordlists. 1757 | return; 1758 | } 1759 | } 1760 | else 1761 | { 1762 | if (((selected_letter >= 0xc0) && (selected_letter <= 0xdf))) 1763 | { 1764 | // Do not accept cyrillic letters in non-cyrillic wordlists. 1765 | return; 1766 | } 1767 | } 1768 | 1769 | *current_letter = selected_letter; 1770 | 1771 | if (0 != ((core->current_index + 1) % 5)) 1772 | { 1773 | if (SDL_TRUE == is_alpha) 1774 | { 1775 | core->current_index++; 1776 | } 1777 | } 1778 | } 1779 | 1780 | static void show_results(game_t* core) 1781 | { 1782 | unsigned char valid_answer[6] = { 0 }; 1783 | 1784 | get_valid_answer(valid_answer, core); 1785 | clear_tiles(SDL_FALSE, core); 1786 | 1787 | if (SDL_TRUE == core->nyt_mode) 1788 | { 1789 | core->show_stats = SDL_TRUE; 1790 | } 1791 | else 1792 | { 1793 | unsigned int start_index = core->attempt * 5; 1794 | 1795 | core->tile[start_index].letter = valid_answer[0]; 1796 | core->tile[start_index + 1].letter = valid_answer[1]; 1797 | core->tile[start_index + 2].letter = valid_answer[2]; 1798 | core->tile[start_index + 3].letter = valid_answer[3]; 1799 | core->tile[start_index + 4].letter = valid_answer[4]; 1800 | } 1801 | 1802 | core->attempt = 6; 1803 | core->current_index = -1; 1804 | } 1805 | 1806 | static void set_zoom_factor(game_t* core) 1807 | { 1808 | SDL_DisplayMode display_mode; 1809 | 1810 | core->zoom_factor = 1u; 1811 | 1812 | #if __SYMBIAN32__ 1813 | return; 1814 | #endif 1815 | 1816 | if (0 == SDL_GetCurrentDisplayMode(0, &display_mode)) 1817 | { 1818 | Uint8 zf_width = (display_mode.w / WINDOW_WIDTH) - 1; 1819 | Uint8 zf_height = (display_mode.h / WINDOW_HEIGHT) - 1; 1820 | 1821 | if (zf_width <= zf_height) 1822 | { 1823 | core->zoom_factor = zf_width; 1824 | } 1825 | else 1826 | { 1827 | core->zoom_factor = zf_height; 1828 | } 1829 | } 1830 | } 1831 | 1832 | static void set_render_offset(game_t* core) 1833 | { 1834 | SDL_DisplayMode display_mode; 1835 | 1836 | if (0 == SDL_GetCurrentDisplayMode(0, &display_mode)) 1837 | { 1838 | core->render_offset_x = (display_mode.w / 2) - ((WINDOW_WIDTH * core->zoom_factor) / 2); 1839 | core->render_offset_y = (display_mode.h / 2) - ((WINDOW_HEIGHT * core->zoom_factor) / 2); 1840 | #if __ANDROID__ 1841 | core->render_offset_y /= 2; 1842 | #endif 1843 | } 1844 | } 1845 | 1846 | static void toggle_fullscreen(game_t* core) 1847 | { 1848 | if(core->is_fullscreen) 1849 | { 1850 | if (0 != SDL_SetWindowFullscreen(core->window, 0)) 1851 | { 1852 | /* Nothing to do here. */ 1853 | } 1854 | else 1855 | { 1856 | core->render_offset_x = 0; 1857 | core->render_offset_y = 0; 1858 | core->is_fullscreen = !core->is_fullscreen; 1859 | } 1860 | } 1861 | else 1862 | { 1863 | if (0 != SDL_SetWindowFullscreen(core->window, SDL_WINDOW_FULLSCREEN_DESKTOP)) 1864 | { 1865 | /* Nothing to do here. */ 1866 | } 1867 | else 1868 | { 1869 | set_render_offset(core); 1870 | core->is_fullscreen = !core->is_fullscreen; 1871 | set_zoom_factor(core); 1872 | } 1873 | } 1874 | } 1875 | -------------------------------------------------------------------------------- /src/game.h: -------------------------------------------------------------------------------- 1 | /** @file game.h 2 | * 3 | * A clone of Wordle for the Nokie N-Gage. 4 | * 5 | * Copyright (c) 2022, Michael Fitzmayer. All rights reserved. 6 | * SPDX-License-Identifier: MIT 7 | * 8 | **/ 9 | 10 | #ifndef GAME_H 11 | #define GAME_H 12 | 13 | #include "SDL.h" 14 | 15 | #ifndef SAVE_VERSION 16 | #define SAVE_VERSION 3 17 | #endif 18 | 19 | #define WINDOW_WIDTH 176u 20 | #define WINDOW_HEIGHT 208u 21 | 22 | typedef enum 23 | { 24 | LETTER_SELECT = 0, 25 | CORRECT_LETTER, 26 | WRONG_LETTER, 27 | WRONG_POSITION, 28 | 29 | } state_t; 30 | 31 | typedef enum 32 | { 33 | LANG_ENGLISH = 0, 34 | LANG_RUSSIAN, 35 | LANG_GERMAN, 36 | LANG_FINNISH 37 | 38 | } lang_t; 39 | 40 | typedef enum 41 | { 42 | MODE_NYT = 0, 43 | MODE_ENDLESS 44 | 45 | } game_mode_t; 46 | 47 | typedef enum 48 | { 49 | EVENT_NONE = 0, 50 | EVENT_KEY_0, 51 | EVENT_KEY_2, 52 | EVENT_KEY_3, 53 | EVENT_KEY_4, 54 | EVENT_KEY_5, 55 | EVENT_KEY_6, 56 | EVENT_KEY_7, 57 | EVENT_KEY_8, 58 | EVENT_KEY_9, 59 | EVENT_CONFIRM, 60 | EVENT_CONFIRM_LETTER, 61 | EVENT_DELETE_LETTER, 62 | EVENT_NEXT_LETTER, 63 | EVENT_PREV_LETTER, 64 | EVENT_TEXTINPUT, 65 | EVENT_CONFIRM_ENDLESS_MODE, 66 | EVENT_CONFIRM_LOAD_GAME, 67 | EVENT_CONFIRM_NEW_GAME, 68 | EVENT_CONFIRM_NYT_MODE, 69 | EVENT_CONFIRM_SET_LANG, 70 | EVENT_MENU_NEXT, 71 | EVENT_MENU_PREV, 72 | EVENT_MENU_SELECT_ENDLESS_MODE, 73 | EVENT_MENU_SELECT_NYT_MODE, 74 | EVENT_MENU_SELECT_NEW_GAME, 75 | EVENT_MENU_SELECT_QUIT, 76 | EVENT_BACK, 77 | EVENT_QUIT, 78 | EVENT_TOGGLE_FS 79 | 80 | } event_t; 81 | 82 | typedef struct tile 83 | { 84 | unsigned char letter; 85 | unsigned int letter_index; 86 | state_t state; 87 | 88 | } tile_t; 89 | 90 | typedef struct wordlist 91 | { 92 | lang_t language; 93 | unsigned int letter_count; 94 | unsigned int word_count; 95 | unsigned int allowed_count; 96 | unsigned char first_letter; 97 | unsigned char last_letter; 98 | SDL_bool is_cyrillic; 99 | const unsigned char* special_chars; 100 | const Uint32* hash; 101 | const unsigned char (*list)[5]; 102 | const Uint32* allowed_hash; 103 | const unsigned char (*allowed_list)[5]; 104 | 105 | } wordlist_t; 106 | 107 | typedef struct save_state 108 | { 109 | unsigned int version; 110 | tile_t tile[30]; 111 | int current_index; 112 | unsigned char previous_letter; 113 | unsigned int valid_answer_index; 114 | Uint8 attempt; 115 | unsigned int seed; 116 | lang_t language; 117 | 118 | } save_state_t; 119 | 120 | typedef struct nyt_save_state 121 | { 122 | tile_t tile[30]; 123 | int current_index; 124 | unsigned char previous_letter; 125 | unsigned int valid_answer_index; 126 | Uint8 attempt; 127 | SDL_bool has_ended; 128 | Uint8 final_attempt; 129 | 130 | } nyt_save_state_t; 131 | 132 | typedef struct game 133 | { 134 | SDL_Renderer* renderer; 135 | SDL_Texture* render_target; 136 | SDL_Texture* tile_texture; 137 | SDL_Texture* font_texture; 138 | SDL_Window* window; 139 | SDL_Event event; 140 | Uint16 render_offset_x; 141 | Uint16 render_offset_y; 142 | Uint8 zoom_factor; 143 | Uint32 time_since_last_frame; 144 | Uint32 time_a; 145 | Uint32 time_b; 146 | SDL_bool is_running; 147 | SDL_bool show_menu; 148 | SDL_bool endless_mode; 149 | SDL_bool nyt_mode; 150 | SDL_bool show_stats; 151 | SDL_bool language_set_once; 152 | SDL_bool nyt_has_ended; 153 | SDL_bool is_fullscreen; 154 | tile_t tile[30]; 155 | int current_index; 156 | unsigned char previous_letter; 157 | unsigned int valid_answer_index; 158 | unsigned char current_guess[6]; 159 | Uint8 attempt; 160 | Uint8 nyt_final_attempt; 161 | unsigned int seed; 162 | wordlist_t wordlist; 163 | game_mode_t selected_mode; 164 | #ifdef __ANDROID__ 165 | SDL_Texture* disclaimer_texture; 166 | SDL_bool show_disclaimer; 167 | float swipe_h; 168 | float swipe_v; 169 | Uint32 touch_down_timestamp; 170 | Uint32 touch_up_timestamp; 171 | #endif 172 | } game_t; 173 | 174 | int game_init(const char* resource_file, const char* title, game_t** core); 175 | SDL_bool game_is_running(game_t* core); 176 | int game_update(game_t* core); 177 | void game_quit(game_t* core); 178 | void game_save(game_t* core); 179 | void game_load(SDL_bool load_daily, game_t* core); 180 | 181 | #endif /* GAME_H */ 182 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | /** @file main.c 2 | * 3 | * A clone of Wordle for the Nokie N-Gage. 4 | * 5 | * Copyright (c) 2022, Michael Fitzmayer. All rights reserved. 6 | * SPDX-License-Identifier: MIT 7 | * 8 | **/ 9 | 10 | #include "SDL.h" 11 | #include "game.h" 12 | 13 | #ifdef __EMSCRIPTEN__ 14 | #include 15 | #include 16 | 17 | void main_loop_iter(void *core) 18 | { 19 | int status = game_update((game_t*)core); 20 | if (0 != status) 21 | { 22 | emscripten_cancel_main_loop(); 23 | } 24 | } 25 | #endif 26 | 27 | #if defined __SYMBIAN32__ 28 | #define RES_FILE "E:\\System\\Apps\\wordle\\data.pfs" 29 | #else 30 | #define RES_FILE "data.pfs" 31 | #endif 32 | 33 | int main(int argc, char *argv[]) 34 | { 35 | int status = 0; 36 | game_t* core = NULL; 37 | 38 | (void)argc; 39 | (void)argv; 40 | 41 | status = game_init(RES_FILE, "Wordle", &core); 42 | if (0 != status) 43 | { 44 | goto quit; 45 | } 46 | 47 | #ifdef __EMSCRIPTEN__ 48 | emscripten_set_main_loop_arg(main_loop_iter, core, -1, 1); 49 | #else 50 | while (game_is_running(core)) 51 | { 52 | status = game_update(core); 53 | if (0 != status) 54 | { 55 | goto quit; 56 | } 57 | } 58 | #endif 59 | 60 | quit: 61 | game_quit(core); 62 | return status; 63 | } 64 | -------------------------------------------------------------------------------- /src/osd.c: -------------------------------------------------------------------------------- 1 | /** @file osd.c 2 | * 3 | * On-screen display handler. 4 | * 5 | * Copyright (c) 2022, Michael Fitzmayer. All rights reserved. 6 | * SPDX-License-Identifier: MIT 7 | * 8 | **/ 9 | 10 | #include "SDL.h" 11 | #include "game.h" 12 | 13 | int osd_init(game_t* core); 14 | void osd_print(const char* display_text, const int pos_x, const int pos_y, game_t* core); 15 | 16 | static void get_character_position(const unsigned char character, int* pos_x, int* pos_y); 17 | 18 | extern int load_texture_from_file(const char* file_name, SDL_Texture** texture, game_t* core); 19 | 20 | int osd_init(game_t* core) 21 | { 22 | int status; 23 | 24 | status = load_texture_from_file((const char*)"font.png", &core->font_texture, core); 25 | return status; 26 | } 27 | 28 | void osd_print(const char* display_text, const int pos_x, const int pos_y, game_t* core) 29 | { 30 | int char_index = 0; 31 | size_t text_length = SDL_strlen(display_text); 32 | SDL_Rect src = { 0, 0, 7, 9 }; 33 | 34 | SDL_Rect dst = { 35 | (pos_x * core->zoom_factor) + (1 * core->zoom_factor), 36 | (pos_y * core->zoom_factor) + (1 * core->zoom_factor), 37 | 7 * core->zoom_factor, 38 | 9 * core->zoom_factor 39 | }; 40 | 41 | SDL_Rect frame = { 42 | pos_x * core->zoom_factor, 43 | pos_y * core->zoom_factor, 44 | (text_length * (7 * core->zoom_factor)) + (2 * core->zoom_factor), 45 | 11 * core->zoom_factor 46 | }; 47 | 48 | SDL_Rect background = { 49 | pos_x * core->zoom_factor, 50 | pos_y * core->zoom_factor, 51 | (text_length * (7 * core->zoom_factor)) + (2 * core->zoom_factor), 52 | 11 * core->zoom_factor 53 | }; 54 | 55 | if (NULL == core) 56 | { 57 | return; 58 | } 59 | 60 | if (NULL == core->font_texture) 61 | { 62 | return; 63 | } 64 | 65 | SDL_SetRenderDrawColor(core->renderer, 0xff, 0xff, 0xff, 0x00); 66 | SDL_RenderFillRect(core->renderer, &background); 67 | SDL_RenderDrawRect(core->renderer, &background); 68 | SDL_SetRenderDrawColor(core->renderer, 0x84, 0x8a, 0x8c, 0x00); 69 | SDL_RenderDrawRect(core->renderer, &frame); 70 | SDL_RenderDrawRect(core->renderer, &frame); 71 | 72 | while ('\0' != display_text[char_index]) 73 | { 74 | get_character_position(display_text[char_index], &src.x, &src.y); 75 | char_index += 1; 76 | 77 | SDL_RenderCopy(core->renderer, core->font_texture, &src, &dst); 78 | dst.x += (7 * core->zoom_factor); 79 | } 80 | } 81 | 82 | static void get_character_position(const unsigned char character, int* pos_x, int* pos_y) 83 | { 84 | int index = 0; 85 | 86 | // If the character is not valid, select space. 87 | if ((character < 0x20) || (character > 0x7e)) 88 | { 89 | index = 0; 90 | } 91 | else 92 | { 93 | index = character - 0x20; 94 | } 95 | 96 | *pos_x = (index % 18) * 7; 97 | *pos_y = (index / 18) * 9; 98 | } 99 | -------------------------------------------------------------------------------- /src/pfs.c: -------------------------------------------------------------------------------- 1 | /** @File pfs.c 2 | * 3 | * Packed file system implementation. 4 | * Adapted version, originally from The Mistral Report: 5 | * https://montyontherun.itch.io/the-mistral-report 6 | * 7 | * Copyright (c) 2019, Daniel Monteiro. All rights reserved. 8 | * SPDX-License-Identifier: BSD-2-Clause 9 | * 10 | **/ 11 | 12 | #include 13 | #include 14 | #include 15 | #include "SDL.h" 16 | 17 | #define kDataPath_MaxLength 256 18 | 19 | char mDataPath[kDataPath_MaxLength]; 20 | 21 | void init_file_reader(const char * dataFilePath); 22 | size_t size_of_file(const char * path); 23 | Uint8 *load_binary_file_from_path(const char * path); 24 | SDL_RWops *open_binary_file_from_path(const char * path); 25 | 26 | void init_file_reader(const char * dataFilePath) 27 | { 28 | sprintf(mDataPath, "%s", dataFilePath); 29 | } 30 | 31 | size_t size_of_file(const char * path) 32 | { 33 | SDL_RWops *mDataPack = SDL_RWFromFile(mDataPath, "rb"); 34 | char buffer[85]; 35 | int c; 36 | Uint32 size = 0; 37 | Uint32 offset = 0; 38 | Uint16 entries = 0; 39 | 40 | if (1 != SDL_RWread(mDataPack, &entries, 2, 1)) 41 | { 42 | /* Nothing to do here. */ 43 | } 44 | 45 | for (c = 0; c < entries; ++c) 46 | { 47 | Uint8 stringSize = 0; 48 | 49 | if (1 != SDL_RWread(mDataPack, &offset, 4, 1)) 50 | { 51 | /* Nothing to do here. */ 52 | } 53 | 54 | if (1 != SDL_RWread(mDataPack, &stringSize, 1, 1)) 55 | { 56 | /* Nothing to do here. */ 57 | } 58 | 59 | if (1 != SDL_RWread(mDataPack, &buffer, stringSize + 1, 1)) 60 | { 61 | /* Nothing to do here. */ 62 | } 63 | 64 | if (0 == strcmp(buffer, path)) 65 | { 66 | goto found; 67 | } 68 | } 69 | 70 | found: 71 | if (offset == 0) 72 | { 73 | printf("failed to load %s\n", path); 74 | exit(-1); 75 | } 76 | 77 | SDL_RWseek(mDataPack, offset, RW_SEEK_SET); 78 | 79 | if (1 != SDL_RWread(mDataPack, &size, 4, 1)) 80 | { 81 | /* Nothing to do here. */ 82 | } 83 | 84 | SDL_RWclose(mDataPack); 85 | 86 | return size; 87 | } 88 | 89 | Uint8 *load_binary_file_from_path(const char * path) 90 | { 91 | SDL_RWops *mDataPack = SDL_RWFromFile(mDataPath, "rb"); 92 | Uint32 offset = 0; 93 | Uint16 entries = 0; 94 | char buffer[85]; 95 | int c; 96 | Uint32 size = 0; 97 | Uint8 *toReturn; 98 | 99 | if (1 != SDL_RWread(mDataPack, &entries, 2, 1)) 100 | { 101 | /* Nothing to do here. */ 102 | } 103 | 104 | for (c = 0; c < entries; ++c) 105 | { 106 | Uint8 stringSize = 0; 107 | 108 | if (1 != SDL_RWread(mDataPack, &offset, 4, 1)) 109 | { 110 | /* Nothing to do here. */ 111 | } 112 | 113 | if (1 != SDL_RWread(mDataPack, &stringSize, 1, 1)) 114 | { 115 | /* Nothing to do here. */ 116 | } 117 | 118 | if (1 != SDL_RWread(mDataPack, &buffer, stringSize + 1, 1)) 119 | { 120 | /* Nothing to do here. */ 121 | } 122 | 123 | if (0 == strcmp(buffer, path)) 124 | { 125 | goto found; 126 | } 127 | } 128 | 129 | found: 130 | if (offset == 0) 131 | { 132 | printf("failed to load %s\n", path); 133 | exit(-1); 134 | } 135 | 136 | SDL_RWseek(mDataPack, offset, RW_SEEK_SET); 137 | 138 | if (1 != SDL_RWread(mDataPack, &size, 4, 1)) 139 | { 140 | /* Nothing to do here. */ 141 | } 142 | 143 | toReturn = (Uint8 *) malloc(size); 144 | 145 | if (size != SDL_RWread(mDataPack, toReturn, sizeof(Uint8), size)) 146 | { 147 | /* Nothing to do here. */ 148 | } 149 | 150 | SDL_RWclose(mDataPack); 151 | 152 | return toReturn; 153 | } 154 | 155 | SDL_RWops *open_binary_file_from_path(const char * path) 156 | { 157 | SDL_RWops *mDataPack = SDL_RWFromFile(mDataPath, "rb"); 158 | Uint32 offset = 0; 159 | Uint16 entries = 0; 160 | char buffer[85]; 161 | int c; 162 | Uint32 size = 0; 163 | 164 | if (1 != SDL_RWread(mDataPack, &entries, 2, 1)) 165 | { 166 | /* Nothing to do here. */ 167 | } 168 | 169 | for (c = 0; c < entries; ++c) 170 | { 171 | Uint8 stringSize = 0; 172 | 173 | if (1 != SDL_RWread(mDataPack, &offset, 4, 1)) 174 | { 175 | /* Nothing to do here. */ 176 | } 177 | 178 | if (1 != SDL_RWread(mDataPack, &stringSize, 1, 1)) 179 | { 180 | /* Nothing to do here. */ 181 | } 182 | 183 | if (1 != SDL_RWread(mDataPack, &buffer, stringSize + 1, 1)) 184 | { 185 | /* Nothing to do here. */ 186 | } 187 | 188 | if (0 == strcmp(buffer, path)) 189 | { 190 | goto found; 191 | } 192 | } 193 | 194 | return NULL; 195 | 196 | found: 197 | if (offset == 0) 198 | { 199 | printf("failed to load %s\n", path); 200 | exit(-1); 201 | } 202 | 203 | SDL_RWseek(mDataPack, offset, RW_SEEK_SET); 204 | 205 | if (1 != SDL_RWread(mDataPack, &size, 4, 1)) 206 | { 207 | /* Nothing to do here. */ 208 | } 209 | 210 | return mDataPack; 211 | } 212 | -------------------------------------------------------------------------------- /src/stb_sprintf.h: -------------------------------------------------------------------------------- 1 | // stb_sprintf - v1.10 - public domain snprintf() implementation 2 | // originally by Jeff Roberts / RAD Game Tools, 2015/10/20 3 | // http://github.com/nothings/stb 4 | // 5 | // allowed types: sc uidBboXx p AaGgEef n 6 | // lengths : hh h ll j z t I64 I32 I 7 | // 8 | // Contributors: 9 | // Fabian "ryg" Giesen (reformatting) 10 | // github:aganm (attribute format) 11 | // 12 | // Contributors (bugfixes): 13 | // github:d26435 14 | // github:trex78 15 | // github:account-login 16 | // Jari Komppa (SI suffixes) 17 | // Rohit Nirmal 18 | // Marcin Wojdyr 19 | // Leonard Ritter 20 | // Stefano Zanotti 21 | // Adam Allison 22 | // Arvid Gerstmann 23 | // Markus Kolb 24 | // 25 | // LICENSE: 26 | // 27 | // See end of file for license information. 28 | 29 | #ifndef STB_SPRINTF_H_INCLUDE 30 | #define STB_SPRINTF_H_INCLUDE 31 | 32 | /* 33 | Single file sprintf replacement. 34 | 35 | Originally written by Jeff Roberts at RAD Game Tools - 2015/10/20. 36 | Hereby placed in public domain. 37 | 38 | This is a full sprintf replacement that supports everything that 39 | the C runtime sprintfs support, including float/double, 64-bit integers, 40 | hex floats, field parameters (%*.*d stuff), length reads backs, etc. 41 | 42 | Why would you need this if sprintf already exists? Well, first off, 43 | it's *much* faster (see below). It's also much smaller than the CRT 44 | versions code-space-wise. We've also added some simple improvements 45 | that are super handy (commas in thousands, callbacks at buffer full, 46 | for example). Finally, the format strings for MSVC and GCC differ 47 | for 64-bit integers (among other small things), so this lets you use 48 | the same format strings in cross platform code. 49 | 50 | It uses the standard single file trick of being both the header file 51 | and the source itself. If you just include it normally, you just get 52 | the header file function definitions. To get the code, you include 53 | it from a C or C++ file and define STB_SPRINTF_IMPLEMENTATION first. 54 | 55 | It only uses va_args macros from the C runtime to do it's work. It 56 | does cast doubles to S64s and shifts and divides U64s, which does 57 | drag in CRT code on most platforms. 58 | 59 | It compiles to roughly 8K with float support, and 4K without. 60 | As a comparison, when using MSVC static libs, calling sprintf drags 61 | in 16K. 62 | 63 | API: 64 | ==== 65 | int stbsp_sprintf( char * buf, char const * fmt, ... ) 66 | int stbsp_snprintf( char * buf, int count, char const * fmt, ... ) 67 | Convert an arg list into a buffer. stbsp_snprintf always returns 68 | a zero-terminated string (unlike regular snprintf). 69 | 70 | int stbsp_vsprintf( char * buf, char const * fmt, va_list va ) 71 | int stbsp_vsnprintf( char * buf, int count, char const * fmt, va_list va ) 72 | Convert a va_list arg list into a buffer. stbsp_vsnprintf always returns 73 | a zero-terminated string (unlike regular snprintf). 74 | 75 | int stbsp_vsprintfcb( STBSP_SPRINTFCB * callback, void * user, char * buf, char const * fmt, va_list va ) 76 | typedef char * STBSP_SPRINTFCB( char const * buf, void * user, int len ); 77 | Convert into a buffer, calling back every STB_SPRINTF_MIN chars. 78 | Your callback can then copy the chars out, print them or whatever. 79 | This function is actually the workhorse for everything else. 80 | The buffer you pass in must hold at least STB_SPRINTF_MIN characters. 81 | // you return the next buffer to use or 0 to stop converting 82 | 83 | void stbsp_set_separators( char comma, char period ) 84 | Set the comma and period characters to use. 85 | 86 | FLOATS/DOUBLES: 87 | =============== 88 | This code uses a internal float->ascii conversion method that uses 89 | doubles with error correction (double-doubles, for ~105 bits of 90 | precision). This conversion is round-trip perfect - that is, an atof 91 | of the values output here will give you the bit-exact double back. 92 | 93 | One difference is that our insignificant digits will be different than 94 | with MSVC or GCC (but they don't match each other either). We also 95 | don't attempt to find the minimum length matching float (pre-MSVC15 96 | doesn't either). 97 | 98 | If you don't need float or doubles at all, define STB_SPRINTF_NOFLOAT 99 | and you'll save 4K of code space. 100 | 101 | 64-BIT INTS: 102 | ============ 103 | This library also supports 64-bit integers and you can use MSVC style or 104 | GCC style indicators (%I64d or %lld). It supports the C99 specifiers 105 | for size_t and ptr_diff_t (%jd %zd) as well. 106 | 107 | EXTRAS: 108 | ======= 109 | Like some GCCs, for integers and floats, you can use a ' (single quote) 110 | specifier and commas will be inserted on the thousands: "%'d" on 12345 111 | would print 12,345. 112 | 113 | For integers and floats, you can use a "$" specifier and the number 114 | will be converted to float and then divided to get kilo, mega, giga or 115 | tera and then printed, so "%$d" 1000 is "1.0 k", "%$.2d" 2536000 is 116 | "2.53 M", etc. For byte values, use two $:s, like "%$$d" to turn 117 | 2536000 to "2.42 Mi". If you prefer JEDEC suffixes to SI ones, use three 118 | $:s: "%$$$d" -> "2.42 M". To remove the space between the number and the 119 | suffix, add "_" specifier: "%_$d" -> "2.53M". 120 | 121 | In addition to octal and hexadecimal conversions, you can print 122 | integers in binary: "%b" for 256 would print 100. 123 | 124 | PERFORMANCE vs MSVC 2008 32-/64-bit (GCC is even slower than MSVC): 125 | =================================================================== 126 | "%d" across all 32-bit ints (4.8x/4.0x faster than 32-/64-bit MSVC) 127 | "%24d" across all 32-bit ints (4.5x/4.2x faster) 128 | "%x" across all 32-bit ints (4.5x/3.8x faster) 129 | "%08x" across all 32-bit ints (4.3x/3.8x faster) 130 | "%f" across e-10 to e+10 floats (7.3x/6.0x faster) 131 | "%e" across e-10 to e+10 floats (8.1x/6.0x faster) 132 | "%g" across e-10 to e+10 floats (10.0x/7.1x faster) 133 | "%f" for values near e-300 (7.9x/6.5x faster) 134 | "%f" for values near e+300 (10.0x/9.1x faster) 135 | "%e" for values near e-300 (10.1x/7.0x faster) 136 | "%e" for values near e+300 (9.2x/6.0x faster) 137 | "%.320f" for values near e-300 (12.6x/11.2x faster) 138 | "%a" for random values (8.6x/4.3x faster) 139 | "%I64d" for 64-bits with 32-bit values (4.8x/3.4x faster) 140 | "%I64d" for 64-bits > 32-bit values (4.9x/5.5x faster) 141 | "%s%s%s" for 64 char strings (7.1x/7.3x faster) 142 | "...512 char string..." ( 35.0x/32.5x faster!) 143 | */ 144 | 145 | #if defined(__clang__) 146 | #if defined(__has_feature) && defined(__has_attribute) 147 | #if __has_feature(address_sanitizer) 148 | #if __has_attribute(__no_sanitize__) 149 | #define STBSP__ASAN __attribute__((__no_sanitize__("address"))) 150 | #elif __has_attribute(__no_sanitize_address__) 151 | #define STBSP__ASAN __attribute__((__no_sanitize_address__)) 152 | #elif __has_attribute(__no_address_safety_analysis__) 153 | #define STBSP__ASAN __attribute__((__no_address_safety_analysis__)) 154 | #endif 155 | #endif 156 | #endif 157 | #elif defined(__GNUC__) && (__GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) 158 | #if defined(__SANITIZE_ADDRESS__) && __SANITIZE_ADDRESS__ 159 | #define STBSP__ASAN __attribute__((__no_sanitize_address__)) 160 | #endif 161 | #endif 162 | 163 | #ifndef STBSP__ASAN 164 | #define STBSP__ASAN 165 | #endif 166 | 167 | #ifdef STB_SPRINTF_STATIC 168 | #define STBSP__PUBLICDEC static 169 | #define STBSP__PUBLICDEF static STBSP__ASAN 170 | #else 171 | #ifdef __cplusplus 172 | #define STBSP__PUBLICDEC extern "C" 173 | #define STBSP__PUBLICDEF extern "C" STBSP__ASAN 174 | #else 175 | #define STBSP__PUBLICDEC extern 176 | #define STBSP__PUBLICDEF STBSP__ASAN 177 | #endif 178 | #endif 179 | 180 | #if defined(__has_attribute) 181 | #if __has_attribute(format) 182 | #define STBSP__ATTRIBUTE_FORMAT(fmt,va) __attribute__((format(printf,fmt,va))) 183 | #endif 184 | #endif 185 | 186 | #ifndef STBSP__ATTRIBUTE_FORMAT 187 | #define STBSP__ATTRIBUTE_FORMAT(fmt,va) 188 | #endif 189 | 190 | #ifdef _MSC_VER 191 | #define STBSP__NOTUSED(v) (void)(v) 192 | #else 193 | #define STBSP__NOTUSED(v) (void)sizeof(v) 194 | #endif 195 | 196 | #include // for va_arg(), va_list() 197 | #include // size_t, ptrdiff_t 198 | 199 | #ifndef STB_SPRINTF_MIN 200 | #define STB_SPRINTF_MIN 512 // how many characters per callback 201 | #endif 202 | typedef char *STBSP_SPRINTFCB(const char *buf, void *user, int len); 203 | 204 | #ifndef STB_SPRINTF_DECORATE 205 | #define STB_SPRINTF_DECORATE(name) stbsp_##name // define this before including if you want to change the names 206 | #endif 207 | 208 | STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va); 209 | STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsnprintf)(char *buf, int count, char const *fmt, va_list va); 210 | STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(2,3); 211 | STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(3,4); 212 | 213 | STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va); 214 | STBSP__PUBLICDEC void STB_SPRINTF_DECORATE(set_separators)(char comma, char period); 215 | 216 | #endif // STB_SPRINTF_H_INCLUDE 217 | 218 | #ifdef STB_SPRINTF_IMPLEMENTATION 219 | 220 | #define stbsp__uint32 unsigned int 221 | #define stbsp__int32 signed int 222 | 223 | #ifdef _MSC_VER 224 | #define stbsp__uint64 unsigned __int64 225 | #define stbsp__int64 signed __int64 226 | #else 227 | #define stbsp__uint64 unsigned long long 228 | #define stbsp__int64 signed long long 229 | #endif 230 | #define stbsp__uint16 unsigned short 231 | 232 | #ifndef stbsp__uintptr 233 | #if defined(__ppc64__) || defined(__powerpc64__) || defined(__aarch64__) || defined(_M_X64) || defined(__x86_64__) || defined(__x86_64) || defined(__s390x__) 234 | #define stbsp__uintptr stbsp__uint64 235 | #else 236 | #define stbsp__uintptr stbsp__uint32 237 | #endif 238 | #endif 239 | 240 | #ifndef STB_SPRINTF_MSVC_MODE // used for MSVC2013 and earlier (MSVC2015 matches GCC) 241 | #if defined(_MSC_VER) && (_MSC_VER < 1900) 242 | #define STB_SPRINTF_MSVC_MODE 243 | #endif 244 | #endif 245 | 246 | #ifdef STB_SPRINTF_NOUNALIGNED // define this before inclusion to force stbsp_sprintf to always use aligned accesses 247 | #define STBSP__UNALIGNED(code) 248 | #else 249 | #define STBSP__UNALIGNED(code) code 250 | #endif 251 | 252 | #ifndef STB_SPRINTF_NOFLOAT 253 | // internal float utility functions 254 | static stbsp__int32 stbsp__real_to_str(char const **start, stbsp__uint32 *len, char *out, stbsp__int32 *decimal_pos, double value, stbsp__uint32 frac_digits); 255 | static stbsp__int32 stbsp__real_to_parts(stbsp__int64 *bits, stbsp__int32 *expo, double value); 256 | #define STBSP__SPECIAL 0x7000 257 | #endif 258 | 259 | static char stbsp__period = '.'; 260 | static char stbsp__comma = ','; 261 | static struct 262 | { 263 | short temp; // force next field to be 2-byte aligned 264 | char pair[201]; 265 | } stbsp__digitpair = 266 | { 267 | 0, 268 | "00010203040506070809101112131415161718192021222324" 269 | "25262728293031323334353637383940414243444546474849" 270 | "50515253545556575859606162636465666768697071727374" 271 | "75767778798081828384858687888990919293949596979899" 272 | }; 273 | 274 | STBSP__PUBLICDEF void STB_SPRINTF_DECORATE(set_separators)(char pcomma, char pperiod) 275 | { 276 | stbsp__period = pperiod; 277 | stbsp__comma = pcomma; 278 | } 279 | 280 | #define STBSP__LEFTJUST 1 281 | #define STBSP__LEADINGPLUS 2 282 | #define STBSP__LEADINGSPACE 4 283 | #define STBSP__LEADING_0X 8 284 | #define STBSP__LEADINGZERO 16 285 | #define STBSP__INTMAX 32 286 | #define STBSP__TRIPLET_COMMA 64 287 | #define STBSP__NEGATIVE 128 288 | #define STBSP__METRIC_SUFFIX 256 289 | #define STBSP__HALFWIDTH 512 290 | #define STBSP__METRIC_NOSPACE 1024 291 | #define STBSP__METRIC_1024 2048 292 | #define STBSP__METRIC_JEDEC 4096 293 | 294 | static void stbsp__lead_sign(stbsp__uint32 fl, char *sign) 295 | { 296 | sign[0] = 0; 297 | if (fl & STBSP__NEGATIVE) { 298 | sign[0] = 1; 299 | sign[1] = '-'; 300 | } else if (fl & STBSP__LEADINGSPACE) { 301 | sign[0] = 1; 302 | sign[1] = ' '; 303 | } else if (fl & STBSP__LEADINGPLUS) { 304 | sign[0] = 1; 305 | sign[1] = '+'; 306 | } 307 | } 308 | 309 | static STBSP__ASAN stbsp__uint32 stbsp__strlen_limited(char const *s, stbsp__uint32 limit) 310 | { 311 | char const * sn = s; 312 | 313 | // get up to 4-byte alignment 314 | for (;;) { 315 | if (((stbsp__uintptr)sn & 3) == 0) 316 | break; 317 | 318 | if (!limit || *sn == 0) 319 | return (stbsp__uint32)(sn - s); 320 | 321 | ++sn; 322 | --limit; 323 | } 324 | 325 | // scan over 4 bytes at a time to find terminating 0 326 | // this will intentionally scan up to 3 bytes past the end of buffers, 327 | // but becase it works 4B aligned, it will never cross page boundaries 328 | // (hence the STBSP__ASAN markup; the over-read here is intentional 329 | // and harmless) 330 | while (limit >= 4) { 331 | stbsp__uint32 v = *(stbsp__uint32 *)sn; 332 | // bit hack to find if there's a 0 byte in there 333 | if ((v - 0x01010101) & (~v) & 0x80808080UL) 334 | break; 335 | 336 | sn += 4; 337 | limit -= 4; 338 | } 339 | 340 | // handle the last few characters to find actual size 341 | while (limit && *sn) { 342 | ++sn; 343 | --limit; 344 | } 345 | 346 | return (stbsp__uint32)(sn - s); 347 | } 348 | 349 | STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va) 350 | { 351 | static char hex[] = "0123456789abcdefxp"; 352 | static char hexu[] = "0123456789ABCDEFXP"; 353 | char *bf; 354 | char const *f; 355 | int tlen = 0; 356 | 357 | bf = buf; 358 | f = fmt; 359 | for (;;) { 360 | stbsp__int32 fw, pr, tz; 361 | stbsp__uint32 fl; 362 | 363 | // macros for the callback buffer stuff 364 | #define stbsp__chk_cb_bufL(bytes) \ 365 | { \ 366 | int len = (int)(bf - buf); \ 367 | if ((len + (bytes)) >= STB_SPRINTF_MIN) { \ 368 | tlen += len; \ 369 | if (0 == (bf = buf = callback(buf, user, len))) \ 370 | goto done; \ 371 | } \ 372 | } 373 | #define stbsp__chk_cb_buf(bytes) \ 374 | { \ 375 | if (callback) { \ 376 | stbsp__chk_cb_bufL(bytes); \ 377 | } \ 378 | } 379 | #define stbsp__flush_cb() \ 380 | { \ 381 | stbsp__chk_cb_bufL(STB_SPRINTF_MIN - 1); \ 382 | } // flush if there is even one byte in the buffer 383 | #define stbsp__cb_buf_clamp(cl, v) \ 384 | cl = v; \ 385 | if (callback) { \ 386 | int lg = STB_SPRINTF_MIN - (int)(bf - buf); \ 387 | if (cl > lg) \ 388 | cl = lg; \ 389 | } 390 | 391 | // fast copy everything up to the next % (or end of string) 392 | for (;;) { 393 | while (((stbsp__uintptr)f) & 3) { 394 | schk1: 395 | if (f[0] == '%') 396 | goto scandd; 397 | schk2: 398 | if (f[0] == 0) 399 | goto endfmt; 400 | stbsp__chk_cb_buf(1); 401 | *bf++ = f[0]; 402 | ++f; 403 | } 404 | for (;;) { 405 | // Check if the next 4 bytes contain %(0x25) or end of string. 406 | // Using the 'hasless' trick: 407 | // https://graphics.stanford.edu/~seander/bithacks.html#HasLessInWord 408 | stbsp__uint32 v, c; 409 | v = *(stbsp__uint32 *)f; 410 | c = (~v) & 0x80808080; 411 | if (((v ^ 0x25252525) - 0x01010101) & c) 412 | goto schk1; 413 | if ((v - 0x01010101) & c) 414 | goto schk2; 415 | if (callback) 416 | if ((STB_SPRINTF_MIN - (int)(bf - buf)) < 4) 417 | goto schk1; 418 | #ifdef STB_SPRINTF_NOUNALIGNED 419 | if(((stbsp__uintptr)bf) & 3) { 420 | bf[0] = f[0]; 421 | bf[1] = f[1]; 422 | bf[2] = f[2]; 423 | bf[3] = f[3]; 424 | } else 425 | #endif 426 | { 427 | *(stbsp__uint32 *)bf = v; 428 | } 429 | bf += 4; 430 | f += 4; 431 | } 432 | } 433 | scandd: 434 | 435 | ++f; 436 | 437 | // ok, we have a percent, read the modifiers first 438 | fw = 0; 439 | pr = -1; 440 | fl = 0; 441 | tz = 0; 442 | 443 | // flags 444 | for (;;) { 445 | switch (f[0]) { 446 | // if we have left justify 447 | case '-': 448 | fl |= STBSP__LEFTJUST; 449 | ++f; 450 | continue; 451 | // if we have leading plus 452 | case '+': 453 | fl |= STBSP__LEADINGPLUS; 454 | ++f; 455 | continue; 456 | // if we have leading space 457 | case ' ': 458 | fl |= STBSP__LEADINGSPACE; 459 | ++f; 460 | continue; 461 | // if we have leading 0x 462 | case '#': 463 | fl |= STBSP__LEADING_0X; 464 | ++f; 465 | continue; 466 | // if we have thousand commas 467 | case '\'': 468 | fl |= STBSP__TRIPLET_COMMA; 469 | ++f; 470 | continue; 471 | // if we have kilo marker (none->kilo->kibi->jedec) 472 | case '$': 473 | if (fl & STBSP__METRIC_SUFFIX) { 474 | if (fl & STBSP__METRIC_1024) { 475 | fl |= STBSP__METRIC_JEDEC; 476 | } else { 477 | fl |= STBSP__METRIC_1024; 478 | } 479 | } else { 480 | fl |= STBSP__METRIC_SUFFIX; 481 | } 482 | ++f; 483 | continue; 484 | // if we don't want space between metric suffix and number 485 | case '_': 486 | fl |= STBSP__METRIC_NOSPACE; 487 | ++f; 488 | continue; 489 | // if we have leading zero 490 | case '0': 491 | fl |= STBSP__LEADINGZERO; 492 | ++f; 493 | goto flags_done; 494 | default: goto flags_done; 495 | } 496 | } 497 | flags_done: 498 | 499 | // get the field width 500 | if (f[0] == '*') { 501 | fw = va_arg(va, stbsp__uint32); 502 | ++f; 503 | } else { 504 | while ((f[0] >= '0') && (f[0] <= '9')) { 505 | fw = fw * 10 + f[0] - '0'; 506 | f++; 507 | } 508 | } 509 | // get the precision 510 | if (f[0] == '.') { 511 | ++f; 512 | if (f[0] == '*') { 513 | pr = va_arg(va, stbsp__uint32); 514 | ++f; 515 | } else { 516 | pr = 0; 517 | while ((f[0] >= '0') && (f[0] <= '9')) { 518 | pr = pr * 10 + f[0] - '0'; 519 | f++; 520 | } 521 | } 522 | } 523 | 524 | // handle integer size overrides 525 | switch (f[0]) { 526 | // are we halfwidth? 527 | case 'h': 528 | fl |= STBSP__HALFWIDTH; 529 | ++f; 530 | if (f[0] == 'h') 531 | ++f; // QUARTERWIDTH 532 | break; 533 | // are we 64-bit (unix style) 534 | case 'l': 535 | fl |= ((sizeof(long) == 8) ? STBSP__INTMAX : 0); 536 | ++f; 537 | if (f[0] == 'l') { 538 | fl |= STBSP__INTMAX; 539 | ++f; 540 | } 541 | break; 542 | // are we 64-bit on intmax? (c99) 543 | case 'j': 544 | fl |= (sizeof(size_t) == 8) ? STBSP__INTMAX : 0; 545 | ++f; 546 | break; 547 | // are we 64-bit on size_t or ptrdiff_t? (c99) 548 | case 'z': 549 | fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0; 550 | ++f; 551 | break; 552 | case 't': 553 | fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0; 554 | ++f; 555 | break; 556 | // are we 64-bit (msft style) 557 | case 'I': 558 | if ((f[1] == '6') && (f[2] == '4')) { 559 | fl |= STBSP__INTMAX; 560 | f += 3; 561 | } else if ((f[1] == '3') && (f[2] == '2')) { 562 | f += 3; 563 | } else { 564 | fl |= ((sizeof(void *) == 8) ? STBSP__INTMAX : 0); 565 | ++f; 566 | } 567 | break; 568 | default: break; 569 | } 570 | 571 | // handle each replacement 572 | switch (f[0]) { 573 | #define STBSP__NUMSZ 512 // big enough for e308 (with commas) or e-307 574 | char num[STBSP__NUMSZ]; 575 | char lead[8]; 576 | char tail[8]; 577 | char *s; 578 | char const *h; 579 | stbsp__uint32 l, n, cs; 580 | stbsp__uint64 n64; 581 | #ifndef STB_SPRINTF_NOFLOAT 582 | double fv; 583 | #endif 584 | stbsp__int32 dp; 585 | char const *sn; 586 | 587 | case 's': 588 | // get the string 589 | s = va_arg(va, char *); 590 | if (s == 0) 591 | s = (char *)"null"; 592 | // get the length, limited to desired precision 593 | // always limit to ~0u chars since our counts are 32b 594 | l = stbsp__strlen_limited(s, (pr >= 0) ? pr : ~0u); 595 | lead[0] = 0; 596 | tail[0] = 0; 597 | pr = 0; 598 | dp = 0; 599 | cs = 0; 600 | // copy the string in 601 | goto scopy; 602 | 603 | case 'c': // char 604 | // get the character 605 | s = num + STBSP__NUMSZ - 1; 606 | *s = (char)va_arg(va, int); 607 | l = 1; 608 | lead[0] = 0; 609 | tail[0] = 0; 610 | pr = 0; 611 | dp = 0; 612 | cs = 0; 613 | goto scopy; 614 | 615 | case 'n': // weird write-bytes specifier 616 | { 617 | int *d = va_arg(va, int *); 618 | *d = tlen + (int)(bf - buf); 619 | } break; 620 | 621 | #ifdef STB_SPRINTF_NOFLOAT 622 | case 'A': // float 623 | case 'a': // hex float 624 | case 'G': // float 625 | case 'g': // float 626 | case 'E': // float 627 | case 'e': // float 628 | case 'f': // float 629 | va_arg(va, double); // eat it 630 | s = (char *)"No float"; 631 | l = 8; 632 | lead[0] = 0; 633 | tail[0] = 0; 634 | pr = 0; 635 | cs = 0; 636 | STBSP__NOTUSED(dp); 637 | goto scopy; 638 | #else 639 | case 'A': // hex float 640 | case 'a': // hex float 641 | h = (f[0] == 'A') ? hexu : hex; 642 | fv = va_arg(va, double); 643 | if (pr == -1) 644 | pr = 6; // default is 6 645 | // read the double into a string 646 | if (stbsp__real_to_parts((stbsp__int64 *)&n64, &dp, fv)) 647 | fl |= STBSP__NEGATIVE; 648 | 649 | s = num + 64; 650 | 651 | stbsp__lead_sign(fl, lead); 652 | 653 | if (dp == -1023) 654 | dp = (n64) ? -1022 : 0; 655 | else 656 | n64 |= (((stbsp__uint64)1) << 52); 657 | n64 <<= (64 - 56); 658 | if (pr < 15) 659 | n64 += ((((stbsp__uint64)8) << 56) >> (pr * 4)); 660 | // add leading chars 661 | 662 | #ifdef STB_SPRINTF_MSVC_MODE 663 | *s++ = '0'; 664 | *s++ = 'x'; 665 | #else 666 | lead[1 + lead[0]] = '0'; 667 | lead[2 + lead[0]] = 'x'; 668 | lead[0] += 2; 669 | #endif 670 | *s++ = h[(n64 >> 60) & 15]; 671 | n64 <<= 4; 672 | if (pr) 673 | *s++ = stbsp__period; 674 | sn = s; 675 | 676 | // print the bits 677 | n = pr; 678 | if (n > 13) 679 | n = 13; 680 | if (pr > (stbsp__int32)n) 681 | tz = pr - n; 682 | pr = 0; 683 | while (n--) { 684 | *s++ = h[(n64 >> 60) & 15]; 685 | n64 <<= 4; 686 | } 687 | 688 | // print the expo 689 | tail[1] = h[17]; 690 | if (dp < 0) { 691 | tail[2] = '-'; 692 | dp = -dp; 693 | } else 694 | tail[2] = '+'; 695 | n = (dp >= 1000) ? 6 : ((dp >= 100) ? 5 : ((dp >= 10) ? 4 : 3)); 696 | tail[0] = (char)n; 697 | for (;;) { 698 | tail[n] = '0' + dp % 10; 699 | if (n <= 3) 700 | break; 701 | --n; 702 | dp /= 10; 703 | } 704 | 705 | dp = (int)(s - sn); 706 | l = (int)(s - (num + 64)); 707 | s = num + 64; 708 | cs = 1 + (3 << 24); 709 | goto scopy; 710 | 711 | case 'G': // float 712 | case 'g': // float 713 | h = (f[0] == 'G') ? hexu : hex; 714 | fv = va_arg(va, double); 715 | if (pr == -1) 716 | pr = 6; 717 | else if (pr == 0) 718 | pr = 1; // default is 6 719 | // read the double into a string 720 | if (stbsp__real_to_str(&sn, &l, num, &dp, fv, (pr - 1) | 0x80000000)) 721 | fl |= STBSP__NEGATIVE; 722 | 723 | // clamp the precision and delete extra zeros after clamp 724 | n = pr; 725 | if (l > (stbsp__uint32)pr) 726 | l = pr; 727 | while ((l > 1) && (pr) && (sn[l - 1] == '0')) { 728 | --pr; 729 | --l; 730 | } 731 | 732 | // should we use %e 733 | if ((dp <= -4) || (dp > (stbsp__int32)n)) { 734 | if (pr > (stbsp__int32)l) 735 | pr = l - 1; 736 | else if (pr) 737 | --pr; // when using %e, there is one digit before the decimal 738 | goto doexpfromg; 739 | } 740 | // this is the insane action to get the pr to match %g semantics for %f 741 | if (dp > 0) { 742 | pr = (dp < (stbsp__int32)l) ? l - dp : 0; 743 | } else { 744 | pr = -dp + ((pr > (stbsp__int32)l) ? (stbsp__int32) l : pr); 745 | } 746 | goto dofloatfromg; 747 | 748 | case 'E': // float 749 | case 'e': // float 750 | h = (f[0] == 'E') ? hexu : hex; 751 | fv = va_arg(va, double); 752 | if (pr == -1) 753 | pr = 6; // default is 6 754 | // read the double into a string 755 | if (stbsp__real_to_str(&sn, &l, num, &dp, fv, pr | 0x80000000)) 756 | fl |= STBSP__NEGATIVE; 757 | doexpfromg: 758 | tail[0] = 0; 759 | stbsp__lead_sign(fl, lead); 760 | if (dp == STBSP__SPECIAL) { 761 | s = (char *)sn; 762 | cs = 0; 763 | pr = 0; 764 | goto scopy; 765 | } 766 | s = num + 64; 767 | // handle leading chars 768 | *s++ = sn[0]; 769 | 770 | if (pr) 771 | *s++ = stbsp__period; 772 | 773 | // handle after decimal 774 | if ((l - 1) > (stbsp__uint32)pr) 775 | l = pr + 1; 776 | for (n = 1; n < l; n++) 777 | *s++ = sn[n]; 778 | // trailing zeros 779 | tz = pr - (l - 1); 780 | pr = 0; 781 | // dump expo 782 | tail[1] = h[0xe]; 783 | dp -= 1; 784 | if (dp < 0) { 785 | tail[2] = '-'; 786 | dp = -dp; 787 | } else 788 | tail[2] = '+'; 789 | #ifdef STB_SPRINTF_MSVC_MODE 790 | n = 5; 791 | #else 792 | n = (dp >= 100) ? 5 : 4; 793 | #endif 794 | tail[0] = (char)n; 795 | for (;;) { 796 | tail[n] = '0' + dp % 10; 797 | if (n <= 3) 798 | break; 799 | --n; 800 | dp /= 10; 801 | } 802 | cs = 1 + (3 << 24); // how many tens 803 | goto flt_lead; 804 | 805 | case 'f': // float 806 | fv = va_arg(va, double); 807 | doafloat: 808 | // do kilos 809 | if (fl & STBSP__METRIC_SUFFIX) { 810 | double divisor; 811 | divisor = 1000.0f; 812 | if (fl & STBSP__METRIC_1024) 813 | divisor = 1024.0; 814 | while (fl < 0x4000000) { 815 | if ((fv < divisor) && (fv > -divisor)) 816 | break; 817 | fv /= divisor; 818 | fl += 0x1000000; 819 | } 820 | } 821 | if (pr == -1) 822 | pr = 6; // default is 6 823 | // read the double into a string 824 | if (stbsp__real_to_str(&sn, &l, num, &dp, fv, pr)) 825 | fl |= STBSP__NEGATIVE; 826 | dofloatfromg: 827 | tail[0] = 0; 828 | stbsp__lead_sign(fl, lead); 829 | if (dp == STBSP__SPECIAL) { 830 | s = (char *)sn; 831 | cs = 0; 832 | pr = 0; 833 | goto scopy; 834 | } 835 | s = num + 64; 836 | 837 | // handle the three decimal varieties 838 | if (dp <= 0) { 839 | stbsp__int32 i; 840 | // handle 0.000*000xxxx 841 | *s++ = '0'; 842 | if (pr) 843 | *s++ = stbsp__period; 844 | n = -dp; 845 | if ((stbsp__int32)n > pr) 846 | n = pr; 847 | i = n; 848 | while (i) { 849 | if ((((stbsp__uintptr)s) & 3) == 0) 850 | break; 851 | *s++ = '0'; 852 | --i; 853 | } 854 | while (i >= 4) { 855 | *(stbsp__uint32 *)s = 0x30303030; 856 | s += 4; 857 | i -= 4; 858 | } 859 | while (i) { 860 | *s++ = '0'; 861 | --i; 862 | } 863 | if ((stbsp__int32)(l + n) > pr) 864 | l = pr - n; 865 | i = l; 866 | while (i) { 867 | *s++ = *sn++; 868 | --i; 869 | } 870 | tz = pr - (n + l); 871 | cs = 1 + (3 << 24); // how many tens did we write (for commas below) 872 | } else { 873 | cs = (fl & STBSP__TRIPLET_COMMA) ? ((600 - (stbsp__uint32)dp) % 3) : 0; 874 | if ((stbsp__uint32)dp >= l) { 875 | // handle xxxx000*000.0 876 | n = 0; 877 | for (;;) { 878 | if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { 879 | cs = 0; 880 | *s++ = stbsp__comma; 881 | } else { 882 | *s++ = sn[n]; 883 | ++n; 884 | if (n >= l) 885 | break; 886 | } 887 | } 888 | if (n < (stbsp__uint32)dp) { 889 | n = dp - n; 890 | if ((fl & STBSP__TRIPLET_COMMA) == 0) { 891 | while (n) { 892 | if ((((stbsp__uintptr)s) & 3) == 0) 893 | break; 894 | *s++ = '0'; 895 | --n; 896 | } 897 | while (n >= 4) { 898 | *(stbsp__uint32 *)s = 0x30303030; 899 | s += 4; 900 | n -= 4; 901 | } 902 | } 903 | while (n) { 904 | if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { 905 | cs = 0; 906 | *s++ = stbsp__comma; 907 | } else { 908 | *s++ = '0'; 909 | --n; 910 | } 911 | } 912 | } 913 | cs = (int)(s - (num + 64)) + (3 << 24); // cs is how many tens 914 | if (pr) { 915 | *s++ = stbsp__period; 916 | tz = pr; 917 | } 918 | } else { 919 | // handle xxxxx.xxxx000*000 920 | n = 0; 921 | for (;;) { 922 | if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { 923 | cs = 0; 924 | *s++ = stbsp__comma; 925 | } else { 926 | *s++ = sn[n]; 927 | ++n; 928 | if (n >= (stbsp__uint32)dp) 929 | break; 930 | } 931 | } 932 | cs = (int)(s - (num + 64)) + (3 << 24); // cs is how many tens 933 | if (pr) 934 | *s++ = stbsp__period; 935 | if ((l - dp) > (stbsp__uint32)pr) 936 | l = pr + dp; 937 | while (n < l) { 938 | *s++ = sn[n]; 939 | ++n; 940 | } 941 | tz = pr - (l - dp); 942 | } 943 | } 944 | pr = 0; 945 | 946 | // handle k,m,g,t 947 | if (fl & STBSP__METRIC_SUFFIX) { 948 | char idx; 949 | idx = 1; 950 | if (fl & STBSP__METRIC_NOSPACE) 951 | idx = 0; 952 | tail[0] = idx; 953 | tail[1] = ' '; 954 | { 955 | if (fl >> 24) { // SI kilo is 'k', JEDEC and SI kibits are 'K'. 956 | if (fl & STBSP__METRIC_1024) 957 | tail[idx + 1] = "_KMGT"[fl >> 24]; 958 | else 959 | tail[idx + 1] = "_kMGT"[fl >> 24]; 960 | idx++; 961 | // If printing kibits and not in jedec, add the 'i'. 962 | if (fl & STBSP__METRIC_1024 && !(fl & STBSP__METRIC_JEDEC)) { 963 | tail[idx + 1] = 'i'; 964 | idx++; 965 | } 966 | tail[0] = idx; 967 | } 968 | } 969 | }; 970 | 971 | flt_lead: 972 | // get the length that we copied 973 | l = (stbsp__uint32)(s - (num + 64)); 974 | s = num + 64; 975 | goto scopy; 976 | #endif 977 | 978 | case 'B': // upper binary 979 | case 'b': // lower binary 980 | h = (f[0] == 'B') ? hexu : hex; 981 | lead[0] = 0; 982 | if (fl & STBSP__LEADING_0X) { 983 | lead[0] = 2; 984 | lead[1] = '0'; 985 | lead[2] = h[0xb]; 986 | } 987 | l = (8 << 4) | (1 << 8); 988 | goto radixnum; 989 | 990 | case 'o': // octal 991 | h = hexu; 992 | lead[0] = 0; 993 | if (fl & STBSP__LEADING_0X) { 994 | lead[0] = 1; 995 | lead[1] = '0'; 996 | } 997 | l = (3 << 4) | (3 << 8); 998 | goto radixnum; 999 | 1000 | case 'p': // pointer 1001 | fl |= (sizeof(void *) == 8) ? STBSP__INTMAX : 0; 1002 | pr = sizeof(void *) * 2; 1003 | fl &= ~STBSP__LEADINGZERO; // 'p' only prints the pointer with zeros 1004 | // fall through - to X 1005 | 1006 | case 'X': // upper hex 1007 | case 'x': // lower hex 1008 | h = (f[0] == 'X') ? hexu : hex; 1009 | l = (4 << 4) | (4 << 8); 1010 | lead[0] = 0; 1011 | if (fl & STBSP__LEADING_0X) { 1012 | lead[0] = 2; 1013 | lead[1] = '0'; 1014 | lead[2] = h[16]; 1015 | } 1016 | radixnum: 1017 | // get the number 1018 | if (fl & STBSP__INTMAX) 1019 | n64 = va_arg(va, stbsp__uint64); 1020 | else 1021 | n64 = va_arg(va, stbsp__uint32); 1022 | 1023 | s = num + STBSP__NUMSZ; 1024 | dp = 0; 1025 | // clear tail, and clear leading if value is zero 1026 | tail[0] = 0; 1027 | if (n64 == 0) { 1028 | lead[0] = 0; 1029 | if (pr == 0) { 1030 | l = 0; 1031 | cs = 0; 1032 | goto scopy; 1033 | } 1034 | } 1035 | // convert to string 1036 | for (;;) { 1037 | *--s = h[n64 & ((1 << (l >> 8)) - 1)]; 1038 | n64 >>= (l >> 8); 1039 | if (!((n64) || ((stbsp__int32)((num + STBSP__NUMSZ) - s) < pr))) 1040 | break; 1041 | if (fl & STBSP__TRIPLET_COMMA) { 1042 | ++l; 1043 | if ((l & 15) == ((l >> 4) & 15)) { 1044 | l &= ~15; 1045 | *--s = stbsp__comma; 1046 | } 1047 | } 1048 | }; 1049 | // get the tens and the comma pos 1050 | cs = (stbsp__uint32)((num + STBSP__NUMSZ) - s) + ((((l >> 4) & 15)) << 24); 1051 | // get the length that we copied 1052 | l = (stbsp__uint32)((num + STBSP__NUMSZ) - s); 1053 | // copy it 1054 | goto scopy; 1055 | 1056 | case 'u': // unsigned 1057 | case 'i': 1058 | case 'd': // integer 1059 | // get the integer and abs it 1060 | if (fl & STBSP__INTMAX) { 1061 | stbsp__int64 i64 = va_arg(va, stbsp__int64); 1062 | n64 = (stbsp__uint64)i64; 1063 | if ((f[0] != 'u') && (i64 < 0)) { 1064 | n64 = (stbsp__uint64)-i64; 1065 | fl |= STBSP__NEGATIVE; 1066 | } 1067 | } else { 1068 | stbsp__int32 i = va_arg(va, stbsp__int32); 1069 | n64 = (stbsp__uint32)i; 1070 | if ((f[0] != 'u') && (i < 0)) { 1071 | n64 = (stbsp__uint32)-i; 1072 | fl |= STBSP__NEGATIVE; 1073 | } 1074 | } 1075 | 1076 | #ifndef STB_SPRINTF_NOFLOAT 1077 | if (fl & STBSP__METRIC_SUFFIX) { 1078 | if (n64 < 1024) 1079 | pr = 0; 1080 | else if (pr == -1) 1081 | pr = 1; 1082 | fv = (double)(stbsp__int64)n64; 1083 | goto doafloat; 1084 | } 1085 | #endif 1086 | 1087 | // convert to string 1088 | s = num + STBSP__NUMSZ; 1089 | l = 0; 1090 | 1091 | for (;;) { 1092 | // do in 32-bit chunks (avoid lots of 64-bit divides even with constant denominators) 1093 | char *o = s - 8; 1094 | if (n64 >= 100000000) { 1095 | n = (stbsp__uint32)(n64 % 100000000); 1096 | n64 /= 100000000; 1097 | } else { 1098 | n = (stbsp__uint32)n64; 1099 | n64 = 0; 1100 | } 1101 | if ((fl & STBSP__TRIPLET_COMMA) == 0) { 1102 | do { 1103 | s -= 2; 1104 | *(stbsp__uint16 *)s = *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2]; 1105 | n /= 100; 1106 | } while (n); 1107 | } 1108 | while (n) { 1109 | if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) { 1110 | l = 0; 1111 | *--s = stbsp__comma; 1112 | --o; 1113 | } else { 1114 | *--s = (char)(n % 10) + '0'; 1115 | n /= 10; 1116 | } 1117 | } 1118 | if (n64 == 0) { 1119 | if ((s[0] == '0') && (s != (num + STBSP__NUMSZ))) 1120 | ++s; 1121 | break; 1122 | } 1123 | while (s != o) 1124 | if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) { 1125 | l = 0; 1126 | *--s = stbsp__comma; 1127 | --o; 1128 | } else { 1129 | *--s = '0'; 1130 | } 1131 | } 1132 | 1133 | tail[0] = 0; 1134 | stbsp__lead_sign(fl, lead); 1135 | 1136 | // get the length that we copied 1137 | l = (stbsp__uint32)((num + STBSP__NUMSZ) - s); 1138 | if (l == 0) { 1139 | *--s = '0'; 1140 | l = 1; 1141 | } 1142 | cs = l + (3 << 24); 1143 | if (pr < 0) 1144 | pr = 0; 1145 | 1146 | scopy: 1147 | // get fw=leading/trailing space, pr=leading zeros 1148 | if (pr < (stbsp__int32)l) 1149 | pr = l; 1150 | n = pr + lead[0] + tail[0] + tz; 1151 | if (fw < (stbsp__int32)n) 1152 | fw = n; 1153 | fw -= n; 1154 | pr -= l; 1155 | 1156 | // handle right justify and leading zeros 1157 | if ((fl & STBSP__LEFTJUST) == 0) { 1158 | if (fl & STBSP__LEADINGZERO) // if leading zeros, everything is in pr 1159 | { 1160 | pr = (fw > pr) ? fw : pr; 1161 | fw = 0; 1162 | } else { 1163 | fl &= ~STBSP__TRIPLET_COMMA; // if no leading zeros, then no commas 1164 | } 1165 | } 1166 | 1167 | // copy the spaces and/or zeros 1168 | if (fw + pr) { 1169 | stbsp__int32 i; 1170 | stbsp__uint32 c; 1171 | 1172 | // copy leading spaces (or when doing %8.4d stuff) 1173 | if ((fl & STBSP__LEFTJUST) == 0) 1174 | while (fw > 0) { 1175 | stbsp__cb_buf_clamp(i, fw); 1176 | fw -= i; 1177 | while (i) { 1178 | if ((((stbsp__uintptr)bf) & 3) == 0) 1179 | break; 1180 | *bf++ = ' '; 1181 | --i; 1182 | } 1183 | while (i >= 4) { 1184 | *(stbsp__uint32 *)bf = 0x20202020; 1185 | bf += 4; 1186 | i -= 4; 1187 | } 1188 | while (i) { 1189 | *bf++ = ' '; 1190 | --i; 1191 | } 1192 | stbsp__chk_cb_buf(1); 1193 | } 1194 | 1195 | // copy leader 1196 | sn = lead + 1; 1197 | while (lead[0]) { 1198 | stbsp__cb_buf_clamp(i, lead[0]); 1199 | lead[0] -= (char)i; 1200 | while (i) { 1201 | *bf++ = *sn++; 1202 | --i; 1203 | } 1204 | stbsp__chk_cb_buf(1); 1205 | } 1206 | 1207 | // copy leading zeros 1208 | c = cs >> 24; 1209 | cs &= 0xffffff; 1210 | cs = (fl & STBSP__TRIPLET_COMMA) ? ((stbsp__uint32)(c - ((pr + cs) % (c + 1)))) : 0; 1211 | while (pr > 0) { 1212 | stbsp__cb_buf_clamp(i, pr); 1213 | pr -= i; 1214 | if ((fl & STBSP__TRIPLET_COMMA) == 0) { 1215 | while (i) { 1216 | if ((((stbsp__uintptr)bf) & 3) == 0) 1217 | break; 1218 | *bf++ = '0'; 1219 | --i; 1220 | } 1221 | while (i >= 4) { 1222 | *(stbsp__uint32 *)bf = 0x30303030; 1223 | bf += 4; 1224 | i -= 4; 1225 | } 1226 | } 1227 | while (i) { 1228 | if ((fl & STBSP__TRIPLET_COMMA) && (cs++ == c)) { 1229 | cs = 0; 1230 | *bf++ = stbsp__comma; 1231 | } else 1232 | *bf++ = '0'; 1233 | --i; 1234 | } 1235 | stbsp__chk_cb_buf(1); 1236 | } 1237 | } 1238 | 1239 | // copy leader if there is still one 1240 | sn = lead + 1; 1241 | while (lead[0]) { 1242 | stbsp__int32 i; 1243 | stbsp__cb_buf_clamp(i, lead[0]); 1244 | lead[0] -= (char)i; 1245 | while (i) { 1246 | *bf++ = *sn++; 1247 | --i; 1248 | } 1249 | stbsp__chk_cb_buf(1); 1250 | } 1251 | 1252 | // copy the string 1253 | n = l; 1254 | while (n) { 1255 | stbsp__int32 i; 1256 | stbsp__cb_buf_clamp(i, n); 1257 | n -= i; 1258 | STBSP__UNALIGNED(while (i >= 4) { 1259 | *(stbsp__uint32 volatile *)bf = *(stbsp__uint32 volatile *)s; 1260 | bf += 4; 1261 | s += 4; 1262 | i -= 4; 1263 | }) 1264 | while (i) { 1265 | *bf++ = *s++; 1266 | --i; 1267 | } 1268 | stbsp__chk_cb_buf(1); 1269 | } 1270 | 1271 | // copy trailing zeros 1272 | while (tz) { 1273 | stbsp__int32 i; 1274 | stbsp__cb_buf_clamp(i, tz); 1275 | tz -= i; 1276 | while (i) { 1277 | if ((((stbsp__uintptr)bf) & 3) == 0) 1278 | break; 1279 | *bf++ = '0'; 1280 | --i; 1281 | } 1282 | while (i >= 4) { 1283 | *(stbsp__uint32 *)bf = 0x30303030; 1284 | bf += 4; 1285 | i -= 4; 1286 | } 1287 | while (i) { 1288 | *bf++ = '0'; 1289 | --i; 1290 | } 1291 | stbsp__chk_cb_buf(1); 1292 | } 1293 | 1294 | // copy tail if there is one 1295 | sn = tail + 1; 1296 | while (tail[0]) { 1297 | stbsp__int32 i; 1298 | stbsp__cb_buf_clamp(i, tail[0]); 1299 | tail[0] -= (char)i; 1300 | while (i) { 1301 | *bf++ = *sn++; 1302 | --i; 1303 | } 1304 | stbsp__chk_cb_buf(1); 1305 | } 1306 | 1307 | // handle the left justify 1308 | if (fl & STBSP__LEFTJUST) 1309 | if (fw > 0) { 1310 | while (fw) { 1311 | stbsp__int32 i; 1312 | stbsp__cb_buf_clamp(i, fw); 1313 | fw -= i; 1314 | while (i) { 1315 | if ((((stbsp__uintptr)bf) & 3) == 0) 1316 | break; 1317 | *bf++ = ' '; 1318 | --i; 1319 | } 1320 | while (i >= 4) { 1321 | *(stbsp__uint32 *)bf = 0x20202020; 1322 | bf += 4; 1323 | i -= 4; 1324 | } 1325 | while (i--) 1326 | *bf++ = ' '; 1327 | stbsp__chk_cb_buf(1); 1328 | } 1329 | } 1330 | break; 1331 | 1332 | default: // unknown, just copy code 1333 | s = num + STBSP__NUMSZ - 1; 1334 | *s = f[0]; 1335 | l = 1; 1336 | fw = fl = 0; 1337 | lead[0] = 0; 1338 | tail[0] = 0; 1339 | pr = 0; 1340 | dp = 0; 1341 | cs = 0; 1342 | goto scopy; 1343 | } 1344 | ++f; 1345 | } 1346 | endfmt: 1347 | 1348 | if (!callback) 1349 | *bf = 0; 1350 | else 1351 | stbsp__flush_cb(); 1352 | 1353 | done: 1354 | return tlen + (int)(bf - buf); 1355 | } 1356 | 1357 | // cleanup 1358 | #undef STBSP__LEFTJUST 1359 | #undef STBSP__LEADINGPLUS 1360 | #undef STBSP__LEADINGSPACE 1361 | #undef STBSP__LEADING_0X 1362 | #undef STBSP__LEADINGZERO 1363 | #undef STBSP__INTMAX 1364 | #undef STBSP__TRIPLET_COMMA 1365 | #undef STBSP__NEGATIVE 1366 | #undef STBSP__METRIC_SUFFIX 1367 | #undef STBSP__NUMSZ 1368 | #undef stbsp__chk_cb_bufL 1369 | #undef stbsp__chk_cb_buf 1370 | #undef stbsp__flush_cb 1371 | #undef stbsp__cb_buf_clamp 1372 | 1373 | // ============================================================================ 1374 | // wrapper functions 1375 | 1376 | STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...) 1377 | { 1378 | int result; 1379 | va_list va; 1380 | va_start(va, fmt); 1381 | result = STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va); 1382 | va_end(va); 1383 | return result; 1384 | } 1385 | 1386 | typedef struct stbsp__context { 1387 | char *buf; 1388 | int count; 1389 | int length; 1390 | char tmp[STB_SPRINTF_MIN]; 1391 | } stbsp__context; 1392 | 1393 | static char *stbsp__clamp_callback(const char *buf, void *user, int len) 1394 | { 1395 | stbsp__context *c = (stbsp__context *)user; 1396 | c->length += len; 1397 | 1398 | if (len > c->count) 1399 | len = c->count; 1400 | 1401 | if (len) { 1402 | if (buf != c->buf) { 1403 | const char *s, *se; 1404 | char *d; 1405 | d = c->buf; 1406 | s = buf; 1407 | se = buf + len; 1408 | do { 1409 | *d++ = *s++; 1410 | } while (s < se); 1411 | } 1412 | c->buf += len; 1413 | c->count -= len; 1414 | } 1415 | 1416 | if (c->count <= 0) 1417 | return c->tmp; 1418 | return (c->count >= STB_SPRINTF_MIN) ? c->buf : c->tmp; // go direct into buffer if you can 1419 | } 1420 | 1421 | static char * stbsp__count_clamp_callback( const char * buf, void * user, int len ) 1422 | { 1423 | stbsp__context * c = (stbsp__context*)user; 1424 | (void) sizeof(buf); 1425 | 1426 | c->length += len; 1427 | return c->tmp; // go direct into buffer if you can 1428 | } 1429 | 1430 | STBSP__PUBLICDEF int STB_SPRINTF_DECORATE( vsnprintf )( char * buf, int count, char const * fmt, va_list va ) 1431 | { 1432 | stbsp__context c; 1433 | 1434 | if ( (count == 0) && !buf ) 1435 | { 1436 | c.length = 0; 1437 | 1438 | STB_SPRINTF_DECORATE( vsprintfcb )( stbsp__count_clamp_callback, &c, c.tmp, fmt, va ); 1439 | } 1440 | else 1441 | { 1442 | int l; 1443 | 1444 | c.buf = buf; 1445 | c.count = count; 1446 | c.length = 0; 1447 | 1448 | STB_SPRINTF_DECORATE( vsprintfcb )( stbsp__clamp_callback, &c, stbsp__clamp_callback(0,&c,0), fmt, va ); 1449 | 1450 | // zero-terminate 1451 | l = (int)( c.buf - buf ); 1452 | if ( l >= count ) // should never be greater, only equal (or less) than count 1453 | l = count - 1; 1454 | buf[l] = 0; 1455 | } 1456 | 1457 | return c.length; 1458 | } 1459 | 1460 | STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...) 1461 | { 1462 | int result; 1463 | va_list va; 1464 | va_start(va, fmt); 1465 | 1466 | result = STB_SPRINTF_DECORATE(vsnprintf)(buf, count, fmt, va); 1467 | va_end(va); 1468 | 1469 | return result; 1470 | } 1471 | 1472 | STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va) 1473 | { 1474 | return STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va); 1475 | } 1476 | 1477 | // ======================================================================= 1478 | // low level float utility functions 1479 | 1480 | #ifndef STB_SPRINTF_NOFLOAT 1481 | 1482 | // copies d to bits w/ strict aliasing (this compiles to nothing on /Ox) 1483 | #define STBSP__COPYFP(dest, src) \ 1484 | { \ 1485 | int cn; \ 1486 | for (cn = 0; cn < 8; cn++) \ 1487 | ((char *)&dest)[cn] = ((char *)&src)[cn]; \ 1488 | } 1489 | 1490 | // get float info 1491 | static stbsp__int32 stbsp__real_to_parts(stbsp__int64 *bits, stbsp__int32 *expo, double value) 1492 | { 1493 | double d; 1494 | stbsp__int64 b = 0; 1495 | 1496 | // load value and round at the frac_digits 1497 | d = value; 1498 | 1499 | STBSP__COPYFP(b, d); 1500 | 1501 | *bits = b & ((((stbsp__uint64)1) << 52) - 1); 1502 | *expo = (stbsp__int32)(((b >> 52) & 2047) - 1023); 1503 | 1504 | return (stbsp__int32)((stbsp__uint64) b >> 63); 1505 | } 1506 | 1507 | static double const stbsp__bot[23] = { 1508 | 1e+000, 1e+001, 1e+002, 1e+003, 1e+004, 1e+005, 1e+006, 1e+007, 1e+008, 1e+009, 1e+010, 1e+011, 1509 | 1e+012, 1e+013, 1e+014, 1e+015, 1e+016, 1e+017, 1e+018, 1e+019, 1e+020, 1e+021, 1e+022 1510 | }; 1511 | static double const stbsp__negbot[22] = { 1512 | 1e-001, 1e-002, 1e-003, 1e-004, 1e-005, 1e-006, 1e-007, 1e-008, 1e-009, 1e-010, 1e-011, 1513 | 1e-012, 1e-013, 1e-014, 1e-015, 1e-016, 1e-017, 1e-018, 1e-019, 1e-020, 1e-021, 1e-022 1514 | }; 1515 | static double const stbsp__negboterr[22] = { 1516 | -5.551115123125783e-018, -2.0816681711721684e-019, -2.0816681711721686e-020, -4.7921736023859299e-021, -8.1803053914031305e-022, 4.5251888174113741e-023, 1517 | 4.5251888174113739e-024, -2.0922560830128471e-025, -6.2281591457779853e-026, -3.6432197315497743e-027, 6.0503030718060191e-028, 2.0113352370744385e-029, 1518 | -3.0373745563400371e-030, 1.1806906454401013e-032, -7.7705399876661076e-032, 2.0902213275965398e-033, -7.1542424054621921e-034, -7.1542424054621926e-035, 1519 | 2.4754073164739869e-036, 5.4846728545790429e-037, 9.2462547772103625e-038, -4.8596774326570872e-039 1520 | }; 1521 | static double const stbsp__top[13] = { 1522 | 1e+023, 1e+046, 1e+069, 1e+092, 1e+115, 1e+138, 1e+161, 1e+184, 1e+207, 1e+230, 1e+253, 1e+276, 1e+299 1523 | }; 1524 | static double const stbsp__negtop[13] = { 1525 | 1e-023, 1e-046, 1e-069, 1e-092, 1e-115, 1e-138, 1e-161, 1e-184, 1e-207, 1e-230, 1e-253, 1e-276, 1e-299 1526 | }; 1527 | static double const stbsp__toperr[13] = { 1528 | 8388608, 1529 | 6.8601809640529717e+028, 1530 | -7.253143638152921e+052, 1531 | -4.3377296974619174e+075, 1532 | -1.5559416129466825e+098, 1533 | -3.2841562489204913e+121, 1534 | -3.7745893248228135e+144, 1535 | -1.7356668416969134e+167, 1536 | -3.8893577551088374e+190, 1537 | -9.9566444326005119e+213, 1538 | 6.3641293062232429e+236, 1539 | -5.2069140800249813e+259, 1540 | -5.2504760255204387e+282 1541 | }; 1542 | static double const stbsp__negtoperr[13] = { 1543 | 3.9565301985100693e-040, -2.299904345391321e-063, 3.6506201437945798e-086, 1.1875228833981544e-109, 1544 | -5.0644902316928607e-132, -6.7156837247865426e-155, -2.812077463003139e-178, -5.7778912386589953e-201, 1545 | 7.4997100559334532e-224, -4.6439668915134491e-247, -6.3691100762962136e-270, -9.436808465446358e-293, 1546 | 8.0970921678014997e-317 1547 | }; 1548 | 1549 | #if defined(_MSC_VER) && (_MSC_VER <= 1200) 1550 | static stbsp__uint64 const stbsp__powten[20] = { 1551 | 1, 1552 | 10, 1553 | 100, 1554 | 1000, 1555 | 10000, 1556 | 100000, 1557 | 1000000, 1558 | 10000000, 1559 | 100000000, 1560 | 1000000000, 1561 | 10000000000, 1562 | 100000000000, 1563 | 1000000000000, 1564 | 10000000000000, 1565 | 100000000000000, 1566 | 1000000000000000, 1567 | 10000000000000000, 1568 | 100000000000000000, 1569 | 1000000000000000000, 1570 | 10000000000000000000U 1571 | }; 1572 | #define stbsp__tento19th ((stbsp__uint64)1000000000000000000) 1573 | #else 1574 | static stbsp__uint64 const stbsp__powten[20] = { 1575 | 1, 1576 | 10, 1577 | 100, 1578 | 1000, 1579 | 10000, 1580 | 100000, 1581 | 1000000, 1582 | 10000000, 1583 | 100000000, 1584 | 1000000000, 1585 | 10000000000ULL, 1586 | 100000000000ULL, 1587 | 1000000000000ULL, 1588 | 10000000000000ULL, 1589 | 100000000000000ULL, 1590 | 1000000000000000ULL, 1591 | 10000000000000000ULL, 1592 | 100000000000000000ULL, 1593 | 1000000000000000000ULL, 1594 | 10000000000000000000ULL 1595 | }; 1596 | #define stbsp__tento19th (1000000000000000000ULL) 1597 | #endif 1598 | 1599 | #define stbsp__ddmulthi(oh, ol, xh, yh) \ 1600 | { \ 1601 | double ahi = 0, alo, bhi = 0, blo; \ 1602 | stbsp__int64 bt; \ 1603 | oh = xh * yh; \ 1604 | STBSP__COPYFP(bt, xh); \ 1605 | bt &= ((~(stbsp__uint64)0) << 27); \ 1606 | STBSP__COPYFP(ahi, bt); \ 1607 | alo = xh - ahi; \ 1608 | STBSP__COPYFP(bt, yh); \ 1609 | bt &= ((~(stbsp__uint64)0) << 27); \ 1610 | STBSP__COPYFP(bhi, bt); \ 1611 | blo = yh - bhi; \ 1612 | ol = ((ahi * bhi - oh) + ahi * blo + alo * bhi) + alo * blo; \ 1613 | } 1614 | 1615 | #define stbsp__ddtoS64(ob, xh, xl) \ 1616 | { \ 1617 | double ahi = 0, alo, vh, t; \ 1618 | ob = (stbsp__int64)xh; \ 1619 | vh = (double)ob; \ 1620 | ahi = (xh - vh); \ 1621 | t = (ahi - xh); \ 1622 | alo = (xh - (ahi - t)) - (vh + t); \ 1623 | ob += (stbsp__int64)(ahi + alo + xl); \ 1624 | } 1625 | 1626 | #define stbsp__ddrenorm(oh, ol) \ 1627 | { \ 1628 | double s; \ 1629 | s = oh + ol; \ 1630 | ol = ol - (s - oh); \ 1631 | oh = s; \ 1632 | } 1633 | 1634 | #define stbsp__ddmultlo(oh, ol, xh, xl, yh, yl) ol = ol + (xh * yl + xl * yh); 1635 | 1636 | #define stbsp__ddmultlos(oh, ol, xh, yl) ol = ol + (xh * yl); 1637 | 1638 | static void stbsp__raise_to_power10(double *ohi, double *olo, double d, stbsp__int32 power) // power can be -323 to +350 1639 | { 1640 | double ph, pl; 1641 | if ((power >= 0) && (power <= 22)) { 1642 | stbsp__ddmulthi(ph, pl, d, stbsp__bot[power]); 1643 | } else { 1644 | stbsp__int32 e, et, eb; 1645 | double p2h, p2l; 1646 | 1647 | e = power; 1648 | if (power < 0) 1649 | e = -e; 1650 | et = (e * 0x2c9) >> 14; /* %23 */ 1651 | if (et > 13) 1652 | et = 13; 1653 | eb = e - (et * 23); 1654 | 1655 | ph = d; 1656 | pl = 0.0; 1657 | if (power < 0) { 1658 | if (eb) { 1659 | --eb; 1660 | stbsp__ddmulthi(ph, pl, d, stbsp__negbot[eb]); 1661 | stbsp__ddmultlos(ph, pl, d, stbsp__negboterr[eb]); 1662 | } 1663 | if (et) { 1664 | stbsp__ddrenorm(ph, pl); 1665 | --et; 1666 | stbsp__ddmulthi(p2h, p2l, ph, stbsp__negtop[et]); 1667 | stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__negtop[et], stbsp__negtoperr[et]); 1668 | ph = p2h; 1669 | pl = p2l; 1670 | } 1671 | } else { 1672 | if (eb) { 1673 | e = eb; 1674 | if (eb > 22) 1675 | eb = 22; 1676 | e -= eb; 1677 | stbsp__ddmulthi(ph, pl, d, stbsp__bot[eb]); 1678 | if (e) { 1679 | stbsp__ddrenorm(ph, pl); 1680 | stbsp__ddmulthi(p2h, p2l, ph, stbsp__bot[e]); 1681 | stbsp__ddmultlos(p2h, p2l, stbsp__bot[e], pl); 1682 | ph = p2h; 1683 | pl = p2l; 1684 | } 1685 | } 1686 | if (et) { 1687 | stbsp__ddrenorm(ph, pl); 1688 | --et; 1689 | stbsp__ddmulthi(p2h, p2l, ph, stbsp__top[et]); 1690 | stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__top[et], stbsp__toperr[et]); 1691 | ph = p2h; 1692 | pl = p2l; 1693 | } 1694 | } 1695 | } 1696 | stbsp__ddrenorm(ph, pl); 1697 | *ohi = ph; 1698 | *olo = pl; 1699 | } 1700 | 1701 | // given a float value, returns the significant bits in bits, and the position of the 1702 | // decimal point in decimal_pos. +/-INF and NAN are specified by special values 1703 | // returned in the decimal_pos parameter. 1704 | // frac_digits is absolute normally, but if you want from first significant digits (got %g and %e), or in 0x80000000 1705 | static stbsp__int32 stbsp__real_to_str(char const **start, stbsp__uint32 *len, char *out, stbsp__int32 *decimal_pos, double value, stbsp__uint32 frac_digits) 1706 | { 1707 | double d; 1708 | stbsp__int64 bits = 0; 1709 | stbsp__int32 expo, e, ng, tens; 1710 | 1711 | d = value; 1712 | STBSP__COPYFP(bits, d); 1713 | expo = (stbsp__int32)((bits >> 52) & 2047); 1714 | ng = (stbsp__int32)((stbsp__uint64) bits >> 63); 1715 | if (ng) 1716 | d = -d; 1717 | 1718 | if (expo == 2047) // is nan or inf? 1719 | { 1720 | *start = (bits & ((((stbsp__uint64)1) << 52) - 1)) ? "NaN" : "Inf"; 1721 | *decimal_pos = STBSP__SPECIAL; 1722 | *len = 3; 1723 | return ng; 1724 | } 1725 | 1726 | if (expo == 0) // is zero or denormal 1727 | { 1728 | if (((stbsp__uint64) bits << 1) == 0) // do zero 1729 | { 1730 | *decimal_pos = 1; 1731 | *start = out; 1732 | out[0] = '0'; 1733 | *len = 1; 1734 | return ng; 1735 | } 1736 | // find the right expo for denormals 1737 | { 1738 | stbsp__int64 v = ((stbsp__uint64)1) << 51; 1739 | while ((bits & v) == 0) { 1740 | --expo; 1741 | v >>= 1; 1742 | } 1743 | } 1744 | } 1745 | 1746 | // find the decimal exponent as well as the decimal bits of the value 1747 | { 1748 | double ph, pl; 1749 | 1750 | // log10 estimate - very specifically tweaked to hit or undershoot by no more than 1 of log10 of all expos 1..2046 1751 | tens = expo - 1023; 1752 | tens = (tens < 0) ? ((tens * 617) / 2048) : (((tens * 1233) / 4096) + 1); 1753 | 1754 | // move the significant bits into position and stick them into an int 1755 | stbsp__raise_to_power10(&ph, &pl, d, 18 - tens); 1756 | 1757 | // get full as much precision from double-double as possible 1758 | stbsp__ddtoS64(bits, ph, pl); 1759 | 1760 | // check if we undershot 1761 | if (((stbsp__uint64)bits) >= stbsp__tento19th) 1762 | ++tens; 1763 | } 1764 | 1765 | // now do the rounding in integer land 1766 | frac_digits = (frac_digits & 0x80000000) ? ((frac_digits & 0x7ffffff) + 1) : (tens + frac_digits); 1767 | if ((frac_digits < 24)) { 1768 | stbsp__uint32 dg = 1; 1769 | if ((stbsp__uint64)bits >= stbsp__powten[9]) 1770 | dg = 10; 1771 | while ((stbsp__uint64)bits >= stbsp__powten[dg]) { 1772 | ++dg; 1773 | if (dg == 20) 1774 | goto noround; 1775 | } 1776 | if (frac_digits < dg) { 1777 | stbsp__uint64 r; 1778 | // add 0.5 at the right position and round 1779 | e = dg - frac_digits; 1780 | if ((stbsp__uint32)e >= 24) 1781 | goto noround; 1782 | r = stbsp__powten[e]; 1783 | bits = bits + (r / 2); 1784 | if ((stbsp__uint64)bits >= stbsp__powten[dg]) 1785 | ++tens; 1786 | bits /= r; 1787 | } 1788 | noround:; 1789 | } 1790 | 1791 | // kill long trailing runs of zeros 1792 | if (bits) { 1793 | stbsp__uint32 n; 1794 | for (;;) { 1795 | if (bits <= 0xffffffff) 1796 | break; 1797 | if (bits % 1000) 1798 | goto donez; 1799 | bits /= 1000; 1800 | } 1801 | n = (stbsp__uint32)bits; 1802 | while ((n % 1000) == 0) 1803 | n /= 1000; 1804 | bits = n; 1805 | donez:; 1806 | } 1807 | 1808 | // convert to string 1809 | out += 64; 1810 | e = 0; 1811 | for (;;) { 1812 | stbsp__uint32 n; 1813 | char *o = out - 8; 1814 | // do the conversion in chunks of U32s (avoid most 64-bit divides, worth it, constant denomiators be damned) 1815 | if (bits >= 100000000) { 1816 | n = (stbsp__uint32)(bits % 100000000); 1817 | bits /= 100000000; 1818 | } else { 1819 | n = (stbsp__uint32)bits; 1820 | bits = 0; 1821 | } 1822 | while (n) { 1823 | out -= 2; 1824 | *(stbsp__uint16 *)out = *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2]; 1825 | n /= 100; 1826 | e += 2; 1827 | } 1828 | if (bits == 0) { 1829 | if ((e) && (out[0] == '0')) { 1830 | ++out; 1831 | --e; 1832 | } 1833 | break; 1834 | } 1835 | while (out != o) { 1836 | *--out = '0'; 1837 | ++e; 1838 | } 1839 | } 1840 | 1841 | *decimal_pos = tens; 1842 | *start = out; 1843 | *len = e; 1844 | return ng; 1845 | } 1846 | 1847 | #undef stbsp__ddmulthi 1848 | #undef stbsp__ddrenorm 1849 | #undef stbsp__ddmultlo 1850 | #undef stbsp__ddmultlos 1851 | #undef STBSP__SPECIAL 1852 | #undef STBSP__COPYFP 1853 | 1854 | #endif // STB_SPRINTF_NOFLOAT 1855 | 1856 | // clean up 1857 | #undef stbsp__uint16 1858 | #undef stbsp__uint32 1859 | #undef stbsp__int32 1860 | #undef stbsp__uint64 1861 | #undef stbsp__int64 1862 | #undef STBSP__UNALIGNED 1863 | 1864 | #endif // STB_SPRINTF_IMPLEMENTATION 1865 | 1866 | /* 1867 | ------------------------------------------------------------------------------ 1868 | This software is available under 2 licenses -- choose whichever you prefer. 1869 | ------------------------------------------------------------------------------ 1870 | ALTERNATIVE A - MIT License 1871 | Copyright (c) 2017 Sean Barrett 1872 | Permission is hereby granted, free of charge, to any person obtaining a copy of 1873 | this software and associated documentation files (the "Software"), to deal in 1874 | the Software without restriction, including without limitation the rights to 1875 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 1876 | of the Software, and to permit persons to whom the Software is furnished to do 1877 | so, subject to the following conditions: 1878 | The above copyright notice and this permission notice shall be included in all 1879 | copies or substantial portions of the Software. 1880 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1881 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1882 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 1883 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 1884 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 1885 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 1886 | SOFTWARE. 1887 | ------------------------------------------------------------------------------ 1888 | ALTERNATIVE B - Public Domain (www.unlicense.org) 1889 | This is free and unencumbered software released into the public domain. 1890 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 1891 | software, either in source code form or as a compiled binary, for any purpose, 1892 | commercial or non-commercial, and by any means. 1893 | In jurisdictions that recognize copyright laws, the author or authors of this 1894 | software dedicate any and all copyright interest in the software to the public 1895 | domain. We make this dedication for the benefit of the public at large and to 1896 | the detriment of our heirs and successors. We intend this dedication to be an 1897 | overt act of relinquishment in perpetuity of all present and future rights to 1898 | this software under copyright law. 1899 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1900 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1901 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 1902 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 1903 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 1904 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 1905 | ------------------------------------------------------------------------------ 1906 | */ 1907 | -------------------------------------------------------------------------------- /src/utils.c: -------------------------------------------------------------------------------- 1 | /** @file utils.c 2 | * 3 | * A clone of Wordle for the Nokie N-Gage. 4 | * 5 | * Copyright (c) 2022, Michael Fitzmayer. All rights reserved. 6 | * SPDX-License-Identifier: MIT 7 | * 8 | **/ 9 | 10 | #include "SDL.h" 11 | #include "game.h" 12 | 13 | #define STB_IMAGE_IMPLEMENTATION 14 | #include "stb_image.h" 15 | 16 | int load_texture_from_file(const char* file_name, SDL_Texture** texture, game_t* core); 17 | Uint32 generate_hash(const unsigned char* name); 18 | unsigned int xorshift(unsigned int* xs); 19 | 20 | extern size_t size_of_file(const char * path); 21 | extern Uint8 *load_binary_file_from_path(const char * path); 22 | 23 | int load_texture_from_file(const char* file_name, SDL_Texture** texture, game_t* core) 24 | { 25 | Uint8* resource_buf; 26 | SDL_RWops* resource; 27 | SDL_Surface* surface; 28 | unsigned char* image_data; 29 | int req_format = STBI_rgb; 30 | int width; 31 | int height; 32 | int orig_format; 33 | 34 | if (NULL == file_name) 35 | { 36 | return 1; 37 | } 38 | 39 | resource_buf = (Uint8*)load_binary_file_from_path(file_name); 40 | if (NULL == resource_buf) 41 | { 42 | return 1; 43 | } 44 | 45 | resource = SDL_RWFromConstMem((Uint8*)resource_buf, size_of_file(file_name)); 46 | if (NULL == resource) 47 | { 48 | free(resource_buf); 49 | return 1; 50 | } 51 | 52 | image_data = stbi_load_from_memory(resource_buf, size_of_file(file_name), &width, &height, &orig_format, req_format); 53 | if (NULL == image_data) 54 | { 55 | free(resource_buf); 56 | return 1; 57 | } 58 | free(resource_buf); 59 | 60 | surface = SDL_CreateRGBSurfaceWithFormatFrom((void*)image_data, width, height, 24, 3 * width, SDL_PIXELFORMAT_RGB24); 61 | 62 | if (NULL == surface) 63 | { 64 | stbi_image_free(image_data); 65 | return 1; 66 | } 67 | 68 | *texture = SDL_CreateTextureFromSurface(core->renderer, surface); 69 | if (NULL == *texture) 70 | { 71 | SDL_FreeSurface(surface); 72 | stbi_image_free(image_data); 73 | return 1; 74 | } 75 | SDL_FreeSurface(surface); 76 | stbi_image_free(image_data); 77 | 78 | return 0; 79 | } 80 | 81 | /* djb2 by Dan Bernstein 82 | * http://www.cse.yorku.ca/~oz/hash.html 83 | */ 84 | Uint32 generate_hash(const unsigned char* name) 85 | { 86 | Uint64 hash = 5381; 87 | Uint32 c; 88 | 89 | while ((c = *name++)) 90 | { 91 | hash = ((hash << 5) + hash) + c; 92 | } 93 | 94 | return (Uint32)(hash & 0xffffffff); 95 | } 96 | 97 | unsigned int xorshift(unsigned int* xs) 98 | { 99 | *xs ^= *xs << 7; 100 | *xs ^= *xs >> 9; 101 | *xs ^= *xs << 8; 102 | return *xs; 103 | } 104 | -------------------------------------------------------------------------------- /src/wordle.cpp: -------------------------------------------------------------------------------- 1 | /** @file wordle.cpp 2 | * 3 | * A clone of Wordle for the Nokie N-Gage. 4 | * 5 | * Copyright (c) 2022, Michael Fitzmayer. All rights reserved. 6 | * SPDX-License-Identifier: MIT 7 | * 8 | **/ 9 | 10 | #include "wordle_application.h" 11 | 12 | GLDEF_C TInt E32Dll(TDllReason /* aReason */) 13 | { 14 | return KErrNone; 15 | } 16 | 17 | EXPORT_C CApaApplication* NewApplication() 18 | { 19 | return (new CWordleApplication); 20 | } 21 | -------------------------------------------------------------------------------- /src/wordle.rss: -------------------------------------------------------------------------------- 1 | /** @file wordle.rss 2 | * 3 | * A clone of Wordle for the Nokie N-Gage. 4 | * 5 | * Copyright (c) 2022, Michael Fitzmayer. All rights reserved. 6 | * SPDX-License-Identifier: MIT 7 | * 8 | **/ 9 | 10 | NAME NWIZ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | RESOURCE RSS_SIGNATURE {} 19 | 20 | RESOURCE TBUF 21 | { 22 | buf = "Wordle"; 23 | } 24 | 25 | RESOURCE EIK_APP_INFO 26 | { 27 | status_pane = r_status_pane_none; 28 | cba = R_AVKON_SOFTKEYS_BACK; 29 | } 30 | 31 | RESOURCE STATUS_PANE_APP_MODEL r_status_pane_none 32 | { 33 | layout = R_AVKON_STATUS_PANE_LAYOUT_EMPTY; 34 | } 35 | -------------------------------------------------------------------------------- /src/wordle_application.cpp: -------------------------------------------------------------------------------- 1 | /** @file wordle_application.cpp 2 | * 3 | * A clone of Wordle for the Nokie N-Gage. 4 | * 5 | * Copyright (c) 2022, Michael Fitzmayer. All rights reserved. 6 | * SPDX-License-Identifier: MIT 7 | * 8 | **/ 9 | 10 | #include "wordle_document.h" 11 | #include "wordle_application.h" 12 | 13 | static const TUid KUidWordleApp = { UID3 }; 14 | 15 | CApaDocument* CWordleApplication::CreateDocumentL() 16 | { 17 | CApaDocument* document = CWordleDocument::NewL(*this); 18 | return document; 19 | } 20 | 21 | TUid CWordleApplication::AppDllUid() const 22 | { 23 | return KUidWordleApp; 24 | } 25 | -------------------------------------------------------------------------------- /src/wordle_application.h: -------------------------------------------------------------------------------- 1 | /** @file wordle_application.h 2 | * 3 | * A clone of Wordle for the Nokie N-Gage. 4 | * 5 | * Copyright (c) 2022, Michael Fitzmayer. All rights reserved. 6 | * SPDX-License-Identifier: MIT 7 | * 8 | **/ 9 | 10 | #ifndef WORDLE_APPLICATION_H 11 | #define WORDLE_APPLICATION_H 12 | 13 | #include 14 | 15 | class CWordleApplication : public CAknApplication 16 | { 17 | public: 18 | TUid AppDllUid() const; 19 | 20 | protected: 21 | CApaDocument* CreateDocumentL(); 22 | }; 23 | 24 | #endif /* WORDLE_APPLICATION_H */ 25 | -------------------------------------------------------------------------------- /src/wordle_appui.cpp: -------------------------------------------------------------------------------- 1 | /** @file wordle_appui.cpp 2 | * 3 | * A clone of Wordle for the Nokie N-Gage. 4 | * 5 | * Copyright (c) 2022, Michael Fitzmayer. All rights reserved. 6 | * SPDX-License-Identifier: MIT 7 | * 8 | **/ 9 | 10 | #include 11 | #include 12 | 13 | #include "wordle_appui.h" 14 | #include "wordle_appview.h" 15 | 16 | void CWordleAppUi::ConstructL() 17 | { 18 | BaseConstructL(); 19 | 20 | iAppView = CWordleAppView::NewL(ClientRect()); 21 | 22 | AddToStackL(iAppView); 23 | } 24 | 25 | CWordleAppUi::CWordleAppUi() 26 | { 27 | RProcess Proc; 28 | 29 | iAppView = NULL; 30 | 31 | if (KErrNone == Proc.Create(_L("E:\\System\\Apps\\wordle\\game.exe"), _L(""))) 32 | { 33 | Proc.Resume(); 34 | Proc.Close(); 35 | User::After(10000000); 36 | Exit(); 37 | } 38 | else 39 | { 40 | Exit(); 41 | } 42 | } 43 | 44 | CWordleAppUi::~CWordleAppUi() 45 | { 46 | if (iAppView) 47 | { 48 | RemoveFromStack(iAppView); 49 | delete iAppView; 50 | iAppView = NULL; 51 | } 52 | } 53 | 54 | void CWordleAppUi::HandleCommandL(TInt aCommand) 55 | { 56 | /* No implementation required. */ 57 | } 58 | -------------------------------------------------------------------------------- /src/wordle_appui.h: -------------------------------------------------------------------------------- 1 | /** @file wordle_appui.h 2 | * 3 | * A clone of Wordle for the Nokie N-Gage. 4 | * 5 | * Copyright (c) 2022, Michael Fitzmayer. All rights reserved. 6 | * SPDX-License-Identifier: MIT 7 | * 8 | **/ 9 | 10 | #ifndef WORDLE_APPUI_H 11 | #define WORDLE_APPUI_H 12 | 13 | #include 14 | 15 | class CWordleAppView; 16 | 17 | class CWordleAppUi : public CAknAppUi 18 | { 19 | public: 20 | void ConstructL(); 21 | 22 | CWordleAppUi(); 23 | ~CWordleAppUi(); 24 | 25 | public: 26 | void HandleCommandL(TInt aCommand); 27 | 28 | private: 29 | CWordleAppView* iAppView; 30 | }; 31 | 32 | #endif /* WORDLE_APPUI_H */ 33 | -------------------------------------------------------------------------------- /src/wordle_appview.cpp: -------------------------------------------------------------------------------- 1 | /** @file wordle_appview.cpp 2 | * 3 | * A clone of Wordle for the Nokie N-Gage. 4 | * 5 | * Copyright (c) 2022, Michael Fitzmayer. All rights reserved. 6 | * SPDX-License-Identifier: MIT 7 | * 8 | **/ 9 | 10 | #include 11 | #include "wordle_appview.h" 12 | 13 | CWordleAppView* CWordleAppView::NewL(const TRect& aRect) 14 | { 15 | CWordleAppView* self = CWordleAppView::NewLC(aRect); 16 | CleanupStack::Pop(self); 17 | return self; 18 | } 19 | 20 | CWordleAppView* CWordleAppView::NewLC(const TRect& aRect) 21 | { 22 | CWordleAppView* self = new (ELeave) CWordleAppView; 23 | CleanupStack::PushL(self); 24 | self->ConstructL(aRect); 25 | return self; 26 | } 27 | 28 | CWordleAppView::CWordleAppView() 29 | { 30 | /* No implementation required. */ 31 | } 32 | 33 | CWordleAppView::~CWordleAppView() 34 | { 35 | /* No implementation required. */ 36 | } 37 | 38 | void CWordleAppView::ConstructL(const TRect& aRect) 39 | { 40 | CreateWindowL(); 41 | SetRect(aRect); 42 | ActivateL(); 43 | } 44 | 45 | // Draw this application's view to the screen 46 | void CWordleAppView::Draw(const TRect& /*aRect*/) const 47 | { 48 | CWindowGc& gc = SystemGc(); 49 | TRect rect = Rect(); 50 | 51 | gc.Clear(rect); 52 | } 53 | -------------------------------------------------------------------------------- /src/wordle_appview.h: -------------------------------------------------------------------------------- 1 | /** @file wordle_appview.h 2 | * 3 | * A clone of Wordle for the Nokie N-Gage. 4 | * 5 | * Copyright (c) 2022, Michael Fitzmayer. All rights reserved. 6 | * SPDX-License-Identifier: MIT 7 | * 8 | **/ 9 | 10 | #ifndef WORDLE_APPVIEW_H 11 | #define WORDLE_APPVIEW_H 12 | 13 | #include 14 | 15 | class CWordleAppView : public CCoeControl 16 | { 17 | public: 18 | static CWordleAppView* NewL(const TRect& aRect); 19 | static CWordleAppView* NewLC(const TRect& aRect); 20 | 21 | ~CWordleAppView(); 22 | 23 | public: 24 | void Draw(const TRect& aRect) const; 25 | 26 | private: 27 | void ConstructL(const TRect& aRect); 28 | 29 | CWordleAppView(); 30 | }; 31 | 32 | #endif /* WORDLE_APPVIEW_H */ 33 | -------------------------------------------------------------------------------- /src/wordle_document.cpp: -------------------------------------------------------------------------------- 1 | /** @file wordle_document.cpp 2 | * 3 | * A clone of Wordle for the Nokie N-Gage. 4 | * 5 | * Copyright (c) 2022, Michael Fitzmayer. All rights reserved. 6 | * SPDX-License-Identifier: MIT 7 | * 8 | **/ 9 | 10 | #include "wordle_appui.h" 11 | #include "wordle_document.h" 12 | 13 | CWordleDocument* CWordleDocument::NewL(CEikApplication& aApp) 14 | { 15 | CWordleDocument* self = NewLC(aApp); 16 | CleanupStack::Pop(self); 17 | return self; 18 | } 19 | 20 | CWordleDocument* CWordleDocument::NewLC(CEikApplication& aApp) 21 | { 22 | CWordleDocument* self = new (ELeave) CWordleDocument(aApp); 23 | CleanupStack::PushL(self); 24 | self->ConstructL(); 25 | return self; 26 | } 27 | 28 | void CWordleDocument::ConstructL() 29 | { 30 | /* No implementation required. */ 31 | } 32 | 33 | CWordleDocument::CWordleDocument(CEikApplication& aApp) : CAknDocument(aApp) 34 | { 35 | /* No implementation required. */ 36 | } 37 | 38 | CWordleDocument::~CWordleDocument() 39 | { 40 | /* No implementation required. */ 41 | } 42 | 43 | CEikAppUi* CWordleDocument::CreateAppUiL() 44 | { 45 | CEikAppUi* appUi = new (ELeave) CWordleAppUi; 46 | return appUi; 47 | } 48 | -------------------------------------------------------------------------------- /src/wordle_document.h: -------------------------------------------------------------------------------- 1 | /** @file wordle_document.h 2 | * 3 | * A clone of Wordle for the Nokie N-Gage. 4 | * 5 | * Copyright (c) 2022, Michael Fitzmayer. All rights reserved. 6 | * SPDX-License-Identifier: MIT 7 | * 8 | **/ 9 | 10 | #ifndef WORDLE_DOCUMENT_H 11 | #define WORDLE_DOCUMENT_H 12 | 13 | #include 14 | 15 | class CWordleAppUi; 16 | class CEikApplication; 17 | 18 | class CWordleDocument : public CAknDocument 19 | { 20 | public: 21 | static CWordleDocument* NewL(CEikApplication& aApp); 22 | static CWordleDocument* NewLC(CEikApplication& aApp); 23 | 24 | ~CWordleDocument(); 25 | 26 | public: 27 | CEikAppUi* CreateAppUiL(); 28 | 29 | private: 30 | void ConstructL(); 31 | CWordleDocument(CEikApplication& aApp); 32 | }; 33 | 34 | #endif /* WORDLE_DOCUMENT_H */ 35 | -------------------------------------------------------------------------------- /src/wordlist_utils.c: -------------------------------------------------------------------------------- 1 | /** @file wordlist_utils.c 2 | * 3 | * Wordlist utilities. 4 | * 5 | **/ 6 | 7 | #include 8 | #include "SDL.h" 9 | #include "game.h" 10 | 11 | void set_language(const lang_t language, const SDL_bool set_title_screen, game_t* core); 12 | void set_next_language(game_t* core); 13 | unsigned int get_nyt_daily_index(void); 14 | void get_valid_answer(unsigned char valid_answer[6], game_t* core); 15 | SDL_bool is_guess_allowed(const unsigned char* guess, game_t* core); 16 | void validate_current_guess(SDL_bool* is_won, game_t* core); 17 | 18 | extern Uint32 generate_hash(const unsigned char* name); 19 | 20 | extern const unsigned int wordlist_en_letter_count; 21 | extern const unsigned int wordlist_en_word_count; 22 | extern const unsigned int wordlist_en_allowed_count; 23 | extern const unsigned char wordlist_en_first_letter; 24 | extern const unsigned char wordlist_en_last_letter; 25 | extern const Uint32 wordlist_en_hash[0x905]; 26 | extern const unsigned char wordlist_en[0x905][5]; 27 | extern const Uint32 wordlist_en_allowed_hash[0x29a9]; 28 | extern const unsigned char wordlist_en_allowed[0x29a9][5]; 29 | extern const SDL_bool wordlist_en_is_cyrillic; 30 | extern const unsigned char wordlist_en_special_chars[1]; 31 | extern const unsigned char wordlist_en_title[5]; 32 | 33 | extern const unsigned int wordlist_ru_letter_count; 34 | extern const unsigned int wordlist_ru_word_count; 35 | extern const unsigned char wordlist_ru_first_letter; 36 | extern const unsigned char wordlist_ru_last_letter; 37 | extern const Uint32 wordlist_ru_hash[0x1039]; 38 | extern const unsigned char wordlist_ru[0x1039][5]; 39 | extern const SDL_bool wordlist_ru_is_cyrillic; 40 | extern const unsigned char wordlist_ru_special_chars[1]; 41 | extern const unsigned char wordlist_ru_title[5]; 42 | 43 | extern const unsigned int wordlist_de_letter_count; 44 | extern const unsigned int wordlist_de_word_count; 45 | extern const unsigned char wordlist_de_first_letter; 46 | extern const unsigned char wordlist_de_last_letter; 47 | extern const Uint32 wordlist_de_hash[0x185d]; 48 | extern const unsigned char wordlist_de[0x185d][5]; 49 | extern const SDL_bool wordlist_de_is_cyrillic; 50 | extern const unsigned char wordlist_de_special_chars[4]; 51 | extern const unsigned char wordlist_de_title[5]; 52 | 53 | extern const unsigned int wordlist_fi_letter_count; 54 | extern const unsigned int wordlist_fi_word_count; 55 | extern const unsigned char wordlist_fi_first_letter; 56 | extern const unsigned char wordlist_fi_last_letter; 57 | extern const Uint32 wordlist_fi_hash[0xcc7]; 58 | extern const unsigned char wordlist_fi[0xcc7][5]; 59 | extern const SDL_bool wordlist_fi_is_cyrillic; 60 | extern const unsigned char wordlist_fi_special_chars[1]; 61 | extern const unsigned char wordlist_fi_title[5]; 62 | 63 | void set_language(const lang_t language, const SDL_bool set_title_screen, game_t* core) 64 | { 65 | int index; 66 | 67 | if (NULL == core) 68 | { 69 | return; 70 | } 71 | 72 | if (SDL_TRUE == set_title_screen) 73 | { 74 | core->show_menu = SDL_TRUE; 75 | core->tile[5].letter = 'N'; 76 | core->tile[6].letter = 'G'; 77 | core->tile[7].letter = 'A'; 78 | core->tile[8].letter = 'G'; 79 | core->tile[9].letter = 'E'; 80 | core->tile[10].state = CORRECT_LETTER; 81 | core->tile[11].state = CORRECT_LETTER; 82 | core->tile[12].state = CORRECT_LETTER; 83 | core->tile[13].state = WRONG_POSITION; 84 | core->tile[14].state = WRONG_POSITION; 85 | 86 | core->tile[25].letter = 0x01; // New game icon 87 | core->tile[26].letter = 0x02; // Load game icon 88 | core->tile[27].letter = 0x03; // Game mode icon 89 | core->tile[28].letter = 0x04; // Set lang. icon 90 | core->tile[29].letter = 0x05; // Quit game icon 91 | 92 | if (SDL_TRUE == core->language_set_once) 93 | { 94 | core->tile[23].letter = 0x06; // Flag icon 95 | } 96 | } 97 | 98 | core->wordlist.language = language; 99 | 100 | switch (language) 101 | { 102 | default: 103 | case LANG_ENGLISH: 104 | if (SDL_TRUE == set_title_screen) 105 | { 106 | for (index = 10; index <= 14; index += 1) 107 | { 108 | core->tile[index].letter = wordlist_en_title[index - 10]; 109 | } 110 | } 111 | 112 | core->wordlist.letter_count = wordlist_en_letter_count; 113 | core->wordlist.word_count = wordlist_en_word_count; 114 | core->wordlist.allowed_count = wordlist_en_allowed_count; 115 | core->wordlist.first_letter = wordlist_en_first_letter; 116 | core->wordlist.last_letter = wordlist_en_last_letter; 117 | core->wordlist.is_cyrillic = wordlist_en_is_cyrillic; 118 | core->wordlist.special_chars = wordlist_en_special_chars; 119 | core->wordlist.hash = wordlist_en_hash; 120 | core->wordlist.list = wordlist_en; 121 | core->wordlist.allowed_hash = wordlist_en_allowed_hash; 122 | core->wordlist.allowed_list = wordlist_en_allowed; 123 | break; 124 | case LANG_RUSSIAN: 125 | if (SDL_TRUE == set_title_screen) 126 | { 127 | for (index = 10; index <= 14; index += 1) 128 | { 129 | core->tile[index].letter = wordlist_ru_title[index - 10]; 130 | } 131 | } 132 | 133 | core->wordlist.letter_count = wordlist_ru_letter_count; 134 | core->wordlist.word_count = wordlist_ru_word_count; 135 | core->wordlist.allowed_count = 0; 136 | core->wordlist.first_letter = wordlist_ru_first_letter; 137 | core->wordlist.last_letter = wordlist_ru_last_letter; 138 | core->wordlist.is_cyrillic = wordlist_ru_is_cyrillic; 139 | core->wordlist.special_chars = wordlist_ru_special_chars; 140 | core->wordlist.hash = wordlist_ru_hash; 141 | core->wordlist.list = wordlist_ru; 142 | core->wordlist.allowed_hash = NULL; 143 | core->wordlist.allowed_list = NULL; 144 | break; 145 | case LANG_GERMAN: 146 | if (SDL_TRUE == set_title_screen) 147 | { 148 | for (index = 10; index <= 14; index += 1) 149 | { 150 | core->tile[index].letter = wordlist_de_title[index - 10]; 151 | } 152 | } 153 | 154 | core->wordlist.letter_count = wordlist_de_letter_count; 155 | core->wordlist.word_count = wordlist_de_word_count; 156 | core->wordlist.allowed_count = 0; 157 | core->wordlist.first_letter = wordlist_de_first_letter; 158 | core->wordlist.last_letter = wordlist_de_last_letter; 159 | core->wordlist.is_cyrillic = wordlist_de_is_cyrillic; 160 | core->wordlist.special_chars = wordlist_de_special_chars; 161 | core->wordlist.hash = wordlist_de_hash; 162 | core->wordlist.list = wordlist_de; 163 | core->wordlist.allowed_hash = NULL; 164 | core->wordlist.allowed_list = NULL; 165 | break; 166 | case LANG_FINNISH: 167 | if (SDL_TRUE == set_title_screen) 168 | { 169 | for (index = 10; index <= 14; index += 1) 170 | { 171 | core->tile[index].letter = wordlist_fi_title[index - 10]; 172 | } 173 | } 174 | 175 | core->wordlist.letter_count = wordlist_fi_letter_count; 176 | core->wordlist.word_count = wordlist_fi_word_count; 177 | core->wordlist.allowed_count = 0; 178 | core->wordlist.first_letter = wordlist_fi_first_letter; 179 | core->wordlist.last_letter = wordlist_fi_last_letter; 180 | core->wordlist.is_cyrillic = wordlist_fi_is_cyrillic; 181 | core->wordlist.special_chars = wordlist_fi_special_chars; 182 | core->wordlist.hash = wordlist_fi_hash; 183 | core->wordlist.list = wordlist_fi; 184 | core->wordlist.allowed_hash = NULL; 185 | core->wordlist.allowed_list = NULL; 186 | break; 187 | } 188 | } 189 | 190 | void set_next_language(game_t* core) 191 | { 192 | switch (core->wordlist.language) 193 | { 194 | case LANG_ENGLISH: 195 | set_language(LANG_RUSSIAN, SDL_TRUE, core); 196 | break; 197 | case LANG_RUSSIAN: 198 | set_language(LANG_GERMAN, SDL_TRUE, core); 199 | break; 200 | case LANG_GERMAN: 201 | set_language(LANG_FINNISH, SDL_TRUE, core); 202 | break; 203 | case LANG_FINNISH: 204 | set_language(LANG_ENGLISH, SDL_TRUE, core); 205 | break; 206 | } 207 | } 208 | 209 | unsigned int get_nyt_daily_index(void) 210 | { 211 | time_t seconds; 212 | int days_since_epoch; 213 | int daily_index; 214 | 215 | seconds = time(NULL); 216 | days_since_epoch = seconds / (60 * 60 * 24); 217 | daily_index = days_since_epoch - 18797u; 218 | 219 | if (daily_index < 0) 220 | { 221 | daily_index = 0; 222 | } 223 | else if (daily_index > (int)(wordlist_en_word_count - 1)) 224 | { 225 | int overlap_index = daily_index % (wordlist_en_word_count - 1); 226 | daily_index = overlap_index; 227 | } 228 | 229 | return daily_index; 230 | } 231 | 232 | void get_valid_answer(unsigned char valid_answer[6], game_t* core) 233 | { 234 | int index; 235 | 236 | if (NULL == core) 237 | { 238 | return; 239 | } 240 | 241 | for (index = 0; index < 5; index += 1) 242 | { 243 | valid_answer[index] = core->wordlist.list[core->valid_answer_index][index]; 244 | } 245 | valid_answer[5] = 0; 246 | } 247 | 248 | SDL_bool is_guess_allowed(const unsigned char* guess, game_t* core) 249 | { 250 | Uint32 guess_hash; 251 | 252 | if (NULL == guess) 253 | { 254 | return SDL_FALSE; 255 | } 256 | 257 | guess_hash = generate_hash(guess); 258 | 259 | if (0x0daa8447 == guess_hash) // NGAGE 260 | { 261 | return SDL_TRUE; // ;-) 262 | } 263 | 264 | { 265 | int index; 266 | int start_index = 0; 267 | int end_index = core->wordlist.word_count - 1; 268 | 269 | for (index = start_index; index < end_index; index +=1) 270 | { 271 | if (guess_hash == core->wordlist.hash[index]) 272 | { 273 | return SDL_TRUE; 274 | } 275 | } 276 | 277 | if ((NULL != core->wordlist.allowed_hash) && 278 | (NULL != core->wordlist.allowed_list) && 279 | (core->wordlist.allowed_count > 0)) 280 | { 281 | end_index = core->wordlist.allowed_count; 282 | for (index = start_index; index < end_index; index +=1) 283 | { 284 | if (guess_hash == core->wordlist.allowed_hash[index]) 285 | { 286 | return SDL_TRUE; 287 | } 288 | } 289 | } 290 | } 291 | 292 | return SDL_FALSE; 293 | } 294 | 295 | void validate_current_guess(SDL_bool* is_won, game_t* core) 296 | { 297 | Uint32 guess_hash; 298 | 299 | if (NULL == core) 300 | { 301 | return; 302 | } 303 | 304 | guess_hash = generate_hash(core->current_guess); 305 | 306 | { 307 | int letter_index; 308 | int answer_index; 309 | Uint32 answer_hash; 310 | int start_index = 0; 311 | int end_index = core->wordlist.word_count - 1; 312 | unsigned char valid_answer[6] = { 0 }; 313 | //unsigned char shelf[5]; = { 0 }; 314 | 315 | for (answer_index = start_index; answer_index < end_index; answer_index +=1) 316 | { 317 | if (guess_hash == core->wordlist.hash[answer_index]) 318 | { 319 | break; 320 | } 321 | } 322 | 323 | get_valid_answer(valid_answer, core); 324 | answer_hash = generate_hash(valid_answer); 325 | 326 | // First check for exact matches. 327 | for (letter_index = 0; letter_index < 5; letter_index += 1) 328 | { 329 | int tile_index = (core->current_index - 4) + letter_index; 330 | tile_t* current_tile = &core->tile[tile_index]; 331 | 332 | if (core->current_guess[letter_index] == valid_answer[letter_index]) 333 | { 334 | current_tile->state = CORRECT_LETTER; 335 | //shelf[letter_index] = valid_answer[letter_index]; 336 | valid_answer[letter_index] = 0; 337 | } 338 | } 339 | 340 | // Then check for matches in the wrong location. 341 | for (letter_index = 0; letter_index < 5; letter_index += 1) 342 | { 343 | int tile_index = (core->current_index - 4) + letter_index; 344 | tile_t* current_tile = &core->tile[tile_index]; 345 | 346 | if (CORRECT_LETTER == current_tile->state) 347 | { 348 | continue; 349 | } 350 | else 351 | { 352 | int temp_index; 353 | for (temp_index = 0; temp_index < 5; temp_index += 1) 354 | { 355 | if (core->current_guess[letter_index] == valid_answer[temp_index]) 356 | { 357 | current_tile->state = WRONG_POSITION; 358 | } 359 | } 360 | } 361 | } 362 | 363 | // Finally, check for wrong letters. 364 | for (letter_index = 0; letter_index < 5; letter_index += 1) 365 | { 366 | int tile_index = (core->current_index - 4) + letter_index; 367 | tile_t* current_tile = &core->tile[tile_index]; 368 | 369 | if (LETTER_SELECT == current_tile->state) 370 | { 371 | current_tile->state = WRONG_LETTER; 372 | } 373 | } 374 | 375 | // Do we have a winner? 376 | if (guess_hash == answer_hash) 377 | { 378 | *is_won = SDL_TRUE; 379 | } 380 | else 381 | { 382 | *is_won = SDL_FALSE; 383 | } 384 | } 385 | } 386 | --------------------------------------------------------------------------------