├── .vscode ├── settings.json └── tasks.json ├── source ├── .vscode │ └── settings.json ├── build.bat ├── tinyengine_types.h ├── build.sh ├── tinyengine.c ├── tinyengine_platform.h ├── win32_tinyengine.c └── stb_sprintf.h ├── .gitignore ├── README.md ├── project.4coder ├── CONTRIBUTING.md └── LICENSE /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "stdbool.h": "c" 4 | } 5 | } -------------------------------------------------------------------------------- /source/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "tinyengine_platform.h": "c" 4 | } 5 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | .vscode/* 3 | !.vscode/settings.json 4 | !.vscode/tasks.json 5 | !.vscode/launch.json 6 | !.vscode/extensions.json 7 | *.code-workspace -------------------------------------------------------------------------------- /source/build.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | REM NOTE(zak): This is an attempt to find cl if the user doesn't have CL. 4 | where /q cl 5 | if ERRORLEVEL 1 ( 6 | for /f "delims=" %%a in ('"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -find VC\Auxiliary\Build\vcvarsall.bat') do (%%a x64) 7 | ) 8 | 9 | 10 | IF NOT EXIST ..\build mkdir ..\build 11 | 12 | pushd ..\build 13 | 14 | cl -nologo -FC -Zi -DTINY_ENGINE_DEBUG ..\source\win32_tinyengine.c /link user32.lib d3d11.lib dxguid.lib ole32.lib 15 | 16 | popd -------------------------------------------------------------------------------- /source/tinyengine_types.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYENGINE_TYPES_H 2 | #define TINYENGINE_TYPES_H 3 | 4 | #include 5 | 6 | typedef uint8_t u8; 7 | typedef uint16_t u16; 8 | typedef uint32_t u32; 9 | typedef uint64_t u64; 10 | 11 | typedef int8_t s8; 12 | typedef int16_t s16; 13 | typedef int32_t s32; 14 | typedef int64_t s64; 15 | 16 | typedef int32_t b32; 17 | 18 | typedef float f32; 19 | typedef float f64; 20 | 21 | #if !__cplusplus 22 | #define false 0 23 | #define true 1 24 | #endif 25 | 26 | #define ArrayCount(Array) (sizeof(Array) / sizeof((Array)[0])) 27 | 28 | #ifdef TINY_ENGINE_DEBUG 29 | #define Assert(Expression) if(!(Expression)) {*(int *)0 = 0;} 30 | #else 31 | #define Assert(Expression) 32 | #endif 33 | 34 | #endif // TINYENGINE_TYPES_H -------------------------------------------------------------------------------- /source/build.sh: -------------------------------------------------------------------------------- 1 | 2 | function assertInstalled() 3 | { 4 | for var in "$@"; do 5 | if ! which $var &> /dev/null; then 6 | echo "Install $var!" 7 | exit 1 8 | fi 9 | done 10 | } 11 | 12 | 13 | if [[ "$OSTYPE" == "linux-gnu" ]]; then 14 | CC=x86_64-w64-mingw32-gcc ; 15 | elif [[ "$OSTYPE" == "darwin"* ]]; then 16 | CC=x86_64-w64-mingw32-gcc ; 17 | elif [[ "$OSTYPE" == "cygwin" ]]; then 18 | CC=gcc ; 19 | elif [[ "$OSTYPE" == "msys" ]]; then 20 | CC=gcc ; 21 | elif [[ "$OSTYPE" == "win32" ]]; then 22 | CC=gcc ; 23 | elif [[ "$OSTYPE" == "freebsd"* ]]; then 24 | CC=x86_64-w64-mingw32-gcc ; 25 | else 26 | echo "unknown OS" 27 | exit 1 28 | fi 29 | 30 | assertInstalled $CC 31 | mkdir -p ../build 32 | $CC win32_tinyengine.c -luser32 -ld3d11 -ldxguid -o ../build/tinyengine.exe 33 | 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **TinyEngine is in active development, and should not be used yet** 2 | 3 | 4 | # TinyEngine 5 | A tiny engine written in C. 6 | 7 | TinyEngine is a very small engine written in C. TinyEngine is written with care, every line of code that goes into master is reviewed and tested. No third party dependencies are even added if they do make sense. TinyEngine does not aim to solve the problems of modern engines, but rather serve as a good example for how quality software should be written. 8 | 9 | ### Platforms 10 | - Windows 11 | - MacOS 12 | - Linux 13 | 14 | ### Building 15 | Open the Git Repository that you cloned and run build script (`build.sh` on Unix Systems, and `build.bat` on Windows. ) 16 | 17 | ### License 18 | 19 | All code that is NOT in the `third-party` folder is CC0, do whatever you want with it, we don't care. 20 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "version": "2.0.0", 4 | "tasks": [ 5 | { 6 | "label": "build", 7 | "type": "shell", 8 | "windows": { 9 | "command": "${workspaceRoot}\\source\\build.bat" 10 | }, 11 | "osx": { 12 | "command": "${workspaceRoot}/source/build.sh" 13 | }, 14 | "linux":{ 15 | "command": "${workspaceRoots}/source/build.sh" 16 | }, 17 | "group": { 18 | "kind": "build", 19 | "isDefault": true 20 | }, 21 | "presentation": { 22 | "panel": "dedicated", 23 | "reveal": "always", 24 | "clear": false, 25 | "showReuseMessage": false 26 | } 27 | } 28 | ] 29 | } -------------------------------------------------------------------------------- /project.4coder: -------------------------------------------------------------------------------- 1 | version(1); 2 | 3 | project_name = "Tiny Engine"; 4 | 5 | patterns = { 6 | "*.c", 7 | "*.cpp", 8 | "*.ds", 9 | "*.h", 10 | "*.bat", 11 | "*.sh", 12 | "*.4coder", 13 | }; 14 | 15 | blacklist_patterns = { 16 | ".*", 17 | }; 18 | 19 | load_paths = { 20 | { 21 | { {"."}, .recursive = true, .relative = true }, .os = "win" 22 | }, 23 | }; 24 | 25 | command_list = { 26 | { 27 | .name = "build", 28 | .out = "*compilation*", 29 | .footer_panel = true, 30 | .save_dirty_files = true, 31 | .cursor_at_end = false, 32 | .cmd = { 33 | { "pushd source && build.bat && popd", .os = "win" }, 34 | }, 35 | }, 36 | 37 | { 38 | .name = "run", 39 | .out = "*compilation*", 40 | .footer_panel = true, 41 | .save_dirty_files = true, 42 | .cursor_at_end = false, 43 | .cmd = { 44 | { "pushd build && win32_tinyengine.exe && popd", .os = "win" }, 45 | }, 46 | }, 47 | }; 48 | 49 | fkey_command[1] = "build"; 50 | fkey_command[3] = "run"; -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Anyone can commit code into the TinyEngine code base if they go through the right process. 4 | 5 | ## License 6 | TinyEngine is CC0, so before you merge your code into the engine be sure that you're okay with your code being released into the Public Domain. 7 | 8 | ## Pull Request workflow 9 | 1. Create a new branch for the feature/bug you are working on. **There should be a Github issue related to this**. 10 | 2. Do the work to fix X bug or add X feature. 11 | 3. Open a Pull Request to merge your code into the master branch. 12 | 4. Wait for CI, and make sure it passes. 13 | 5. Get your PR reviewed. Once it is approved the code will be merged into master 14 | 15 | ## Style Guide 16 | 17 | Snake case for types, structs, and classes: 18 | `typedef struct my_awesome_struct {} my_awesome_struct; ` 19 | 20 | Functions and methods should be pascal case: 21 | ` 22 | void MyFunction(int X) 23 | { 24 | } 25 | ` 26 | 27 | Prefer project types over build in C types. So instead of `unsigned int` use `u32`. 28 | Always use `b32`, instead of `bool`. 29 | Braces should always go on a new line when introducing scope, this is **not** okay: 30 | ``` 31 | // I didn't read the style guide look at me 32 | void ThisIsTheBestFuncEver(int X) { 33 | } 34 | ``` 35 | 36 | With pointers the `*` should always be with the name instead of the type i.e: `void *TheBuffer` 37 | 38 | Filenames should be prefixed with the platform name i.e `win32_tinyengine.cpp`, if the file isn't platform specific it should be prefixed with the project name i.e: `tinyengine_renderer.cpp` 39 | 40 | Variable names should be long and verbose, one or two letter variable names is not acceptable. 41 | 42 | ### General Programming Practices: 43 | - Keep the code base simple and make abstractions where it makes sense 44 | - Use normal modern-ish api's that are used in games shipping today 45 | - Use libraries where they make sense stb_image, stb_ttf, handmade math, etc 46 | - Define a very clear line between the engine and platform layer, so that the engine could be easily ported 47 | - Extensively test your code before you open a PR (I know this should be a given, but some people don't) 48 | - If you are introducing a new system, or something larger to the code base be sure to run it through a profiler. 49 | -------------------------------------------------------------------------------- /source/tinyengine.c: -------------------------------------------------------------------------------- 1 | #include "tinyengine_platform.h" 2 | 3 | static void 4 | Tiny_ProcessDigitalButton(tiny_digital_button *Button, b32 WasDown) 5 | { 6 | // TODO(hayden): Avoid wrapping Up/Down? 7 | Button->Down = WasDown ? ++Button->Down : false; 8 | Button->Up = !WasDown ? ++Button->Up : false; 9 | Button->Pressed = (Button->Down == 1); 10 | Button->Released = (Button->Up == 1); 11 | } 12 | 13 | static b32 14 | Tiny_GetMessage(tiny_platform *Platform, tiny_event *Event) 15 | { 16 | b32 HasRemainingData; 17 | static s32 CountUpToEventQueueIndex = 0; 18 | 19 | // Copy event over 20 | *Event = Platform->EventQueue[Platform->EventQueueIndex]; 21 | 22 | // Clear the Platform's event 23 | Platform->EventQueue[Platform->EventQueueIndex] = (tiny_event){0}; 24 | 25 | // If the event has a Type, assume there is remaining data 26 | HasRemainingData = Event->Type; 27 | 28 | // First In, First Out 29 | if(CountUpToEventQueueIndex == Platform->EventQueueIndex) 30 | { 31 | Platform->EventQueueIndex = 0; 32 | CountUpToEventQueueIndex = 0; 33 | } 34 | else 35 | { 36 | ++CountUpToEventQueueIndex; 37 | } 38 | 39 | return(HasRemainingData); 40 | } 41 | 42 | b32 KeyboardButtonState[KEY_COUNT]; // NOTE: true == IsDown 43 | tiny_digital_button Keyboard[KEY_COUNT]; 44 | b32 MouseButtonState[MOUSE_COUNT]; // NOTE: true == IsDown 45 | tiny_digital_button Mouse[MOUSE_COUNT]; 46 | 47 | static void 48 | Tiny_Update(tiny_platform *Platform) 49 | { 50 | // TODO(hayden): Normalize mouse input here? 51 | 52 | { // Update input events 53 | // Update KeyboardState/MouseButtonState with new events 54 | tiny_event Event; 55 | while(Tiny_GetMessage(Platform, &Event)) 56 | { 57 | if(Event.Type == TINY_EVENT_TYPE_KEYBOARD) 58 | { 59 | KeyboardButtonState[Event.Keyboard.KeyType] = Event.Keyboard.IsDown; 60 | } 61 | else if(Event.Type == TINY_EVENT_TYPE_MOUSE) 62 | { 63 | if(Event.Mouse.Type == TINY_EVENT_MOUSE_CLICK) 64 | { 65 | MouseButtonState[Event.Mouse.Button] = Event.Mouse.IsDown; 66 | } 67 | } 68 | else 69 | { 70 | Assert(!"Unhandled message!"); 71 | } 72 | } 73 | 74 | // Update Keyboard based on KeyboardState 75 | for(int InputIndex = 0; InputIndex < KEY_COUNT; ++InputIndex) 76 | { 77 | Tiny_ProcessDigitalButton(&Keyboard[InputIndex], KeyboardButtonState[InputIndex]); 78 | } 79 | 80 | // Update Mouse based on MouseButtonState 81 | for(int InputIndex = 0; InputIndex < MOUSE_COUNT; ++InputIndex) 82 | { 83 | Tiny_ProcessDigitalButton(&Mouse[InputIndex], MouseButtonState[InputIndex]); 84 | } 85 | } 86 | } 87 | 88 | static void 89 | Tiny_Render() 90 | { 91 | // Nothing happens! D: 92 | } -------------------------------------------------------------------------------- /source/tinyengine_platform.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYENGINE_PLATFORM_H 2 | #define TINYENGINE_PLATFORM_H 3 | 4 | typedef enum tiny_event_type 5 | { 6 | TINY_EVENT_TYPE_KEYBOARD = 1, // NOTE(hayden): Starts at 1 so that 0 can represent no input 7 | TINY_EVENT_TYPE_MOUSE, 8 | } tiny_event_type; 9 | 10 | typedef enum tiny_event_mouse_button 11 | { 12 | MOUSE_LEFT = 1, 13 | MOUSE_MIDDLE, 14 | MOUSE_RIGHT, 15 | MOUSE_EXTRA1, 16 | MOUSE_EXTRA2, 17 | 18 | MOUSE_COUNT, 19 | } tiny_event_mouse_button; 20 | 21 | // NOTE(hayden): The order here matters for calculations from KEY_0 to KEY_DOWN 22 | typedef enum tiny_event_key_type 23 | { 24 | KEY_A = 1, // NOTE(hayden): Starts at 1 so that 0 can represent no input 25 | KEY_B, 26 | KEY_C, 27 | KEY_D, 28 | KEY_E, 29 | KEY_F, 30 | KEY_G, 31 | KEY_H, 32 | KEY_I, 33 | KEY_J, 34 | KEY_K, 35 | KEY_L, 36 | KEY_M, 37 | KEY_N, 38 | KEY_O, 39 | KEY_P, 40 | KEY_Q, 41 | KEY_R, 42 | KEY_S, 43 | KEY_T, 44 | KEY_U, 45 | KEY_V, 46 | KEY_W, 47 | KEY_X, 48 | KEY_Y, 49 | KEY_Z, 50 | 51 | KEY_0, 52 | KEY_1, 53 | KEY_2, 54 | KEY_3, 55 | KEY_4, 56 | KEY_5, 57 | KEY_6, 58 | KEY_7, 59 | KEY_8, 60 | KEY_9, 61 | 62 | KEY_NUMPAD_0, 63 | KEY_NUMPAD_1, 64 | KEY_NUMPAD_2, 65 | KEY_NUMPAD_3, 66 | KEY_NUMPAD_4, 67 | KEY_NUMPAD_5, 68 | KEY_NUMPAD_6, 69 | KEY_NUMPAD_7, 70 | KEY_NUMPAD_8, 71 | KEY_NUMPAD_9, 72 | KEY_NUMPAD_MULTIPLY, 73 | KEY_NUMPAD_ADD, 74 | KEY_NUMPAD_SEPARATOR, // TODO(hayden): What is this? Seems to trigger if any numpad button is pressed..? 75 | KEY_NUMPAD_SUBTRACT, 76 | KEY_NUMPAD_DECIMAL, 77 | KEY_NUMPAD_DIVIDE, 78 | 79 | KEY_F1, 80 | KEY_F2, 81 | KEY_F3, 82 | KEY_F4, 83 | KEY_F5, 84 | KEY_F6, 85 | KEY_F7, 86 | KEY_F8, 87 | KEY_F9, 88 | KEY_F10, 89 | KEY_F11, 90 | KEY_F12, 91 | KEY_F13, 92 | KEY_F14, 93 | KEY_F15, 94 | KEY_F16, 95 | KEY_F17, 96 | KEY_F18, 97 | KEY_F19, 98 | KEY_F20, 99 | KEY_F21, 100 | KEY_F22, 101 | KEY_F23, 102 | KEY_F24, 103 | 104 | KEY_LEFT, 105 | KEY_UP, 106 | KEY_RIGHT, 107 | KEY_DOWN, 108 | 109 | KEY_BACKSPACE, 110 | KEY_TAB, 111 | KEY_RETURN, 112 | KEY_SPACE, 113 | KEY_LSHIFT, 114 | KEY_RSHIFT, 115 | KEY_LCONTROL, 116 | KEY_RCONTROL, 117 | KEY_LALT, 118 | KEY_RALT, 119 | KEY_LSUPER, // Windows Key 120 | KEY_RSUPER, // Windows Key 121 | KEY_CAPSLOCK, 122 | 123 | KEY_ESCAPE, 124 | KEY_PAGEUP, 125 | KEY_PAGEDOWN, 126 | KEY_HOME, 127 | KEY_END, 128 | KEY_INSERT, 129 | KEY_DELETE, 130 | KEY_PAUSE, 131 | KEY_NUMLOCK, 132 | KEY_SCROLLLOCK, // TODO(hayden): NOT WORKING!!! 133 | KEY_PRINTSCREEN, 134 | 135 | // TODO(hayden): Non-US/manufacturer-specific handling 136 | KEY_PLUS, 137 | KEY_COMMA, 138 | KEY_MINUS, 139 | KEY_SEMICOLON, 140 | KEY_OEM_1 = KEY_SEMICOLON, 141 | KEY_SLASH, 142 | KEY_OEM_2 = KEY_SLASH, 143 | KEY_GRAVE, 144 | KEY_OEM_3 = KEY_GRAVE, 145 | KEY_LBRACKET, 146 | KEY_OEM_4 = KEY_LBRACKET, 147 | KEY_RBRACKET, 148 | KEY_OEM_6 = KEY_RBRACKET, 149 | KEY_BACKSLASH, 150 | KEY_OEM_5 = KEY_BACKSLASH, 151 | KEY_QUOTE, 152 | KEY_OEM_7 = KEY_QUOTE, 153 | KEY_OEM_8, 154 | KEY_OEM_102, 155 | KEY_OEM_SPECIFIC1, 156 | KEY_OEM_SPECIFIC2, 157 | KEY_OEM_SPECIFIC3, 158 | KEY_OEM_CLEAR, 159 | 160 | // TODO(hayden): Most of these values are untested 161 | KEY_BROWSER_BACK, 162 | KEY_BROWSER_FORWARD, 163 | KEY_BROWSER_REFRESH, 164 | KEY_BROWSER_STOP, 165 | KEY_BROWSER_SEARCH, 166 | KEY_BROWSER_FAVORITES, 167 | KEY_BROWSER_HOME, 168 | 169 | KEY_VOLUME_MUTE, 170 | KEY_VOLUME_UP, 171 | KEY_VOLUME_DOWN, 172 | 173 | KEY_MEDIA_NEXT_TRACK, 174 | KEY_MEDIA_PREV_TRACK, 175 | KEY_MEDIA_STOP, 176 | KEY_MEDIA_PLAYPAUSE, 177 | 178 | KEY_LAUNCH_MAIL, 179 | KEY_LAUNCH_MEDIASELECT, 180 | KEY_LAUNCH_APPLICATION1, 181 | KEY_LAUNCH_APPLICATION2, 182 | 183 | KEY_HELP, 184 | KEY_MENU, 185 | KEY_PRINT, 186 | KEY_SELECT, 187 | KEY_EXEC, // TODO(hayden): KEY_EXECUTE was already defined somewhere... Undefine it or leave this as the only abbreviated key? 188 | KEY_APPLICATIONS, 189 | KEY_SLEEP, 190 | 191 | // TODO(hayden): Where are VK_IME_* defined??? 192 | KEY_IME_ON, 193 | KEY_IME_OFF, 194 | KEY_IME_MODECHANGE, 195 | KEY_IME_CONVERT, 196 | KEY_IME_NONCONVERT, 197 | KEY_IME_ACCEPT, 198 | KEY_IME_PROCESS, 199 | 200 | KEY_MODE_KANA, 201 | KEY_MODE_HANGEUL = KEY_MODE_KANA, 202 | KEY_MODE_HANGUL = KEY_MODE_KANA, 203 | KEY_MODE_JUNJA, 204 | KEY_MODE_FINAL, 205 | KEY_MODE_HANJA, 206 | KEY_MODE_KANJI = KEY_MODE_HANJA, 207 | 208 | KEY_ATTN, 209 | KEY_CRSEL, 210 | KEY_EXSEL, 211 | KEY_EREOF, 212 | KEY_PLAY, 213 | KEY_ZOOM, 214 | KEY_NONAME, 215 | KEY_PA1, 216 | 217 | KEY_COUNT, 218 | } tiny_event_key_type; 219 | 220 | typedef struct tiny_event_keyboard 221 | { 222 | tiny_event_key_type KeyType; 223 | b32 IsDown; 224 | } tiny_event_keyboard; 225 | 226 | // TODO(hayden): Preface with TINY_EVENT or not? 227 | typedef enum tiny_event_mouse_event_type 228 | { 229 | TINY_EVENT_MOUSE_MOVE = 1, // NOTE(hayden): Starts at 1 so that 0 can represent no input 230 | TINY_EVENT_MOUSE_CLICK, 231 | } tiny_event_mouse_event_type; 232 | 233 | typedef struct tiny_event_mouse 234 | { 235 | tiny_event_mouse_event_type Type; 236 | u32 X, Y; 237 | s16 WheelDelta; 238 | f32 NormalizedX, NormalizedY; 239 | tiny_event_mouse_button Button; 240 | b32 IsDown; 241 | } tiny_event_mouse; 242 | 243 | typedef struct tiny_event 244 | { 245 | tiny_event_type Type; 246 | union 247 | { 248 | struct { tiny_event_keyboard Keyboard; }; 249 | struct { tiny_event_mouse Mouse; }; 250 | }; 251 | } tiny_event; 252 | 253 | typedef struct tiny_digital_button 254 | { 255 | b32 Pressed; 256 | b32 Released; 257 | b32 Up; 258 | b32 Down; 259 | } tiny_digital_button; 260 | 261 | typedef struct tiny_platform_audio 262 | { 263 | u32 ChannelCount; 264 | u32 SampleRate; 265 | s32 SampleCount; 266 | 267 | void *Buffer; 268 | } tiny_platform_audio; 269 | 270 | typedef struct tiny_platform 271 | { 272 | tiny_event EventQueue[512]; 273 | int EventQueueIndex; 274 | } tiny_platform; 275 | 276 | tiny_platform Global_Platform; // TODO(hayden): Is it better for this to be here or the platform layer? 277 | 278 | ////// 279 | 280 | // TODO(hayden): Better name for this? 281 | // TODO(hayden): tinyengine_platform.c? 282 | static void 283 | Tiny_PushInputEvent(tiny_event Event) 284 | { 285 | Global_Platform.EventQueue[Global_Platform.EventQueueIndex] = Event; 286 | ++Global_Platform.EventQueueIndex; 287 | } 288 | 289 | #endif // TINYENGINE_PLATFORM_H -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | -------------------------------------------------------------------------------- /source/win32_tinyengine.c: -------------------------------------------------------------------------------- 1 | #define WIN32_LEAN_AND_MEAN 2 | #include 3 | 4 | #define CINTERFACE 5 | #define COBJMACROS 6 | #define D3D11_NO_HELPERS 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #ifndef STB_SPRINTF_IMPLEMENTATION 15 | #define STB_SPRINTF_IMPLEMENTATION 16 | #endif 17 | #include "stb_sprintf.h" 18 | 19 | #include "tinyengine_types.h" 20 | #include "tinyengine_platform.h" 21 | 22 | #include "tinyengine.c" 23 | 24 | static b32 IsRunning; 25 | static ID3D11Device *Device; 26 | static ID3D11DeviceContext *DeviceContext; 27 | static IDXGISwapChain *Swapchain; 28 | static ID3D11RenderTargetView *RenderTargetView; 29 | static D3D_FEATURE_LEVEL ActiveFeatureLevel; 30 | 31 | DEFINE_GUID(CLSID_MMDeviceEnumerator, 0xBCDE0395, 0xE52F, 0x467C, 0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69, 0x2E); 32 | DEFINE_GUID(IID_IMMDeviceEnumerator, 0xA95664D2, 0x9614, 0x4F35, 0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36, 0x17, 0xE6); 33 | DEFINE_GUID(IID_IAudioClient, 0x1CB9AD4C, 0xDBFA, 0x4C32, 0xB1, 0x78, 0xC2, 0xF5, 0x68, 0xA7, 0x03, 0xB2); 34 | DEFINE_GUID(IID_IAudioRenderClient, 0xF294ACFC, 0x3146, 0x4483, 0xA7, 0xBF, 0xAD, 0xDC, 0xA7, 0xC2, 0x60, 0xE2); 35 | 36 | //////////////////////////////////// 37 | 38 | static void 39 | Win32PrintDebugString(char* Format, ...) 40 | { 41 | static char Buffer[1024]; 42 | va_list ArgumentList; 43 | va_start(ArgumentList, Format); 44 | stbsp_vsprintf(Buffer, Format, ArgumentList); 45 | va_end(ArgumentList); 46 | OutputDebugStringA(Buffer); 47 | } 48 | 49 | typedef struct win32_audio_thread_params 50 | { 51 | IAudioClient *AudioClient; 52 | IAudioRenderClient *AudioRenderClient; 53 | u32 SamplesPerSecond; 54 | u32 ChannelCount; 55 | } win32_audio_thread_params; 56 | 57 | static DWORD WINAPI 58 | Win32AudioThread(void *Parameter) 59 | { 60 | win32_audio_thread_params *Params = (win32_audio_thread_params *)Parameter; 61 | if(Params) 62 | { 63 | SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL); 64 | 65 | HANDLE BufferReadyEvent = CreateEvent(0, 0, 0, 0); 66 | if(BufferReadyEvent != INVALID_HANDLE_VALUE) 67 | { 68 | if(Params->AudioClient->lpVtbl->SetEventHandle(Params->AudioClient, BufferReadyEvent) == 0) 69 | { 70 | UINT BufferSize = 0; 71 | if(Params->AudioClient->lpVtbl->GetBufferSize(Params->AudioClient, &BufferSize) == 0) 72 | { 73 | if(Params->AudioClient->lpVtbl->Start(Params->AudioClient) == 0) 74 | { 75 | for(;;) 76 | { 77 | if(WaitForSingleObject(BufferReadyEvent, INFINITE) == WAIT_OBJECT_0) 78 | { 79 | UINT Padding = 0; 80 | if(Params->AudioClient->lpVtbl->GetCurrentPadding(Params->AudioClient, &Padding) == 0) 81 | { 82 | UINT WriteAmount = BufferSize - Padding; 83 | if(WriteAmount > 0) 84 | { 85 | BYTE *Buffer = 0; 86 | if(Params->AudioRenderClient->lpVtbl->GetBuffer(Params->AudioRenderClient, WriteAmount, &Buffer) == 0) 87 | { 88 | tiny_platform_audio Audio = {0}; 89 | Audio.ChannelCount = Params->ChannelCount; 90 | Audio.SampleCount = Params->SamplesPerSecond; 91 | Audio.SampleCount = WriteAmount; 92 | Audio.Buffer = Buffer; 93 | 94 | // Pass this buffer to the engine and have it fill it with the audio samples 95 | // that the game/app layer requests to play 96 | 97 | Params->AudioRenderClient->lpVtbl->ReleaseBuffer(Params->AudioRenderClient, WriteAmount, 0); 98 | } 99 | } 100 | } 101 | } 102 | } 103 | } 104 | } 105 | } 106 | } 107 | } 108 | 109 | return(0); 110 | } 111 | 112 | static b32 113 | Win32InitWASAPI() 114 | { 115 | b32 Result = false; 116 | 117 | IMMDeviceEnumerator *DeviceEnumerator = 0; 118 | if(CoCreateInstance(&CLSID_MMDeviceEnumerator, 0, CLSCTX_ALL, &IID_IMMDeviceEnumerator, (void **)&DeviceEnumerator) == 0) 119 | { 120 | IMMDevice *Device = 0; 121 | if(DeviceEnumerator->lpVtbl->GetDefaultAudioEndpoint(DeviceEnumerator, eRender, eConsole, &Device) == 0) 122 | { 123 | DeviceEnumerator->lpVtbl->Release(DeviceEnumerator); 124 | 125 | IAudioClient *AudioClient = 0; 126 | if(Device->lpVtbl->Activate(Device, &IID_IAudioClient, CLSCTX_ALL, 0, (void **)&AudioClient) == 0) 127 | { 128 | WAVEFORMATEX WaveFormat = {0}; 129 | WaveFormat.wFormatTag = WAVE_FORMAT_PCM; 130 | WaveFormat.nChannels = 2; 131 | WaveFormat.nSamplesPerSec = 44100; 132 | WaveFormat.wBitsPerSample = 16; 133 | WaveFormat.nBlockAlign = (WaveFormat.nChannels * WaveFormat.wBitsPerSample) / 8; 134 | WaveFormat.nAvgBytesPerSec = (WaveFormat.nSamplesPerSec * WaveFormat.nBlockAlign); 135 | 136 | s64 SecondsToHundredNanos = 10000000; 137 | REFERENCE_TIME BufferDuration = (SecondsToHundredNanos / 20); 138 | if(AudioClient->lpVtbl->Initialize(AudioClient, AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_RATEADJUST | AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM, BufferDuration, 0, &WaveFormat, 0) == 0) 139 | { 140 | IAudioRenderClient *AudioRenderClient = 0; 141 | if(AudioClient->lpVtbl->GetService(AudioClient, &IID_IAudioRenderClient, (void **)&AudioRenderClient) == 0) 142 | { 143 | win32_audio_thread_params *AudioThreadParams = HeapAlloc(GetProcessHeap(), 0, sizeof(win32_audio_thread_params)); 144 | if(AudioThreadParams) 145 | { 146 | AudioThreadParams->AudioClient = AudioClient; 147 | AudioThreadParams->AudioRenderClient = AudioRenderClient; 148 | AudioThreadParams->SamplesPerSecond = WaveFormat.nSamplesPerSec; 149 | AudioThreadParams->ChannelCount = WaveFormat.nChannels; 150 | 151 | if(CreateThread(0, 0, Win32AudioThread, AudioThreadParams, 0, 0) != INVALID_HANDLE_VALUE) 152 | { 153 | Result = true; 154 | } 155 | } 156 | } 157 | } 158 | } 159 | } 160 | } 161 | else 162 | { 163 | // TODO(zak): Logging 164 | } 165 | 166 | return(Result); 167 | } 168 | 169 | static b32 170 | Win32InitD3D11(HWND Window) 171 | { 172 | b32 Result = false; 173 | 174 | UINT Flags = 0; 175 | #if TINYENGINE_DEBUG 176 | Flags |= D3D11_CREATE_DEVICE_DEBUG; 177 | #endif 178 | 179 | D3D_FEATURE_LEVEL SupportedFeatureLevels[3] = 180 | { 181 | D3D_FEATURE_LEVEL_11_0, 182 | D3D_FEATURE_LEVEL_10_1, 183 | D3D_FEATURE_LEVEL_10_0, 184 | }; 185 | 186 | // TODO(zak): Using the default adapter is not a great idea on systems with multiple gpus, handle this. 187 | if(D3D11CreateDevice(0, D3D_DRIVER_TYPE_HARDWARE, 0, Flags, SupportedFeatureLevels, ArrayCount(SupportedFeatureLevels), D3D11_SDK_VERSION, &Device, &ActiveFeatureLevel, &DeviceContext) != 0) 188 | { 189 | // TODO(zak): Log something saying we couldn't create the device with the hardware driver so now we will try the warp driver instead. 190 | if(D3D11CreateDevice(0, D3D_DRIVER_TYPE_WARP, 0, Flags, SupportedFeatureLevels, ArrayCount(SupportedFeatureLevels), D3D11_SDK_VERSION, &Device, &ActiveFeatureLevel, &DeviceContext) != 0) 191 | { 192 | // TODO(zak): Log error that we couldn't initialize d3d11 with the warp driver either 193 | } 194 | } 195 | 196 | if(Device) 197 | { 198 | // Now we need to get the DXGIFactory from the Device to create a swapchain 199 | IDXGIDevice *DXGIDevice = 0; 200 | if(Device->lpVtbl->QueryInterface(Device, &IID_IDXGIDevice, (void **)&DXGIDevice) == 0) 201 | { 202 | IDXGIAdapter *DXGIAdapter = 0; 203 | if(DXGIDevice->lpVtbl->GetAdapter(DXGIDevice, &DXGIAdapter) == 0) 204 | { 205 | DXGIDevice->lpVtbl->Release(DXGIDevice); 206 | 207 | IDXGIFactory *DXGIFactory = 0; 208 | if(DXGIAdapter->lpVtbl->GetParent(DXGIAdapter, &IID_IDXGIFactory, (void **)&DXGIFactory) == 0) 209 | { 210 | DXGIAdapter->lpVtbl->Release(DXGIAdapter); 211 | 212 | int MonitorHz = 60; 213 | 214 | DEVMODEW DevMode = {0}; 215 | DevMode.dmSize = sizeof(DEVMODEW); 216 | if(EnumDisplaySettingsW(0, ENUM_CURRENT_SETTINGS, &DevMode)) 217 | { 218 | MonitorHz = DevMode.dmDisplayFrequency; 219 | } 220 | else 221 | { 222 | // TODO(zak): Logging we weren't able to query the monitorhz falling back to 60 223 | } 224 | 225 | DXGI_SWAP_CHAIN_DESC SwapchainDescription = {0}; 226 | SwapchainDescription.BufferDesc.Width = GetSystemMetrics(SM_CXSCREEN); // NOTE(zak): We will make the Swapchain's with and height the size of the monitor, so that we only need to resize the viewport and not the buffers when we get WM_SIZE 227 | SwapchainDescription.BufferDesc.Height = GetSystemMetrics(SM_CYSCREEN); 228 | SwapchainDescription.BufferDesc.RefreshRate.Numerator = MonitorHz; 229 | SwapchainDescription.BufferDesc.RefreshRate.Denominator = 1; 230 | SwapchainDescription.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM_SRGB; // BGRA Blts are faster than RGBA blts in d3d11 231 | SwapchainDescription.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; 232 | SwapchainDescription.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED; 233 | SwapchainDescription.SampleDesc.Count = 1; // NOTE(zak): If we ever want MSAA you turn it on here 234 | SwapchainDescription.SampleDesc.Quality = 0; 235 | SwapchainDescription.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; 236 | SwapchainDescription.BufferCount = 1; // NOTE(zak): When/If this Swapchain goes full screen the buffer count needs to be increased to two as it wont be able to use desktop as its front buffer. 237 | SwapchainDescription.OutputWindow = Window; 238 | SwapchainDescription.Windowed = 1; 239 | SwapchainDescription.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; // TODO(zak): On Win8/10 we should be using Flip model swapchains instead, these are not supported on Win7. 240 | SwapchainDescription.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; // DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH allows us to switch between a Windows and Fullscreen modes. 241 | 242 | if(DXGIFactory->lpVtbl->CreateSwapChain(DXGIFactory, (IUnknown*)Device, &SwapchainDescription, &Swapchain) == 0) 243 | { 244 | DXGIFactory->lpVtbl->Release(DXGIFactory); 245 | 246 | ID3D11Texture2D *Backbuffer = 0; 247 | if(Swapchain->lpVtbl->GetBuffer(Swapchain, 0, &IID_ID3D11Texture2D, (void**)&Backbuffer) == 0) 248 | { 249 | if(Device->lpVtbl->CreateRenderTargetView(Device, (ID3D11Resource*)Backbuffer, 0, &RenderTargetView) == 0) 250 | { 251 | Backbuffer->lpVtbl->Release(Backbuffer); 252 | 253 | RECT WindowRect = {0}; 254 | if(GetWindowRect(Window, &WindowRect)) 255 | { 256 | D3D11_VIEWPORT Viewport = {0}; 257 | Viewport.Width = WindowRect.right - WindowRect.bottom; 258 | Viewport.Height = WindowRect.bottom - WindowRect.top; 259 | Viewport.MaxDepth = 1.0f; 260 | 261 | DeviceContext->lpVtbl->RSSetViewports(DeviceContext, 1, &Viewport); 262 | 263 | Result = true; 264 | } 265 | else 266 | { 267 | // TODO(zak): Logging 268 | } 269 | } 270 | else 271 | { 272 | // TODO(zak): Logging 273 | } 274 | } 275 | else 276 | { 277 | // TODO(zak): Logging 278 | } 279 | } 280 | else 281 | { 282 | // TODO(zak): Logging 283 | } 284 | } 285 | else 286 | { 287 | // TODO(zak): Logging 288 | } 289 | } 290 | else 291 | { 292 | // TODO(zak): Logging 293 | } 294 | } 295 | else 296 | { 297 | // TODO(zak): Logging 298 | } 299 | } 300 | else 301 | { 302 | // TODO(zak): Logging 303 | } 304 | 305 | return(Result); 306 | } 307 | 308 | static LRESULT CALLBACK 309 | Win32MainWindowCallback(HWND Window, UINT Message, WPARAM WParam, LPARAM LParam) 310 | { 311 | LRESULT Result = 0; 312 | 313 | switch(Message) 314 | { 315 | case WM_CLOSE: 316 | { 317 | IsRunning = false; 318 | } break; 319 | 320 | case WM_INPUT: 321 | { 322 | UINT SizeOfBuffer = sizeof(RAWINPUT); 323 | char Data[sizeof(RAWINPUT)] = {0}; 324 | if(GetRawInputData((HRAWINPUT)LParam, RID_INPUT, Data, &SizeOfBuffer, sizeof(RAWINPUTHEADER)) <= SizeOfBuffer) 325 | { 326 | RAWINPUT *RawInput = (RAWINPUT *)Data; 327 | 328 | if(RawInput->header.dwType == RIM_TYPEMOUSE) 329 | { 330 | // Mouse Buttons & Wheel 331 | tiny_event Event = {0}; 332 | Event.Type = TINY_EVENT_TYPE_MOUSE; 333 | Event.Mouse.Type = TINY_EVENT_MOUSE_CLICK; 334 | Event.Mouse.Button = false; 335 | Event.Mouse.IsDown = false; 336 | 337 | USHORT CurrentMouseButton = RawInput->data.mouse.usButtonFlags; 338 | switch(CurrentMouseButton) 339 | { 340 | case RI_MOUSE_LEFT_BUTTON_UP: 341 | case RI_MOUSE_LEFT_BUTTON_DOWN: 342 | { 343 | Event.Mouse.Button = MOUSE_LEFT; 344 | Event.Mouse.IsDown = (CurrentMouseButton & RI_MOUSE_LEFT_BUTTON_DOWN); 345 | } break; 346 | 347 | case RI_MOUSE_MIDDLE_BUTTON_UP: 348 | case RI_MOUSE_MIDDLE_BUTTON_DOWN: 349 | { 350 | Event.Mouse.Button = MOUSE_MIDDLE; 351 | Event.Mouse.IsDown = (CurrentMouseButton & RI_MOUSE_MIDDLE_BUTTON_DOWN); 352 | } break; 353 | 354 | case RI_MOUSE_RIGHT_BUTTON_UP: 355 | case RI_MOUSE_RIGHT_BUTTON_DOWN: 356 | { 357 | Event.Mouse.Button = MOUSE_RIGHT; 358 | Event.Mouse.IsDown = (CurrentMouseButton & RI_MOUSE_RIGHT_BUTTON_DOWN); 359 | } break; 360 | 361 | case RI_MOUSE_BUTTON_4_UP: 362 | case RI_MOUSE_BUTTON_4_DOWN: 363 | { 364 | Event.Mouse.Button = MOUSE_EXTRA1; 365 | Event.Mouse.IsDown = (CurrentMouseButton & RI_MOUSE_BUTTON_4_DOWN); 366 | } break; 367 | 368 | case RI_MOUSE_BUTTON_5_UP: 369 | case RI_MOUSE_BUTTON_5_DOWN: 370 | { 371 | Event.Mouse.Button = MOUSE_EXTRA2; 372 | Event.Mouse.IsDown = (CurrentMouseButton & RI_MOUSE_BUTTON_5_DOWN); 373 | } break; 374 | 375 | case RI_MOUSE_WHEEL: 376 | { 377 | Event.Mouse.WheelDelta = RawInput->data.mouse.usButtonData; 378 | } break; 379 | 380 | default: 381 | { 382 | // TODO(hayden): Does anything else need to be handled? 383 | } 384 | } 385 | 386 | // TODO(hayden): Assert this? 387 | if(Event.Mouse.Button != false) // Don't push events we aren't choosing to handle 388 | { 389 | Tiny_PushInputEvent(Event); 390 | } 391 | } 392 | else if(RawInput->header.dwType == RIM_TYPEKEYBOARD) 393 | { 394 | // TODO(hayden): Handle Alt-F4! 395 | const RAWKEYBOARD RawKeyboard = RawInput->data.keyboard; 396 | 397 | UINT VirtualKey = RawKeyboard.VKey; 398 | UINT ScanCode = RawKeyboard.MakeCode; 399 | UINT Flags = RawKeyboard.Flags; 400 | 401 | if(VirtualKey != 255) 402 | { 403 | if(VirtualKey == VK_SHIFT) 404 | { 405 | VirtualKey = MapVirtualKeyW(ScanCode, MAPVK_VSC_TO_VK_EX); // Corrects Left/Right Shift w/ VK_LSHIFT/VK_RSHIFT 406 | } 407 | else if(VirtualKey == VK_NUMLOCK) 408 | { 409 | // Corrects PAUSE/BREAK and NUMLOCK having the same scan code.. And a bug--MapVirtualKey doesn't set the extended bit! 410 | ScanCode = (MapVirtualKeyW(VirtualKey, MAPVK_VK_TO_VSC) | 0x100); 411 | } 412 | 413 | // E0 and E1 are escape sequences used for certain special keys, such as PRINT and PAUSE/BREAK. 414 | const b32 IsE0 = ((Flags & RI_KEY_E0) != 0); 415 | const b32 IsE1 = ((Flags & RI_KEY_E1) != 0); 416 | 417 | if(IsE1) 418 | { 419 | if(VirtualKey == VK_PAUSE) 420 | { 421 | ScanCode = 0x45; // MapVirtualKey is unable to map VK_PAUSE, so it is mapped by hand 422 | } 423 | else 424 | { 425 | ScanCode = MapVirtualKey(VirtualKey, MAPVK_VK_TO_VSC); // Turns Escape Sequence VirtualKey into correct ScanCode using MapVirtualKey 426 | } 427 | } 428 | 429 | // Assign the last of the weird ones 430 | switch(VirtualKey) 431 | { 432 | // Right-hand CONTROL and ALT have their E0 bit set 433 | case VK_CONTROL: 434 | { 435 | if(IsE0) 436 | { 437 | VirtualKey = VK_RCONTROL; 438 | } 439 | else 440 | { 441 | VirtualKey = VK_LCONTROL; 442 | } 443 | } break; 444 | 445 | case VK_MENU: 446 | { 447 | if(IsE0) 448 | { 449 | VirtualKey = VK_RMENU; 450 | } 451 | else 452 | { 453 | VirtualKey = VK_LMENU; 454 | } 455 | } break; 456 | 457 | // NUMPAD ENTER has its E0 bit set 458 | case VK_RETURN: 459 | { 460 | if(IsE0) 461 | { 462 | VirtualKey = VK_SEPARATOR; 463 | } 464 | } break; 465 | 466 | // The standard INSERT, DELETE, HOME, END, PRIOR and NEXT keys will always have their E0 bit set, but the corresponding keys on the NUMPAD will not 467 | case VK_INSERT: 468 | { 469 | if(!IsE0) 470 | { 471 | VirtualKey = VK_NUMPAD0; 472 | } 473 | } break; 474 | 475 | case VK_DELETE: 476 | { 477 | if(!IsE0) 478 | { 479 | VirtualKey = VK_DECIMAL; 480 | } 481 | } break; 482 | 483 | case VK_HOME: 484 | { 485 | if(!IsE0) 486 | { 487 | VirtualKey = VK_NUMPAD7; 488 | } 489 | } break; 490 | 491 | case VK_END: 492 | { 493 | if(!IsE0) 494 | { 495 | VirtualKey = VK_NUMPAD1; 496 | } 497 | } break; 498 | 499 | case VK_PRIOR: 500 | { 501 | if(!IsE0) 502 | { 503 | VirtualKey = VK_NUMPAD9; 504 | } 505 | } break; 506 | 507 | case VK_NEXT: 508 | { 509 | if(!IsE0) 510 | { 511 | VirtualKey = VK_NUMPAD3; 512 | } 513 | } break; 514 | 515 | // The standard arrow keys will always have their E0 bit set, but the corresponding keys on the NUMPAD will not 516 | case VK_LEFT: 517 | { 518 | if(!IsE0) 519 | { 520 | VirtualKey = VK_NUMPAD4; 521 | } 522 | } break; 523 | 524 | case VK_RIGHT: 525 | { 526 | if(!IsE0) 527 | { 528 | VirtualKey = VK_NUMPAD6; 529 | } 530 | } break; 531 | 532 | case VK_UP: 533 | { 534 | if(!IsE0) 535 | { 536 | VirtualKey = VK_NUMPAD8; 537 | } 538 | } break; 539 | 540 | case VK_DOWN: 541 | { 542 | if(!IsE0) 543 | { 544 | VirtualKey = VK_NUMPAD2; 545 | } 546 | } break; 547 | 548 | // NUMPAD 5 doesn't have its E0 bit set 549 | case VK_CLEAR: 550 | { 551 | if(!IsE0) 552 | { 553 | VirtualKey = VK_NUMPAD5; 554 | } 555 | } break; 556 | } 557 | 558 | b32 IsDown = !(Flags & RI_KEY_BREAK); 559 | 560 | tiny_event Event = {0}; 561 | Event.Type = TINY_EVENT_TYPE_KEYBOARD; 562 | Event.Keyboard.IsDown = IsDown; 563 | Event.Keyboard.KeyType = false; 564 | 565 | // Assign VirtualKey to Engine's te_key_type enum 566 | if((VirtualKey >= 'A') && (VirtualKey <= 'Z')) 567 | { 568 | for(int VKIndex = 'A'; VKIndex <= 'Z'; ++VKIndex) 569 | { 570 | if(VKIndex == VirtualKey) 571 | { 572 | Event.Keyboard.KeyType = (VKIndex - 'A') + KEY_A; 573 | } 574 | } 575 | } 576 | else if((VirtualKey >= '0') && (VirtualKey <= '9')) 577 | { 578 | for(int VKIndex = '0'; VKIndex <= '9'; ++VKIndex) 579 | { 580 | int Offset = KEY_0; 581 | if(VKIndex == VirtualKey) 582 | { 583 | Event.Keyboard.KeyType = VKIndex - ('0' - Offset); 584 | } 585 | } 586 | } 587 | else if((VirtualKey >= VK_NUMPAD0) && (VirtualKey <= VK_DIVIDE)) 588 | { 589 | int Offset = KEY_NUMPAD_0; 590 | for(int VKIndex = VK_NUMPAD0; VKIndex <= VK_DIVIDE; ++VKIndex) 591 | { 592 | if(VKIndex == VirtualKey) 593 | { 594 | Event.Keyboard.KeyType = VKIndex - (VK_NUMPAD0-Offset); 595 | } 596 | } 597 | } 598 | else if((VirtualKey >= VK_F1) && (VirtualKey <= VK_F24)) 599 | { 600 | int Offset = KEY_F1; 601 | for(int VKIndex = VK_F1; VKIndex <= VK_F24; ++VKIndex) 602 | { 603 | if(VKIndex == VirtualKey) 604 | { 605 | Event.Keyboard.KeyType = VKIndex - (VK_F1-Offset); 606 | } 607 | } 608 | } 609 | else if((VirtualKey >= VK_LEFT) && (VirtualKey <= VK_DOWN)) 610 | { 611 | int Offset = KEY_LEFT; 612 | for(int VKIndex = VK_LEFT; VKIndex <= VK_DOWN; ++VKIndex) 613 | { 614 | if(VKIndex == VirtualKey) 615 | { 616 | Event.Keyboard.KeyType = VKIndex - (VK_LEFT-Offset); 617 | } 618 | } 619 | } 620 | else 621 | { 622 | switch(VirtualKey) 623 | { 624 | case VK_BACK: 625 | { 626 | Event.Keyboard.KeyType = KEY_BACKSPACE; 627 | } break; 628 | 629 | case VK_TAB: 630 | { 631 | Event.Keyboard.KeyType = KEY_TAB; 632 | } break; 633 | 634 | case VK_RETURN: 635 | { 636 | Event.Keyboard.KeyType = KEY_RETURN; 637 | } break; 638 | 639 | case VK_LSHIFT: 640 | { 641 | Event.Keyboard.KeyType = KEY_LSHIFT; 642 | } break; 643 | 644 | case VK_RSHIFT: 645 | { 646 | Event.Keyboard.KeyType = KEY_RSHIFT; 647 | } break; 648 | 649 | case VK_LCONTROL: 650 | { 651 | Event.Keyboard.KeyType = KEY_LCONTROL; 652 | } break; 653 | 654 | case VK_RCONTROL: 655 | { 656 | Event.Keyboard.KeyType = KEY_RCONTROL; 657 | } break; 658 | 659 | case VK_CAPITAL: 660 | { 661 | Event.Keyboard.KeyType = KEY_CAPSLOCK; 662 | } break; 663 | 664 | /// 665 | 666 | case VK_SPACE: 667 | { 668 | Event.Keyboard.KeyType = KEY_SPACE; 669 | } break; 670 | 671 | case VK_LWIN: 672 | { 673 | Event.Keyboard.KeyType = KEY_LSUPER; 674 | } break; 675 | 676 | case VK_RWIN: 677 | { 678 | Event.Keyboard.KeyType = KEY_RSUPER; 679 | } break; 680 | 681 | case VK_LMENU: 682 | { 683 | Event.Keyboard.KeyType = KEY_LALT; 684 | } break; 685 | 686 | case VK_RMENU: 687 | { 688 | Event.Keyboard.KeyType = KEY_RALT; 689 | } break; 690 | 691 | /// 692 | 693 | case VK_ESCAPE: 694 | { 695 | Event.Keyboard.KeyType = KEY_ESCAPE; 696 | } break; 697 | 698 | case VK_PRIOR: 699 | { 700 | Event.Keyboard.KeyType = KEY_PAGEUP; 701 | } break; 702 | 703 | case VK_NEXT: 704 | { 705 | Event.Keyboard.KeyType = KEY_PAGEDOWN; 706 | } break; 707 | 708 | case VK_HOME: 709 | { 710 | Event.Keyboard.KeyType = KEY_HOME; 711 | } break; 712 | 713 | case VK_END: 714 | { 715 | Event.Keyboard.KeyType = KEY_END; 716 | } break; 717 | 718 | case VK_INSERT: 719 | { 720 | Event.Keyboard.KeyType = KEY_INSERT; 721 | } break; 722 | 723 | case VK_DELETE: 724 | { 725 | Event.Keyboard.KeyType = KEY_DELETE; 726 | } break; 727 | 728 | case VK_PAUSE: 729 | { 730 | Event.Keyboard.KeyType = KEY_PAUSE; 731 | } break; 732 | 733 | case VK_NUMLOCK: 734 | { 735 | Event.Keyboard.KeyType = KEY_NUMLOCK; 736 | } break; 737 | 738 | case VK_SCROLL: // TODO(hayden): Not being handled correctly!!! 739 | { 740 | Event.Keyboard.KeyType = KEY_SCROLLLOCK; 741 | } break; 742 | 743 | case VK_SNAPSHOT: 744 | { 745 | Event.Keyboard.KeyType = KEY_PRINTSCREEN; 746 | } break; 747 | 748 | /// 749 | 750 | case VK_OEM_PLUS: 751 | { 752 | Event.Keyboard.KeyType = KEY_PLUS; 753 | } break; 754 | 755 | case VK_OEM_COMMA: 756 | { 757 | Event.Keyboard.KeyType = KEY_COMMA; 758 | } break; 759 | 760 | case VK_OEM_MINUS: 761 | { 762 | Event.Keyboard.KeyType = KEY_MINUS; 763 | } break; 764 | 765 | case VK_OEM_1: 766 | { 767 | // NOTE(hayden): Shared VKCode 768 | Event.Keyboard.KeyType = KEY_SEMICOLON; 769 | } break; 770 | 771 | case VK_OEM_2: 772 | { 773 | // NOTE(hayden): Shared VKCode 774 | Event.Keyboard.KeyType = KEY_SLASH; 775 | } break; 776 | 777 | case VK_OEM_3: 778 | { 779 | // NOTE(hayden): Shared VKCode 780 | Event.Keyboard.KeyType = KEY_GRAVE; 781 | } break; 782 | 783 | case VK_OEM_4: 784 | { 785 | // NOTE(hayden): Shared VKCode 786 | Event.Keyboard.KeyType = KEY_LBRACKET; 787 | } break; 788 | 789 | case VK_OEM_5: 790 | { 791 | // NOTE(hayden): Shared VKCode 792 | Event.Keyboard.KeyType = KEY_BACKSLASH; 793 | } break; 794 | 795 | case VK_OEM_6: 796 | { 797 | // NOTE(hayden): Shared VKCode 798 | Event.Keyboard.KeyType = KEY_RBRACKET; 799 | } break; 800 | 801 | case VK_OEM_7: 802 | { 803 | // NOTE(hayden): Shared VKCode 804 | Event.Keyboard.KeyType = KEY_QUOTE; 805 | } break; 806 | 807 | case VK_OEM_8: 808 | { 809 | Event.Keyboard.KeyType = KEY_OEM_8; 810 | } break; 811 | 812 | case VK_OEM_102: 813 | { 814 | Event.Keyboard.KeyType = KEY_OEM_102; 815 | } break; 816 | 817 | case 0xE1: // OEM Specific 818 | { 819 | Event.Keyboard.KeyType = KEY_OEM_SPECIFIC1; 820 | } break; 821 | 822 | case 0xE3: // OEM Specific 823 | { 824 | Event.Keyboard.KeyType = KEY_OEM_SPECIFIC2; 825 | } break; 826 | 827 | case 0xE4: // OEM Specific 828 | { 829 | Event.Keyboard.KeyType = KEY_OEM_SPECIFIC3; 830 | } break; 831 | 832 | case VK_OEM_CLEAR: 833 | { 834 | Event.Keyboard.KeyType = KEY_OEM_CLEAR; 835 | } break; 836 | 837 | /// 838 | 839 | case VK_BROWSER_BACK: 840 | { 841 | Event.Keyboard.KeyType = KEY_BROWSER_BACK; 842 | } break; 843 | 844 | case VK_BROWSER_FORWARD: 845 | { 846 | Event.Keyboard.KeyType = KEY_BROWSER_FORWARD; 847 | } break; 848 | 849 | case VK_BROWSER_REFRESH: 850 | { 851 | Event.Keyboard.KeyType = KEY_BROWSER_REFRESH; 852 | } break; 853 | 854 | case VK_BROWSER_STOP: 855 | { 856 | Event.Keyboard.KeyType = VK_BROWSER_STOP; 857 | } break; 858 | 859 | case VK_BROWSER_SEARCH: 860 | { 861 | Event.Keyboard.KeyType = KEY_BROWSER_SEARCH; 862 | } break; 863 | 864 | case VK_BROWSER_FAVORITES: 865 | { 866 | Event.Keyboard.KeyType = KEY_BROWSER_FAVORITES; 867 | } break; 868 | 869 | case VK_BROWSER_HOME: 870 | { 871 | Event.Keyboard.KeyType = KEY_BROWSER_HOME; 872 | } break; 873 | 874 | /// 875 | 876 | case VK_VOLUME_MUTE: 877 | { 878 | Event.Keyboard.KeyType = KEY_VOLUME_MUTE; 879 | } break; 880 | 881 | case VK_VOLUME_UP: 882 | { 883 | Event.Keyboard.KeyType = KEY_VOLUME_UP; 884 | } break; 885 | 886 | case VK_VOLUME_DOWN: 887 | { 888 | Event.Keyboard.KeyType = KEY_VOLUME_DOWN; 889 | } break; 890 | 891 | /// 892 | 893 | case VK_MEDIA_NEXT_TRACK: 894 | { 895 | Event.Keyboard.KeyType = KEY_MEDIA_NEXT_TRACK; 896 | } break; 897 | 898 | case VK_MEDIA_PREV_TRACK: 899 | { 900 | Event.Keyboard.KeyType = KEY_MEDIA_PREV_TRACK; 901 | } break; 902 | 903 | case VK_MEDIA_STOP: 904 | { 905 | Event.Keyboard.KeyType = KEY_MEDIA_STOP; 906 | } break; 907 | 908 | case VK_MEDIA_PLAY_PAUSE: 909 | { 910 | Event.Keyboard.KeyType = KEY_MEDIA_PLAYPAUSE; 911 | } break; 912 | 913 | /// 914 | 915 | case VK_LAUNCH_MAIL: 916 | { 917 | Event.Keyboard.KeyType = KEY_LAUNCH_MAIL; 918 | } break; 919 | 920 | case VK_LAUNCH_MEDIA_SELECT: 921 | { 922 | Event.Keyboard.KeyType = KEY_LAUNCH_MEDIASELECT; 923 | } break; 924 | 925 | case VK_LAUNCH_APP1: 926 | { 927 | Event.Keyboard.KeyType = KEY_LAUNCH_APPLICATION1; 928 | } break; 929 | 930 | case VK_LAUNCH_APP2: 931 | { 932 | Event.Keyboard.KeyType = KEY_LAUNCH_APPLICATION2; 933 | } break; 934 | 935 | /// 936 | 937 | case VK_HELP: 938 | { 939 | Event.Keyboard.KeyType = KEY_HELP; 940 | } break; 941 | 942 | case VK_MENU: 943 | { 944 | Event.Keyboard.KeyType = KEY_MENU; 945 | } break; 946 | 947 | case VK_PRINT: 948 | { 949 | Event.Keyboard.KeyType = KEY_PRINT; 950 | } break; 951 | 952 | case VK_SELECT: 953 | { 954 | Event.Keyboard.KeyType = KEY_SELECT; 955 | } break; 956 | 957 | case VK_EXECUTE: 958 | { 959 | Event.Keyboard.KeyType = KEY_EXEC; 960 | } break; 961 | 962 | case VK_APPS: 963 | { 964 | Event.Keyboard.KeyType = KEY_APPLICATIONS; 965 | } break; 966 | 967 | case VK_SLEEP: 968 | { 969 | Event.Keyboard.KeyType = KEY_SLEEP; 970 | } break; 971 | 972 | /// 973 | 974 | case VK_KANA: 975 | { 976 | // NOTE(hayden): Shared VKCode 977 | Event.Keyboard.KeyType = KEY_MODE_KANA; 978 | } break; 979 | 980 | case VK_JUNJA: 981 | { 982 | Event.Keyboard.KeyType = KEY_MODE_JUNJA; 983 | } break; 984 | 985 | case VK_FINAL: 986 | { 987 | Event.Keyboard.KeyType = KEY_MODE_FINAL; 988 | } break; 989 | 990 | case VK_KANJI: 991 | { 992 | // NOTE(hayden): Shared VKCode 993 | Event.Keyboard.KeyType = KEY_MODE_KANJI; 994 | } break; 995 | 996 | /// 997 | 998 | case VK_ATTN: 999 | { 1000 | Event.Keyboard.KeyType = KEY_ATTN; 1001 | } break; 1002 | 1003 | case VK_CRSEL: 1004 | { 1005 | Event.Keyboard.KeyType = KEY_CRSEL; 1006 | } break; 1007 | 1008 | case VK_EXSEL: 1009 | { 1010 | Event.Keyboard.KeyType = KEY_EXSEL; 1011 | } break; 1012 | 1013 | case VK_EREOF: 1014 | { 1015 | Event.Keyboard.KeyType = KEY_EREOF; 1016 | } break; 1017 | 1018 | case VK_PLAY: 1019 | { 1020 | Event.Keyboard.KeyType = KEY_PLAY; 1021 | } break; 1022 | 1023 | case VK_ZOOM: 1024 | { 1025 | Event.Keyboard.KeyType = KEY_ZOOM; 1026 | 1027 | } break; 1028 | 1029 | case VK_NONAME: 1030 | { 1031 | Event.Keyboard.KeyType = KEY_NONAME; 1032 | } break; 1033 | 1034 | case VK_PA1: 1035 | { 1036 | Event.Keyboard.KeyType = KEY_PA1; 1037 | } break; 1038 | } 1039 | } 1040 | 1041 | Tiny_PushInputEvent(Event); 1042 | 1043 | // getting a human-readable string 1044 | //UINT Key = (ScanCode << 16) | (IsE0 << 24); 1045 | //char Buffer[512] = {0}; 1046 | //GetKeyNameTextW((LONG)Key, Buffer, 512); 1047 | } 1048 | } 1049 | } 1050 | } break; 1051 | 1052 | case WM_MOUSEMOVE: 1053 | { 1054 | // Cursor 1055 | tiny_event Event = {0}; 1056 | Event.Type = TINY_EVENT_TYPE_MOUSE; 1057 | Event.Mouse.Type = TINY_EVENT_MOUSE_MOVE; 1058 | Event.Mouse.X = LParam & 0xFFFF; // TODO(hayden): There is a macro for this in 1059 | Event.Mouse.Y = (LParam >> 16) & 0xFFFF; // TODO(hayden): There is a macro for this in 1060 | 1061 | // Normalized 1062 | // TODO(hayden): Pull this out into the engine code! 1063 | // TODO(hayden): Also, this isn't "centered" (where it's (0,0) at the center of the screen) 1064 | RECT ClientRect; 1065 | GetClientRect(Window, &ClientRect); 1066 | Event.Mouse.NormalizedX = (f32)Event.Mouse.X / (f32)(ClientRect.right-1); 1067 | Event.Mouse.NormalizedY = (f32)Event.Mouse.Y / (f32)(ClientRect.bottom-1); 1068 | 1069 | Tiny_PushInputEvent(Event); 1070 | } break; 1071 | 1072 | default: 1073 | { 1074 | Result = DefWindowProcW(Window, Message, WParam, LParam); 1075 | } break; 1076 | } 1077 | 1078 | return(Result); 1079 | } 1080 | 1081 | int WINAPI 1082 | WinMain(HINSTANCE Instance, HINSTANCE PrevInstance, LPSTR CommandLine, int ShowCode) 1083 | { 1084 | WNDCLASSW WindowClass = {0}; 1085 | WindowClass.style = CS_OWNDC; 1086 | WindowClass.lpfnWndProc = Win32MainWindowCallback; 1087 | WindowClass.hInstance = Instance; 1088 | WindowClass.hCursor = LoadCursor(NULL, IDC_ARROW); 1089 | WindowClass.hIcon = LoadIcon(NULL, IDI_WINLOGO); 1090 | WindowClass.lpszClassName = L"Win32TinyEngineWindowClass"; // NOTE(zak): At somepoint the window class should reflect the applications name 1091 | 1092 | if(RegisterClassW(&WindowClass)) 1093 | { 1094 | HWND Window = CreateWindowExW(0, WindowClass.lpszClassName, L"TinyEngine", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, Instance, 0); // NOTE(zak): At somepoint the window name should reflect the applications name 1095 | if(Window != INVALID_HANDLE_VALUE) 1096 | { 1097 | // Input 1098 | RAWINPUTDEVICE RawInputDevices[2]; // TODO(hayden): Joystick 1099 | { 1100 | // Mouse 1101 | RawInputDevices[0].usUsagePage = 0x01; 1102 | RawInputDevices[0].usUsage = 0x02; 1103 | RawInputDevices[0].dwFlags = 0; 1104 | RawInputDevices[0].hwndTarget = Window; 1105 | 1106 | // Keyboard 1107 | RawInputDevices[1].usUsagePage = 0x01; 1108 | RawInputDevices[1].usUsage = 0x06; 1109 | RawInputDevices[1].dwFlags = RIDEV_NOLEGACY; 1110 | RawInputDevices[1].hwndTarget = Window; 1111 | } 1112 | 1113 | if(RegisterRawInputDevices(RawInputDevices, ArrayCount(RawInputDevices), sizeof(RAWINPUTDEVICE))) 1114 | { 1115 | if(Win32InitD3D11(Window)) 1116 | { 1117 | b32 DidComInitialize = true; 1118 | if(CoInitializeEx(0, COINIT_MULTITHREADED) != S_OK) 1119 | { 1120 | // NOTE(zak): I've seen CoInitializeEx fail on some systems before so if it fails lets try to just CoInitalize 1121 | // TODO(zak): Logging that CoInitializeEx failed 1122 | if(CoInitialize(0) != S_OK) 1123 | { 1124 | // TODO(zak): CoInitialize failed as well lets log this 1125 | DidComInitialize = false; 1126 | } 1127 | } 1128 | 1129 | if(DidComInitialize) 1130 | { 1131 | if(!Win32InitWASAPI()) 1132 | { 1133 | // TODO(zak): Logging 1134 | } 1135 | } 1136 | 1137 | if(ShowWindow(Window, SW_SHOW) == 0) 1138 | { 1139 | IsRunning = true; 1140 | while(IsRunning) 1141 | { 1142 | MSG Message; 1143 | while(PeekMessageW(&Message, Window, 0, 0, PM_REMOVE)) 1144 | { 1145 | TranslateMessage(&Message); 1146 | DispatchMessageW(&Message); 1147 | } 1148 | 1149 | // TODO(zak): I dont remember if we need to OMSetRenderTargets every frame. Lets see 1150 | DeviceContext->lpVtbl->OMSetRenderTargets(DeviceContext, 1, &RenderTargetView, 0); 1151 | 1152 | float ClearColor[4] = {1.0f, 0.0f, 0.0f, 1.0f}; 1153 | DeviceContext->lpVtbl->ClearRenderTargetView(DeviceContext, RenderTargetView, ClearColor); 1154 | 1155 | Tiny_Update(&Global_Platform); 1156 | Tiny_Render(); 1157 | 1158 | Swapchain->lpVtbl->Present(Swapchain, 1, 0); // VSync is on! Change the `1` to a `0` to turn it off 1159 | } 1160 | } 1161 | else 1162 | { 1163 | // TODO(zak): Logging 1164 | } 1165 | 1166 | RenderTargetView->lpVtbl->Release(RenderTargetView); 1167 | Swapchain->lpVtbl->Release(Swapchain); 1168 | DeviceContext->lpVtbl->Release(DeviceContext); 1169 | Device->lpVtbl->Release(Device); 1170 | } 1171 | else 1172 | { 1173 | // TODO(zak): Loggings 1174 | } 1175 | } 1176 | else 1177 | { 1178 | // TODO(hayden): Logging 1179 | } 1180 | } 1181 | else 1182 | { 1183 | // TODO(zak): Logging 1184 | } 1185 | 1186 | } 1187 | else 1188 | { 1189 | // TODO(zak): Logging 1190 | } 1191 | 1192 | return(0); 1193 | } -------------------------------------------------------------------------------- /source/stb_sprintf.h: -------------------------------------------------------------------------------- 1 | // stb_sprintf - v1.06 - 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 : h ll j z t I64 I32 I 7 | // 8 | // Contributors: 9 | // Fabian "ryg" Giesen (reformatting) 10 | // 11 | // Contributors (bugfixes): 12 | // github:d26435 13 | // github:trex78 14 | // github:account-login 15 | // Jari Komppa (SI suffixes) 16 | // Rohit Nirmal 17 | // Marcin Wojdyr 18 | // Leonard Ritter 19 | // Stefano Zanotti 20 | // Adam Allison 21 | // 22 | // LICENSE: 23 | // 24 | // See end of file for license information. 25 | 26 | #ifndef STB_SPRINTF_H_INCLUDE 27 | #define STB_SPRINTF_H_INCLUDE 28 | 29 | /* 30 | Single file sprintf replacement. 31 | 32 | Originally written by Jeff Roberts at RAD Game Tools - 2015/10/20. 33 | Hereby placed in public domain. 34 | 35 | This is a full sprintf replacement that supports everything that 36 | the C runtime sprintfs support, including float/double, 64-bit integers, 37 | hex floats, field parameters (%*.*d stuff), length reads backs, etc. 38 | 39 | Why would you need this if sprintf already exists? Well, first off, 40 | it's *much* faster (see below). It's also much smaller than the CRT 41 | versions code-space-wise. We've also added some simple improvements 42 | that are super handy (commas in thousands, callbacks at buffer full, 43 | for example). Finally, the format strings for MSVC and GCC differ 44 | for 64-bit integers (among other small things), so this lets you use 45 | the same format strings in cross platform code. 46 | 47 | It uses the standard single file trick of being both the header file 48 | and the source itself. If you just include it normally, you just get 49 | the header file function definitions. To get the code, you include 50 | it from a C or C++ file and define STB_SPRINTF_IMPLEMENTATION first. 51 | 52 | It only uses va_args macros from the C runtime to do it's work. It 53 | does cast doubles to S64s and shifts and divides U64s, which does 54 | drag in CRT code on most platforms. 55 | 56 | It compiles to roughly 8K with float support, and 4K without. 57 | As a comparison, when using MSVC static libs, calling sprintf drags 58 | in 16K. 59 | 60 | API: 61 | ==== 62 | int stbsp_sprintf( char * buf, char const * fmt, ... ) 63 | int stbsp_snprintf( char * buf, int count, char const * fmt, ... ) 64 | Convert an arg list into a buffer. stbsp_snprintf always returns 65 | a zero-terminated string (unlike regular snprintf). 66 | 67 | int stbsp_vsprintf( char * buf, char const * fmt, va_list va ) 68 | int stbsp_vsnprintf( char * buf, int count, char const * fmt, va_list va ) 69 | Convert a va_list arg list into a buffer. stbsp_vsnprintf always returns 70 | a zero-terminated string (unlike regular snprintf). 71 | 72 | int stbsp_vsprintfcb( STBSP_SPRINTFCB * callback, void * user, char * buf, char const * fmt, va_list va ) 73 | typedef char * STBSP_SPRINTFCB( char const * buf, void * user, int len ); 74 | Convert into a buffer, calling back every STB_SPRINTF_MIN chars. 75 | Your callback can then copy the chars out, print them or whatever. 76 | This function is actually the workhorse for everything else. 77 | The buffer you pass in must hold at least STB_SPRINTF_MIN characters. 78 | // you return the next buffer to use or 0 to stop converting 79 | 80 | void stbsp_set_separators( char comma, char period ) 81 | Set the comma and period characters to use. 82 | 83 | FLOATS/DOUBLES: 84 | =============== 85 | This code uses a internal float->ascii conversion method that uses 86 | doubles with error correction (double-doubles, for ~105 bits of 87 | precision). This conversion is round-trip perfect - that is, an atof 88 | of the values output here will give you the bit-exact double back. 89 | 90 | One difference is that our insignificant digits will be different than 91 | with MSVC or GCC (but they don't match each other either). We also 92 | don't attempt to find the minimum length matching float (pre-MSVC15 93 | doesn't either). 94 | 95 | If you don't need float or doubles at all, define STB_SPRINTF_NOFLOAT 96 | and you'll save 4K of code space. 97 | 98 | 64-BIT INTS: 99 | ============ 100 | This library also supports 64-bit integers and you can use MSVC style or 101 | GCC style indicators (%I64d or %lld). It supports the C99 specifiers 102 | for size_t and ptr_diff_t (%jd %zd) as well. 103 | 104 | EXTRAS: 105 | ======= 106 | Like some GCCs, for integers and floats, you can use a ' (single quote) 107 | specifier and commas will be inserted on the thousands: "%'d" on 12345 108 | would print 12,345. 109 | 110 | For integers and floats, you can use a "$" specifier and the number 111 | will be converted to float and then divided to get kilo, mega, giga or 112 | tera and then printed, so "%$d" 1000 is "1.0 k", "%$.2d" 2536000 is 113 | "2.53 M", etc. For byte values, use two $:s, like "%$$d" to turn 114 | 2536000 to "2.42 Mi". If you prefer JEDEC suffixes to SI ones, use three 115 | $:s: "%$$$d" -> "2.42 M". To remove the space between the number and the 116 | suffix, add "_" specifier: "%_$d" -> "2.53M". 117 | 118 | In addition to octal and hexadecimal conversions, you can print 119 | integers in binary: "%b" for 256 would print 100. 120 | 121 | PERFORMANCE vs MSVC 2008 32-/64-bit (GCC is even slower than MSVC): 122 | =================================================================== 123 | "%d" across all 32-bit ints (4.8x/4.0x faster than 32-/64-bit MSVC) 124 | "%24d" across all 32-bit ints (4.5x/4.2x faster) 125 | "%x" across all 32-bit ints (4.5x/3.8x faster) 126 | "%08x" across all 32-bit ints (4.3x/3.8x faster) 127 | "%f" across e-10 to e+10 floats (7.3x/6.0x faster) 128 | "%e" across e-10 to e+10 floats (8.1x/6.0x faster) 129 | "%g" across e-10 to e+10 floats (10.0x/7.1x faster) 130 | "%f" for values near e-300 (7.9x/6.5x faster) 131 | "%f" for values near e+300 (10.0x/9.1x faster) 132 | "%e" for values near e-300 (10.1x/7.0x faster) 133 | "%e" for values near e+300 (9.2x/6.0x faster) 134 | "%.320f" for values near e-300 (12.6x/11.2x faster) 135 | "%a" for random values (8.6x/4.3x faster) 136 | "%I64d" for 64-bits with 32-bit values (4.8x/3.4x faster) 137 | "%I64d" for 64-bits > 32-bit values (4.9x/5.5x faster) 138 | "%s%s%s" for 64 char strings (7.1x/7.3x faster) 139 | "...512 char string..." ( 35.0x/32.5x faster!) 140 | */ 141 | 142 | #if defined(__has_feature) 143 | #if __has_feature(address_sanitizer) 144 | #define STBI__ASAN __attribute__((no_sanitize("address"))) 145 | #endif 146 | #endif 147 | #ifndef STBI__ASAN 148 | #define STBI__ASAN 149 | #endif 150 | 151 | #ifdef STB_SPRINTF_STATIC 152 | #define STBSP__PUBLICDEC static 153 | #define STBSP__PUBLICDEF static STBI__ASAN 154 | #else 155 | #ifdef __cplusplus 156 | #define STBSP__PUBLICDEC extern "C" 157 | #define STBSP__PUBLICDEF extern "C" STBI__ASAN 158 | #else 159 | #define STBSP__PUBLICDEC extern 160 | #define STBSP__PUBLICDEF STBI__ASAN 161 | #endif 162 | #endif 163 | 164 | #include // for va_list() 165 | #include // size_t, ptrdiff_t 166 | 167 | #ifndef STB_SPRINTF_MIN 168 | #define STB_SPRINTF_MIN 512 // how many characters per callback 169 | #endif 170 | typedef char *STBSP_SPRINTFCB(char *buf, void *user, int len); 171 | 172 | #ifndef STB_SPRINTF_DECORATE 173 | #define STB_SPRINTF_DECORATE(name) stbsp_##name // define this before including if you want to change the names 174 | #endif 175 | 176 | STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va); 177 | STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsnprintf)(char *buf, int count, char const *fmt, va_list va); 178 | STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...); 179 | STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...); 180 | 181 | STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va); 182 | STBSP__PUBLICDEF void STB_SPRINTF_DECORATE(set_separators)(char comma, char period); 183 | 184 | #endif // STB_SPRINTF_H_INCLUDE 185 | 186 | #ifdef STB_SPRINTF_IMPLEMENTATION 187 | 188 | #include // for va_arg() 189 | 190 | #define stbsp__uint32 unsigned int 191 | #define stbsp__int32 signed int 192 | 193 | #ifdef _MSC_VER 194 | #define stbsp__uint64 unsigned __int64 195 | #define stbsp__int64 signed __int64 196 | #else 197 | #define stbsp__uint64 unsigned long long 198 | #define stbsp__int64 signed long long 199 | #endif 200 | #define stbsp__uint16 unsigned short 201 | 202 | #ifndef stbsp__uintptr 203 | #if defined(__ppc64__) || defined(__aarch64__) || defined(_M_X64) || defined(__x86_64__) || defined(__x86_64) 204 | #define stbsp__uintptr stbsp__uint64 205 | #else 206 | #define stbsp__uintptr stbsp__uint32 207 | #endif 208 | #endif 209 | 210 | #ifndef STB_SPRINTF_MSVC_MODE // used for MSVC2013 and earlier (MSVC2015 matches GCC) 211 | #if defined(_MSC_VER) && (_MSC_VER < 1900) 212 | #define STB_SPRINTF_MSVC_MODE 213 | #endif 214 | #endif 215 | 216 | #ifdef STB_SPRINTF_NOUNALIGNED // define this before inclusion to force stbsp_sprintf to always use aligned accesses 217 | #define STBSP__UNALIGNED(code) 218 | #else 219 | #define STBSP__UNALIGNED(code) code 220 | #endif 221 | 222 | #ifndef STB_SPRINTF_NOFLOAT 223 | // internal float utility functions 224 | 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); 225 | static stbsp__int32 stbsp__real_to_parts(stbsp__int64 *bits, stbsp__int32 *expo, double value); 226 | #define STBSP__SPECIAL 0x7000 227 | #endif 228 | 229 | static char stbsp__period = '.'; 230 | static char stbsp__comma = ','; 231 | static struct 232 | { 233 | short temp; // force next field to be 2-byte aligned 234 | char pair[201]; 235 | } stbsp__digitpair = 236 | { 237 | 0, 238 | "00010203040506070809101112131415161718192021222324" 239 | "25262728293031323334353637383940414243444546474849" 240 | "50515253545556575859606162636465666768697071727374" 241 | "75767778798081828384858687888990919293949596979899" 242 | }; 243 | 244 | STBSP__PUBLICDEF void STB_SPRINTF_DECORATE(set_separators)(char pcomma, char pperiod) 245 | { 246 | stbsp__period = pperiod; 247 | stbsp__comma = pcomma; 248 | } 249 | 250 | #define STBSP__LEFTJUST 1 251 | #define STBSP__LEADINGPLUS 2 252 | #define STBSP__LEADINGSPACE 4 253 | #define STBSP__LEADING_0X 8 254 | #define STBSP__LEADINGZERO 16 255 | #define STBSP__INTMAX 32 256 | #define STBSP__TRIPLET_COMMA 64 257 | #define STBSP__NEGATIVE 128 258 | #define STBSP__METRIC_SUFFIX 256 259 | #define STBSP__HALFWIDTH 512 260 | #define STBSP__METRIC_NOSPACE 1024 261 | #define STBSP__METRIC_1024 2048 262 | #define STBSP__METRIC_JEDEC 4096 263 | 264 | static void stbsp__lead_sign(stbsp__uint32 fl, char *sign) 265 | { 266 | sign[0] = 0; 267 | if (fl & STBSP__NEGATIVE) { 268 | sign[0] = 1; 269 | sign[1] = '-'; 270 | } else if (fl & STBSP__LEADINGSPACE) { 271 | sign[0] = 1; 272 | sign[1] = ' '; 273 | } else if (fl & STBSP__LEADINGPLUS) { 274 | sign[0] = 1; 275 | sign[1] = '+'; 276 | } 277 | } 278 | 279 | STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va) 280 | { 281 | static char hex[] = "0123456789abcdefxp"; 282 | static char hexu[] = "0123456789ABCDEFXP"; 283 | char *bf; 284 | char const *f; 285 | int tlen = 0; 286 | 287 | bf = buf; 288 | f = fmt; 289 | for (;;) { 290 | stbsp__int32 fw, pr, tz; 291 | stbsp__uint32 fl; 292 | 293 | // macros for the callback buffer stuff 294 | #define stbsp__chk_cb_bufL(bytes) \ 295 | { \ 296 | int len = (int)(bf - buf); \ 297 | if ((len + (bytes)) >= STB_SPRINTF_MIN) { \ 298 | tlen += len; \ 299 | if (0 == (bf = buf = callback(buf, user, len))) \ 300 | goto done; \ 301 | } \ 302 | } 303 | #define stbsp__chk_cb_buf(bytes) \ 304 | { \ 305 | if (callback) { \ 306 | stbsp__chk_cb_bufL(bytes); \ 307 | } \ 308 | } 309 | #define stbsp__flush_cb() \ 310 | { \ 311 | stbsp__chk_cb_bufL(STB_SPRINTF_MIN - 1); \ 312 | } // flush if there is even one byte in the buffer 313 | #define stbsp__cb_buf_clamp(cl, v) \ 314 | cl = v; \ 315 | if (callback) { \ 316 | int lg = STB_SPRINTF_MIN - (int)(bf - buf); \ 317 | if (cl > lg) \ 318 | cl = lg; \ 319 | } 320 | 321 | // fast copy everything up to the next % (or end of string) 322 | for (;;) { 323 | while (((stbsp__uintptr)f) & 3) { 324 | schk1: 325 | if (f[0] == '%') 326 | goto scandd; 327 | schk2: 328 | if (f[0] == 0) 329 | goto endfmt; 330 | stbsp__chk_cb_buf(1); 331 | *bf++ = f[0]; 332 | ++f; 333 | } 334 | for (;;) { 335 | // Check if the next 4 bytes contain %(0x25) or end of string. 336 | // Using the 'hasless' trick: 337 | // https://graphics.stanford.edu/~seander/bithacks.html#HasLessInWord 338 | stbsp__uint32 v, c; 339 | v = *(stbsp__uint32 *)f; 340 | c = (~v) & 0x80808080; 341 | if (((v ^ 0x25252525) - 0x01010101) & c) 342 | goto schk1; 343 | if ((v - 0x01010101) & c) 344 | goto schk2; 345 | if (callback) 346 | if ((STB_SPRINTF_MIN - (int)(bf - buf)) < 4) 347 | goto schk1; 348 | #ifdef STB_SPRINTF_NOUNALIGNED 349 | if(((stbsp__uintptr)bf) & 3) { 350 | bf[0] = f[0]; 351 | bf[1] = f[1]; 352 | bf[2] = f[2]; 353 | bf[3] = f[3]; 354 | } else 355 | #endif 356 | { 357 | *(stbsp__uint32 *)bf = v; 358 | } 359 | bf += 4; 360 | f += 4; 361 | } 362 | } 363 | scandd: 364 | 365 | ++f; 366 | 367 | // ok, we have a percent, read the modifiers first 368 | fw = 0; 369 | pr = -1; 370 | fl = 0; 371 | tz = 0; 372 | 373 | // flags 374 | for (;;) { 375 | switch (f[0]) { 376 | // if we have left justify 377 | case '-': 378 | fl |= STBSP__LEFTJUST; 379 | ++f; 380 | continue; 381 | // if we have leading plus 382 | case '+': 383 | fl |= STBSP__LEADINGPLUS; 384 | ++f; 385 | continue; 386 | // if we have leading space 387 | case ' ': 388 | fl |= STBSP__LEADINGSPACE; 389 | ++f; 390 | continue; 391 | // if we have leading 0x 392 | case '#': 393 | fl |= STBSP__LEADING_0X; 394 | ++f; 395 | continue; 396 | // if we have thousand commas 397 | case '\'': 398 | fl |= STBSP__TRIPLET_COMMA; 399 | ++f; 400 | continue; 401 | // if we have kilo marker (none->kilo->kibi->jedec) 402 | case '$': 403 | if (fl & STBSP__METRIC_SUFFIX) { 404 | if (fl & STBSP__METRIC_1024) { 405 | fl |= STBSP__METRIC_JEDEC; 406 | } else { 407 | fl |= STBSP__METRIC_1024; 408 | } 409 | } else { 410 | fl |= STBSP__METRIC_SUFFIX; 411 | } 412 | ++f; 413 | continue; 414 | // if we don't want space between metric suffix and number 415 | case '_': 416 | fl |= STBSP__METRIC_NOSPACE; 417 | ++f; 418 | continue; 419 | // if we have leading zero 420 | case '0': 421 | fl |= STBSP__LEADINGZERO; 422 | ++f; 423 | goto flags_done; 424 | default: goto flags_done; 425 | } 426 | } 427 | flags_done: 428 | 429 | // get the field width 430 | if (f[0] == '*') { 431 | fw = va_arg(va, stbsp__uint32); 432 | ++f; 433 | } else { 434 | while ((f[0] >= '0') && (f[0] <= '9')) { 435 | fw = fw * 10 + f[0] - '0'; 436 | f++; 437 | } 438 | } 439 | // get the precision 440 | if (f[0] == '.') { 441 | ++f; 442 | if (f[0] == '*') { 443 | pr = va_arg(va, stbsp__uint32); 444 | ++f; 445 | } else { 446 | pr = 0; 447 | while ((f[0] >= '0') && (f[0] <= '9')) { 448 | pr = pr * 10 + f[0] - '0'; 449 | f++; 450 | } 451 | } 452 | } 453 | 454 | // handle integer size overrides 455 | switch (f[0]) { 456 | // are we halfwidth? 457 | case 'h': 458 | fl |= STBSP__HALFWIDTH; 459 | ++f; 460 | break; 461 | // are we 64-bit (unix style) 462 | case 'l': 463 | fl |= ((sizeof(long) == 8) ? STBSP__INTMAX : 0); 464 | ++f; 465 | if (f[0] == 'l') { 466 | fl |= STBSP__INTMAX; 467 | ++f; 468 | } 469 | break; 470 | // are we 64-bit on intmax? (c99) 471 | case 'j': 472 | fl |= (sizeof(size_t) == 8) ? STBSP__INTMAX : 0; 473 | ++f; 474 | break; 475 | // are we 64-bit on size_t or ptrdiff_t? (c99) 476 | case 'z': 477 | fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0; 478 | ++f; 479 | break; 480 | case 't': 481 | fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0; 482 | ++f; 483 | break; 484 | // are we 64-bit (msft style) 485 | case 'I': 486 | if ((f[1] == '6') && (f[2] == '4')) { 487 | fl |= STBSP__INTMAX; 488 | f += 3; 489 | } else if ((f[1] == '3') && (f[2] == '2')) { 490 | f += 3; 491 | } else { 492 | fl |= ((sizeof(void *) == 8) ? STBSP__INTMAX : 0); 493 | ++f; 494 | } 495 | break; 496 | default: break; 497 | } 498 | 499 | // handle each replacement 500 | switch (f[0]) { 501 | #define STBSP__NUMSZ 512 // big enough for e308 (with commas) or e-307 502 | char num[STBSP__NUMSZ]; 503 | char lead[8]; 504 | char tail[8]; 505 | char *s; 506 | char const *h; 507 | stbsp__uint32 l, n, cs; 508 | stbsp__uint64 n64; 509 | #ifndef STB_SPRINTF_NOFLOAT 510 | double fv; 511 | #endif 512 | stbsp__int32 dp; 513 | char const *sn; 514 | 515 | case 's': 516 | // get the string 517 | s = va_arg(va, char *); 518 | if (s == 0) 519 | s = (char *)"null"; 520 | // get the length 521 | sn = s; 522 | for (;;) { 523 | if ((((stbsp__uintptr)sn) & 3) == 0) 524 | break; 525 | lchk: 526 | if (sn[0] == 0) 527 | goto ld; 528 | ++sn; 529 | } 530 | n = 0xffffffff; 531 | if (pr >= 0) { 532 | n = (stbsp__uint32)(sn - s); 533 | if (n >= (stbsp__uint32)pr) 534 | goto ld; 535 | n = ((stbsp__uint32)(pr - n)) >> 2; 536 | } 537 | while (n) { 538 | stbsp__uint32 v = *(stbsp__uint32 *)sn; 539 | if ((v - 0x01010101) & (~v) & 0x80808080UL) 540 | goto lchk; 541 | sn += 4; 542 | --n; 543 | } 544 | goto lchk; 545 | ld: 546 | 547 | l = (stbsp__uint32)(sn - s); 548 | // clamp to precision 549 | if (l > (stbsp__uint32)pr) 550 | l = pr; 551 | lead[0] = 0; 552 | tail[0] = 0; 553 | pr = 0; 554 | dp = 0; 555 | cs = 0; 556 | // copy the string in 557 | goto scopy; 558 | 559 | case 'c': // char 560 | // get the character 561 | s = num + STBSP__NUMSZ - 1; 562 | *s = (char)va_arg(va, int); 563 | l = 1; 564 | lead[0] = 0; 565 | tail[0] = 0; 566 | pr = 0; 567 | dp = 0; 568 | cs = 0; 569 | goto scopy; 570 | 571 | case 'n': // weird write-bytes specifier 572 | { 573 | int *d = va_arg(va, int *); 574 | *d = tlen + (int)(bf - buf); 575 | } break; 576 | 577 | #ifdef STB_SPRINTF_NOFLOAT 578 | case 'A': // float 579 | case 'a': // hex float 580 | case 'G': // float 581 | case 'g': // float 582 | case 'E': // float 583 | case 'e': // float 584 | case 'f': // float 585 | va_arg(va, double); // eat it 586 | s = (char *)"No float"; 587 | l = 8; 588 | lead[0] = 0; 589 | tail[0] = 0; 590 | pr = 0; 591 | dp = 0; 592 | cs = 0; 593 | goto scopy; 594 | #else 595 | case 'A': // hex float 596 | case 'a': // hex float 597 | h = (f[0] == 'A') ? hexu : hex; 598 | fv = va_arg(va, double); 599 | if (pr == -1) 600 | pr = 6; // default is 6 601 | // read the double into a string 602 | if (stbsp__real_to_parts((stbsp__int64 *)&n64, &dp, fv)) 603 | fl |= STBSP__NEGATIVE; 604 | 605 | s = num + 64; 606 | 607 | stbsp__lead_sign(fl, lead); 608 | 609 | if (dp == -1023) 610 | dp = (n64) ? -1022 : 0; 611 | else 612 | n64 |= (((stbsp__uint64)1) << 52); 613 | n64 <<= (64 - 56); 614 | if (pr < 15) 615 | n64 += ((((stbsp__uint64)8) << 56) >> (pr * 4)); 616 | // add leading chars 617 | 618 | #ifdef STB_SPRINTF_MSVC_MODE 619 | *s++ = '0'; 620 | *s++ = 'x'; 621 | #else 622 | lead[1 + lead[0]] = '0'; 623 | lead[2 + lead[0]] = 'x'; 624 | lead[0] += 2; 625 | #endif 626 | *s++ = h[(n64 >> 60) & 15]; 627 | n64 <<= 4; 628 | if (pr) 629 | *s++ = stbsp__period; 630 | sn = s; 631 | 632 | // print the bits 633 | n = pr; 634 | if (n > 13) 635 | n = 13; 636 | if (pr > (stbsp__int32)n) 637 | tz = pr - n; 638 | pr = 0; 639 | while (n--) { 640 | *s++ = h[(n64 >> 60) & 15]; 641 | n64 <<= 4; 642 | } 643 | 644 | // print the expo 645 | tail[1] = h[17]; 646 | if (dp < 0) { 647 | tail[2] = '-'; 648 | dp = -dp; 649 | } else 650 | tail[2] = '+'; 651 | n = (dp >= 1000) ? 6 : ((dp >= 100) ? 5 : ((dp >= 10) ? 4 : 3)); 652 | tail[0] = (char)n; 653 | for (;;) { 654 | tail[n] = '0' + dp % 10; 655 | if (n <= 3) 656 | break; 657 | --n; 658 | dp /= 10; 659 | } 660 | 661 | dp = (int)(s - sn); 662 | l = (int)(s - (num + 64)); 663 | s = num + 64; 664 | cs = 1 + (3 << 24); 665 | goto scopy; 666 | 667 | case 'G': // float 668 | case 'g': // float 669 | h = (f[0] == 'G') ? hexu : hex; 670 | fv = va_arg(va, double); 671 | if (pr == -1) 672 | pr = 6; 673 | else if (pr == 0) 674 | pr = 1; // default is 6 675 | // read the double into a string 676 | if (stbsp__real_to_str(&sn, &l, num, &dp, fv, (pr - 1) | 0x80000000)) 677 | fl |= STBSP__NEGATIVE; 678 | 679 | // clamp the precision and delete extra zeros after clamp 680 | n = pr; 681 | if (l > (stbsp__uint32)pr) 682 | l = pr; 683 | while ((l > 1) && (pr) && (sn[l - 1] == '0')) { 684 | --pr; 685 | --l; 686 | } 687 | 688 | // should we use %e 689 | if ((dp <= -4) || (dp > (stbsp__int32)n)) { 690 | if (pr > (stbsp__int32)l) 691 | pr = l - 1; 692 | else if (pr) 693 | --pr; // when using %e, there is one digit before the decimal 694 | goto doexpfromg; 695 | } 696 | // this is the insane action to get the pr to match %g semantics for %f 697 | if (dp > 0) { 698 | pr = (dp < (stbsp__int32)l) ? l - dp : 0; 699 | } else { 700 | pr = -dp + ((pr > (stbsp__int32)l) ? (stbsp__int32) l : pr); 701 | } 702 | goto dofloatfromg; 703 | 704 | case 'E': // float 705 | case 'e': // float 706 | h = (f[0] == 'E') ? hexu : hex; 707 | fv = va_arg(va, double); 708 | if (pr == -1) 709 | pr = 6; // default is 6 710 | // read the double into a string 711 | if (stbsp__real_to_str(&sn, &l, num, &dp, fv, pr | 0x80000000)) 712 | fl |= STBSP__NEGATIVE; 713 | doexpfromg: 714 | tail[0] = 0; 715 | stbsp__lead_sign(fl, lead); 716 | if (dp == STBSP__SPECIAL) { 717 | s = (char *)sn; 718 | cs = 0; 719 | pr = 0; 720 | goto scopy; 721 | } 722 | s = num + 64; 723 | // handle leading chars 724 | *s++ = sn[0]; 725 | 726 | if (pr) 727 | *s++ = stbsp__period; 728 | 729 | // handle after decimal 730 | if ((l - 1) > (stbsp__uint32)pr) 731 | l = pr + 1; 732 | for (n = 1; n < l; n++) 733 | *s++ = sn[n]; 734 | // trailing zeros 735 | tz = pr - (l - 1); 736 | pr = 0; 737 | // dump expo 738 | tail[1] = h[0xe]; 739 | dp -= 1; 740 | if (dp < 0) { 741 | tail[2] = '-'; 742 | dp = -dp; 743 | } else 744 | tail[2] = '+'; 745 | #ifdef STB_SPRINTF_MSVC_MODE 746 | n = 5; 747 | #else 748 | n = (dp >= 100) ? 5 : 4; 749 | #endif 750 | tail[0] = (char)n; 751 | for (;;) { 752 | tail[n] = '0' + dp % 10; 753 | if (n <= 3) 754 | break; 755 | --n; 756 | dp /= 10; 757 | } 758 | cs = 1 + (3 << 24); // how many tens 759 | goto flt_lead; 760 | 761 | case 'f': // float 762 | fv = va_arg(va, double); 763 | doafloat: 764 | // do kilos 765 | if (fl & STBSP__METRIC_SUFFIX) { 766 | double divisor; 767 | divisor = 1000.0f; 768 | if (fl & STBSP__METRIC_1024) 769 | divisor = 1024.0; 770 | while (fl < 0x4000000) { 771 | if ((fv < divisor) && (fv > -divisor)) 772 | break; 773 | fv /= divisor; 774 | fl += 0x1000000; 775 | } 776 | } 777 | if (pr == -1) 778 | pr = 6; // default is 6 779 | // read the double into a string 780 | if (stbsp__real_to_str(&sn, &l, num, &dp, fv, pr)) 781 | fl |= STBSP__NEGATIVE; 782 | dofloatfromg: 783 | tail[0] = 0; 784 | stbsp__lead_sign(fl, lead); 785 | if (dp == STBSP__SPECIAL) { 786 | s = (char *)sn; 787 | cs = 0; 788 | pr = 0; 789 | goto scopy; 790 | } 791 | s = num + 64; 792 | 793 | // handle the three decimal varieties 794 | if (dp <= 0) { 795 | stbsp__int32 i; 796 | // handle 0.000*000xxxx 797 | *s++ = '0'; 798 | if (pr) 799 | *s++ = stbsp__period; 800 | n = -dp; 801 | if ((stbsp__int32)n > pr) 802 | n = pr; 803 | i = n; 804 | while (i) { 805 | if ((((stbsp__uintptr)s) & 3) == 0) 806 | break; 807 | *s++ = '0'; 808 | --i; 809 | } 810 | while (i >= 4) { 811 | *(stbsp__uint32 *)s = 0x30303030; 812 | s += 4; 813 | i -= 4; 814 | } 815 | while (i) { 816 | *s++ = '0'; 817 | --i; 818 | } 819 | if ((stbsp__int32)(l + n) > pr) 820 | l = pr - n; 821 | i = l; 822 | while (i) { 823 | *s++ = *sn++; 824 | --i; 825 | } 826 | tz = pr - (n + l); 827 | cs = 1 + (3 << 24); // how many tens did we write (for commas below) 828 | } else { 829 | cs = (fl & STBSP__TRIPLET_COMMA) ? ((600 - (stbsp__uint32)dp) % 3) : 0; 830 | if ((stbsp__uint32)dp >= l) { 831 | // handle xxxx000*000.0 832 | n = 0; 833 | for (;;) { 834 | if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { 835 | cs = 0; 836 | *s++ = stbsp__comma; 837 | } else { 838 | *s++ = sn[n]; 839 | ++n; 840 | if (n >= l) 841 | break; 842 | } 843 | } 844 | if (n < (stbsp__uint32)dp) { 845 | n = dp - n; 846 | if ((fl & STBSP__TRIPLET_COMMA) == 0) { 847 | while (n) { 848 | if ((((stbsp__uintptr)s) & 3) == 0) 849 | break; 850 | *s++ = '0'; 851 | --n; 852 | } 853 | while (n >= 4) { 854 | *(stbsp__uint32 *)s = 0x30303030; 855 | s += 4; 856 | n -= 4; 857 | } 858 | } 859 | while (n) { 860 | if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { 861 | cs = 0; 862 | *s++ = stbsp__comma; 863 | } else { 864 | *s++ = '0'; 865 | --n; 866 | } 867 | } 868 | } 869 | cs = (int)(s - (num + 64)) + (3 << 24); // cs is how many tens 870 | if (pr) { 871 | *s++ = stbsp__period; 872 | tz = pr; 873 | } 874 | } else { 875 | // handle xxxxx.xxxx000*000 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 >= (stbsp__uint32)dp) 885 | break; 886 | } 887 | } 888 | cs = (int)(s - (num + 64)) + (3 << 24); // cs is how many tens 889 | if (pr) 890 | *s++ = stbsp__period; 891 | if ((l - dp) > (stbsp__uint32)pr) 892 | l = pr + dp; 893 | while (n < l) { 894 | *s++ = sn[n]; 895 | ++n; 896 | } 897 | tz = pr - (l - dp); 898 | } 899 | } 900 | pr = 0; 901 | 902 | // handle k,m,g,t 903 | if (fl & STBSP__METRIC_SUFFIX) { 904 | char idx; 905 | idx = 1; 906 | if (fl & STBSP__METRIC_NOSPACE) 907 | idx = 0; 908 | tail[0] = idx; 909 | tail[1] = ' '; 910 | { 911 | if (fl >> 24) { // SI kilo is 'k', JEDEC and SI kibits are 'K'. 912 | if (fl & STBSP__METRIC_1024) 913 | tail[idx + 1] = "_KMGT"[fl >> 24]; 914 | else 915 | tail[idx + 1] = "_kMGT"[fl >> 24]; 916 | idx++; 917 | // If printing kibits and not in jedec, add the 'i'. 918 | if (fl & STBSP__METRIC_1024 && !(fl & STBSP__METRIC_JEDEC)) { 919 | tail[idx + 1] = 'i'; 920 | idx++; 921 | } 922 | tail[0] = idx; 923 | } 924 | } 925 | }; 926 | 927 | flt_lead: 928 | // get the length that we copied 929 | l = (stbsp__uint32)(s - (num + 64)); 930 | s = num + 64; 931 | goto scopy; 932 | #endif 933 | 934 | case 'B': // upper binary 935 | case 'b': // lower binary 936 | h = (f[0] == 'B') ? hexu : hex; 937 | lead[0] = 0; 938 | if (fl & STBSP__LEADING_0X) { 939 | lead[0] = 2; 940 | lead[1] = '0'; 941 | lead[2] = h[0xb]; 942 | } 943 | l = (8 << 4) | (1 << 8); 944 | goto radixnum; 945 | 946 | case 'o': // octal 947 | h = hexu; 948 | lead[0] = 0; 949 | if (fl & STBSP__LEADING_0X) { 950 | lead[0] = 1; 951 | lead[1] = '0'; 952 | } 953 | l = (3 << 4) | (3 << 8); 954 | goto radixnum; 955 | 956 | case 'p': // pointer 957 | fl |= (sizeof(void *) == 8) ? STBSP__INTMAX : 0; 958 | pr = sizeof(void *) * 2; 959 | fl &= ~STBSP__LEADINGZERO; // 'p' only prints the pointer with zeros 960 | // fall through - to X 961 | 962 | case 'X': // upper hex 963 | case 'x': // lower hex 964 | h = (f[0] == 'X') ? hexu : hex; 965 | l = (4 << 4) | (4 << 8); 966 | lead[0] = 0; 967 | if (fl & STBSP__LEADING_0X) { 968 | lead[0] = 2; 969 | lead[1] = '0'; 970 | lead[2] = h[16]; 971 | } 972 | radixnum: 973 | // get the number 974 | if (fl & STBSP__INTMAX) 975 | n64 = va_arg(va, stbsp__uint64); 976 | else 977 | n64 = va_arg(va, stbsp__uint32); 978 | 979 | s = num + STBSP__NUMSZ; 980 | dp = 0; 981 | // clear tail, and clear leading if value is zero 982 | tail[0] = 0; 983 | if (n64 == 0) { 984 | lead[0] = 0; 985 | if (pr == 0) { 986 | l = 0; 987 | cs = (((l >> 4) & 15)) << 24; 988 | goto scopy; 989 | } 990 | } 991 | // convert to string 992 | for (;;) { 993 | *--s = h[n64 & ((1 << (l >> 8)) - 1)]; 994 | n64 >>= (l >> 8); 995 | if (!((n64) || ((stbsp__int32)((num + STBSP__NUMSZ) - s) < pr))) 996 | break; 997 | if (fl & STBSP__TRIPLET_COMMA) { 998 | ++l; 999 | if ((l & 15) == ((l >> 4) & 15)) { 1000 | l &= ~15; 1001 | *--s = stbsp__comma; 1002 | } 1003 | } 1004 | }; 1005 | // get the tens and the comma pos 1006 | cs = (stbsp__uint32)((num + STBSP__NUMSZ) - s) + ((((l >> 4) & 15)) << 24); 1007 | // get the length that we copied 1008 | l = (stbsp__uint32)((num + STBSP__NUMSZ) - s); 1009 | // copy it 1010 | goto scopy; 1011 | 1012 | case 'u': // unsigned 1013 | case 'i': 1014 | case 'd': // integer 1015 | // get the integer and abs it 1016 | if (fl & STBSP__INTMAX) { 1017 | stbsp__int64 i64 = va_arg(va, stbsp__int64); 1018 | n64 = (stbsp__uint64)i64; 1019 | if ((f[0] != 'u') && (i64 < 0)) { 1020 | n64 = (stbsp__uint64)-i64; 1021 | fl |= STBSP__NEGATIVE; 1022 | } 1023 | } else { 1024 | stbsp__int32 i = va_arg(va, stbsp__int32); 1025 | n64 = (stbsp__uint32)i; 1026 | if ((f[0] != 'u') && (i < 0)) { 1027 | n64 = (stbsp__uint32)-i; 1028 | fl |= STBSP__NEGATIVE; 1029 | } 1030 | } 1031 | 1032 | #ifndef STB_SPRINTF_NOFLOAT 1033 | if (fl & STBSP__METRIC_SUFFIX) { 1034 | if (n64 < 1024) 1035 | pr = 0; 1036 | else if (pr == -1) 1037 | pr = 1; 1038 | fv = (double)(stbsp__int64)n64; 1039 | goto doafloat; 1040 | } 1041 | #endif 1042 | 1043 | // convert to string 1044 | s = num + STBSP__NUMSZ; 1045 | l = 0; 1046 | 1047 | for (;;) { 1048 | // do in 32-bit chunks (avoid lots of 64-bit divides even with constant denominators) 1049 | char *o = s - 8; 1050 | if (n64 >= 100000000) { 1051 | n = (stbsp__uint32)(n64 % 100000000); 1052 | n64 /= 100000000; 1053 | } else { 1054 | n = (stbsp__uint32)n64; 1055 | n64 = 0; 1056 | } 1057 | if ((fl & STBSP__TRIPLET_COMMA) == 0) { 1058 | do { 1059 | s -= 2; 1060 | *(stbsp__uint16 *)s = *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2]; 1061 | n /= 100; 1062 | } while (n); 1063 | } 1064 | while (n) { 1065 | if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) { 1066 | l = 0; 1067 | *--s = stbsp__comma; 1068 | --o; 1069 | } else { 1070 | *--s = (char)(n % 10) + '0'; 1071 | n /= 10; 1072 | } 1073 | } 1074 | if (n64 == 0) { 1075 | if ((s[0] == '0') && (s != (num + STBSP__NUMSZ))) 1076 | ++s; 1077 | break; 1078 | } 1079 | while (s != o) 1080 | if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) { 1081 | l = 0; 1082 | *--s = stbsp__comma; 1083 | --o; 1084 | } else { 1085 | *--s = '0'; 1086 | } 1087 | } 1088 | 1089 | tail[0] = 0; 1090 | stbsp__lead_sign(fl, lead); 1091 | 1092 | // get the length that we copied 1093 | l = (stbsp__uint32)((num + STBSP__NUMSZ) - s); 1094 | if (l == 0) { 1095 | *--s = '0'; 1096 | l = 1; 1097 | } 1098 | cs = l + (3 << 24); 1099 | if (pr < 0) 1100 | pr = 0; 1101 | 1102 | scopy: 1103 | // get fw=leading/trailing space, pr=leading zeros 1104 | if (pr < (stbsp__int32)l) 1105 | pr = l; 1106 | n = pr + lead[0] + tail[0] + tz; 1107 | if (fw < (stbsp__int32)n) 1108 | fw = n; 1109 | fw -= n; 1110 | pr -= l; 1111 | 1112 | // handle right justify and leading zeros 1113 | if ((fl & STBSP__LEFTJUST) == 0) { 1114 | if (fl & STBSP__LEADINGZERO) // if leading zeros, everything is in pr 1115 | { 1116 | pr = (fw > pr) ? fw : pr; 1117 | fw = 0; 1118 | } else { 1119 | fl &= ~STBSP__TRIPLET_COMMA; // if no leading zeros, then no commas 1120 | } 1121 | } 1122 | 1123 | // copy the spaces and/or zeros 1124 | if (fw + pr) { 1125 | stbsp__int32 i; 1126 | stbsp__uint32 c; 1127 | 1128 | // copy leading spaces (or when doing %8.4d stuff) 1129 | if ((fl & STBSP__LEFTJUST) == 0) 1130 | while (fw > 0) { 1131 | stbsp__cb_buf_clamp(i, fw); 1132 | fw -= i; 1133 | while (i) { 1134 | if ((((stbsp__uintptr)bf) & 3) == 0) 1135 | break; 1136 | *bf++ = ' '; 1137 | --i; 1138 | } 1139 | while (i >= 4) { 1140 | *(stbsp__uint32 *)bf = 0x20202020; 1141 | bf += 4; 1142 | i -= 4; 1143 | } 1144 | while (i) { 1145 | *bf++ = ' '; 1146 | --i; 1147 | } 1148 | stbsp__chk_cb_buf(1); 1149 | } 1150 | 1151 | // copy leader 1152 | sn = lead + 1; 1153 | while (lead[0]) { 1154 | stbsp__cb_buf_clamp(i, lead[0]); 1155 | lead[0] -= (char)i; 1156 | while (i) { 1157 | *bf++ = *sn++; 1158 | --i; 1159 | } 1160 | stbsp__chk_cb_buf(1); 1161 | } 1162 | 1163 | // copy leading zeros 1164 | c = cs >> 24; 1165 | cs &= 0xffffff; 1166 | cs = (fl & STBSP__TRIPLET_COMMA) ? ((stbsp__uint32)(c - ((pr + cs) % (c + 1)))) : 0; 1167 | while (pr > 0) { 1168 | stbsp__cb_buf_clamp(i, pr); 1169 | pr -= i; 1170 | if ((fl & STBSP__TRIPLET_COMMA) == 0) { 1171 | while (i) { 1172 | if ((((stbsp__uintptr)bf) & 3) == 0) 1173 | break; 1174 | *bf++ = '0'; 1175 | --i; 1176 | } 1177 | while (i >= 4) { 1178 | *(stbsp__uint32 *)bf = 0x30303030; 1179 | bf += 4; 1180 | i -= 4; 1181 | } 1182 | } 1183 | while (i) { 1184 | if ((fl & STBSP__TRIPLET_COMMA) && (cs++ == c)) { 1185 | cs = 0; 1186 | *bf++ = stbsp__comma; 1187 | } else 1188 | *bf++ = '0'; 1189 | --i; 1190 | } 1191 | stbsp__chk_cb_buf(1); 1192 | } 1193 | } 1194 | 1195 | // copy leader if there is still one 1196 | sn = lead + 1; 1197 | while (lead[0]) { 1198 | stbsp__int32 i; 1199 | stbsp__cb_buf_clamp(i, lead[0]); 1200 | lead[0] -= (char)i; 1201 | while (i) { 1202 | *bf++ = *sn++; 1203 | --i; 1204 | } 1205 | stbsp__chk_cb_buf(1); 1206 | } 1207 | 1208 | // copy the string 1209 | n = l; 1210 | while (n) { 1211 | stbsp__int32 i; 1212 | stbsp__cb_buf_clamp(i, n); 1213 | n -= i; 1214 | STBSP__UNALIGNED(while (i >= 4) { 1215 | *(stbsp__uint32 *)bf = *(stbsp__uint32 *)s; 1216 | bf += 4; 1217 | s += 4; 1218 | i -= 4; 1219 | }) 1220 | while (i) { 1221 | *bf++ = *s++; 1222 | --i; 1223 | } 1224 | stbsp__chk_cb_buf(1); 1225 | } 1226 | 1227 | // copy trailing zeros 1228 | while (tz) { 1229 | stbsp__int32 i; 1230 | stbsp__cb_buf_clamp(i, tz); 1231 | tz -= i; 1232 | while (i) { 1233 | if ((((stbsp__uintptr)bf) & 3) == 0) 1234 | break; 1235 | *bf++ = '0'; 1236 | --i; 1237 | } 1238 | while (i >= 4) { 1239 | *(stbsp__uint32 *)bf = 0x30303030; 1240 | bf += 4; 1241 | i -= 4; 1242 | } 1243 | while (i) { 1244 | *bf++ = '0'; 1245 | --i; 1246 | } 1247 | stbsp__chk_cb_buf(1); 1248 | } 1249 | 1250 | // copy tail if there is one 1251 | sn = tail + 1; 1252 | while (tail[0]) { 1253 | stbsp__int32 i; 1254 | stbsp__cb_buf_clamp(i, tail[0]); 1255 | tail[0] -= (char)i; 1256 | while (i) { 1257 | *bf++ = *sn++; 1258 | --i; 1259 | } 1260 | stbsp__chk_cb_buf(1); 1261 | } 1262 | 1263 | // handle the left justify 1264 | if (fl & STBSP__LEFTJUST) 1265 | if (fw > 0) { 1266 | while (fw) { 1267 | stbsp__int32 i; 1268 | stbsp__cb_buf_clamp(i, fw); 1269 | fw -= i; 1270 | while (i) { 1271 | if ((((stbsp__uintptr)bf) & 3) == 0) 1272 | break; 1273 | *bf++ = ' '; 1274 | --i; 1275 | } 1276 | while (i >= 4) { 1277 | *(stbsp__uint32 *)bf = 0x20202020; 1278 | bf += 4; 1279 | i -= 4; 1280 | } 1281 | while (i--) 1282 | *bf++ = ' '; 1283 | stbsp__chk_cb_buf(1); 1284 | } 1285 | } 1286 | break; 1287 | 1288 | default: // unknown, just copy code 1289 | s = num + STBSP__NUMSZ - 1; 1290 | *s = f[0]; 1291 | l = 1; 1292 | fw = fl = 0; 1293 | lead[0] = 0; 1294 | tail[0] = 0; 1295 | pr = 0; 1296 | dp = 0; 1297 | cs = 0; 1298 | goto scopy; 1299 | } 1300 | ++f; 1301 | } 1302 | endfmt: 1303 | 1304 | if (!callback) 1305 | *bf = 0; 1306 | else 1307 | stbsp__flush_cb(); 1308 | 1309 | done: 1310 | return tlen + (int)(bf - buf); 1311 | } 1312 | 1313 | // cleanup 1314 | #undef STBSP__LEFTJUST 1315 | #undef STBSP__LEADINGPLUS 1316 | #undef STBSP__LEADINGSPACE 1317 | #undef STBSP__LEADING_0X 1318 | #undef STBSP__LEADINGZERO 1319 | #undef STBSP__INTMAX 1320 | #undef STBSP__TRIPLET_COMMA 1321 | #undef STBSP__NEGATIVE 1322 | #undef STBSP__METRIC_SUFFIX 1323 | #undef STBSP__NUMSZ 1324 | #undef stbsp__chk_cb_bufL 1325 | #undef stbsp__chk_cb_buf 1326 | #undef stbsp__flush_cb 1327 | #undef stbsp__cb_buf_clamp 1328 | 1329 | // ============================================================================ 1330 | // wrapper functions 1331 | 1332 | STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...) 1333 | { 1334 | int result; 1335 | va_list va; 1336 | va_start(va, fmt); 1337 | result = STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va); 1338 | va_end(va); 1339 | return result; 1340 | } 1341 | 1342 | typedef struct stbsp__context { 1343 | char *buf; 1344 | int count; 1345 | char tmp[STB_SPRINTF_MIN]; 1346 | } stbsp__context; 1347 | 1348 | static char *stbsp__clamp_callback(char *buf, void *user, int len) 1349 | { 1350 | stbsp__context *c = (stbsp__context *)user; 1351 | 1352 | if (len > c->count) 1353 | len = c->count; 1354 | 1355 | if (len) { 1356 | if (buf != c->buf) { 1357 | char *s, *d, *se; 1358 | d = c->buf; 1359 | s = buf; 1360 | se = buf + len; 1361 | do { 1362 | *d++ = *s++; 1363 | } while (s < se); 1364 | } 1365 | c->buf += len; 1366 | c->count -= len; 1367 | } 1368 | 1369 | if (c->count <= 0) 1370 | return 0; 1371 | return (c->count >= STB_SPRINTF_MIN) ? c->buf : c->tmp; // go direct into buffer if you can 1372 | } 1373 | 1374 | static char * stbsp__count_clamp_callback( char * buf, void * user, int len ) 1375 | { 1376 | stbsp__context * c = (stbsp__context*)user; 1377 | 1378 | c->count += len; 1379 | return c->tmp; // go direct into buffer if you can 1380 | } 1381 | 1382 | STBSP__PUBLICDEF int STB_SPRINTF_DECORATE( vsnprintf )( char * buf, int count, char const * fmt, va_list va ) 1383 | { 1384 | stbsp__context c; 1385 | int l; 1386 | 1387 | if ( (count == 0) && !buf ) 1388 | { 1389 | c.count = 0; 1390 | 1391 | STB_SPRINTF_DECORATE( vsprintfcb )( stbsp__count_clamp_callback, &c, c.tmp, fmt, va ); 1392 | l = c.count; 1393 | } 1394 | else 1395 | { 1396 | if ( count == 0 ) 1397 | return 0; 1398 | 1399 | c.buf = buf; 1400 | c.count = count; 1401 | 1402 | STB_SPRINTF_DECORATE( vsprintfcb )( stbsp__clamp_callback, &c, stbsp__clamp_callback(0,&c,0), fmt, va ); 1403 | 1404 | // zero-terminate 1405 | l = (int)( c.buf - buf ); 1406 | if ( l >= count ) // should never be greater, only equal (or less) than count 1407 | l = count - 1; 1408 | buf[l] = 0; 1409 | } 1410 | 1411 | return l; 1412 | } 1413 | 1414 | STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...) 1415 | { 1416 | int result; 1417 | va_list va; 1418 | va_start(va, fmt); 1419 | 1420 | result = STB_SPRINTF_DECORATE(vsnprintf)(buf, count, fmt, va); 1421 | va_end(va); 1422 | 1423 | return result; 1424 | } 1425 | 1426 | STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va) 1427 | { 1428 | return STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va); 1429 | } 1430 | 1431 | // ======================================================================= 1432 | // low level float utility functions 1433 | 1434 | #ifndef STB_SPRINTF_NOFLOAT 1435 | 1436 | // copies d to bits w/ strict aliasing (this compiles to nothing on /Ox) 1437 | #define STBSP__COPYFP(dest, src) \ 1438 | { \ 1439 | int cn; \ 1440 | for (cn = 0; cn < 8; cn++) \ 1441 | ((char *)&dest)[cn] = ((char *)&src)[cn]; \ 1442 | } 1443 | 1444 | // get float info 1445 | static stbsp__int32 stbsp__real_to_parts(stbsp__int64 *bits, stbsp__int32 *expo, double value) 1446 | { 1447 | double d; 1448 | stbsp__int64 b = 0; 1449 | 1450 | // load value and round at the frac_digits 1451 | d = value; 1452 | 1453 | STBSP__COPYFP(b, d); 1454 | 1455 | *bits = b & ((((stbsp__uint64)1) << 52) - 1); 1456 | *expo = (stbsp__int32)(((b >> 52) & 2047) - 1023); 1457 | 1458 | return (stbsp__int32)((stbsp__uint64) b >> 63); 1459 | } 1460 | 1461 | static double const stbsp__bot[23] = { 1462 | 1e+000, 1e+001, 1e+002, 1e+003, 1e+004, 1e+005, 1e+006, 1e+007, 1e+008, 1e+009, 1e+010, 1e+011, 1463 | 1e+012, 1e+013, 1e+014, 1e+015, 1e+016, 1e+017, 1e+018, 1e+019, 1e+020, 1e+021, 1e+022 1464 | }; 1465 | static double const stbsp__negbot[22] = { 1466 | 1e-001, 1e-002, 1e-003, 1e-004, 1e-005, 1e-006, 1e-007, 1e-008, 1e-009, 1e-010, 1e-011, 1467 | 1e-012, 1e-013, 1e-014, 1e-015, 1e-016, 1e-017, 1e-018, 1e-019, 1e-020, 1e-021, 1e-022 1468 | }; 1469 | static double const stbsp__negboterr[22] = { 1470 | -5.551115123125783e-018, -2.0816681711721684e-019, -2.0816681711721686e-020, -4.7921736023859299e-021, -8.1803053914031305e-022, 4.5251888174113741e-023, 1471 | 4.5251888174113739e-024, -2.0922560830128471e-025, -6.2281591457779853e-026, -3.6432197315497743e-027, 6.0503030718060191e-028, 2.0113352370744385e-029, 1472 | -3.0373745563400371e-030, 1.1806906454401013e-032, -7.7705399876661076e-032, 2.0902213275965398e-033, -7.1542424054621921e-034, -7.1542424054621926e-035, 1473 | 2.4754073164739869e-036, 5.4846728545790429e-037, 9.2462547772103625e-038, -4.8596774326570872e-039 1474 | }; 1475 | static double const stbsp__top[13] = { 1476 | 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 1477 | }; 1478 | static double const stbsp__negtop[13] = { 1479 | 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 1480 | }; 1481 | static double const stbsp__toperr[13] = { 1482 | 8388608, 1483 | 6.8601809640529717e+028, 1484 | -7.253143638152921e+052, 1485 | -4.3377296974619174e+075, 1486 | -1.5559416129466825e+098, 1487 | -3.2841562489204913e+121, 1488 | -3.7745893248228135e+144, 1489 | -1.7356668416969134e+167, 1490 | -3.8893577551088374e+190, 1491 | -9.9566444326005119e+213, 1492 | 6.3641293062232429e+236, 1493 | -5.2069140800249813e+259, 1494 | -5.2504760255204387e+282 1495 | }; 1496 | static double const stbsp__negtoperr[13] = { 1497 | 3.9565301985100693e-040, -2.299904345391321e-063, 3.6506201437945798e-086, 1.1875228833981544e-109, 1498 | -5.0644902316928607e-132, -6.7156837247865426e-155, -2.812077463003139e-178, -5.7778912386589953e-201, 1499 | 7.4997100559334532e-224, -4.6439668915134491e-247, -6.3691100762962136e-270, -9.436808465446358e-293, 1500 | 8.0970921678014997e-317 1501 | }; 1502 | 1503 | #if defined(_MSC_VER) && (_MSC_VER <= 1200) 1504 | static stbsp__uint64 const stbsp__powten[20] = { 1505 | 1, 1506 | 10, 1507 | 100, 1508 | 1000, 1509 | 10000, 1510 | 100000, 1511 | 1000000, 1512 | 10000000, 1513 | 100000000, 1514 | 1000000000, 1515 | 10000000000, 1516 | 100000000000, 1517 | 1000000000000, 1518 | 10000000000000, 1519 | 100000000000000, 1520 | 1000000000000000, 1521 | 10000000000000000, 1522 | 100000000000000000, 1523 | 1000000000000000000, 1524 | 10000000000000000000U 1525 | }; 1526 | #define stbsp__tento19th ((stbsp__uint64)1000000000000000000) 1527 | #else 1528 | static stbsp__uint64 const stbsp__powten[20] = { 1529 | 1, 1530 | 10, 1531 | 100, 1532 | 1000, 1533 | 10000, 1534 | 100000, 1535 | 1000000, 1536 | 10000000, 1537 | 100000000, 1538 | 1000000000, 1539 | 10000000000ULL, 1540 | 100000000000ULL, 1541 | 1000000000000ULL, 1542 | 10000000000000ULL, 1543 | 100000000000000ULL, 1544 | 1000000000000000ULL, 1545 | 10000000000000000ULL, 1546 | 100000000000000000ULL, 1547 | 1000000000000000000ULL, 1548 | 10000000000000000000ULL 1549 | }; 1550 | #define stbsp__tento19th (1000000000000000000ULL) 1551 | #endif 1552 | 1553 | #define stbsp__ddmulthi(oh, ol, xh, yh) \ 1554 | { \ 1555 | double ahi = 0, alo, bhi = 0, blo; \ 1556 | stbsp__int64 bt; \ 1557 | oh = xh * yh; \ 1558 | STBSP__COPYFP(bt, xh); \ 1559 | bt &= ((~(stbsp__uint64)0) << 27); \ 1560 | STBSP__COPYFP(ahi, bt); \ 1561 | alo = xh - ahi; \ 1562 | STBSP__COPYFP(bt, yh); \ 1563 | bt &= ((~(stbsp__uint64)0) << 27); \ 1564 | STBSP__COPYFP(bhi, bt); \ 1565 | blo = yh - bhi; \ 1566 | ol = ((ahi * bhi - oh) + ahi * blo + alo * bhi) + alo * blo; \ 1567 | } 1568 | 1569 | #define stbsp__ddtoS64(ob, xh, xl) \ 1570 | { \ 1571 | double ahi = 0, alo, vh, t; \ 1572 | ob = (stbsp__int64)ph; \ 1573 | vh = (double)ob; \ 1574 | ahi = (xh - vh); \ 1575 | t = (ahi - xh); \ 1576 | alo = (xh - (ahi - t)) - (vh + t); \ 1577 | ob += (stbsp__int64)(ahi + alo + xl); \ 1578 | } 1579 | 1580 | #define stbsp__ddrenorm(oh, ol) \ 1581 | { \ 1582 | double s; \ 1583 | s = oh + ol; \ 1584 | ol = ol - (s - oh); \ 1585 | oh = s; \ 1586 | } 1587 | 1588 | #define stbsp__ddmultlo(oh, ol, xh, xl, yh, yl) ol = ol + (xh * yl + xl * yh); 1589 | 1590 | #define stbsp__ddmultlos(oh, ol, xh, yl) ol = ol + (xh * yl); 1591 | 1592 | static void stbsp__raise_to_power10(double *ohi, double *olo, double d, stbsp__int32 power) // power can be -323 to +350 1593 | { 1594 | double ph, pl; 1595 | if ((power >= 0) && (power <= 22)) { 1596 | stbsp__ddmulthi(ph, pl, d, stbsp__bot[power]); 1597 | } else { 1598 | stbsp__int32 e, et, eb; 1599 | double p2h, p2l; 1600 | 1601 | e = power; 1602 | if (power < 0) 1603 | e = -e; 1604 | et = (e * 0x2c9) >> 14; /* %23 */ 1605 | if (et > 13) 1606 | et = 13; 1607 | eb = e - (et * 23); 1608 | 1609 | ph = d; 1610 | pl = 0.0; 1611 | if (power < 0) { 1612 | if (eb) { 1613 | --eb; 1614 | stbsp__ddmulthi(ph, pl, d, stbsp__negbot[eb]); 1615 | stbsp__ddmultlos(ph, pl, d, stbsp__negboterr[eb]); 1616 | } 1617 | if (et) { 1618 | stbsp__ddrenorm(ph, pl); 1619 | --et; 1620 | stbsp__ddmulthi(p2h, p2l, ph, stbsp__negtop[et]); 1621 | stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__negtop[et], stbsp__negtoperr[et]); 1622 | ph = p2h; 1623 | pl = p2l; 1624 | } 1625 | } else { 1626 | if (eb) { 1627 | e = eb; 1628 | if (eb > 22) 1629 | eb = 22; 1630 | e -= eb; 1631 | stbsp__ddmulthi(ph, pl, d, stbsp__bot[eb]); 1632 | if (e) { 1633 | stbsp__ddrenorm(ph, pl); 1634 | stbsp__ddmulthi(p2h, p2l, ph, stbsp__bot[e]); 1635 | stbsp__ddmultlos(p2h, p2l, stbsp__bot[e], pl); 1636 | ph = p2h; 1637 | pl = p2l; 1638 | } 1639 | } 1640 | if (et) { 1641 | stbsp__ddrenorm(ph, pl); 1642 | --et; 1643 | stbsp__ddmulthi(p2h, p2l, ph, stbsp__top[et]); 1644 | stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__top[et], stbsp__toperr[et]); 1645 | ph = p2h; 1646 | pl = p2l; 1647 | } 1648 | } 1649 | } 1650 | stbsp__ddrenorm(ph, pl); 1651 | *ohi = ph; 1652 | *olo = pl; 1653 | } 1654 | 1655 | // given a float value, returns the significant bits in bits, and the position of the 1656 | // decimal point in decimal_pos. +/-INF and NAN are specified by special values 1657 | // returned in the decimal_pos parameter. 1658 | // frac_digits is absolute normally, but if you want from first significant digits (got %g and %e), or in 0x80000000 1659 | 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) 1660 | { 1661 | double d; 1662 | stbsp__int64 bits = 0; 1663 | stbsp__int32 expo, e, ng, tens; 1664 | 1665 | d = value; 1666 | STBSP__COPYFP(bits, d); 1667 | expo = (stbsp__int32)((bits >> 52) & 2047); 1668 | ng = (stbsp__int32)((stbsp__uint64) bits >> 63); 1669 | if (ng) 1670 | d = -d; 1671 | 1672 | if (expo == 2047) // is nan or inf? 1673 | { 1674 | *start = (bits & ((((stbsp__uint64)1) << 52) - 1)) ? "NaN" : "Inf"; 1675 | *decimal_pos = STBSP__SPECIAL; 1676 | *len = 3; 1677 | return ng; 1678 | } 1679 | 1680 | if (expo == 0) // is zero or denormal 1681 | { 1682 | if ((bits << 1) == 0) // do zero 1683 | { 1684 | *decimal_pos = 1; 1685 | *start = out; 1686 | out[0] = '0'; 1687 | *len = 1; 1688 | return ng; 1689 | } 1690 | // find the right expo for denormals 1691 | { 1692 | stbsp__int64 v = ((stbsp__uint64)1) << 51; 1693 | while ((bits & v) == 0) { 1694 | --expo; 1695 | v >>= 1; 1696 | } 1697 | } 1698 | } 1699 | 1700 | // find the decimal exponent as well as the decimal bits of the value 1701 | { 1702 | double ph, pl; 1703 | 1704 | // log10 estimate - very specifically tweaked to hit or undershoot by no more than 1 of log10 of all expos 1..2046 1705 | tens = expo - 1023; 1706 | tens = (tens < 0) ? ((tens * 617) / 2048) : (((tens * 1233) / 4096) + 1); 1707 | 1708 | // move the significant bits into position and stick them into an int 1709 | stbsp__raise_to_power10(&ph, &pl, d, 18 - tens); 1710 | 1711 | // get full as much precision from double-double as possible 1712 | stbsp__ddtoS64(bits, ph, pl); 1713 | 1714 | // check if we undershot 1715 | if (((stbsp__uint64)bits) >= stbsp__tento19th) 1716 | ++tens; 1717 | } 1718 | 1719 | // now do the rounding in integer land 1720 | frac_digits = (frac_digits & 0x80000000) ? ((frac_digits & 0x7ffffff) + 1) : (tens + frac_digits); 1721 | if ((frac_digits < 24)) { 1722 | stbsp__uint32 dg = 1; 1723 | if ((stbsp__uint64)bits >= stbsp__powten[9]) 1724 | dg = 10; 1725 | while ((stbsp__uint64)bits >= stbsp__powten[dg]) { 1726 | ++dg; 1727 | if (dg == 20) 1728 | goto noround; 1729 | } 1730 | if (frac_digits < dg) { 1731 | stbsp__uint64 r; 1732 | // add 0.5 at the right position and round 1733 | e = dg - frac_digits; 1734 | if ((stbsp__uint32)e >= 24) 1735 | goto noround; 1736 | r = stbsp__powten[e]; 1737 | bits = bits + (r / 2); 1738 | if ((stbsp__uint64)bits >= stbsp__powten[dg]) 1739 | ++tens; 1740 | bits /= r; 1741 | } 1742 | noround:; 1743 | } 1744 | 1745 | // kill long trailing runs of zeros 1746 | if (bits) { 1747 | stbsp__uint32 n; 1748 | for (;;) { 1749 | if (bits <= 0xffffffff) 1750 | break; 1751 | if (bits % 1000) 1752 | goto donez; 1753 | bits /= 1000; 1754 | } 1755 | n = (stbsp__uint32)bits; 1756 | while ((n % 1000) == 0) 1757 | n /= 1000; 1758 | bits = n; 1759 | donez:; 1760 | } 1761 | 1762 | // convert to string 1763 | out += 64; 1764 | e = 0; 1765 | for (;;) { 1766 | stbsp__uint32 n; 1767 | char *o = out - 8; 1768 | // do the conversion in chunks of U32s (avoid most 64-bit divides, worth it, constant denomiators be damned) 1769 | if (bits >= 100000000) { 1770 | n = (stbsp__uint32)(bits % 100000000); 1771 | bits /= 100000000; 1772 | } else { 1773 | n = (stbsp__uint32)bits; 1774 | bits = 0; 1775 | } 1776 | while (n) { 1777 | out -= 2; 1778 | *(stbsp__uint16 *)out = *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2]; 1779 | n /= 100; 1780 | e += 2; 1781 | } 1782 | if (bits == 0) { 1783 | if ((e) && (out[0] == '0')) { 1784 | ++out; 1785 | --e; 1786 | } 1787 | break; 1788 | } 1789 | while (out != o) { 1790 | *--out = '0'; 1791 | ++e; 1792 | } 1793 | } 1794 | 1795 | *decimal_pos = tens; 1796 | *start = out; 1797 | *len = e; 1798 | return ng; 1799 | } 1800 | 1801 | #undef stbsp__ddmulthi 1802 | #undef stbsp__ddrenorm 1803 | #undef stbsp__ddmultlo 1804 | #undef stbsp__ddmultlos 1805 | #undef STBSP__SPECIAL 1806 | #undef STBSP__COPYFP 1807 | 1808 | #endif // STB_SPRINTF_NOFLOAT 1809 | 1810 | // clean up 1811 | #undef stbsp__uint16 1812 | #undef stbsp__uint32 1813 | #undef stbsp__int32 1814 | #undef stbsp__uint64 1815 | #undef stbsp__int64 1816 | #undef STBSP__UNALIGNED 1817 | 1818 | #endif // STB_SPRINTF_IMPLEMENTATION 1819 | 1820 | /* 1821 | ------------------------------------------------------------------------------ 1822 | This software is available under 2 licenses -- choose whichever you prefer. 1823 | ------------------------------------------------------------------------------ 1824 | ALTERNATIVE A - MIT License 1825 | Copyright (c) 2017 Sean Barrett 1826 | Permission is hereby granted, free of charge, to any person obtaining a copy of 1827 | this software and associated documentation files (the "Software"), to deal in 1828 | the Software without restriction, including without limitation the rights to 1829 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 1830 | of the Software, and to permit persons to whom the Software is furnished to do 1831 | so, subject to the following conditions: 1832 | The above copyright notice and this permission notice shall be included in all 1833 | copies or substantial portions of the Software. 1834 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1835 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1836 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 1837 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 1838 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 1839 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 1840 | SOFTWARE. 1841 | ------------------------------------------------------------------------------ 1842 | ALTERNATIVE B - Public Domain (www.unlicense.org) 1843 | This is free and unencumbered software released into the public domain. 1844 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 1845 | software, either in source code form or as a compiled binary, for any purpose, 1846 | commercial or non-commercial, and by any means. 1847 | In jurisdictions that recognize copyright laws, the author or authors of this 1848 | software dedicate any and all copyright interest in the software to the public 1849 | domain. We make this dedication for the benefit of the public at large and to 1850 | the detriment of our heirs and successors. We intend this dedication to be an 1851 | overt act of relinquishment in perpetuity of all present and future rights to 1852 | this software under copyright law. 1853 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1854 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1855 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 1856 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 1857 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 1858 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 1859 | ------------------------------------------------------------------------------ 1860 | */ 1861 | --------------------------------------------------------------------------------