├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── LICENCE.txt ├── README.md ├── assets ├── clownmdemu.rc ├── com.clownacy.clownmdemu.appdata.xml ├── com.clownacy.clownmdemu.desktop ├── icon │ ├── generate.sh │ ├── icon-128.png │ ├── icon-16.png │ ├── icon-20.png │ ├── icon-24.png │ ├── icon-256.png │ ├── icon-32.png │ ├── icon-40.png │ ├── icon-48.png │ ├── icon-512.png │ ├── icon-64.png │ ├── icon-master.png │ └── icon.ico ├── logo.png ├── logo.xcf ├── screenshot-debug.png └── screenshot-minimal.png ├── audio-device.cpp ├── audio-device.h ├── audio-output.cpp ├── audio-output.h ├── cd-reader.cpp ├── cd-reader.h ├── debug-log.cpp ├── debug-log.h ├── emulator-instance.cpp ├── emulator-instance.h ├── file-utilities.cpp ├── file-utilities.h ├── frontend.cpp ├── frontend.h ├── licences ├── clownmdemu.h ├── clownmdemu.txt ├── converter │ ├── .gitignore │ ├── CMakeLists.txt │ └── main.c ├── dear-imgui.h ├── dear-imgui.txt ├── emscripten-browser-file.h ├── emscripten-browser-file.txt ├── freetype-bdf.h ├── freetype-bdf.txt ├── freetype-ft-hb.h ├── freetype-ft-hb.txt ├── freetype-fthash.h ├── freetype-fthash.txt ├── freetype-pcf.h ├── freetype-pcf.txt ├── freetype.h ├── freetype.txt ├── inconsolata.h ├── inconsolata.txt ├── noto-sans.h └── noto-sans.txt ├── main.cpp ├── raii-wrapper.h ├── scripts └── AppImage │ ├── .gitignore │ └── makeAppImage.sh ├── sdl-wrapper.h ├── text-encoding.cpp ├── text-encoding.h └── windows ├── common ├── inconsolata-regular.h ├── noto-sans-regular.h ├── winapi.cpp ├── winapi.h ├── window-popup.cpp ├── window-popup.h ├── window-with-dear-imgui.cpp ├── window-with-dear-imgui.h ├── window-with-framebuffer.cpp ├── window-with-framebuffer.h ├── window.cpp └── window.h ├── debug-fm.cpp ├── debug-fm.h ├── debug-frontend.cpp ├── debug-frontend.h ├── debug-log-viewer.cpp ├── debug-log-viewer.h ├── debug-m68k.cpp ├── debug-m68k.h ├── debug-memory.cpp ├── debug-memory.h ├── debug-pcm.cpp ├── debug-pcm.h ├── debug-psg.cpp ├── debug-psg.h ├── debug-vdp.cpp ├── debug-vdp.h ├── debug-z80.cpp ├── debug-z80.h ├── disassembler.cpp └── disassembler.h /.gitignore: -------------------------------------------------------------------------------- 1 | # CMake build directory 2 | /build 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "frontend/libraries/SDL"] 2 | path = libraries/SDL 3 | url = https://github.com/libsdl-org/SDL.git 4 | [submodule "frontend/freetype"] 5 | path = libraries/freetype 6 | url = https://gitlab.freedesktop.org/freetype/freetype.git 7 | [submodule "frontend/libraries/imgui"] 8 | path = libraries/imgui 9 | url = https://github.com/Clownacy/imgui.git 10 | branch = font-nonlinear 11 | [submodule "clownmdemu-frontend-common"] 12 | path = common 13 | url = https://github.com/Clownacy/clownmdemu-frontend-common 14 | [submodule "libraries/emscripten-browser-file"] 15 | path = libraries/emscripten-browser-file 16 | url = https://github.com/Armchair-Software/emscripten-browser-file 17 | [submodule "libraries/fmt"] 18 | path = libraries/fmt 19 | url = https://github.com/fmtlib/fmt 20 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16.3) 2 | 3 | option(CLOWNMDEMU_FRONTEND_FREETYPE "Use FreeType font rasteriser instead of stb_truetype for Dear ImGui" OFF) 4 | 5 | project(clownmdemu-frontend LANGUAGES C CXX) 6 | 7 | ########### 8 | # Sources # 9 | ########### 10 | 11 | add_executable(clownmdemu WIN32 12 | "main.cpp" 13 | "audio-device.cpp" 14 | "audio-device.h" 15 | "audio-output.cpp" 16 | "audio-output.h" 17 | "cd-reader.cpp" 18 | "cd-reader.h" 19 | "debug-log.cpp" 20 | "debug-log.h" 21 | "emulator-instance.cpp" 22 | "emulator-instance.h" 23 | "file-utilities.cpp" 24 | "file-utilities.h" 25 | "frontend.cpp" 26 | "frontend.h" 27 | "raii-wrapper.h" 28 | "sdl-wrapper.h" 29 | "text-encoding.cpp" 30 | "text-encoding.h" 31 | "common/cd-reader.c" 32 | "common/cd-reader.h" 33 | "common/mixer.h" 34 | "libraries/imgui/imconfig.h" 35 | "libraries/imgui/imgui.cpp" 36 | "libraries/imgui/imgui.h" 37 | "libraries/imgui/imgui_demo.cpp" 38 | "libraries/imgui/imgui_draw.cpp" 39 | "libraries/imgui/imgui_internal.h" 40 | "libraries/imgui/imgui_tables.cpp" 41 | "libraries/imgui/imgui_widgets.cpp" 42 | "libraries/imgui/imstb_rectpack.h" 43 | "libraries/imgui/imstb_textedit.h" 44 | "libraries/imgui/backends/imgui_impl_sdl3.cpp" 45 | "libraries/imgui/backends/imgui_impl_sdl3.h" 46 | "libraries/imgui/backends/imgui_impl_sdlrenderer3.cpp" 47 | "libraries/imgui/backends/imgui_impl_sdlrenderer3.h" 48 | "windows/debug-m68k.cpp" 49 | "windows/debug-m68k.h" 50 | "windows/debug-memory.cpp" 51 | "windows/debug-memory.h" 52 | "windows/debug-fm.cpp" 53 | "windows/debug-fm.h" 54 | "windows/debug-frontend.cpp" 55 | "windows/debug-frontend.h" 56 | "windows/debug-log-viewer.cpp" 57 | "windows/debug-log-viewer.h" 58 | "windows/debug-pcm.cpp" 59 | "windows/debug-pcm.h" 60 | "windows/debug-psg.cpp" 61 | "windows/debug-psg.h" 62 | "windows/debug-vdp.cpp" 63 | "windows/debug-vdp.h" 64 | "windows/debug-z80.cpp" 65 | "windows/debug-z80.h" 66 | "windows/disassembler.cpp" 67 | "windows/disassembler.h" 68 | "windows/common/inconsolata-regular.h" 69 | "windows/common/noto-sans-regular.h" 70 | "windows/common/winapi.cpp" 71 | "windows/common/winapi.h" 72 | "windows/common/window.cpp" 73 | "windows/common/window.h" 74 | "windows/common/window-popup.cpp" 75 | "windows/common/window-popup.h" 76 | "windows/common/window-with-dear-imgui.cpp" 77 | "windows/common/window-with-dear-imgui.h" 78 | "windows/common/window-with-framebuffer.cpp" 79 | "windows/common/window-with-framebuffer.h" 80 | ) 81 | 82 | set_property(DIRECTORY . PROPERTY VS_STARTUP_PROJECT clownmdemu) 83 | 84 | if(WIN32) 85 | target_sources(clownmdemu PRIVATE "assets/clownmdemu.rc") 86 | endif() 87 | 88 | ############################## 89 | # Required language versions # 90 | ############################## 91 | 92 | # Only require C90 and C++20 93 | set_target_properties(clownmdemu PROPERTIES 94 | C_STANDARD 90 95 | C_STANDARD_REQUIRED NO 96 | C_EXTENSIONS OFF 97 | CXX_STANDARD 20 98 | CXX_STANDARD_REQUIRED YES 99 | CXX_EXTENSIONS OFF 100 | ) 101 | 102 | #################### 103 | # Emscripten hacks # 104 | #################### 105 | 106 | if(EMSCRIPTEN) 107 | # Add the file dialog library 108 | target_sources(clownmdemu PRIVATE "libraries/emscripten-browser-file/emscripten_browser_file.h") 109 | 110 | # Dependencies of the 'emscripten-browser-file' library 111 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -sEXPORTED_FUNCTIONS=[_main,_malloc,_free] -sEXPORTED_RUNTIME_METHODS=[ccall] -sALLOW_MEMORY_GROWTH=1") 112 | 113 | # Link the persistent file storage library 114 | target_link_libraries(clownmdemu "idbfs.js") 115 | endif() 116 | 117 | ################## 118 | # Handle options # 119 | ################## 120 | 121 | # Handle selecting either FreeType or stb_truetype font rendering 122 | if(CLOWNMDEMU_FRONTEND_FREETYPE) 123 | target_sources(clownmdemu PRIVATE 124 | "libraries/imgui/misc/freetype/imgui_freetype.cpp" 125 | "libraries/imgui/misc/freetype/imgui_freetype.h" 126 | ) 127 | 128 | target_compile_definitions(clownmdemu PRIVATE IMGUI_ENABLE_FREETYPE) 129 | else() 130 | target_sources(clownmdemu PRIVATE 131 | "libraries/imgui/imstb_truetype.h" 132 | ) 133 | endif() 134 | 135 | #################### 136 | # Dear ImGui stuff # 137 | #################### 138 | 139 | # Disable development stuff in non-development builds 140 | target_compile_definitions(clownmdemu PRIVATE $<$>,$>>:IMGUI_DISABLE_DEMO_WINDOWS IMGUI_DISABLE_DEBUG_TOOLS>) 141 | 142 | # Disable some deprecated junk in Dear ImGui 143 | target_compile_definitions(clownmdemu PRIVATE IMGUI_DISABLE_OBSOLETE_FUNCTIONS IMGUI_DISABLE_OBSOLETE_KEYIO) 144 | 145 | # 'ImTextureID' needs to be a pointer, not an integer 146 | target_compile_definitions(clownmdemu PRIVATE ImTextureID=void*) 147 | 148 | # Dear ImGui needs these directories in the include path 149 | target_include_directories(clownmdemu PRIVATE "libraries/imgui" "libraries/imgui/backends") 150 | 151 | ################## 152 | # Link libraries # 153 | ################## 154 | 155 | # Link clownmdemu-frontend-common 156 | add_subdirectory("common" EXCLUDE_FROM_ALL) 157 | target_link_libraries(clownmdemu clownmdemu-frontend-common) 158 | 159 | # Link SDL 160 | find_package(SDL3 3.1.3 CONFIG COMPONENTS SDL3) 161 | if(SDL3_FOUND) 162 | target_link_libraries(clownmdemu SDL3::SDL3) 163 | else() 164 | set(SDL_STATIC ON) 165 | set(SDL_SHARED OFF) 166 | add_subdirectory("libraries/SDL" EXCLUDE_FROM_ALL) 167 | target_link_libraries(clownmdemu SDL3::SDL3-static) 168 | endif() 169 | 170 | if(CLOWNMDEMU_FRONTEND_FREETYPE) 171 | # Link FreeType 172 | find_package(Freetype) 173 | if(FREETYPE_FOUND) 174 | target_link_libraries(clownmdemu Freetype::Freetype) 175 | else() 176 | add_subdirectory("libraries/freetype" EXCLUDE_FROM_ALL) 177 | target_link_libraries(clownmdemu freetype) 178 | endif() 179 | endif() 180 | 181 | # Link 68000 disassembler 182 | target_link_libraries(clownmdemu clown68000-disassembler) 183 | 184 | # Link fmt 185 | find_package(fmt 8.0.0) 186 | if(NOT fmt_FOUND) 187 | add_subdirectory("libraries/fmt" EXCLUDE_FROM_ALL) 188 | endif() 189 | target_link_libraries(clownmdemu fmt::fmt) 190 | 191 | ################ 192 | # Installation # 193 | ################ 194 | 195 | install(TARGETS clownmdemu DESTINATION "bin") 196 | install(FILES "assets/com.clownacy.clownmdemu.desktop" DESTINATION "share/applications") 197 | install(FILES "assets/com.clownacy.clownmdemu.appdata.xml" DESTINATION "share/metainfo") 198 | install(FILES "assets/icon/icon-512.png" DESTINATION "share/icons/hicolor/512x512/apps" RENAME "clownmdemu.png") 199 | install(FILES "assets/icon/icon-256.png" DESTINATION "share/icons/hicolor/256x256/apps" RENAME "clownmdemu.png") 200 | install(FILES "assets/icon/icon-128.png" DESTINATION "share/icons/hicolor/128x128/apps" RENAME "clownmdemu.png") 201 | install(FILES "assets/icon/icon-64.png" DESTINATION "share/icons/hicolor/64x64/apps" RENAME "clownmdemu.png") 202 | install(FILES "assets/icon/icon-48.png" DESTINATION "share/icons/hicolor/48x48/apps" RENAME "clownmdemu.png") 203 | install(FILES "assets/icon/icon-40.png" DESTINATION "share/icons/hicolor/40x40/apps" RENAME "clownmdemu.png") 204 | install(FILES "assets/icon/icon-32.png" DESTINATION "share/icons/hicolor/32x32/apps" RENAME "clownmdemu.png") 205 | install(FILES "assets/icon/icon-24.png" DESTINATION "share/icons/hicolor/24x24/apps" RENAME "clownmdemu.png") 206 | install(FILES "assets/icon/icon-20.png" DESTINATION "share/icons/hicolor/20x20/apps" RENAME "clownmdemu.png") 207 | install(FILES "assets/icon/icon-16.png" DESTINATION "share/icons/hicolor/16x16/apps" RENAME "clownmdemu.png") 208 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Logo](/assets/logo.png) 2 | 3 | # Try It Yourself 4 | 5 | You can try ClownMDEmu in your web browser at [clownmdemu.clownacy.com](http://clownmdemu.clownacy.com). 6 | 7 | # Overview 8 | 9 | This is ClownMDEmu, a Sega Mega Drive (a.k.a. Sega Genesis) emulator. 10 | 11 | Some standard features of the Mega Drive are currently unemulated (see 12 | `common/core/TODO.md` for more information). 13 | 14 | ![Minimal](/assets/screenshot-minimal.png) 15 | ![Debug](/assets/screenshot-debug.png) 16 | 17 | The repository contains ClownMDEmu's standalone frontend; it is written in 18 | C++20 and leverages the SDL3, Dear ImGui, and FreeType libraries. 19 | 20 | ## Controls 21 | 22 | The default control scheme is as follows: 23 | 24 | ### Keyboard 25 | 26 | | Input | Action | 27 | | --------- | ------ | 28 | | Up | Up | 29 | | Down | Down | 30 | | Left | Left | 31 | | Right | Right | 32 | | Z | A | 33 | | X | B | 34 | | C | C | 35 | | A | X | 36 | | S | Y | 37 | | D | Z | 38 | | Enter | Start | 39 | | Backspace | Mode | 40 | 41 | ### Controller 42 | 43 | | Input | Action | 44 | | ----- | ------------ | 45 | | Up | Up | 46 | | Down | Down | 47 | | Left | Left | 48 | | Right | Right | 49 | | X | A | 50 | | A | B | 51 | | B | C | 52 | | LB | X | 53 | | Y | Y | 54 | | RB | Z | 55 | | Start | Start | 56 | | Back | Mode | 57 | | LT | Rewind | 58 | | RT | Fast-forward | 59 | | RSB | [Toggle menu controls](http://www.dearimgui.org/controls_sheets/imgui%20controls%20v6%20-%20Xbox.png) | 60 | 61 | ### Hotkeys 62 | 63 | | Input | Action | 64 | | ----- | ----------------------------------------------- | 65 | | Pause | Pause | 66 | | Space | Fast-forward (unpaused), frame-advance (paused) | 67 | | R | Rewind | 68 | | Tab | Soft reset | 69 | | F1 | Toggle which Control Pad the keyboard controls | 70 | | F5 | Create save state | 71 | | F9 | Load save state | 72 | | F11 | Toggle fullscreen | 73 | 74 | # Licence 75 | 76 | ClownMDEmu is free software, licensed under the AGPLv3 (or any later version). 77 | See `LICENCE.txt` for more information. 78 | -------------------------------------------------------------------------------- /assets/clownmdemu.rc: -------------------------------------------------------------------------------- 1 | #include "winuser.rh" 2 | 3 | 100 ICON "icon/icon.ico" 4 | -------------------------------------------------------------------------------- /assets/com.clownacy.clownmdemu.appdata.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | com.clownacy.clownmdemu 4 | 5 | ClownMDEmu 6 | Sega Mega Drive Emulator 7 | 8 | FSFAP 9 | AGPL-3.0-or-later 10 | 11 | 12 | pointing 13 | keyboard 14 | gamepad 15 | 16 | 17 | 18 |

19 | An emulator for the Sega Mega Drive/Sega Genesis, featuring a suite of debugging tools to assist in the development of homebrew and ROM-hacks. 20 |

21 |
22 | 23 | com.clownacy.clownmdemu.desktop 24 | 25 | 26 | https://raw.githubusercontent.com/Clownacy/clownmdemu-frontend/refs/heads/master/assets/screenshot-debug.png 27 | 28 | 29 | https://raw.githubusercontent.com/Clownacy/clownmdemu-frontend/refs/heads/master/assets/screenshot-minimal.png 30 | 31 | 32 |
33 | -------------------------------------------------------------------------------- /assets/com.clownacy.clownmdemu.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Version=1.0 3 | Icon=clownmdemu 4 | Exec=clownmdemu 5 | Terminal=false 6 | Type=Application 7 | MimeType=application/x-genesis-rom 8 | Categories=Game;Emulator; 9 | Name=ClownMDEmu 10 | GenericName=Sega Mega Drive Emulator 11 | Keywords=emulator;genesis;mega drive;megadrive 12 | -------------------------------------------------------------------------------- /assets/icon/generate.sh: -------------------------------------------------------------------------------- 1 | magick icon-master.png -resize 512x512 icon-512.png 2 | magick icon-master.png -resize 256x256 icon-256.png 3 | magick icon-master.png -resize 128x128 icon-128.png 4 | magick icon-master.png -resize 64x64 icon-64.png 5 | magick icon-master.png -resize 48x48 icon-48.png 6 | magick icon-master.png -resize 40x40 icon-40.png 7 | #magick icon-master.png -resize 32x32 icon-32.png 8 | #magick icon-master.png -resize 24x24 icon-24.png 9 | #magick icon-master.png -resize 20x20 icon-20.png 10 | #magick icon-master.png -resize 16x16 icon-16.png 11 | magick icon-256.png icon-64.png icon-48.png icon-40.png icon-32.png icon-24.png icon-20.png icon-16.png icon.ico 12 | -------------------------------------------------------------------------------- /assets/icon/icon-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clownacy/clownmdemu-frontend/8f9f7a1d4ff2a29d217ce63c68365077f4e2db13/assets/icon/icon-128.png -------------------------------------------------------------------------------- /assets/icon/icon-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clownacy/clownmdemu-frontend/8f9f7a1d4ff2a29d217ce63c68365077f4e2db13/assets/icon/icon-16.png -------------------------------------------------------------------------------- /assets/icon/icon-20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clownacy/clownmdemu-frontend/8f9f7a1d4ff2a29d217ce63c68365077f4e2db13/assets/icon/icon-20.png -------------------------------------------------------------------------------- /assets/icon/icon-24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clownacy/clownmdemu-frontend/8f9f7a1d4ff2a29d217ce63c68365077f4e2db13/assets/icon/icon-24.png -------------------------------------------------------------------------------- /assets/icon/icon-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clownacy/clownmdemu-frontend/8f9f7a1d4ff2a29d217ce63c68365077f4e2db13/assets/icon/icon-256.png -------------------------------------------------------------------------------- /assets/icon/icon-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clownacy/clownmdemu-frontend/8f9f7a1d4ff2a29d217ce63c68365077f4e2db13/assets/icon/icon-32.png -------------------------------------------------------------------------------- /assets/icon/icon-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clownacy/clownmdemu-frontend/8f9f7a1d4ff2a29d217ce63c68365077f4e2db13/assets/icon/icon-40.png -------------------------------------------------------------------------------- /assets/icon/icon-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clownacy/clownmdemu-frontend/8f9f7a1d4ff2a29d217ce63c68365077f4e2db13/assets/icon/icon-48.png -------------------------------------------------------------------------------- /assets/icon/icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clownacy/clownmdemu-frontend/8f9f7a1d4ff2a29d217ce63c68365077f4e2db13/assets/icon/icon-512.png -------------------------------------------------------------------------------- /assets/icon/icon-64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clownacy/clownmdemu-frontend/8f9f7a1d4ff2a29d217ce63c68365077f4e2db13/assets/icon/icon-64.png -------------------------------------------------------------------------------- /assets/icon/icon-master.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clownacy/clownmdemu-frontend/8f9f7a1d4ff2a29d217ce63c68365077f4e2db13/assets/icon/icon-master.png -------------------------------------------------------------------------------- /assets/icon/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clownacy/clownmdemu-frontend/8f9f7a1d4ff2a29d217ce63c68365077f4e2db13/assets/icon/icon.ico -------------------------------------------------------------------------------- /assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clownacy/clownmdemu-frontend/8f9f7a1d4ff2a29d217ce63c68365077f4e2db13/assets/logo.png -------------------------------------------------------------------------------- /assets/logo.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clownacy/clownmdemu-frontend/8f9f7a1d4ff2a29d217ce63c68365077f4e2db13/assets/logo.xcf -------------------------------------------------------------------------------- /assets/screenshot-debug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clownacy/clownmdemu-frontend/8f9f7a1d4ff2a29d217ce63c68365077f4e2db13/assets/screenshot-debug.png -------------------------------------------------------------------------------- /assets/screenshot-minimal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clownacy/clownmdemu-frontend/8f9f7a1d4ff2a29d217ce63c68365077f4e2db13/assets/screenshot-minimal.png -------------------------------------------------------------------------------- /audio-device.cpp: -------------------------------------------------------------------------------- 1 | #include "audio-device.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "frontend.h" 7 | 8 | AudioDevice::AudioDevice(const cc_u8f channels, const cc_u32f sample_rate) 9 | : channels(channels) 10 | { 11 | SDL_AudioSpec specification; 12 | specification.freq = sample_rate; 13 | specification.format = SDL_AUDIO_S16; 14 | specification.channels = channels; 15 | 16 | // Create audio stream. 17 | stream = SDL::AudioStream(SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &specification, nullptr, nullptr)); 18 | 19 | if (stream == nullptr) 20 | { 21 | Frontend::debug_log.Log("SDL_GetAudioDeviceFormat failed with the following message - '{}'", SDL_GetError()); 22 | } 23 | else 24 | { 25 | // Unpause audio device, so that playback can begin. 26 | SDL_ResumeAudioDevice(SDL_GetAudioStreamDevice(stream)); 27 | } 28 | } 29 | 30 | void AudioDevice::QueueFrames(const cc_s16l *buffer, cc_u32f total_frames) 31 | { 32 | SDL_PutAudioStreamData(stream, buffer, total_frames * SIZE_OF_FRAME); 33 | } 34 | 35 | cc_u32f AudioDevice::GetTotalQueuedFrames() 36 | { 37 | return SDL_GetAudioStreamQueued(stream) / SIZE_OF_FRAME; 38 | } 39 | -------------------------------------------------------------------------------- /audio-device.h: -------------------------------------------------------------------------------- 1 | #ifndef AUDIO_DEVICE_H 2 | #define AUDIO_DEVICE_H 3 | 4 | #include 5 | 6 | #include "sdl-wrapper.h" 7 | 8 | #include "common/core/clowncommon/clowncommon.h" 9 | 10 | class AudioDevice 11 | { 12 | private: 13 | const cc_u8f channels; 14 | const std::size_t SIZE_OF_FRAME = channels * sizeof(cc_s16l); 15 | 16 | SDL::AudioStream stream; 17 | 18 | public: 19 | AudioDevice(cc_u8f channels, cc_u32f sample_rate); 20 | AudioDevice(const AudioDevice&) = delete; 21 | AudioDevice& operator=(const AudioDevice&) = delete; 22 | 23 | void QueueFrames(const cc_s16l *buffer, cc_u32f total_frames); 24 | cc_u32f GetTotalQueuedFrames(); 25 | void SetPlaybackSpeed(const cc_u32f numerator, const cc_u32f denominator) 26 | { 27 | SDL_SetAudioStreamFrequencyRatio(stream, static_cast(numerator) / denominator); 28 | } 29 | }; 30 | 31 | #endif /* AUDIO_DEVICE_H */ 32 | -------------------------------------------------------------------------------- /audio-output.cpp: -------------------------------------------------------------------------------- 1 | #include "audio-output.h" 2 | 3 | #include 4 | 5 | #include 6 | 7 | #define MIXER_IMPLEMENTATION 8 | #define MIXER_ASSERT SDL_assert 9 | #define MIXER_FREE SDL_free 10 | #define MIXER_CALLOC SDL_calloc 11 | #define MIXER_MEMMOVE SDL_memmove 12 | #define MIXER_MEMSET SDL_memset 13 | #include "common/mixer.h" 14 | 15 | static constexpr cc_u32f BufferSizeFromSampleRate(const cc_u32f sample_rate) 16 | { 17 | // We want a 10ms buffer (this value must be a power of two). 18 | // TODO: Use `std::bit_ceil` for this instead. 19 | cc_u32f samples = 1; 20 | while (samples < sample_rate / (1000 / 10)) 21 | samples *= 2; 22 | return samples; 23 | } 24 | 25 | AudioOutput::AudioOutput() 26 | : device(MIXER_CHANNEL_COUNT, MIXER_OUTPUT_SAMPLE_RATE) 27 | , total_buffer_frames(BufferSizeFromSampleRate(MIXER_OUTPUT_SAMPLE_RATE)) 28 | { 29 | Mixer_Initialise(&mixer, pal_mode); 30 | } 31 | 32 | AudioOutput::~AudioOutput() 33 | { 34 | Mixer_Deinitialise(&mixer); 35 | } 36 | 37 | void AudioOutput::MixerBegin() 38 | { 39 | Mixer_Begin(&mixer); 40 | } 41 | 42 | void AudioOutput::MixerEnd() 43 | { 44 | const cc_u32f target_frames = GetTargetFrames(); 45 | const cc_u32f queued_frames = device.GetTotalQueuedFrames(); 46 | 47 | rolling_average_buffer[rolling_average_buffer_index] = queued_frames; 48 | rolling_average_buffer_index = (rolling_average_buffer_index + 1) % rolling_average_buffer.size(); 49 | 50 | // If there is too much audio, just drop it because the dynamic rate control will be unable to handle it. 51 | if (queued_frames < target_frames * 2) 52 | { 53 | const auto callback = [](void* const user_data, const cc_s16l* const audio_samples, const std::size_t total_frames) 54 | { 55 | AudioOutput *audio_output = static_cast(user_data); 56 | 57 | audio_output->device.QueueFrames(audio_samples, total_frames); 58 | }; 59 | 60 | Mixer_End(&mixer, callback, this); 61 | 62 | // Hans-Kristian Arntzen's Dynamic Rate Control formula. 63 | // https://github.com/libretro/docs/blob/master/archive/ratecontrol.pdf 64 | const cc_u32f divisor = target_frames * 0x100; // The number here is the inverse of the formula's 'd' value. 65 | device.SetPlaybackSpeed(queued_frames - target_frames + divisor, divisor); 66 | } 67 | } 68 | 69 | cc_s16l* AudioOutput::MixerAllocateFMSamples(const std::size_t total_frames) 70 | { 71 | return Mixer_AllocateFMSamples(&mixer, total_frames); 72 | } 73 | 74 | cc_s16l* AudioOutput::MixerAllocatePSGSamples(const std::size_t total_frames) 75 | { 76 | return Mixer_AllocatePSGSamples(&mixer, total_frames); 77 | } 78 | 79 | cc_s16l* AudioOutput::MixerAllocatePCMSamples(const std::size_t total_frames) 80 | { 81 | return Mixer_AllocatePCMSamples(&mixer, total_frames); 82 | } 83 | 84 | cc_s16l* AudioOutput::MixerAllocateCDDASamples(const std::size_t total_frames) 85 | { 86 | return Mixer_AllocateCDDASamples(&mixer, total_frames); 87 | } 88 | 89 | cc_u32f AudioOutput::GetAverageFrames() const 90 | { 91 | return std::accumulate(rolling_average_buffer.cbegin(), rolling_average_buffer.cend(), cc_u32f(0)) / rolling_average_buffer.size(); 92 | } 93 | 94 | void AudioOutput::SetPALMode(const bool enabled) 95 | { 96 | pal_mode = enabled; 97 | Mixer_Deinitialise(&mixer); 98 | Mixer_Initialise(&mixer, pal_mode); 99 | } 100 | -------------------------------------------------------------------------------- /audio-output.h: -------------------------------------------------------------------------------- 1 | #ifndef AUDIO_OUTPUT_H 2 | #define AUDIO_OUTPUT_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "common/core/clowncommon/clowncommon.h" 9 | 10 | #include "common/mixer.h" 11 | 12 | #include "audio-device.h" 13 | 14 | class AudioOutput 15 | { 16 | private: 17 | AudioDevice device; 18 | cc_u32f total_buffer_frames; 19 | 20 | bool pal_mode = false; 21 | std::array rolling_average_buffer = {0}; 22 | cc_u8f rolling_average_buffer_index = 0; 23 | 24 | Mixer_State mixer; 25 | 26 | public: 27 | AudioOutput(); 28 | ~AudioOutput(); 29 | void MixerBegin(); 30 | void MixerEnd(); 31 | cc_s16l* MixerAllocateFMSamples(std::size_t total_frames); 32 | cc_s16l* MixerAllocatePSGSamples(std::size_t total_frames); 33 | cc_s16l* MixerAllocatePCMSamples(std::size_t total_frames); 34 | cc_s16l* MixerAllocateCDDASamples(std::size_t total_frames); 35 | cc_u32f GetAverageFrames() const; 36 | cc_u32f GetTargetFrames() const { return std::max(total_buffer_frames * 2, MIXER_OUTPUT_SAMPLE_RATE / 20); } // 50ms 37 | cc_u32f GetTotalBufferFrames() const { return total_buffer_frames; } 38 | cc_u32f GetSampleRate() const { return MIXER_OUTPUT_SAMPLE_RATE; } 39 | 40 | void SetPALMode(bool enabled); 41 | bool GetPALMode() const { return pal_mode; } 42 | }; 43 | 44 | #endif /* AUDIO_OUTPUT_H */ 45 | -------------------------------------------------------------------------------- /cd-reader.cpp: -------------------------------------------------------------------------------- 1 | #include "cd-reader.h" 2 | 3 | #include 4 | 5 | void* CDReader::FileOpenCallback(const char* const filename, const ClownCD_FileMode mode) 6 | { 7 | const char *mode_string; 8 | 9 | switch (mode) 10 | { 11 | case CLOWNCD_RB: 12 | mode_string = "rb"; 13 | break; 14 | 15 | case CLOWNCD_WB: 16 | mode_string = "wb"; 17 | break; 18 | 19 | default: 20 | return nullptr; 21 | } 22 | 23 | return SDL_IOFromFile(filename, mode_string); 24 | } 25 | 26 | int CDReader::FileCloseCallback(void* const stream) 27 | { 28 | return SDL_CloseIO(static_cast(stream)); 29 | } 30 | 31 | std::size_t CDReader::FileReadCallback(void* const buffer, const std::size_t size, const std::size_t count, void* const stream) 32 | { 33 | if (size == 0 || count == 0) 34 | return 0; 35 | 36 | return SDL_ReadIO(static_cast(stream), buffer, size * count) / size; 37 | } 38 | 39 | std::size_t CDReader::FileWriteCallback(const void* const buffer, const std::size_t size, const std::size_t count, void* const stream) 40 | { 41 | if (size == 0 || count == 0) 42 | return 0; 43 | 44 | return SDL_WriteIO(static_cast(stream), buffer, size * count) / size; 45 | } 46 | 47 | long CDReader::FileTellCallback(void* const stream) 48 | { 49 | const auto position = SDL_TellIO(static_cast(stream)); 50 | 51 | if (position < LONG_MIN || position > LONG_MAX) 52 | return -1L; 53 | 54 | return position; 55 | } 56 | 57 | int CDReader::FileSeekCallback(void* const stream, const long position, const ClownCD_FileOrigin origin) 58 | { 59 | SDL_IOWhence whence; 60 | 61 | switch (origin) 62 | { 63 | case CLOWNCD_SEEK_SET: 64 | whence = SDL_IO_SEEK_SET; 65 | break; 66 | 67 | case CLOWNCD_SEEK_CUR: 68 | whence = SDL_IO_SEEK_CUR; 69 | break; 70 | 71 | case CLOWNCD_SEEK_END: 72 | whence = SDL_IO_SEEK_END; 73 | break; 74 | 75 | default: 76 | return -1; 77 | } 78 | 79 | return SDL_SeekIO(static_cast(stream), position, whence) == -1 ? -1 : 0; 80 | } 81 | -------------------------------------------------------------------------------- /cd-reader.h: -------------------------------------------------------------------------------- 1 | #ifndef CD_READER_H 2 | #define CD_READER_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "sdl-wrapper.h" 9 | 10 | #include "common/cd-reader.h" 11 | 12 | class CDReader 13 | { 14 | public: 15 | enum class PlaybackSetting 16 | { 17 | ALL = CDREADER_PLAYBACK_ALL, 18 | ONCE = CDREADER_PLAYBACK_ONCE, 19 | REPEAT = CDREADER_PLAYBACK_REPEAT 20 | }; 21 | 22 | using SectorIndex = CDReader_SectorIndex; 23 | using TrackIndex = CDReader_TrackIndex; 24 | using FrameIndex = CDReader_FrameIndex; 25 | 26 | private: 27 | CDReader_State state; 28 | 29 | static void* FileOpenCallback(const char *filename, ClownCD_FileMode mode); 30 | static int FileCloseCallback(void *stream); 31 | static std::size_t FileReadCallback(void *buffer, std::size_t size, std::size_t count, void *stream); 32 | static std::size_t FileWriteCallback(const void *buffer, std::size_t size, std::size_t count, void *stream); 33 | static long FileTellCallback(void *stream); 34 | static int FileSeekCallback(void *stream, long position, ClownCD_FileOrigin origin); 35 | 36 | static constexpr ClownCD_FileCallbacks callbacks = {FileOpenCallback, FileCloseCallback, FileReadCallback, FileWriteCallback, FileTellCallback, FileSeekCallback}; 37 | 38 | public: 39 | using State = CDReader_StateBackup; 40 | 41 | static constexpr cc_u16f SECTOR_SIZE = CDREADER_SECTOR_SIZE; 42 | 43 | CDReader() 44 | { 45 | CDReader_Initialise(&state); 46 | } 47 | ~CDReader() 48 | { 49 | CDReader_Deinitialise(&state); 50 | } 51 | CDReader(const CDReader &other) = delete; 52 | CDReader(CDReader &&other) = delete; 53 | CDReader& operator=(const CDReader &other) = delete; 54 | CDReader& operator=(CDReader &&other) = delete; 55 | void Open(void* const stream, const std::filesystem::path &path) 56 | { 57 | CDReader_Open(&state, stream, reinterpret_cast(path.u8string().c_str()), &callbacks); 58 | } 59 | void Open(SDL::IOStream &&stream, const std::filesystem::path &path) 60 | { 61 | // Transfer ownership of the stream to ClownCD. 62 | Open(stream.release(), path); 63 | } 64 | void Close() 65 | { 66 | CDReader_Close(&state); 67 | } 68 | bool IsOpen() const 69 | { 70 | return CDReader_IsOpen(&state); 71 | } 72 | bool SeekToSector(const SectorIndex sector_index) 73 | { 74 | return CDReader_SeekToSector(&state, sector_index); 75 | } 76 | bool SeekToFrame(const FrameIndex frame_index) 77 | { 78 | return CDReader_SeekToFrame(&state, frame_index); 79 | } 80 | void ReadSector(cc_u16l* const buffer) 81 | { 82 | CDReader_ReadSector(&state, buffer); 83 | } 84 | bool PlayAudio(const TrackIndex track_index, const PlaybackSetting setting) 85 | { 86 | return CDReader_PlayAudio(&state, track_index, static_cast(setting)); 87 | } 88 | cc_u32f ReadAudio(cc_s16l* const sample_buffer, const cc_u32f total_frames) 89 | { 90 | return CDReader_ReadAudio(&state, sample_buffer, total_frames); 91 | } 92 | 93 | State GetState() 94 | { 95 | CDReader_StateBackup backup; 96 | CDReader_GetStateBackup(&state, &backup); 97 | return backup; 98 | } 99 | bool SetState(const State &state) 100 | { 101 | return CDReader_SetStateBackup(&this->state, &state); 102 | } 103 | 104 | bool ReadMegaCDHeaderSector(unsigned char* const buffer) 105 | { 106 | return CDReader_ReadMegaCDHeaderSector(&state, buffer); 107 | } 108 | bool IsMegaCDGame() 109 | { 110 | return CDReader_IsMegaCDGame(&state); 111 | } 112 | static bool IsMegaCDGame(const std::filesystem::path &path) 113 | { 114 | CDReader cd_reader; 115 | cd_reader.Open(nullptr, path); 116 | const bool is_mega_cd_game = cd_reader.IsMegaCDGame(); 117 | cd_reader.Close(); 118 | return is_mega_cd_game; 119 | } 120 | }; 121 | 122 | #endif /* CD_READER_H */ 123 | -------------------------------------------------------------------------------- /debug-log.cpp: -------------------------------------------------------------------------------- 1 | #include "debug-log.h" 2 | 3 | DebugLog Frontend::debug_log; 4 | 5 | void DebugLog::Log(const std::function &GetSize, const std::function &WriteBuffer) 6 | { 7 | if (logging_enabled || force_console_output) 8 | { 9 | const std::size_t message_buffer_size = GetSize(); 10 | 11 | try 12 | { 13 | if (lines.length() != 0) 14 | lines += '\n'; 15 | 16 | const auto length = lines.length(); 17 | lines.resize(length + message_buffer_size); 18 | char* const message_buffer = &lines[length]; 19 | WriteBuffer(message_buffer, message_buffer_size + 1); 20 | 21 | if (log_to_console || force_console_output) 22 | SDL_LogMessage(SDL_LOG_CATEGORY_ERROR, SDL_LOG_PRIORITY_ERROR, "%s", message_buffer); 23 | } 24 | catch (const std::bad_alloc&) 25 | { 26 | // Wipe the line buffer to reclaim RAM. It's better than nothing. 27 | lines.clear(); 28 | lines.shrink_to_fit(); 29 | } 30 | } 31 | } 32 | 33 | void DebugLog::Log(const char* const format, std::va_list args) 34 | { 35 | const auto GetSize = [&]() 36 | { 37 | std::va_list args2; 38 | va_copy(args2, args); 39 | const std::size_t message_buffer_size = static_cast(SDL_vsnprintf(nullptr, 0, format, args2)); 40 | va_end(args2); 41 | return message_buffer_size; 42 | }; 43 | 44 | const auto WriteBuffer = [&](char* const message_buffer, const std::size_t message_buffer_size) 45 | { 46 | SDL_vsnprintf(message_buffer, message_buffer_size + 1, format, args); 47 | }; 48 | 49 | Log(GetSize, WriteBuffer); 50 | } 51 | -------------------------------------------------------------------------------- /debug-log.h: -------------------------------------------------------------------------------- 1 | #ifndef DEBUG_LOG_H 2 | #define DEBUG_LOG_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | #include 12 | 13 | #include "libraries/imgui/imgui.h" 14 | #include "common/core/clowncommon/clowncommon.h" 15 | 16 | class DebugLog 17 | { 18 | private: 19 | void Log(const std::function &GetSize, const std::function &WriteBuffer); 20 | 21 | public: 22 | std::string lines; 23 | bool logging_enabled = false, log_to_console = false, force_console_output = true; 24 | 25 | DebugLog() 26 | { 27 | SDL_SetLogPriority(SDL_LOG_CATEGORY_ERROR, SDL_LOG_PRIORITY_ERROR); 28 | } 29 | 30 | void ForceConsoleOutput(const bool forced) 31 | { 32 | force_console_output = forced; 33 | } 34 | 35 | void Log(const char *format, std::va_list args); 36 | 37 | template 38 | void Log(fmt::format_string format, T&&... args) 39 | { 40 | const auto GetSize = [&]() 41 | { 42 | return fmt::formatted_size(format, std::forward(args)...); 43 | }; 44 | 45 | const auto WriteBuffer = [&](char* const message_buffer, const std::size_t message_buffer_size) 46 | { 47 | fmt::format_to_n(message_buffer, message_buffer_size, format, std::forward(args)...); 48 | }; 49 | 50 | Log(GetSize, WriteBuffer); 51 | } 52 | }; 53 | 54 | namespace Frontend 55 | { 56 | extern DebugLog debug_log; 57 | } 58 | 59 | #endif /* DEBUG_LOG_H */ 60 | -------------------------------------------------------------------------------- /emulator-instance.h: -------------------------------------------------------------------------------- 1 | #ifndef EMULATOR_INSTANCE_H 2 | #define EMULATOR_INSTANCE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "common/core/clownmdemu.h" 12 | 13 | #include "audio-output.h" 14 | #include "cd-reader.h" 15 | #include "sdl-wrapper.h" 16 | 17 | class EmulatorInstance 18 | { 19 | public: 20 | using InputCallback = std::function; 21 | 22 | struct State 23 | { 24 | static constexpr unsigned int total_brightnesses = 3; 25 | static constexpr unsigned int total_palette_lines = 4; 26 | static constexpr unsigned int total_colours_in_palette_line = 16; 27 | 28 | ClownMDEmu_State clownmdemu; 29 | std::array colours; 30 | CDReader::State cd; 31 | 32 | auto GetPaletteLine(const cc_u8f brightness, const cc_u8f palette_line) const 33 | { 34 | return &colours[brightness * total_palette_lines * total_colours_in_palette_line + palette_line * total_colours_in_palette_line]; 35 | } 36 | auto GetColour(const cc_u8f brightness, const cc_u8f palette_line, const cc_u8f colour_index) const 37 | { 38 | return GetPaletteLine(brightness, palette_line)[colour_index]; 39 | } 40 | }; 41 | 42 | private: 43 | class Cartridge 44 | { 45 | private: 46 | std::vector rom_file_buffer; 47 | std::filesystem::path save_data_path; 48 | EmulatorInstance &emulator; 49 | 50 | public: 51 | Cartridge(EmulatorInstance &emulator) 52 | : emulator(emulator) 53 | {} 54 | 55 | cc_u8f Read(cc_u32f address); 56 | const std::vector& GetROMBuffer() const { return rom_file_buffer; } 57 | bool IsInserted() const { return !rom_file_buffer.empty(); } 58 | void Insert(const std::vector &rom_file_buffer, const std::filesystem::path &save_data_path); 59 | void Eject(); 60 | }; 61 | 62 | static const ClownMDEmu_Constant clownmdemu_constant; 63 | 64 | AudioOutput audio_output; 65 | SDL::Texture &texture; 66 | const InputCallback input_callback; 67 | ClownMDEmu_Callbacks callbacks; 68 | 69 | ClownMDEmu_Configuration clownmdemu_configuration = {}; 70 | ClownMDEmu clownmdemu; 71 | 72 | Cartridge cartridge = {*this}; 73 | CDReader cd_file; 74 | 75 | SDL::Pixel *framebuffer_texture_pixels = nullptr; 76 | int framebuffer_texture_pitch = 0; 77 | 78 | class Rewind 79 | { 80 | private: 81 | bool in_progress = false; 82 | 83 | public: 84 | std::vector buffer = std::vector(1); 85 | std::size_t index = 0; 86 | std::size_t remaining = 0; 87 | 88 | bool Enabled() const 89 | { 90 | return buffer.size() != 1; 91 | } 92 | bool Enable(const bool enabled) 93 | { 94 | if (enabled == Enabled()) 95 | return false; 96 | 97 | std::vector new_buffer(enabled ? 60 * 10 : 1); // Roughly 10 seconds of rewinding at 60FPS 98 | // Transfer the old state to the new buffer. 99 | new_buffer[0] = buffer[index]; 100 | 101 | // Reinitialise 102 | std::swap(buffer, new_buffer); 103 | in_progress = false; 104 | index = 0; 105 | remaining = 0; 106 | 107 | return true; 108 | } 109 | 110 | bool InProgress() const { return Enabled() ? in_progress : false; } 111 | void InProgress(const bool active) { in_progress = Enabled() ? active : false; } 112 | // We need at least two frames and the frame before it, because rewinding pops one frame and then samples the frame below the head. 113 | bool Exhausted() const { return Enabled() ? in_progress && remaining <= 2 : false; } 114 | }; 115 | 116 | Rewind rewind; 117 | 118 | State *state = &rewind.buffer[0]; 119 | 120 | unsigned int current_screen_width = 0; 121 | unsigned int current_screen_height = 0; 122 | 123 | SDL::IOStream save_data_stream; 124 | 125 | static cc_u8f CartridgeReadCallback(void *user_data, cc_u32f address); 126 | static void CartridgeWrittenCallback(void *user_data, cc_u32f address, cc_u8f value); 127 | static void ColourUpdatedCallback(void *user_data, cc_u16f index, cc_u16f colour); 128 | static void ScanlineRenderedCallback(void *user_data, cc_u16f scanline, const cc_u8l *pixels, cc_u16f left_boundary, cc_u16f right_boundary, cc_u16f screen_width, cc_u16f screen_height); 129 | static cc_bool ReadInputCallback(void *user_data, cc_u8f player_id, ClownMDEmu_Button button_id); 130 | 131 | static void FMAudioCallback(void *user_data, const ClownMDEmu *clownmdemu, std::size_t total_frames, void (*generate_fm_audio)(const ClownMDEmu *clownmdemu, cc_s16l *sample_buffer, std::size_t total_frames)); 132 | static void PSGAudioCallback(void *user_data, const ClownMDEmu *clownmdemu, std::size_t total_samples, void (*generate_psg_audio)(const ClownMDEmu *clownmdemu, cc_s16l *sample_buffer, std::size_t total_samples)); 133 | static void PCMAudioCallback(void *user_data, const ClownMDEmu *clownmdemu, std::size_t total_frames, void (*generate_pcm_audio)(const ClownMDEmu *clownmdemu, cc_s16l *sample_buffer, std::size_t total_frames)); 134 | static void CDDAAudioCallback(void *user_data, const ClownMDEmu *clownmdemu, std::size_t total_frames, void (*generate_cdda_audio)(const ClownMDEmu *clownmdemu, cc_s16l *sample_buffer, std::size_t total_frames)); 135 | 136 | static void CDSeekCallback(void *user_data, cc_u32f sector_index); 137 | static void CDSectorReadCallback(void *user_data, cc_u16l *buffer); 138 | static cc_bool CDSeekTrackCallback(void *user_data, cc_u16f track_index, ClownMDEmu_CDDAMode mode); 139 | static std::size_t CDAudioReadCallback(void *user_data, cc_s16l *sample_buffer, std::size_t total_frames); 140 | 141 | static cc_bool SaveFileOpenedForReadingCallback(void *user_data, const char *filename); 142 | static cc_s16f SaveFileReadCallback(void *user_data); 143 | static cc_bool SaveFileOpenedForWriting(void *user_data, const char *filename); 144 | static void SaveFileWritten(void *user_data, cc_u8f byte); 145 | static void SaveFileClosed(void *user_data); 146 | static cc_bool SaveFileRemoved(void *user_data, const char *filename); 147 | static cc_bool SaveFileSizeObtained(void *user_data, const char *filename, std::size_t *size); 148 | 149 | public: 150 | EmulatorInstance(SDL::Texture &texture, const InputCallback &input_callback); 151 | ~EmulatorInstance() 152 | { 153 | cartridge.Eject(); 154 | } 155 | 156 | void Update(cc_bool fast_forward); 157 | void SoftResetConsole(); 158 | void HardResetConsole(); 159 | void LoadCartridgeFile(std::vector &&file_buffer, const std::filesystem::path &path); 160 | void UnloadCartridgeFile(); 161 | bool LoadCDFile(SDL::IOStream &&stream, const std::filesystem::path &path); 162 | void UnloadCDFile(); 163 | 164 | void LoadState(const void *buffer); 165 | void SaveState(void *buffer); 166 | 167 | bool ValidateSaveStateFile(const std::vector &file_buffer); 168 | bool LoadSaveStateFile(const std::vector &file_buffer); 169 | std::size_t GetSaveStateFileSize(); 170 | bool WriteSaveStateFile(SDL::IOStream &file); 171 | 172 | bool IsCartridgeFileLoaded() const { return cartridge.IsInserted(); } 173 | bool IsCDFileLoaded() const { return cd_file.IsOpen(); } 174 | 175 | bool RewindingEnabled() const { return rewind.Enabled(); } 176 | void EnableRewinding(const bool enabled) 177 | { 178 | if (rewind.Enable(enabled)) 179 | { 180 | state = &rewind.buffer[0]; 181 | ClownMDEmu_Parameters_Initialise(&clownmdemu, &clownmdemu_configuration, &clownmdemu_constant, &state->clownmdemu, &callbacks); 182 | } 183 | } 184 | bool IsRewinding() const { return rewind.InProgress(); } 185 | void Rewind(const bool active) { rewind.InProgress(active); } 186 | bool RewindingExhausted() const { return rewind.Exhausted(); } 187 | 188 | unsigned int GetCurrentScreenWidth() const { return current_screen_width; } 189 | unsigned int GetCurrentScreenHeight() const { return current_screen_height; } 190 | const State& CurrentState() const { return *state; } 191 | void OverwriteCurrentState(const State &new_state) { LoadState(&new_state); } 192 | const std::vector& GetROMBuffer() const { return cartridge.GetROMBuffer(); } 193 | 194 | bool GetPALMode() const { return clownmdemu_configuration.general.tv_standard == CLOWNMDEMU_TV_STANDARD_PAL; } 195 | 196 | void SetPALMode(const bool enabled) 197 | { 198 | clownmdemu_configuration.general.tv_standard = enabled ? CLOWNMDEMU_TV_STANDARD_PAL : CLOWNMDEMU_TV_STANDARD_NTSC; 199 | audio_output.SetPALMode(enabled); 200 | } 201 | 202 | bool GetDomestic() const { return clownmdemu_configuration.general.region == CLOWNMDEMU_REGION_DOMESTIC; } 203 | void SetDomestic(const bool enabled) { clownmdemu_configuration.general.region = enabled ? CLOWNMDEMU_REGION_DOMESTIC : CLOWNMDEMU_REGION_OVERSEAS; } 204 | bool GetLowPassFilter() const { return !clownmdemu_configuration.general.low_pass_filter_disabled; } 205 | void SetLowPassFilter(const bool enabled) { clownmdemu_configuration.general.low_pass_filter_disabled = !enabled; } 206 | VDP_Configuration& GetConfigurationVDP() { return clownmdemu_configuration.vdp; } 207 | FM_Configuration& GetConfigurationFM() { return clownmdemu_configuration.fm; } 208 | PSG_Configuration& GetConfigurationPSG() { return clownmdemu_configuration.psg; } 209 | PCM_Configuration& GetConfigurationPCM() { return clownmdemu_configuration.pcm; } 210 | 211 | cc_u32f GetAudioAverageFrames() const { return audio_output.GetAverageFrames(); } 212 | cc_u32f GetAudioTargetFrames() const { return audio_output.GetTargetFrames(); } 213 | cc_u32f GetAudioTotalBufferFrames() const { return audio_output.GetTotalBufferFrames(); } 214 | cc_u32f GetAudioSampleRate() const { return audio_output.GetSampleRate(); } 215 | 216 | std::string GetSoftwareName(); 217 | }; 218 | 219 | #endif /* EMULATOR_INSTANCE_H */ 220 | -------------------------------------------------------------------------------- /file-utilities.cpp: -------------------------------------------------------------------------------- 1 | #include "file-utilities.h" 2 | 3 | #include 4 | #include 5 | 6 | #ifdef __EMSCRIPTEN__ 7 | #include "libraries/emscripten-browser-file/emscripten_browser_file.h" 8 | #endif 9 | #include "libraries/imgui/imgui.h" 10 | 11 | #include "common/core/clowncommon/clowncommon.h" 12 | 13 | void FileUtilities::CreateFileDialog([[maybe_unused]] Window &window, const std::string &title, const PopupCallback &callback, const bool save) 14 | { 15 | bool done = false; 16 | 17 | if (use_native_file_dialogs) 18 | { 19 | // TODO: According to documentation, the callback may be ran on a different thread, so find some way to ensure that there are no race conditions! 20 | try 21 | { 22 | PopupCallback* const callback_detatched = new PopupCallback(callback); 23 | 24 | const auto call_callback = [](void* const user_data, const char* const* const file_list, [[maybe_unused]] const int filter) 25 | { 26 | const std::unique_ptr callback(static_cast(user_data)); 27 | 28 | if (file_list == nullptr || *file_list == nullptr) 29 | return; 30 | 31 | (*callback.get())(reinterpret_cast(*file_list)); 32 | }; 33 | 34 | const auto properties = SDL_CreateProperties(); 35 | SDL_SetPointerProperty(properties, SDL_PROP_FILE_DIALOG_WINDOW_POINTER, window.GetSDLWindow()); 36 | SDL_SetStringProperty(properties, SDL_PROP_FILE_DIALOG_TITLE_STRING, title.c_str()); 37 | SDL_ShowFileDialogWithProperties(save ? SDL_FILEDIALOG_SAVEFILE : SDL_FILEDIALOG_OPENFILE, call_callback, callback_detatched, properties); 38 | SDL_DestroyProperties(properties); 39 | 40 | done = true; 41 | } 42 | catch (const std::bad_alloc&) 43 | { 44 | Frontend::debug_log.Log("FileUtilities::CreateFileDialog: Failed to allocate memory."); 45 | } 46 | } 47 | 48 | if (!done) 49 | { 50 | active_file_picker_popup = title; 51 | popup_callback = callback; 52 | is_save_dialog = save; 53 | } 54 | } 55 | 56 | void FileUtilities::CreateOpenFileDialog(Window &window, const std::string &title, const PopupCallback &callback) 57 | { 58 | CreateFileDialog(window, title, callback, false); 59 | } 60 | 61 | void FileUtilities::CreateSaveFileDialog(Window &window, const std::string &title, const PopupCallback &callback) 62 | { 63 | CreateFileDialog(window, title, callback, true); 64 | } 65 | 66 | void FileUtilities::DisplayFileDialog(std::filesystem::path &drag_and_drop_filename) 67 | { 68 | if (active_file_picker_popup.has_value()) 69 | { 70 | if (!ImGui::IsPopupOpen(active_file_picker_popup->c_str())) 71 | ImGui::OpenPopup(active_file_picker_popup->c_str()); 72 | 73 | ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_Always, ImVec2(0.5f, 0.5f)); 74 | 75 | if (ImGui::BeginPopupModal(active_file_picker_popup->c_str(), nullptr, ImGuiWindowFlags_AlwaysAutoResize)) 76 | { 77 | const ImGuiInputTextCallback callback = [](ImGuiInputTextCallbackData* const data) 78 | { 79 | FileUtilities* const file_utilities = static_cast(data->UserData); 80 | 81 | if (data->EventFlag == ImGuiInputTextFlags_CallbackResize) 82 | { 83 | if (static_cast(data->BufSize) > file_utilities->text_buffer.size()) 84 | { 85 | file_utilities->text_buffer.resize(data->BufSize); 86 | data->Buf = file_utilities->text_buffer.data(); 87 | } 88 | } 89 | 90 | return 0; 91 | }; 92 | 93 | /* Make it so that the text box is selected by default, 94 | so that the user doesn't have to click on it first. 95 | If a file is dropped onto the dialog, focus on the 96 | 'open' button instead or else the text box won't show 97 | the dropped file's path. */ 98 | if (!drag_and_drop_filename.empty()) 99 | ImGui::SetKeyboardFocusHere(1); 100 | else if (ImGui::IsWindowAppearing()) 101 | ImGui::SetKeyboardFocusHere(); 102 | 103 | ImGui::TextUnformatted("Filename:"); 104 | const bool enter_pressed = ImGui::InputText("##filename", text_buffer.data(), text_buffer.capacity() + 1, ImGuiInputTextFlags_CallbackResize | ImGuiInputTextFlags_CallbackAlways | ImGuiInputTextFlags_EnterReturnsTrue, callback, this); 105 | 106 | // Set the text box's contents to the dropped file's path. 107 | if (!drag_and_drop_filename.empty()) 108 | { 109 | text_buffer = drag_and_drop_filename.string(); 110 | drag_and_drop_filename.clear(); 111 | } 112 | 113 | const auto centre_buttons = [](const std::vector &labels) 114 | { 115 | float width = 0; 116 | 117 | width += ImGui::GetStyle().ItemSpacing.x * (labels.size() - 1); 118 | width += ImGui::GetStyle().FramePadding.x * 2 * labels.size(); 119 | 120 | for (const auto label : labels) 121 | width += ImGui::CalcTextSize(label).x; 122 | 123 | ImGui::SetCursorPosX(ImGui::GetStyle().WindowPadding.x + (ImGui::GetContentRegionAvail().x - width) / 2); 124 | }; 125 | 126 | centre_buttons({is_save_dialog ? "Save" : "Open", "Cancel"}); 127 | const bool ok_pressed = ImGui::Button(is_save_dialog ? "Save" : "Open"); 128 | ImGui::SameLine(); 129 | bool exit = ImGui::Button("Cancel"); 130 | 131 | bool submit = false; 132 | 133 | if (enter_pressed || ok_pressed) 134 | { 135 | if (!is_save_dialog || !FileExists(text_buffer)) 136 | submit = true; 137 | else 138 | ImGui::OpenPopup("File Already Exists"); 139 | } 140 | 141 | ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_Always, ImVec2(0.5f, 0.5f)); 142 | 143 | if (ImGui::BeginPopupModal("File Already Exists", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) 144 | { 145 | ImGui::TextUnformatted("A file with that name already exists. Overwrite it?"); 146 | 147 | centre_buttons({"Yes", "No"}); 148 | if (ImGui::Button("Yes")) 149 | { 150 | submit = true; 151 | ImGui::CloseCurrentPopup(); 152 | } 153 | 154 | ImGui::SameLine(); 155 | 156 | if (ImGui::Button("No")) 157 | ImGui::CloseCurrentPopup(); 158 | 159 | ImGui::EndPopup(); 160 | } 161 | 162 | if (submit) 163 | { 164 | if (popup_callback(text_buffer)) 165 | exit = true; 166 | else 167 | ImGui::OpenPopup(is_save_dialog ? "Could Not Save File" : "Could Not Open File"); 168 | } 169 | 170 | if (ImGui::BeginPopupModal("Could Not Open File", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) 171 | { 172 | ImGui::TextUnformatted("File could not be opened."); 173 | 174 | centre_buttons({"OK"}); 175 | if (ImGui::Button("OK")) 176 | ImGui::CloseCurrentPopup(); 177 | 178 | ImGui::EndPopup(); 179 | } 180 | 181 | if (ImGui::BeginPopupModal("Could Not Save File", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) 182 | { 183 | ImGui::TextUnformatted("File could not be saved."); 184 | 185 | centre_buttons({"OK"}); 186 | if (ImGui::Button("OK")) 187 | ImGui::CloseCurrentPopup(); 188 | 189 | ImGui::EndPopup(); 190 | } 191 | 192 | if (exit) 193 | { 194 | ImGui::CloseCurrentPopup(); 195 | text_buffer.clear(); 196 | text_buffer.shrink_to_fit(); 197 | active_file_picker_popup.reset(); 198 | } 199 | 200 | ImGui::EndPopup(); 201 | } 202 | } 203 | } 204 | 205 | bool FileUtilities::FileExists(const std::filesystem::path &path) 206 | { 207 | return SDL::GetPathInfo(path, nullptr); 208 | } 209 | 210 | std::optional> FileUtilities::LoadFileToBuffer(const std::filesystem::path &path) 211 | { 212 | SDL::IOStream file = SDL::IOFromFile(path, "rb"); 213 | 214 | if (!file) 215 | { 216 | Frontend::debug_log.Log("SDL_IOFromFile failed with the following message - '{}'", SDL_GetError()); 217 | return std::nullopt; 218 | } 219 | 220 | return LoadFileToBuffer(file); 221 | } 222 | 223 | std::optional> FileUtilities::LoadFileToBuffer(SDL::IOStream &file) 224 | { 225 | const Sint64 size_s64 = SDL_GetIOSize(file); 226 | 227 | if (size_s64 < 0) 228 | { 229 | Frontend::debug_log.Log("SDL_GetIOSize failed with the following message - '{}'", SDL_GetError()); 230 | } 231 | else 232 | { 233 | const std::size_t size = static_cast(size_s64); 234 | 235 | try 236 | { 237 | std::vector file_buffer(size); 238 | SDL_ReadIO(file, file_buffer.data(), size); 239 | return file_buffer; 240 | } 241 | catch (const std::bad_alloc&) 242 | { 243 | Frontend::debug_log.Log("Could not allocate memory for file"); 244 | } 245 | } 246 | 247 | return std::nullopt; 248 | } 249 | 250 | void FileUtilities::LoadFile([[maybe_unused]] Window &window, [[maybe_unused]] const std::string &title, const LoadFileCallback &callback) 251 | { 252 | #ifdef __EMSCRIPTEN__ 253 | try 254 | { 255 | LoadFileCallback* const callback_detatched = new LoadFileCallback(callback); 256 | 257 | const auto call_callback = [](const std::string &filename, const std::string &/*mime_type*/, std::string_view buffer, void* const user_data) 258 | { 259 | const std::unique_ptr callback(static_cast(user_data)); 260 | SDL::IOStream file = SDL::IOStream(SDL_IOFromConstMem(buffer.data(), buffer.size())); 261 | 262 | if (file) 263 | (*callback.get())(filename, std::move(file)); 264 | }; 265 | 266 | emscripten_browser_file::upload("", call_callback, callback_detatched); 267 | } 268 | catch (const std::bad_alloc&) 269 | { 270 | Frontend::debug_log.Log("FileUtilities::LoadFile: Failed to allocate memory."); 271 | } 272 | #else 273 | CreateOpenFileDialog(window, title, [callback](const std::filesystem::path &path) 274 | { 275 | SDL::IOStream file = SDL::IOFromFile(path, "rb"); 276 | 277 | if (!file) 278 | return false; 279 | 280 | return callback(path, std::move(file)); 281 | }); 282 | #endif 283 | } 284 | 285 | void FileUtilities::SaveFile([[maybe_unused]] Window &window, [[maybe_unused]] const std::string &title, const SaveFileCallback &callback) 286 | { 287 | #ifdef __EMSCRIPTEN__ 288 | callback([](const void* const data, const std::size_t data_size) 289 | { 290 | emscripten_browser_file::download("", "application/octet-stream", std::string_view(static_cast(data), data_size)); 291 | return true; 292 | }); 293 | #else 294 | CreateSaveFileDialog(window, title, [callback](const std::filesystem::path &path) 295 | { 296 | const auto save_file = [path](const void* const data, const std::size_t data_size) 297 | { 298 | SDL::IOStream file = SDL::IOFromFile(path, "wb"); 299 | 300 | if (!file) 301 | return false; 302 | 303 | return SDL_WriteIO(file, data, data_size) == data_size; 304 | }; 305 | 306 | return callback(save_file); 307 | }); 308 | #endif 309 | } 310 | -------------------------------------------------------------------------------- /file-utilities.h: -------------------------------------------------------------------------------- 1 | #ifndef FILE_UTILITIES_H 2 | #define FILE_UTILITIES_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "sdl-wrapper.h" 12 | 13 | #include "debug-log.h" 14 | #include "windows/common/window.h" 15 | 16 | class FileUtilities 17 | { 18 | public: 19 | using SaveFileInnerCallback = std::function; 20 | 21 | private: 22 | using PopupCallback = std::function; 23 | using LoadFileCallback = std::function; 24 | using SaveFileCallback = std::function; 25 | 26 | std::string text_buffer; 27 | 28 | std::optional active_file_picker_popup; 29 | PopupCallback popup_callback; 30 | bool is_save_dialog = false; 31 | 32 | void CreateFileDialog(Window &window, const std::string &title, const PopupCallback &callback, bool save); 33 | 34 | public: 35 | bool use_native_file_dialogs = true; 36 | 37 | void CreateOpenFileDialog(Window &window, const std::string &title, const PopupCallback &callback); 38 | void CreateSaveFileDialog(Window &window, const std::string &title, const PopupCallback &callback); 39 | void DisplayFileDialog(std::filesystem::path &drag_and_drop_filename); 40 | bool IsDialogOpen() const { return active_file_picker_popup.has_value(); } 41 | 42 | bool FileExists(const std::filesystem::path &path); 43 | std::optional> LoadFileToBuffer(const std::filesystem::path &path); 44 | std::optional> LoadFileToBuffer(SDL::IOStream &file); 45 | 46 | void LoadFile(Window &window, const std::string &title, const LoadFileCallback &callback); 47 | void SaveFile(Window &window, const std::string &title, const SaveFileCallback &callback); 48 | }; 49 | 50 | #endif /* FILE_UTILITIES_H */ 51 | -------------------------------------------------------------------------------- /frontend.h: -------------------------------------------------------------------------------- 1 | #ifndef FRONTEND_H 2 | #define FRONTEND_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | #include "common/core/clownmdemu.h" 12 | 13 | #include "debug-log.h" 14 | #include "emulator-instance.h" 15 | #include "file-utilities.h" 16 | #include "windows/common/window-with-framebuffer.h" 17 | 18 | template 19 | inline void DoToolTip(const T& text) 20 | { 21 | if (ImGui::IsItemHovered()) 22 | { 23 | ImGui::BeginTooltip(); 24 | ImGui::TextUnformatted(text); 25 | ImGui::EndTooltip(); 26 | } 27 | } 28 | 29 | namespace Frontend 30 | { 31 | struct Input 32 | { 33 | unsigned int bound_joypad = 0; 34 | std::array buttons = {0}; 35 | unsigned char fast_forward = 0; 36 | unsigned char rewind = 0; 37 | }; 38 | 39 | enum InputBinding 40 | { 41 | INPUT_BINDING_NONE, 42 | INPUT_BINDING_CONTROLLER_UP, 43 | INPUT_BINDING_CONTROLLER_DOWN, 44 | INPUT_BINDING_CONTROLLER_LEFT, 45 | INPUT_BINDING_CONTROLLER_RIGHT, 46 | INPUT_BINDING_CONTROLLER_A, 47 | INPUT_BINDING_CONTROLLER_B, 48 | INPUT_BINDING_CONTROLLER_C, 49 | INPUT_BINDING_CONTROLLER_X, 50 | INPUT_BINDING_CONTROLLER_Y, 51 | INPUT_BINDING_CONTROLLER_Z, 52 | INPUT_BINDING_CONTROLLER_START, 53 | INPUT_BINDING_CONTROLLER_MODE, 54 | INPUT_BINDING_PAUSE, 55 | INPUT_BINDING_RESET, 56 | INPUT_BINDING_FAST_FORWARD, 57 | INPUT_BINDING_REWIND, 58 | INPUT_BINDING_QUICK_SAVE_STATE, 59 | INPUT_BINDING_QUICK_LOAD_STATE, 60 | INPUT_BINDING_TOGGLE_FULLSCREEN, 61 | INPUT_BINDING_TOGGLE_CONTROL_PAD, 62 | INPUT_BINDING__TOTAL 63 | }; 64 | 65 | using FrameRateCallback = std::function; 66 | 67 | extern std::optional emulator; 68 | extern std::optional window; 69 | extern FileUtilities file_utilities; 70 | extern unsigned int frame_counter; 71 | 72 | extern Input keyboard_input; 73 | extern std::array keyboard_bindings; // TODO: `SDL_SCANCODE_COUNT` is an internal macro, so use something standard! 74 | 75 | extern bool integer_screen_scaling; 76 | extern bool tall_double_resolution_mode; 77 | extern bool fast_forward_in_progress; 78 | extern bool native_windows; 79 | 80 | std::filesystem::path GetConfigurationDirectoryPath(); 81 | std::filesystem::path GetSaveDataDirectoryPath(); 82 | void SetAudioPALMode(bool enabled); 83 | bool Initialise(const int argc, char** const argv, const FrameRateCallback &frame_rate_callback); 84 | void HandleEvent(const SDL_Event &event); 85 | void Update(); 86 | void Deinitialise(); 87 | bool WantsToQuit(); 88 | template 89 | T DivideByPALFramerate(T value) { return CLOWNMDEMU_DIVIDE_BY_PAL_FRAMERATE(value); } 90 | template 91 | T DivideByNTSCFramerate(T value) { return CLOWNMDEMU_DIVIDE_BY_NTSC_FRAMERATE(value); }; 92 | } 93 | 94 | #endif /* FRONTEND_H */ 95 | -------------------------------------------------------------------------------- /licences/converter/.gitignore: -------------------------------------------------------------------------------- 1 | # Typical CMake build directory. 2 | /build 3 | -------------------------------------------------------------------------------- /licences/converter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | 3 | project(arrayifier LANGUAGES C) 4 | 5 | add_executable(arrayifier 6 | "main.c" 7 | ) 8 | -------------------------------------------------------------------------------- /licences/converter/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main(const int argc, char** const argv) 5 | { 6 | int exit_code = EXIT_FAILURE; 7 | 8 | if (argc < 2) 9 | { 10 | fputs("Pass path to input file as an argument.\n", stderr); 11 | } 12 | else 13 | { 14 | FILE* const file = fopen(argv[1], "rb"); 15 | 16 | if (file == NULL) 17 | { 18 | fputs("Could not open input file for reading.\n", stderr); 19 | } 20 | else 21 | { 22 | int character; 23 | 24 | while ((character = fgetc(file)) != EOF) 25 | fprintf(stdout, "'\\x%02X',", character); 26 | 27 | exit_code = EXIT_SUCCESS; 28 | 29 | fclose(file); 30 | } 31 | } 32 | 33 | return exit_code; 34 | } 35 | -------------------------------------------------------------------------------- /licences/dear-imgui.h: -------------------------------------------------------------------------------- 1 | '\x43','\x6F','\x70','\x79','\x72','\x69','\x67','\x68','\x74','\x20','\x28','\x63','\x29','\x20','\x32','\x30','\x31','\x34','\x2D','\x32','\x30','\x31','\x37','\x20','\x4F','\x6D','\x61','\x72','\x20','\x43','\x6F','\x72','\x6E','\x75','\x74','\x20','\x61','\x6E','\x64','\x20','\x49','\x6D','\x47','\x75','\x69','\x20','\x63','\x6F','\x6E','\x74','\x72','\x69','\x62','\x75','\x74','\x6F','\x72','\x73','\x0D','\x0A','\x43','\x6F','\x70','\x79','\x72','\x69','\x67','\x68','\x74','\x20','\x28','\x63','\x29','\x20','\x32','\x30','\x31','\x34','\x2D','\x32','\x30','\x32','\x35','\x20','\x4F','\x6D','\x61','\x72','\x20','\x43','\x6F','\x72','\x6E','\x75','\x74','\x0D','\x0A','\x0D','\x0A','\x50','\x65','\x72','\x6D','\x69','\x73','\x73','\x69','\x6F','\x6E','\x20','\x69','\x73','\x20','\x68','\x65','\x72','\x65','\x62','\x79','\x20','\x67','\x72','\x61','\x6E','\x74','\x65','\x64','\x2C','\x20','\x66','\x72','\x65','\x65','\x20','\x6F','\x66','\x20','\x63','\x68','\x61','\x72','\x67','\x65','\x2C','\x20','\x74','\x6F','\x20','\x61','\x6E','\x79','\x20','\x70','\x65','\x72','\x73','\x6F','\x6E','\x20','\x6F','\x62','\x74','\x61','\x69','\x6E','\x69','\x6E','\x67','\x20','\x61','\x20','\x63','\x6F','\x70','\x79','\x0D','\x0A','\x6F','\x66','\x20','\x74','\x68','\x69','\x73','\x20','\x73','\x6F','\x66','\x74','\x77','\x61','\x72','\x65','\x20','\x61','\x6E','\x64','\x20','\x61','\x73','\x73','\x6F','\x63','\x69','\x61','\x74','\x65','\x64','\x20','\x64','\x6F','\x63','\x75','\x6D','\x65','\x6E','\x74','\x61','\x74','\x69','\x6F','\x6E','\x20','\x66','\x69','\x6C','\x65','\x73','\x20','\x28','\x74','\x68','\x65','\x20','\x22','\x53','\x6F','\x66','\x74','\x77','\x61','\x72','\x65','\x22','\x29','\x2C','\x20','\x74','\x6F','\x20','\x64','\x65','\x61','\x6C','\x0D','\x0A','\x69','\x6E','\x20','\x74','\x68','\x65','\x20','\x53','\x6F','\x66','\x74','\x77','\x61','\x72','\x65','\x20','\x77','\x69','\x74','\x68','\x6F','\x75','\x74','\x20','\x72','\x65','\x73','\x74','\x72','\x69','\x63','\x74','\x69','\x6F','\x6E','\x2C','\x20','\x69','\x6E','\x63','\x6C','\x75','\x64','\x69','\x6E','\x67','\x20','\x77','\x69','\x74','\x68','\x6F','\x75','\x74','\x20','\x6C','\x69','\x6D','\x69','\x74','\x61','\x74','\x69','\x6F','\x6E','\x20','\x74','\x68','\x65','\x20','\x72','\x69','\x67','\x68','\x74','\x73','\x0D','\x0A','\x74','\x6F','\x20','\x75','\x73','\x65','\x2C','\x20','\x63','\x6F','\x70','\x79','\x2C','\x20','\x6D','\x6F','\x64','\x69','\x66','\x79','\x2C','\x20','\x6D','\x65','\x72','\x67','\x65','\x2C','\x20','\x70','\x75','\x62','\x6C','\x69','\x73','\x68','\x2C','\x20','\x64','\x69','\x73','\x74','\x72','\x69','\x62','\x75','\x74','\x65','\x2C','\x20','\x73','\x75','\x62','\x6C','\x69','\x63','\x65','\x6E','\x73','\x65','\x2C','\x20','\x61','\x6E','\x64','\x2F','\x6F','\x72','\x20','\x73','\x65','\x6C','\x6C','\x0D','\x0A','\x63','\x6F','\x70','\x69','\x65','\x73','\x20','\x6F','\x66','\x20','\x74','\x68','\x65','\x20','\x53','\x6F','\x66','\x74','\x77','\x61','\x72','\x65','\x2C','\x20','\x61','\x6E','\x64','\x20','\x74','\x6F','\x20','\x70','\x65','\x72','\x6D','\x69','\x74','\x20','\x70','\x65','\x72','\x73','\x6F','\x6E','\x73','\x20','\x74','\x6F','\x20','\x77','\x68','\x6F','\x6D','\x20','\x74','\x68','\x65','\x20','\x53','\x6F','\x66','\x74','\x77','\x61','\x72','\x65','\x20','\x69','\x73','\x0D','\x0A','\x66','\x75','\x72','\x6E','\x69','\x73','\x68','\x65','\x64','\x20','\x74','\x6F','\x20','\x64','\x6F','\x20','\x73','\x6F','\x2C','\x20','\x73','\x75','\x62','\x6A','\x65','\x63','\x74','\x20','\x74','\x6F','\x20','\x74','\x68','\x65','\x20','\x66','\x6F','\x6C','\x6C','\x6F','\x77','\x69','\x6E','\x67','\x20','\x63','\x6F','\x6E','\x64','\x69','\x74','\x69','\x6F','\x6E','\x73','\x3A','\x0D','\x0A','\x0D','\x0A','\x54','\x68','\x65','\x20','\x61','\x62','\x6F','\x76','\x65','\x20','\x63','\x6F','\x70','\x79','\x72','\x69','\x67','\x68','\x74','\x20','\x6E','\x6F','\x74','\x69','\x63','\x65','\x20','\x61','\x6E','\x64','\x20','\x74','\x68','\x69','\x73','\x20','\x70','\x65','\x72','\x6D','\x69','\x73','\x73','\x69','\x6F','\x6E','\x20','\x6E','\x6F','\x74','\x69','\x63','\x65','\x20','\x73','\x68','\x61','\x6C','\x6C','\x20','\x62','\x65','\x20','\x69','\x6E','\x63','\x6C','\x75','\x64','\x65','\x64','\x20','\x69','\x6E','\x20','\x61','\x6C','\x6C','\x0D','\x0A','\x63','\x6F','\x70','\x69','\x65','\x73','\x20','\x6F','\x72','\x20','\x73','\x75','\x62','\x73','\x74','\x61','\x6E','\x74','\x69','\x61','\x6C','\x20','\x70','\x6F','\x72','\x74','\x69','\x6F','\x6E','\x73','\x20','\x6F','\x66','\x20','\x74','\x68','\x65','\x20','\x53','\x6F','\x66','\x74','\x77','\x61','\x72','\x65','\x2E','\x0D','\x0A','\x0D','\x0A','\x54','\x48','\x45','\x20','\x53','\x4F','\x46','\x54','\x57','\x41','\x52','\x45','\x20','\x49','\x53','\x20','\x50','\x52','\x4F','\x56','\x49','\x44','\x45','\x44','\x20','\x22','\x41','\x53','\x20','\x49','\x53','\x22','\x2C','\x20','\x57','\x49','\x54','\x48','\x4F','\x55','\x54','\x20','\x57','\x41','\x52','\x52','\x41','\x4E','\x54','\x59','\x20','\x4F','\x46','\x20','\x41','\x4E','\x59','\x20','\x4B','\x49','\x4E','\x44','\x2C','\x20','\x45','\x58','\x50','\x52','\x45','\x53','\x53','\x20','\x4F','\x52','\x0D','\x0A','\x49','\x4D','\x50','\x4C','\x49','\x45','\x44','\x2C','\x20','\x49','\x4E','\x43','\x4C','\x55','\x44','\x49','\x4E','\x47','\x20','\x42','\x55','\x54','\x20','\x4E','\x4F','\x54','\x20','\x4C','\x49','\x4D','\x49','\x54','\x45','\x44','\x20','\x54','\x4F','\x20','\x54','\x48','\x45','\x20','\x57','\x41','\x52','\x52','\x41','\x4E','\x54','\x49','\x45','\x53','\x20','\x4F','\x46','\x20','\x4D','\x45','\x52','\x43','\x48','\x41','\x4E','\x54','\x41','\x42','\x49','\x4C','\x49','\x54','\x59','\x2C','\x0D','\x0A','\x46','\x49','\x54','\x4E','\x45','\x53','\x53','\x20','\x46','\x4F','\x52','\x20','\x41','\x20','\x50','\x41','\x52','\x54','\x49','\x43','\x55','\x4C','\x41','\x52','\x20','\x50','\x55','\x52','\x50','\x4F','\x53','\x45','\x20','\x41','\x4E','\x44','\x20','\x4E','\x4F','\x4E','\x49','\x4E','\x46','\x52','\x49','\x4E','\x47','\x45','\x4D','\x45','\x4E','\x54','\x2E','\x20','\x49','\x4E','\x20','\x4E','\x4F','\x20','\x45','\x56','\x45','\x4E','\x54','\x20','\x53','\x48','\x41','\x4C','\x4C','\x20','\x54','\x48','\x45','\x0D','\x0A','\x41','\x55','\x54','\x48','\x4F','\x52','\x53','\x20','\x4F','\x52','\x20','\x43','\x4F','\x50','\x59','\x52','\x49','\x47','\x48','\x54','\x20','\x48','\x4F','\x4C','\x44','\x45','\x52','\x53','\x20','\x42','\x45','\x20','\x4C','\x49','\x41','\x42','\x4C','\x45','\x20','\x46','\x4F','\x52','\x20','\x41','\x4E','\x59','\x20','\x43','\x4C','\x41','\x49','\x4D','\x2C','\x20','\x44','\x41','\x4D','\x41','\x47','\x45','\x53','\x20','\x4F','\x52','\x20','\x4F','\x54','\x48','\x45','\x52','\x0D','\x0A','\x4C','\x49','\x41','\x42','\x49','\x4C','\x49','\x54','\x59','\x2C','\x20','\x57','\x48','\x45','\x54','\x48','\x45','\x52','\x20','\x49','\x4E','\x20','\x41','\x4E','\x20','\x41','\x43','\x54','\x49','\x4F','\x4E','\x20','\x4F','\x46','\x20','\x43','\x4F','\x4E','\x54','\x52','\x41','\x43','\x54','\x2C','\x20','\x54','\x4F','\x52','\x54','\x20','\x4F','\x52','\x20','\x4F','\x54','\x48','\x45','\x52','\x57','\x49','\x53','\x45','\x2C','\x20','\x41','\x52','\x49','\x53','\x49','\x4E','\x47','\x20','\x46','\x52','\x4F','\x4D','\x2C','\x0D','\x0A','\x4F','\x55','\x54','\x20','\x4F','\x46','\x20','\x4F','\x52','\x20','\x49','\x4E','\x20','\x43','\x4F','\x4E','\x4E','\x45','\x43','\x54','\x49','\x4F','\x4E','\x20','\x57','\x49','\x54','\x48','\x20','\x54','\x48','\x45','\x20','\x53','\x4F','\x46','\x54','\x57','\x41','\x52','\x45','\x20','\x4F','\x52','\x20','\x54','\x48','\x45','\x20','\x55','\x53','\x45','\x20','\x4F','\x52','\x20','\x4F','\x54','\x48','\x45','\x52','\x20','\x44','\x45','\x41','\x4C','\x49','\x4E','\x47','\x53','\x20','\x49','\x4E','\x20','\x54','\x48','\x45','\x0D','\x0A','\x53','\x4F','\x46','\x54','\x57','\x41','\x52','\x45','\x2E', -------------------------------------------------------------------------------- /licences/dear-imgui.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014-2017 Omar Cornut and ImGui contributors 2 | Copyright (c) 2014-2025 Omar Cornut 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. -------------------------------------------------------------------------------- /licences/emscripten-browser-file.h: -------------------------------------------------------------------------------- 1 | '\x4D','\x49','\x54','\x20','\x4C','\x69','\x63','\x65','\x6E','\x73','\x65','\x0A','\x0A','\x43','\x6F','\x70','\x79','\x72','\x69','\x67','\x68','\x74','\x20','\x28','\x63','\x29','\x20','\x32','\x30','\x32','\x33','\x20','\x41','\x72','\x6D','\x63','\x68','\x61','\x69','\x72','\x2D','\x53','\x6F','\x66','\x74','\x77','\x61','\x72','\x65','\x0A','\x0A','\x50','\x65','\x72','\x6D','\x69','\x73','\x73','\x69','\x6F','\x6E','\x20','\x69','\x73','\x20','\x68','\x65','\x72','\x65','\x62','\x79','\x20','\x67','\x72','\x61','\x6E','\x74','\x65','\x64','\x2C','\x20','\x66','\x72','\x65','\x65','\x20','\x6F','\x66','\x20','\x63','\x68','\x61','\x72','\x67','\x65','\x2C','\x20','\x74','\x6F','\x20','\x61','\x6E','\x79','\x20','\x70','\x65','\x72','\x73','\x6F','\x6E','\x20','\x6F','\x62','\x74','\x61','\x69','\x6E','\x69','\x6E','\x67','\x20','\x61','\x20','\x63','\x6F','\x70','\x79','\x0A','\x6F','\x66','\x20','\x74','\x68','\x69','\x73','\x20','\x73','\x6F','\x66','\x74','\x77','\x61','\x72','\x65','\x20','\x61','\x6E','\x64','\x20','\x61','\x73','\x73','\x6F','\x63','\x69','\x61','\x74','\x65','\x64','\x20','\x64','\x6F','\x63','\x75','\x6D','\x65','\x6E','\x74','\x61','\x74','\x69','\x6F','\x6E','\x20','\x66','\x69','\x6C','\x65','\x73','\x20','\x28','\x74','\x68','\x65','\x20','\x22','\x53','\x6F','\x66','\x74','\x77','\x61','\x72','\x65','\x22','\x29','\x2C','\x20','\x74','\x6F','\x20','\x64','\x65','\x61','\x6C','\x0A','\x69','\x6E','\x20','\x74','\x68','\x65','\x20','\x53','\x6F','\x66','\x74','\x77','\x61','\x72','\x65','\x20','\x77','\x69','\x74','\x68','\x6F','\x75','\x74','\x20','\x72','\x65','\x73','\x74','\x72','\x69','\x63','\x74','\x69','\x6F','\x6E','\x2C','\x20','\x69','\x6E','\x63','\x6C','\x75','\x64','\x69','\x6E','\x67','\x20','\x77','\x69','\x74','\x68','\x6F','\x75','\x74','\x20','\x6C','\x69','\x6D','\x69','\x74','\x61','\x74','\x69','\x6F','\x6E','\x20','\x74','\x68','\x65','\x20','\x72','\x69','\x67','\x68','\x74','\x73','\x0A','\x74','\x6F','\x20','\x75','\x73','\x65','\x2C','\x20','\x63','\x6F','\x70','\x79','\x2C','\x20','\x6D','\x6F','\x64','\x69','\x66','\x79','\x2C','\x20','\x6D','\x65','\x72','\x67','\x65','\x2C','\x20','\x70','\x75','\x62','\x6C','\x69','\x73','\x68','\x2C','\x20','\x64','\x69','\x73','\x74','\x72','\x69','\x62','\x75','\x74','\x65','\x2C','\x20','\x73','\x75','\x62','\x6C','\x69','\x63','\x65','\x6E','\x73','\x65','\x2C','\x20','\x61','\x6E','\x64','\x2F','\x6F','\x72','\x20','\x73','\x65','\x6C','\x6C','\x0A','\x63','\x6F','\x70','\x69','\x65','\x73','\x20','\x6F','\x66','\x20','\x74','\x68','\x65','\x20','\x53','\x6F','\x66','\x74','\x77','\x61','\x72','\x65','\x2C','\x20','\x61','\x6E','\x64','\x20','\x74','\x6F','\x20','\x70','\x65','\x72','\x6D','\x69','\x74','\x20','\x70','\x65','\x72','\x73','\x6F','\x6E','\x73','\x20','\x74','\x6F','\x20','\x77','\x68','\x6F','\x6D','\x20','\x74','\x68','\x65','\x20','\x53','\x6F','\x66','\x74','\x77','\x61','\x72','\x65','\x20','\x69','\x73','\x0A','\x66','\x75','\x72','\x6E','\x69','\x73','\x68','\x65','\x64','\x20','\x74','\x6F','\x20','\x64','\x6F','\x20','\x73','\x6F','\x2C','\x20','\x73','\x75','\x62','\x6A','\x65','\x63','\x74','\x20','\x74','\x6F','\x20','\x74','\x68','\x65','\x20','\x66','\x6F','\x6C','\x6C','\x6F','\x77','\x69','\x6E','\x67','\x20','\x63','\x6F','\x6E','\x64','\x69','\x74','\x69','\x6F','\x6E','\x73','\x3A','\x0A','\x0A','\x54','\x68','\x65','\x20','\x61','\x62','\x6F','\x76','\x65','\x20','\x63','\x6F','\x70','\x79','\x72','\x69','\x67','\x68','\x74','\x20','\x6E','\x6F','\x74','\x69','\x63','\x65','\x20','\x61','\x6E','\x64','\x20','\x74','\x68','\x69','\x73','\x20','\x70','\x65','\x72','\x6D','\x69','\x73','\x73','\x69','\x6F','\x6E','\x20','\x6E','\x6F','\x74','\x69','\x63','\x65','\x20','\x73','\x68','\x61','\x6C','\x6C','\x20','\x62','\x65','\x20','\x69','\x6E','\x63','\x6C','\x75','\x64','\x65','\x64','\x20','\x69','\x6E','\x20','\x61','\x6C','\x6C','\x0A','\x63','\x6F','\x70','\x69','\x65','\x73','\x20','\x6F','\x72','\x20','\x73','\x75','\x62','\x73','\x74','\x61','\x6E','\x74','\x69','\x61','\x6C','\x20','\x70','\x6F','\x72','\x74','\x69','\x6F','\x6E','\x73','\x20','\x6F','\x66','\x20','\x74','\x68','\x65','\x20','\x53','\x6F','\x66','\x74','\x77','\x61','\x72','\x65','\x2E','\x0A','\x0A','\x54','\x48','\x45','\x20','\x53','\x4F','\x46','\x54','\x57','\x41','\x52','\x45','\x20','\x49','\x53','\x20','\x50','\x52','\x4F','\x56','\x49','\x44','\x45','\x44','\x20','\x22','\x41','\x53','\x20','\x49','\x53','\x22','\x2C','\x20','\x57','\x49','\x54','\x48','\x4F','\x55','\x54','\x20','\x57','\x41','\x52','\x52','\x41','\x4E','\x54','\x59','\x20','\x4F','\x46','\x20','\x41','\x4E','\x59','\x20','\x4B','\x49','\x4E','\x44','\x2C','\x20','\x45','\x58','\x50','\x52','\x45','\x53','\x53','\x20','\x4F','\x52','\x0A','\x49','\x4D','\x50','\x4C','\x49','\x45','\x44','\x2C','\x20','\x49','\x4E','\x43','\x4C','\x55','\x44','\x49','\x4E','\x47','\x20','\x42','\x55','\x54','\x20','\x4E','\x4F','\x54','\x20','\x4C','\x49','\x4D','\x49','\x54','\x45','\x44','\x20','\x54','\x4F','\x20','\x54','\x48','\x45','\x20','\x57','\x41','\x52','\x52','\x41','\x4E','\x54','\x49','\x45','\x53','\x20','\x4F','\x46','\x20','\x4D','\x45','\x52','\x43','\x48','\x41','\x4E','\x54','\x41','\x42','\x49','\x4C','\x49','\x54','\x59','\x2C','\x0A','\x46','\x49','\x54','\x4E','\x45','\x53','\x53','\x20','\x46','\x4F','\x52','\x20','\x41','\x20','\x50','\x41','\x52','\x54','\x49','\x43','\x55','\x4C','\x41','\x52','\x20','\x50','\x55','\x52','\x50','\x4F','\x53','\x45','\x20','\x41','\x4E','\x44','\x20','\x4E','\x4F','\x4E','\x49','\x4E','\x46','\x52','\x49','\x4E','\x47','\x45','\x4D','\x45','\x4E','\x54','\x2E','\x20','\x49','\x4E','\x20','\x4E','\x4F','\x20','\x45','\x56','\x45','\x4E','\x54','\x20','\x53','\x48','\x41','\x4C','\x4C','\x20','\x54','\x48','\x45','\x0A','\x41','\x55','\x54','\x48','\x4F','\x52','\x53','\x20','\x4F','\x52','\x20','\x43','\x4F','\x50','\x59','\x52','\x49','\x47','\x48','\x54','\x20','\x48','\x4F','\x4C','\x44','\x45','\x52','\x53','\x20','\x42','\x45','\x20','\x4C','\x49','\x41','\x42','\x4C','\x45','\x20','\x46','\x4F','\x52','\x20','\x41','\x4E','\x59','\x20','\x43','\x4C','\x41','\x49','\x4D','\x2C','\x20','\x44','\x41','\x4D','\x41','\x47','\x45','\x53','\x20','\x4F','\x52','\x20','\x4F','\x54','\x48','\x45','\x52','\x0A','\x4C','\x49','\x41','\x42','\x49','\x4C','\x49','\x54','\x59','\x2C','\x20','\x57','\x48','\x45','\x54','\x48','\x45','\x52','\x20','\x49','\x4E','\x20','\x41','\x4E','\x20','\x41','\x43','\x54','\x49','\x4F','\x4E','\x20','\x4F','\x46','\x20','\x43','\x4F','\x4E','\x54','\x52','\x41','\x43','\x54','\x2C','\x20','\x54','\x4F','\x52','\x54','\x20','\x4F','\x52','\x20','\x4F','\x54','\x48','\x45','\x52','\x57','\x49','\x53','\x45','\x2C','\x20','\x41','\x52','\x49','\x53','\x49','\x4E','\x47','\x20','\x46','\x52','\x4F','\x4D','\x2C','\x0A','\x4F','\x55','\x54','\x20','\x4F','\x46','\x20','\x4F','\x52','\x20','\x49','\x4E','\x20','\x43','\x4F','\x4E','\x4E','\x45','\x43','\x54','\x49','\x4F','\x4E','\x20','\x57','\x49','\x54','\x48','\x20','\x54','\x48','\x45','\x20','\x53','\x4F','\x46','\x54','\x57','\x41','\x52','\x45','\x20','\x4F','\x52','\x20','\x54','\x48','\x45','\x20','\x55','\x53','\x45','\x20','\x4F','\x52','\x20','\x4F','\x54','\x48','\x45','\x52','\x20','\x44','\x45','\x41','\x4C','\x49','\x4E','\x47','\x53','\x20','\x49','\x4E','\x20','\x54','\x48','\x45','\x0A','\x53','\x4F','\x46','\x54','\x57','\x41','\x52','\x45','\x2E','\x0A', -------------------------------------------------------------------------------- /licences/emscripten-browser-file.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Armchair-Software 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /licences/freetype-bdf.h: -------------------------------------------------------------------------------- 1 | '\x43','\x6F','\x70','\x79','\x72','\x69','\x67','\x68','\x74','\x20','\x28','\x43','\x29','\x20','\x32','\x30','\x30','\x31','\x2D','\x32','\x30','\x30','\x32','\x20','\x62','\x79','\x20','\x46','\x72','\x61','\x6E','\x63','\x65','\x73','\x63','\x6F','\x20','\x5A','\x61','\x70','\x70','\x61','\x20','\x4E','\x61','\x72','\x64','\x65','\x6C','\x6C','\x69','\x0D','\x0A','\x0D','\x0A','\x50','\x65','\x72','\x6D','\x69','\x73','\x73','\x69','\x6F','\x6E','\x20','\x69','\x73','\x20','\x68','\x65','\x72','\x65','\x62','\x79','\x20','\x67','\x72','\x61','\x6E','\x74','\x65','\x64','\x2C','\x20','\x66','\x72','\x65','\x65','\x20','\x6F','\x66','\x20','\x63','\x68','\x61','\x72','\x67','\x65','\x2C','\x20','\x74','\x6F','\x20','\x61','\x6E','\x79','\x20','\x70','\x65','\x72','\x73','\x6F','\x6E','\x20','\x6F','\x62','\x74','\x61','\x69','\x6E','\x69','\x6E','\x67','\x0D','\x0A','\x61','\x20','\x63','\x6F','\x70','\x79','\x20','\x6F','\x66','\x20','\x74','\x68','\x69','\x73','\x20','\x73','\x6F','\x66','\x74','\x77','\x61','\x72','\x65','\x20','\x61','\x6E','\x64','\x20','\x61','\x73','\x73','\x6F','\x63','\x69','\x61','\x74','\x65','\x64','\x20','\x64','\x6F','\x63','\x75','\x6D','\x65','\x6E','\x74','\x61','\x74','\x69','\x6F','\x6E','\x20','\x66','\x69','\x6C','\x65','\x73','\x20','\x28','\x74','\x68','\x65','\x0D','\x0A','\x22','\x53','\x6F','\x66','\x74','\x77','\x61','\x72','\x65','\x22','\x29','\x2C','\x20','\x74','\x6F','\x20','\x64','\x65','\x61','\x6C','\x20','\x69','\x6E','\x20','\x74','\x68','\x65','\x20','\x53','\x6F','\x66','\x74','\x77','\x61','\x72','\x65','\x20','\x77','\x69','\x74','\x68','\x6F','\x75','\x74','\x20','\x72','\x65','\x73','\x74','\x72','\x69','\x63','\x74','\x69','\x6F','\x6E','\x2C','\x20','\x69','\x6E','\x63','\x6C','\x75','\x64','\x69','\x6E','\x67','\x0D','\x0A','\x77','\x69','\x74','\x68','\x6F','\x75','\x74','\x20','\x6C','\x69','\x6D','\x69','\x74','\x61','\x74','\x69','\x6F','\x6E','\x20','\x74','\x68','\x65','\x20','\x72','\x69','\x67','\x68','\x74','\x73','\x20','\x74','\x6F','\x20','\x75','\x73','\x65','\x2C','\x20','\x63','\x6F','\x70','\x79','\x2C','\x20','\x6D','\x6F','\x64','\x69','\x66','\x79','\x2C','\x20','\x6D','\x65','\x72','\x67','\x65','\x2C','\x20','\x70','\x75','\x62','\x6C','\x69','\x73','\x68','\x2C','\x0D','\x0A','\x64','\x69','\x73','\x74','\x72','\x69','\x62','\x75','\x74','\x65','\x2C','\x20','\x73','\x75','\x62','\x6C','\x69','\x63','\x65','\x6E','\x73','\x65','\x2C','\x20','\x61','\x6E','\x64','\x2F','\x6F','\x72','\x20','\x73','\x65','\x6C','\x6C','\x20','\x63','\x6F','\x70','\x69','\x65','\x73','\x20','\x6F','\x66','\x20','\x74','\x68','\x65','\x20','\x53','\x6F','\x66','\x74','\x77','\x61','\x72','\x65','\x2C','\x20','\x61','\x6E','\x64','\x20','\x74','\x6F','\x0D','\x0A','\x70','\x65','\x72','\x6D','\x69','\x74','\x20','\x70','\x65','\x72','\x73','\x6F','\x6E','\x73','\x20','\x74','\x6F','\x20','\x77','\x68','\x6F','\x6D','\x20','\x74','\x68','\x65','\x20','\x53','\x6F','\x66','\x74','\x77','\x61','\x72','\x65','\x20','\x69','\x73','\x20','\x66','\x75','\x72','\x6E','\x69','\x73','\x68','\x65','\x64','\x20','\x74','\x6F','\x20','\x64','\x6F','\x20','\x73','\x6F','\x2C','\x20','\x73','\x75','\x62','\x6A','\x65','\x63','\x74','\x20','\x74','\x6F','\x0D','\x0A','\x74','\x68','\x65','\x20','\x66','\x6F','\x6C','\x6C','\x6F','\x77','\x69','\x6E','\x67','\x20','\x63','\x6F','\x6E','\x64','\x69','\x74','\x69','\x6F','\x6E','\x73','\x3A','\x0D','\x0A','\x0D','\x0A','\x54','\x68','\x65','\x20','\x61','\x62','\x6F','\x76','\x65','\x20','\x63','\x6F','\x70','\x79','\x72','\x69','\x67','\x68','\x74','\x20','\x6E','\x6F','\x74','\x69','\x63','\x65','\x20','\x61','\x6E','\x64','\x20','\x74','\x68','\x69','\x73','\x20','\x70','\x65','\x72','\x6D','\x69','\x73','\x73','\x69','\x6F','\x6E','\x20','\x6E','\x6F','\x74','\x69','\x63','\x65','\x20','\x73','\x68','\x61','\x6C','\x6C','\x20','\x62','\x65','\x0D','\x0A','\x69','\x6E','\x63','\x6C','\x75','\x64','\x65','\x64','\x20','\x69','\x6E','\x20','\x61','\x6C','\x6C','\x20','\x63','\x6F','\x70','\x69','\x65','\x73','\x20','\x6F','\x72','\x20','\x73','\x75','\x62','\x73','\x74','\x61','\x6E','\x74','\x69','\x61','\x6C','\x20','\x70','\x6F','\x72','\x74','\x69','\x6F','\x6E','\x73','\x20','\x6F','\x66','\x20','\x74','\x68','\x65','\x20','\x53','\x6F','\x66','\x74','\x77','\x61','\x72','\x65','\x2E','\x0D','\x0A','\x0D','\x0A','\x54','\x48','\x45','\x20','\x53','\x4F','\x46','\x54','\x57','\x41','\x52','\x45','\x20','\x49','\x53','\x20','\x50','\x52','\x4F','\x56','\x49','\x44','\x45','\x44','\x20','\x22','\x41','\x53','\x20','\x49','\x53','\x22','\x2C','\x20','\x57','\x49','\x54','\x48','\x4F','\x55','\x54','\x20','\x57','\x41','\x52','\x52','\x41','\x4E','\x54','\x59','\x20','\x4F','\x46','\x20','\x41','\x4E','\x59','\x20','\x4B','\x49','\x4E','\x44','\x2C','\x0D','\x0A','\x45','\x58','\x50','\x52','\x45','\x53','\x53','\x20','\x4F','\x52','\x20','\x49','\x4D','\x50','\x4C','\x49','\x45','\x44','\x2C','\x20','\x49','\x4E','\x43','\x4C','\x55','\x44','\x49','\x4E','\x47','\x20','\x42','\x55','\x54','\x20','\x4E','\x4F','\x54','\x20','\x4C','\x49','\x4D','\x49','\x54','\x45','\x44','\x20','\x54','\x4F','\x20','\x54','\x48','\x45','\x20','\x57','\x41','\x52','\x52','\x41','\x4E','\x54','\x49','\x45','\x53','\x20','\x4F','\x46','\x0D','\x0A','\x4D','\x45','\x52','\x43','\x48','\x41','\x4E','\x54','\x41','\x42','\x49','\x4C','\x49','\x54','\x59','\x2C','\x20','\x46','\x49','\x54','\x4E','\x45','\x53','\x53','\x20','\x46','\x4F','\x52','\x20','\x41','\x20','\x50','\x41','\x52','\x54','\x49','\x43','\x55','\x4C','\x41','\x52','\x20','\x50','\x55','\x52','\x50','\x4F','\x53','\x45','\x20','\x41','\x4E','\x44','\x20','\x4E','\x4F','\x4E','\x49','\x4E','\x46','\x52','\x49','\x4E','\x47','\x45','\x4D','\x45','\x4E','\x54','\x2E','\x0D','\x0A','\x49','\x4E','\x20','\x4E','\x4F','\x20','\x45','\x56','\x45','\x4E','\x54','\x20','\x53','\x48','\x41','\x4C','\x4C','\x20','\x54','\x48','\x45','\x20','\x41','\x55','\x54','\x48','\x4F','\x52','\x53','\x20','\x4F','\x52','\x20','\x43','\x4F','\x50','\x59','\x52','\x49','\x47','\x48','\x54','\x20','\x48','\x4F','\x4C','\x44','\x45','\x52','\x53','\x20','\x42','\x45','\x20','\x4C','\x49','\x41','\x42','\x4C','\x45','\x20','\x46','\x4F','\x52','\x20','\x41','\x4E','\x59','\x0D','\x0A','\x43','\x4C','\x41','\x49','\x4D','\x2C','\x20','\x44','\x41','\x4D','\x41','\x47','\x45','\x53','\x20','\x4F','\x52','\x20','\x4F','\x54','\x48','\x45','\x52','\x20','\x4C','\x49','\x41','\x42','\x49','\x4C','\x49','\x54','\x59','\x2C','\x20','\x57','\x48','\x45','\x54','\x48','\x45','\x52','\x20','\x49','\x4E','\x20','\x41','\x4E','\x20','\x41','\x43','\x54','\x49','\x4F','\x4E','\x20','\x4F','\x46','\x20','\x43','\x4F','\x4E','\x54','\x52','\x41','\x43','\x54','\x2C','\x0D','\x0A','\x54','\x4F','\x52','\x54','\x20','\x4F','\x52','\x20','\x4F','\x54','\x48','\x45','\x52','\x57','\x49','\x53','\x45','\x2C','\x20','\x41','\x52','\x49','\x53','\x49','\x4E','\x47','\x20','\x46','\x52','\x4F','\x4D','\x2C','\x20','\x4F','\x55','\x54','\x20','\x4F','\x46','\x20','\x4F','\x52','\x20','\x49','\x4E','\x20','\x43','\x4F','\x4E','\x4E','\x45','\x43','\x54','\x49','\x4F','\x4E','\x20','\x57','\x49','\x54','\x48','\x20','\x54','\x48','\x45','\x0D','\x0A','\x53','\x4F','\x46','\x54','\x57','\x41','\x52','\x45','\x20','\x4F','\x52','\x20','\x54','\x48','\x45','\x20','\x55','\x53','\x45','\x20','\x4F','\x52','\x20','\x4F','\x54','\x48','\x45','\x52','\x20','\x44','\x45','\x41','\x4C','\x49','\x4E','\x47','\x53','\x20','\x49','\x4E','\x20','\x54','\x48','\x45','\x20','\x53','\x4F','\x46','\x54','\x57','\x41','\x52','\x45','\x2E','\x0D','\x0A','\x0D','\x0A','\x2A','\x2A','\x2A','\x20','\x50','\x6F','\x72','\x74','\x69','\x6F','\x6E','\x73','\x20','\x6F','\x66','\x20','\x74','\x68','\x65','\x20','\x64','\x72','\x69','\x76','\x65','\x72','\x20','\x28','\x74','\x68','\x61','\x74','\x20','\x69','\x73','\x2C','\x20','\x62','\x64','\x66','\x6C','\x69','\x62','\x2E','\x63','\x20','\x61','\x6E','\x64','\x20','\x62','\x64','\x66','\x2E','\x68','\x29','\x3A','\x0D','\x0A','\x0D','\x0A','\x43','\x6F','\x70','\x79','\x72','\x69','\x67','\x68','\x74','\x20','\x32','\x30','\x30','\x30','\x20','\x43','\x6F','\x6D','\x70','\x75','\x74','\x69','\x6E','\x67','\x20','\x52','\x65','\x73','\x65','\x61','\x72','\x63','\x68','\x20','\x4C','\x61','\x62','\x73','\x2C','\x20','\x4E','\x65','\x77','\x20','\x4D','\x65','\x78','\x69','\x63','\x6F','\x20','\x53','\x74','\x61','\x74','\x65','\x20','\x55','\x6E','\x69','\x76','\x65','\x72','\x73','\x69','\x74','\x79','\x0D','\x0A','\x43','\x6F','\x70','\x79','\x72','\x69','\x67','\x68','\x74','\x20','\x32','\x30','\x30','\x31','\x2D','\x32','\x30','\x30','\x32','\x2C','\x20','\x32','\x30','\x31','\x31','\x20','\x46','\x72','\x61','\x6E','\x63','\x65','\x73','\x63','\x6F','\x20','\x5A','\x61','\x70','\x70','\x61','\x20','\x4E','\x61','\x72','\x64','\x65','\x6C','\x6C','\x69','\x0D','\x0A','\x0D','\x0A','\x50','\x65','\x72','\x6D','\x69','\x73','\x73','\x69','\x6F','\x6E','\x20','\x69','\x73','\x20','\x68','\x65','\x72','\x65','\x62','\x79','\x20','\x67','\x72','\x61','\x6E','\x74','\x65','\x64','\x2C','\x20','\x66','\x72','\x65','\x65','\x20','\x6F','\x66','\x20','\x63','\x68','\x61','\x72','\x67','\x65','\x2C','\x20','\x74','\x6F','\x20','\x61','\x6E','\x79','\x20','\x70','\x65','\x72','\x73','\x6F','\x6E','\x20','\x6F','\x62','\x74','\x61','\x69','\x6E','\x69','\x6E','\x67','\x20','\x61','\x0D','\x0A','\x63','\x6F','\x70','\x79','\x20','\x6F','\x66','\x20','\x74','\x68','\x69','\x73','\x20','\x73','\x6F','\x66','\x74','\x77','\x61','\x72','\x65','\x20','\x61','\x6E','\x64','\x20','\x61','\x73','\x73','\x6F','\x63','\x69','\x61','\x74','\x65','\x64','\x20','\x64','\x6F','\x63','\x75','\x6D','\x65','\x6E','\x74','\x61','\x74','\x69','\x6F','\x6E','\x20','\x66','\x69','\x6C','\x65','\x73','\x20','\x28','\x74','\x68','\x65','\x20','\x22','\x53','\x6F','\x66','\x74','\x77','\x61','\x72','\x65','\x22','\x29','\x2C','\x0D','\x0A','\x74','\x6F','\x20','\x64','\x65','\x61','\x6C','\x20','\x69','\x6E','\x20','\x74','\x68','\x65','\x20','\x53','\x6F','\x66','\x74','\x77','\x61','\x72','\x65','\x20','\x77','\x69','\x74','\x68','\x6F','\x75','\x74','\x20','\x72','\x65','\x73','\x74','\x72','\x69','\x63','\x74','\x69','\x6F','\x6E','\x2C','\x20','\x69','\x6E','\x63','\x6C','\x75','\x64','\x69','\x6E','\x67','\x20','\x77','\x69','\x74','\x68','\x6F','\x75','\x74','\x20','\x6C','\x69','\x6D','\x69','\x74','\x61','\x74','\x69','\x6F','\x6E','\x0D','\x0A','\x74','\x68','\x65','\x20','\x72','\x69','\x67','\x68','\x74','\x73','\x20','\x74','\x6F','\x20','\x75','\x73','\x65','\x2C','\x20','\x63','\x6F','\x70','\x79','\x2C','\x20','\x6D','\x6F','\x64','\x69','\x66','\x79','\x2C','\x20','\x6D','\x65','\x72','\x67','\x65','\x2C','\x20','\x70','\x75','\x62','\x6C','\x69','\x73','\x68','\x2C','\x20','\x64','\x69','\x73','\x74','\x72','\x69','\x62','\x75','\x74','\x65','\x2C','\x20','\x73','\x75','\x62','\x6C','\x69','\x63','\x65','\x6E','\x73','\x65','\x2C','\x0D','\x0A','\x61','\x6E','\x64','\x2F','\x6F','\x72','\x20','\x73','\x65','\x6C','\x6C','\x20','\x63','\x6F','\x70','\x69','\x65','\x73','\x20','\x6F','\x66','\x20','\x74','\x68','\x65','\x20','\x53','\x6F','\x66','\x74','\x77','\x61','\x72','\x65','\x2C','\x20','\x61','\x6E','\x64','\x20','\x74','\x6F','\x20','\x70','\x65','\x72','\x6D','\x69','\x74','\x20','\x70','\x65','\x72','\x73','\x6F','\x6E','\x73','\x20','\x74','\x6F','\x20','\x77','\x68','\x6F','\x6D','\x20','\x74','\x68','\x65','\x0D','\x0A','\x53','\x6F','\x66','\x74','\x77','\x61','\x72','\x65','\x20','\x69','\x73','\x20','\x66','\x75','\x72','\x6E','\x69','\x73','\x68','\x65','\x64','\x20','\x74','\x6F','\x20','\x64','\x6F','\x20','\x73','\x6F','\x2C','\x20','\x73','\x75','\x62','\x6A','\x65','\x63','\x74','\x20','\x74','\x6F','\x20','\x74','\x68','\x65','\x20','\x66','\x6F','\x6C','\x6C','\x6F','\x77','\x69','\x6E','\x67','\x20','\x63','\x6F','\x6E','\x64','\x69','\x74','\x69','\x6F','\x6E','\x73','\x3A','\x0D','\x0A','\x0D','\x0A','\x54','\x68','\x65','\x20','\x61','\x62','\x6F','\x76','\x65','\x20','\x63','\x6F','\x70','\x79','\x72','\x69','\x67','\x68','\x74','\x20','\x6E','\x6F','\x74','\x69','\x63','\x65','\x20','\x61','\x6E','\x64','\x20','\x74','\x68','\x69','\x73','\x20','\x70','\x65','\x72','\x6D','\x69','\x73','\x73','\x69','\x6F','\x6E','\x20','\x6E','\x6F','\x74','\x69','\x63','\x65','\x20','\x73','\x68','\x61','\x6C','\x6C','\x20','\x62','\x65','\x20','\x69','\x6E','\x63','\x6C','\x75','\x64','\x65','\x64','\x20','\x69','\x6E','\x0D','\x0A','\x61','\x6C','\x6C','\x20','\x63','\x6F','\x70','\x69','\x65','\x73','\x20','\x6F','\x72','\x20','\x73','\x75','\x62','\x73','\x74','\x61','\x6E','\x74','\x69','\x61','\x6C','\x20','\x70','\x6F','\x72','\x74','\x69','\x6F','\x6E','\x73','\x20','\x6F','\x66','\x20','\x74','\x68','\x65','\x20','\x53','\x6F','\x66','\x74','\x77','\x61','\x72','\x65','\x2E','\x0D','\x0A','\x0D','\x0A','\x54','\x48','\x45','\x20','\x53','\x4F','\x46','\x54','\x57','\x41','\x52','\x45','\x20','\x49','\x53','\x20','\x50','\x52','\x4F','\x56','\x49','\x44','\x45','\x44','\x20','\x22','\x41','\x53','\x20','\x49','\x53','\x22','\x2C','\x20','\x57','\x49','\x54','\x48','\x4F','\x55','\x54','\x20','\x57','\x41','\x52','\x52','\x41','\x4E','\x54','\x59','\x20','\x4F','\x46','\x20','\x41','\x4E','\x59','\x20','\x4B','\x49','\x4E','\x44','\x2C','\x20','\x45','\x58','\x50','\x52','\x45','\x53','\x53','\x20','\x4F','\x52','\x0D','\x0A','\x49','\x4D','\x50','\x4C','\x49','\x45','\x44','\x2C','\x20','\x49','\x4E','\x43','\x4C','\x55','\x44','\x49','\x4E','\x47','\x20','\x42','\x55','\x54','\x20','\x4E','\x4F','\x54','\x20','\x4C','\x49','\x4D','\x49','\x54','\x45','\x44','\x20','\x54','\x4F','\x20','\x54','\x48','\x45','\x20','\x57','\x41','\x52','\x52','\x41','\x4E','\x54','\x49','\x45','\x53','\x20','\x4F','\x46','\x20','\x4D','\x45','\x52','\x43','\x48','\x41','\x4E','\x54','\x41','\x42','\x49','\x4C','\x49','\x54','\x59','\x2C','\x0D','\x0A','\x46','\x49','\x54','\x4E','\x45','\x53','\x53','\x20','\x46','\x4F','\x52','\x20','\x41','\x20','\x50','\x41','\x52','\x54','\x49','\x43','\x55','\x4C','\x41','\x52','\x20','\x50','\x55','\x52','\x50','\x4F','\x53','\x45','\x20','\x41','\x4E','\x44','\x20','\x4E','\x4F','\x4E','\x49','\x4E','\x46','\x52','\x49','\x4E','\x47','\x45','\x4D','\x45','\x4E','\x54','\x2E','\x20','\x20','\x49','\x4E','\x20','\x4E','\x4F','\x20','\x45','\x56','\x45','\x4E','\x54','\x20','\x53','\x48','\x41','\x4C','\x4C','\x0D','\x0A','\x54','\x48','\x45','\x20','\x43','\x4F','\x4D','\x50','\x55','\x54','\x49','\x4E','\x47','\x20','\x52','\x45','\x53','\x45','\x41','\x52','\x43','\x48','\x20','\x4C','\x41','\x42','\x20','\x4F','\x52','\x20','\x4E','\x45','\x57','\x20','\x4D','\x45','\x58','\x49','\x43','\x4F','\x20','\x53','\x54','\x41','\x54','\x45','\x20','\x55','\x4E','\x49','\x56','\x45','\x52','\x53','\x49','\x54','\x59','\x20','\x42','\x45','\x20','\x4C','\x49','\x41','\x42','\x4C','\x45','\x20','\x46','\x4F','\x52','\x20','\x41','\x4E','\x59','\x0D','\x0A','\x43','\x4C','\x41','\x49','\x4D','\x2C','\x20','\x44','\x41','\x4D','\x41','\x47','\x45','\x53','\x20','\x4F','\x52','\x20','\x4F','\x54','\x48','\x45','\x52','\x20','\x4C','\x49','\x41','\x42','\x49','\x4C','\x49','\x54','\x59','\x2C','\x20','\x57','\x48','\x45','\x54','\x48','\x45','\x52','\x20','\x49','\x4E','\x20','\x41','\x4E','\x20','\x41','\x43','\x54','\x49','\x4F','\x4E','\x20','\x4F','\x46','\x20','\x43','\x4F','\x4E','\x54','\x52','\x41','\x43','\x54','\x2C','\x20','\x54','\x4F','\x52','\x54','\x0D','\x0A','\x4F','\x52','\x20','\x4F','\x54','\x48','\x45','\x52','\x57','\x49','\x53','\x45','\x2C','\x20','\x41','\x52','\x49','\x53','\x49','\x4E','\x47','\x20','\x46','\x52','\x4F','\x4D','\x2C','\x20','\x4F','\x55','\x54','\x20','\x4F','\x46','\x20','\x4F','\x52','\x20','\x49','\x4E','\x20','\x43','\x4F','\x4E','\x4E','\x45','\x43','\x54','\x49','\x4F','\x4E','\x20','\x57','\x49','\x54','\x48','\x20','\x54','\x48','\x45','\x20','\x53','\x4F','\x46','\x54','\x57','\x41','\x52','\x45','\x20','\x4F','\x52','\x0D','\x0A','\x54','\x48','\x45','\x20','\x55','\x53','\x45','\x20','\x4F','\x52','\x20','\x4F','\x54','\x48','\x45','\x52','\x20','\x44','\x45','\x41','\x4C','\x49','\x4E','\x47','\x53','\x20','\x49','\x4E','\x20','\x54','\x48','\x45','\x20','\x53','\x4F','\x46','\x54','\x57','\x41','\x52','\x45','\x2E', -------------------------------------------------------------------------------- /licences/freetype-bdf.txt: -------------------------------------------------------------------------------- 1 | Copyright (C) 2001-2002 by Francesco Zappa Nardelli 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 18 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 19 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 20 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | *** Portions of the driver (that is, bdflib.c and bdf.h): 23 | 24 | Copyright 2000 Computing Research Labs, New Mexico State University 25 | Copyright 2001-2002, 2011 Francesco Zappa Nardelli 26 | 27 | Permission is hereby granted, free of charge, to any person obtaining a 28 | copy of this software and associated documentation files (the "Software"), 29 | to deal in the Software without restriction, including without limitation 30 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 31 | and/or sell copies of the Software, and to permit persons to whom the 32 | Software is furnished to do so, subject to the following conditions: 33 | 34 | The above copyright notice and this permission notice shall be included in 35 | all copies or substantial portions of the Software. 36 | 37 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 38 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 39 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 40 | THE COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY BE LIABLE FOR ANY 41 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT 42 | OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR 43 | THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /licences/freetype-ft-hb.h: -------------------------------------------------------------------------------- 1 | '\x43','\x6F','\x70','\x79','\x72','\x69','\x67','\x68','\x74','\x20','\xC2','\xA9','\x20','\x32','\x30','\x30','\x39','\x2C','\x20','\x32','\x30','\x32','\x33','\x20','\x20','\x52','\x65','\x64','\x20','\x48','\x61','\x74','\x2C','\x20','\x49','\x6E','\x63','\x2E','\x0D','\x0A','\x43','\x6F','\x70','\x79','\x72','\x69','\x67','\x68','\x74','\x20','\xC2','\xA9','\x20','\x32','\x30','\x31','\x35','\x20','\x20','\x47','\x6F','\x6F','\x67','\x6C','\x65','\x2C','\x20','\x49','\x6E','\x63','\x2E','\x0D','\x0A','\x0D','\x0A','\x50','\x65','\x72','\x6D','\x69','\x73','\x73','\x69','\x6F','\x6E','\x20','\x69','\x73','\x20','\x68','\x65','\x72','\x65','\x62','\x79','\x20','\x67','\x72','\x61','\x6E','\x74','\x65','\x64','\x2C','\x20','\x77','\x69','\x74','\x68','\x6F','\x75','\x74','\x20','\x77','\x72','\x69','\x74','\x74','\x65','\x6E','\x20','\x61','\x67','\x72','\x65','\x65','\x6D','\x65','\x6E','\x74','\x20','\x61','\x6E','\x64','\x20','\x77','\x69','\x74','\x68','\x6F','\x75','\x74','\x0D','\x0A','\x6C','\x69','\x63','\x65','\x6E','\x73','\x65','\x20','\x6F','\x72','\x20','\x72','\x6F','\x79','\x61','\x6C','\x74','\x79','\x20','\x66','\x65','\x65','\x73','\x2C','\x20','\x74','\x6F','\x20','\x75','\x73','\x65','\x2C','\x20','\x63','\x6F','\x70','\x79','\x2C','\x20','\x6D','\x6F','\x64','\x69','\x66','\x79','\x2C','\x20','\x61','\x6E','\x64','\x20','\x64','\x69','\x73','\x74','\x72','\x69','\x62','\x75','\x74','\x65','\x20','\x74','\x68','\x69','\x73','\x0D','\x0A','\x73','\x6F','\x66','\x74','\x77','\x61','\x72','\x65','\x20','\x61','\x6E','\x64','\x20','\x69','\x74','\x73','\x20','\x64','\x6F','\x63','\x75','\x6D','\x65','\x6E','\x74','\x61','\x74','\x69','\x6F','\x6E','\x20','\x66','\x6F','\x72','\x20','\x61','\x6E','\x79','\x20','\x70','\x75','\x72','\x70','\x6F','\x73','\x65','\x2C','\x20','\x70','\x72','\x6F','\x76','\x69','\x64','\x65','\x64','\x20','\x74','\x68','\x61','\x74','\x20','\x74','\x68','\x65','\x0D','\x0A','\x61','\x62','\x6F','\x76','\x65','\x20','\x63','\x6F','\x70','\x79','\x72','\x69','\x67','\x68','\x74','\x20','\x6E','\x6F','\x74','\x69','\x63','\x65','\x20','\x61','\x6E','\x64','\x20','\x74','\x68','\x65','\x20','\x66','\x6F','\x6C','\x6C','\x6F','\x77','\x69','\x6E','\x67','\x20','\x74','\x77','\x6F','\x20','\x70','\x61','\x72','\x61','\x67','\x72','\x61','\x70','\x68','\x73','\x20','\x61','\x70','\x70','\x65','\x61','\x72','\x20','\x69','\x6E','\x0D','\x0A','\x61','\x6C','\x6C','\x20','\x63','\x6F','\x70','\x69','\x65','\x73','\x20','\x6F','\x66','\x20','\x74','\x68','\x69','\x73','\x20','\x73','\x6F','\x66','\x74','\x77','\x61','\x72','\x65','\x2E','\x0D','\x0A','\x0D','\x0A','\x49','\x4E','\x20','\x4E','\x4F','\x20','\x45','\x56','\x45','\x4E','\x54','\x20','\x53','\x48','\x41','\x4C','\x4C','\x20','\x54','\x48','\x45','\x20','\x43','\x4F','\x50','\x59','\x52','\x49','\x47','\x48','\x54','\x20','\x48','\x4F','\x4C','\x44','\x45','\x52','\x20','\x42','\x45','\x20','\x4C','\x49','\x41','\x42','\x4C','\x45','\x20','\x54','\x4F','\x20','\x41','\x4E','\x59','\x20','\x50','\x41','\x52','\x54','\x59','\x20','\x46','\x4F','\x52','\x0D','\x0A','\x44','\x49','\x52','\x45','\x43','\x54','\x2C','\x20','\x49','\x4E','\x44','\x49','\x52','\x45','\x43','\x54','\x2C','\x20','\x53','\x50','\x45','\x43','\x49','\x41','\x4C','\x2C','\x20','\x49','\x4E','\x43','\x49','\x44','\x45','\x4E','\x54','\x41','\x4C','\x2C','\x20','\x4F','\x52','\x20','\x43','\x4F','\x4E','\x53','\x45','\x51','\x55','\x45','\x4E','\x54','\x49','\x41','\x4C','\x20','\x44','\x41','\x4D','\x41','\x47','\x45','\x53','\x0D','\x0A','\x41','\x52','\x49','\x53','\x49','\x4E','\x47','\x20','\x4F','\x55','\x54','\x20','\x4F','\x46','\x20','\x54','\x48','\x45','\x20','\x55','\x53','\x45','\x20','\x4F','\x46','\x20','\x54','\x48','\x49','\x53','\x20','\x53','\x4F','\x46','\x54','\x57','\x41','\x52','\x45','\x20','\x41','\x4E','\x44','\x20','\x49','\x54','\x53','\x20','\x44','\x4F','\x43','\x55','\x4D','\x45','\x4E','\x54','\x41','\x54','\x49','\x4F','\x4E','\x2C','\x20','\x45','\x56','\x45','\x4E','\x0D','\x0A','\x49','\x46','\x20','\x54','\x48','\x45','\x20','\x43','\x4F','\x50','\x59','\x52','\x49','\x47','\x48','\x54','\x20','\x48','\x4F','\x4C','\x44','\x45','\x52','\x20','\x48','\x41','\x53','\x20','\x42','\x45','\x45','\x4E','\x20','\x41','\x44','\x56','\x49','\x53','\x45','\x44','\x20','\x4F','\x46','\x20','\x54','\x48','\x45','\x20','\x50','\x4F','\x53','\x53','\x49','\x42','\x49','\x4C','\x49','\x54','\x59','\x20','\x4F','\x46','\x20','\x53','\x55','\x43','\x48','\x0D','\x0A','\x44','\x41','\x4D','\x41','\x47','\x45','\x2E','\x0D','\x0A','\x0D','\x0A','\x54','\x48','\x45','\x20','\x43','\x4F','\x50','\x59','\x52','\x49','\x47','\x48','\x54','\x20','\x48','\x4F','\x4C','\x44','\x45','\x52','\x20','\x53','\x50','\x45','\x43','\x49','\x46','\x49','\x43','\x41','\x4C','\x4C','\x59','\x20','\x44','\x49','\x53','\x43','\x4C','\x41','\x49','\x4D','\x53','\x20','\x41','\x4E','\x59','\x20','\x57','\x41','\x52','\x52','\x41','\x4E','\x54','\x49','\x45','\x53','\x2C','\x20','\x49','\x4E','\x43','\x4C','\x55','\x44','\x49','\x4E','\x47','\x2C','\x0D','\x0A','\x42','\x55','\x54','\x20','\x4E','\x4F','\x54','\x20','\x4C','\x49','\x4D','\x49','\x54','\x45','\x44','\x20','\x54','\x4F','\x2C','\x20','\x54','\x48','\x45','\x20','\x49','\x4D','\x50','\x4C','\x49','\x45','\x44','\x20','\x57','\x41','\x52','\x52','\x41','\x4E','\x54','\x49','\x45','\x53','\x20','\x4F','\x46','\x20','\x4D','\x45','\x52','\x43','\x48','\x41','\x4E','\x54','\x41','\x42','\x49','\x4C','\x49','\x54','\x59','\x20','\x41','\x4E','\x44','\x0D','\x0A','\x46','\x49','\x54','\x4E','\x45','\x53','\x53','\x20','\x46','\x4F','\x52','\x20','\x41','\x20','\x50','\x41','\x52','\x54','\x49','\x43','\x55','\x4C','\x41','\x52','\x20','\x50','\x55','\x52','\x50','\x4F','\x53','\x45','\x2E','\x20','\x20','\x54','\x48','\x45','\x20','\x53','\x4F','\x46','\x54','\x57','\x41','\x52','\x45','\x20','\x50','\x52','\x4F','\x56','\x49','\x44','\x45','\x44','\x20','\x48','\x45','\x52','\x45','\x55','\x4E','\x44','\x45','\x52','\x20','\x49','\x53','\x0D','\x0A','\x4F','\x4E','\x20','\x41','\x4E','\x20','\x22','\x41','\x53','\x20','\x49','\x53','\x22','\x20','\x42','\x41','\x53','\x49','\x53','\x2C','\x20','\x41','\x4E','\x44','\x20','\x54','\x48','\x45','\x20','\x43','\x4F','\x50','\x59','\x52','\x49','\x47','\x48','\x54','\x20','\x48','\x4F','\x4C','\x44','\x45','\x52','\x20','\x48','\x41','\x53','\x20','\x4E','\x4F','\x20','\x4F','\x42','\x4C','\x49','\x47','\x41','\x54','\x49','\x4F','\x4E','\x20','\x54','\x4F','\x0D','\x0A','\x50','\x52','\x4F','\x56','\x49','\x44','\x45','\x20','\x4D','\x41','\x49','\x4E','\x54','\x45','\x4E','\x41','\x4E','\x43','\x45','\x2C','\x20','\x53','\x55','\x50','\x50','\x4F','\x52','\x54','\x2C','\x20','\x55','\x50','\x44','\x41','\x54','\x45','\x53','\x2C','\x20','\x45','\x4E','\x48','\x41','\x4E','\x43','\x45','\x4D','\x45','\x4E','\x54','\x53','\x2C','\x20','\x4F','\x52','\x20','\x4D','\x4F','\x44','\x49','\x46','\x49','\x43','\x41','\x54','\x49','\x4F','\x4E','\x53','\x2E','\x0D','\x0A','\x0D','\x0A','\x52','\x65','\x64','\x20','\x48','\x61','\x74','\x20','\x41','\x75','\x74','\x68','\x6F','\x72','\x28','\x73','\x29','\x3A','\x20','\x42','\x65','\x68','\x64','\x61','\x64','\x20','\x45','\x73','\x66','\x61','\x68','\x62','\x6F','\x64','\x2C','\x20','\x4D','\x61','\x74','\x74','\x68','\x69','\x61','\x73','\x20','\x43','\x6C','\x61','\x73','\x65','\x6E','\x0D','\x0A','\x47','\x6F','\x6F','\x67','\x6C','\x65','\x20','\x41','\x75','\x74','\x68','\x6F','\x72','\x28','\x73','\x29','\x3A','\x20','\x42','\x65','\x68','\x64','\x61','\x64','\x20','\x45','\x73','\x66','\x61','\x68','\x62','\x6F','\x64', -------------------------------------------------------------------------------- /licences/freetype-ft-hb.txt: -------------------------------------------------------------------------------- 1 | Copyright © 2009, 2023 Red Hat, Inc. 2 | Copyright © 2015 Google, Inc. 3 | 4 | Permission is hereby granted, without written agreement and without 5 | license or royalty fees, to use, copy, modify, and distribute this 6 | software and its documentation for any purpose, provided that the 7 | above copyright notice and the following two paragraphs appear in 8 | all copies of this software. 9 | 10 | IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR 11 | DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 12 | ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN 13 | IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 14 | DAMAGE. 15 | 16 | THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, 17 | BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 18 | FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS 19 | ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO 20 | PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 21 | 22 | Red Hat Author(s): Behdad Esfahbod, Matthias Clasen 23 | Google Author(s): Behdad Esfahbod -------------------------------------------------------------------------------- /licences/freetype-fthash.h: -------------------------------------------------------------------------------- 1 | '\x43','\x6F','\x70','\x79','\x72','\x69','\x67','\x68','\x74','\x20','\x32','\x30','\x30','\x30','\x20','\x43','\x6F','\x6D','\x70','\x75','\x74','\x69','\x6E','\x67','\x20','\x52','\x65','\x73','\x65','\x61','\x72','\x63','\x68','\x20','\x4C','\x61','\x62','\x73','\x2C','\x20','\x4E','\x65','\x77','\x20','\x4D','\x65','\x78','\x69','\x63','\x6F','\x20','\x53','\x74','\x61','\x74','\x65','\x20','\x55','\x6E','\x69','\x76','\x65','\x72','\x73','\x69','\x74','\x79','\x0D','\x0A','\x43','\x6F','\x70','\x79','\x72','\x69','\x67','\x68','\x74','\x20','\x32','\x30','\x30','\x31','\x2D','\x32','\x30','\x31','\x35','\x0D','\x0A','\x20','\x20','\x46','\x72','\x61','\x6E','\x63','\x65','\x73','\x63','\x6F','\x20','\x5A','\x61','\x70','\x70','\x61','\x20','\x4E','\x61','\x72','\x64','\x65','\x6C','\x6C','\x69','\x0D','\x0A','\x0D','\x0A','\x50','\x65','\x72','\x6D','\x69','\x73','\x73','\x69','\x6F','\x6E','\x20','\x69','\x73','\x20','\x68','\x65','\x72','\x65','\x62','\x79','\x20','\x67','\x72','\x61','\x6E','\x74','\x65','\x64','\x2C','\x20','\x66','\x72','\x65','\x65','\x20','\x6F','\x66','\x20','\x63','\x68','\x61','\x72','\x67','\x65','\x2C','\x20','\x74','\x6F','\x20','\x61','\x6E','\x79','\x20','\x70','\x65','\x72','\x73','\x6F','\x6E','\x20','\x6F','\x62','\x74','\x61','\x69','\x6E','\x69','\x6E','\x67','\x20','\x61','\x0D','\x0A','\x63','\x6F','\x70','\x79','\x20','\x6F','\x66','\x20','\x74','\x68','\x69','\x73','\x20','\x73','\x6F','\x66','\x74','\x77','\x61','\x72','\x65','\x20','\x61','\x6E','\x64','\x20','\x61','\x73','\x73','\x6F','\x63','\x69','\x61','\x74','\x65','\x64','\x20','\x64','\x6F','\x63','\x75','\x6D','\x65','\x6E','\x74','\x61','\x74','\x69','\x6F','\x6E','\x20','\x66','\x69','\x6C','\x65','\x73','\x20','\x28','\x74','\x68','\x65','\x20','\x22','\x53','\x6F','\x66','\x74','\x77','\x61','\x72','\x65','\x22','\x29','\x2C','\x0D','\x0A','\x74','\x6F','\x20','\x64','\x65','\x61','\x6C','\x20','\x69','\x6E','\x20','\x74','\x68','\x65','\x20','\x53','\x6F','\x66','\x74','\x77','\x61','\x72','\x65','\x20','\x77','\x69','\x74','\x68','\x6F','\x75','\x74','\x20','\x72','\x65','\x73','\x74','\x72','\x69','\x63','\x74','\x69','\x6F','\x6E','\x2C','\x20','\x69','\x6E','\x63','\x6C','\x75','\x64','\x69','\x6E','\x67','\x20','\x77','\x69','\x74','\x68','\x6F','\x75','\x74','\x20','\x6C','\x69','\x6D','\x69','\x74','\x61','\x74','\x69','\x6F','\x6E','\x0D','\x0A','\x74','\x68','\x65','\x20','\x72','\x69','\x67','\x68','\x74','\x73','\x20','\x74','\x6F','\x20','\x75','\x73','\x65','\x2C','\x20','\x63','\x6F','\x70','\x79','\x2C','\x20','\x6D','\x6F','\x64','\x69','\x66','\x79','\x2C','\x20','\x6D','\x65','\x72','\x67','\x65','\x2C','\x20','\x70','\x75','\x62','\x6C','\x69','\x73','\x68','\x2C','\x20','\x64','\x69','\x73','\x74','\x72','\x69','\x62','\x75','\x74','\x65','\x2C','\x20','\x73','\x75','\x62','\x6C','\x69','\x63','\x65','\x6E','\x73','\x65','\x2C','\x0D','\x0A','\x61','\x6E','\x64','\x2F','\x6F','\x72','\x20','\x73','\x65','\x6C','\x6C','\x20','\x63','\x6F','\x70','\x69','\x65','\x73','\x20','\x6F','\x66','\x20','\x74','\x68','\x65','\x20','\x53','\x6F','\x66','\x74','\x77','\x61','\x72','\x65','\x2C','\x20','\x61','\x6E','\x64','\x20','\x74','\x6F','\x20','\x70','\x65','\x72','\x6D','\x69','\x74','\x20','\x70','\x65','\x72','\x73','\x6F','\x6E','\x73','\x20','\x74','\x6F','\x20','\x77','\x68','\x6F','\x6D','\x20','\x74','\x68','\x65','\x0D','\x0A','\x53','\x6F','\x66','\x74','\x77','\x61','\x72','\x65','\x20','\x69','\x73','\x20','\x66','\x75','\x72','\x6E','\x69','\x73','\x68','\x65','\x64','\x20','\x74','\x6F','\x20','\x64','\x6F','\x20','\x73','\x6F','\x2C','\x20','\x73','\x75','\x62','\x6A','\x65','\x63','\x74','\x20','\x74','\x6F','\x20','\x74','\x68','\x65','\x20','\x66','\x6F','\x6C','\x6C','\x6F','\x77','\x69','\x6E','\x67','\x20','\x63','\x6F','\x6E','\x64','\x69','\x74','\x69','\x6F','\x6E','\x73','\x3A','\x0D','\x0A','\x0D','\x0A','\x54','\x68','\x65','\x20','\x61','\x62','\x6F','\x76','\x65','\x20','\x63','\x6F','\x70','\x79','\x72','\x69','\x67','\x68','\x74','\x20','\x6E','\x6F','\x74','\x69','\x63','\x65','\x20','\x61','\x6E','\x64','\x20','\x74','\x68','\x69','\x73','\x20','\x70','\x65','\x72','\x6D','\x69','\x73','\x73','\x69','\x6F','\x6E','\x20','\x6E','\x6F','\x74','\x69','\x63','\x65','\x20','\x73','\x68','\x61','\x6C','\x6C','\x20','\x62','\x65','\x20','\x69','\x6E','\x63','\x6C','\x75','\x64','\x65','\x64','\x20','\x69','\x6E','\x0D','\x0A','\x61','\x6C','\x6C','\x20','\x63','\x6F','\x70','\x69','\x65','\x73','\x20','\x6F','\x72','\x20','\x73','\x75','\x62','\x73','\x74','\x61','\x6E','\x74','\x69','\x61','\x6C','\x20','\x70','\x6F','\x72','\x74','\x69','\x6F','\x6E','\x73','\x20','\x6F','\x66','\x20','\x74','\x68','\x65','\x20','\x53','\x6F','\x66','\x74','\x77','\x61','\x72','\x65','\x2E','\x0D','\x0A','\x0D','\x0A','\x54','\x48','\x45','\x20','\x53','\x4F','\x46','\x54','\x57','\x41','\x52','\x45','\x20','\x49','\x53','\x20','\x50','\x52','\x4F','\x56','\x49','\x44','\x45','\x44','\x20','\x22','\x41','\x53','\x20','\x49','\x53','\x22','\x2C','\x20','\x57','\x49','\x54','\x48','\x4F','\x55','\x54','\x20','\x57','\x41','\x52','\x52','\x41','\x4E','\x54','\x59','\x20','\x4F','\x46','\x20','\x41','\x4E','\x59','\x20','\x4B','\x49','\x4E','\x44','\x2C','\x20','\x45','\x58','\x50','\x52','\x45','\x53','\x53','\x20','\x4F','\x52','\x0D','\x0A','\x49','\x4D','\x50','\x4C','\x49','\x45','\x44','\x2C','\x20','\x49','\x4E','\x43','\x4C','\x55','\x44','\x49','\x4E','\x47','\x20','\x42','\x55','\x54','\x20','\x4E','\x4F','\x54','\x20','\x4C','\x49','\x4D','\x49','\x54','\x45','\x44','\x20','\x54','\x4F','\x20','\x54','\x48','\x45','\x20','\x57','\x41','\x52','\x52','\x41','\x4E','\x54','\x49','\x45','\x53','\x20','\x4F','\x46','\x20','\x4D','\x45','\x52','\x43','\x48','\x41','\x4E','\x54','\x41','\x42','\x49','\x4C','\x49','\x54','\x59','\x2C','\x0D','\x0A','\x46','\x49','\x54','\x4E','\x45','\x53','\x53','\x20','\x46','\x4F','\x52','\x20','\x41','\x20','\x50','\x41','\x52','\x54','\x49','\x43','\x55','\x4C','\x41','\x52','\x20','\x50','\x55','\x52','\x50','\x4F','\x53','\x45','\x20','\x41','\x4E','\x44','\x20','\x4E','\x4F','\x4E','\x49','\x4E','\x46','\x52','\x49','\x4E','\x47','\x45','\x4D','\x45','\x4E','\x54','\x2E','\x20','\x20','\x49','\x4E','\x20','\x4E','\x4F','\x20','\x45','\x56','\x45','\x4E','\x54','\x20','\x53','\x48','\x41','\x4C','\x4C','\x0D','\x0A','\x54','\x48','\x45','\x20','\x43','\x4F','\x4D','\x50','\x55','\x54','\x49','\x4E','\x47','\x20','\x52','\x45','\x53','\x45','\x41','\x52','\x43','\x48','\x20','\x4C','\x41','\x42','\x20','\x4F','\x52','\x20','\x4E','\x45','\x57','\x20','\x4D','\x45','\x58','\x49','\x43','\x4F','\x20','\x53','\x54','\x41','\x54','\x45','\x20','\x55','\x4E','\x49','\x56','\x45','\x52','\x53','\x49','\x54','\x59','\x20','\x42','\x45','\x20','\x4C','\x49','\x41','\x42','\x4C','\x45','\x20','\x46','\x4F','\x52','\x20','\x41','\x4E','\x59','\x0D','\x0A','\x43','\x4C','\x41','\x49','\x4D','\x2C','\x20','\x44','\x41','\x4D','\x41','\x47','\x45','\x53','\x20','\x4F','\x52','\x20','\x4F','\x54','\x48','\x45','\x52','\x20','\x4C','\x49','\x41','\x42','\x49','\x4C','\x49','\x54','\x59','\x2C','\x20','\x57','\x48','\x45','\x54','\x48','\x45','\x52','\x20','\x49','\x4E','\x20','\x41','\x4E','\x20','\x41','\x43','\x54','\x49','\x4F','\x4E','\x20','\x4F','\x46','\x20','\x43','\x4F','\x4E','\x54','\x52','\x41','\x43','\x54','\x2C','\x20','\x54','\x4F','\x52','\x54','\x0D','\x0A','\x4F','\x52','\x20','\x4F','\x54','\x48','\x45','\x52','\x57','\x49','\x53','\x45','\x2C','\x20','\x41','\x52','\x49','\x53','\x49','\x4E','\x47','\x20','\x46','\x52','\x4F','\x4D','\x2C','\x20','\x4F','\x55','\x54','\x20','\x4F','\x46','\x20','\x4F','\x52','\x20','\x49','\x4E','\x20','\x43','\x4F','\x4E','\x4E','\x45','\x43','\x54','\x49','\x4F','\x4E','\x20','\x57','\x49','\x54','\x48','\x20','\x54','\x48','\x45','\x20','\x53','\x4F','\x46','\x54','\x57','\x41','\x52','\x45','\x20','\x4F','\x52','\x0D','\x0A','\x54','\x48','\x45','\x20','\x55','\x53','\x45','\x20','\x4F','\x52','\x20','\x4F','\x54','\x48','\x45','\x52','\x20','\x44','\x45','\x41','\x4C','\x49','\x4E','\x47','\x53','\x20','\x49','\x4E','\x20','\x54','\x48','\x45','\x20','\x53','\x4F','\x46','\x54','\x57','\x41','\x52','\x45','\x2E', -------------------------------------------------------------------------------- /licences/freetype-fthash.txt: -------------------------------------------------------------------------------- 1 | Copyright 2000 Computing Research Labs, New Mexico State University 2 | Copyright 2001-2015 3 | Francesco Zappa Nardelli 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a 6 | copy of this software and associated documentation files (the "Software"), 7 | to deal in the Software without restriction, including without limitation 8 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 | and/or sell copies of the Software, and to permit persons to whom the 10 | Software is furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 | THE COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY BE LIABLE FOR ANY 19 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT 20 | OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR 21 | THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /licences/freetype-pcf.h: -------------------------------------------------------------------------------- 1 | '\x43','\x6F','\x70','\x79','\x72','\x69','\x67','\x68','\x74','\x20','\x28','\x43','\x29','\x20','\x32','\x30','\x30','\x30','\x20','\x62','\x79','\x20','\x46','\x72','\x61','\x6E','\x63','\x65','\x73','\x63','\x6F','\x20','\x5A','\x61','\x70','\x70','\x61','\x20','\x4E','\x61','\x72','\x64','\x65','\x6C','\x6C','\x69','\x0D','\x0A','\x0D','\x0A','\x50','\x65','\x72','\x6D','\x69','\x73','\x73','\x69','\x6F','\x6E','\x20','\x69','\x73','\x20','\x68','\x65','\x72','\x65','\x62','\x79','\x20','\x67','\x72','\x61','\x6E','\x74','\x65','\x64','\x2C','\x20','\x66','\x72','\x65','\x65','\x20','\x6F','\x66','\x20','\x63','\x68','\x61','\x72','\x67','\x65','\x2C','\x20','\x74','\x6F','\x20','\x61','\x6E','\x79','\x20','\x70','\x65','\x72','\x73','\x6F','\x6E','\x20','\x6F','\x62','\x74','\x61','\x69','\x6E','\x69','\x6E','\x67','\x0D','\x0A','\x61','\x20','\x63','\x6F','\x70','\x79','\x20','\x6F','\x66','\x20','\x74','\x68','\x69','\x73','\x20','\x73','\x6F','\x66','\x74','\x77','\x61','\x72','\x65','\x20','\x61','\x6E','\x64','\x20','\x61','\x73','\x73','\x6F','\x63','\x69','\x61','\x74','\x65','\x64','\x20','\x64','\x6F','\x63','\x75','\x6D','\x65','\x6E','\x74','\x61','\x74','\x69','\x6F','\x6E','\x20','\x66','\x69','\x6C','\x65','\x73','\x20','\x28','\x74','\x68','\x65','\x0D','\x0A','\x22','\x53','\x6F','\x66','\x74','\x77','\x61','\x72','\x65','\x22','\x29','\x2C','\x20','\x74','\x6F','\x20','\x64','\x65','\x61','\x6C','\x20','\x69','\x6E','\x20','\x74','\x68','\x65','\x20','\x53','\x6F','\x66','\x74','\x77','\x61','\x72','\x65','\x20','\x77','\x69','\x74','\x68','\x6F','\x75','\x74','\x20','\x72','\x65','\x73','\x74','\x72','\x69','\x63','\x74','\x69','\x6F','\x6E','\x2C','\x20','\x69','\x6E','\x63','\x6C','\x75','\x64','\x69','\x6E','\x67','\x0D','\x0A','\x77','\x69','\x74','\x68','\x6F','\x75','\x74','\x20','\x6C','\x69','\x6D','\x69','\x74','\x61','\x74','\x69','\x6F','\x6E','\x20','\x74','\x68','\x65','\x20','\x72','\x69','\x67','\x68','\x74','\x73','\x20','\x74','\x6F','\x20','\x75','\x73','\x65','\x2C','\x20','\x63','\x6F','\x70','\x79','\x2C','\x20','\x6D','\x6F','\x64','\x69','\x66','\x79','\x2C','\x20','\x6D','\x65','\x72','\x67','\x65','\x2C','\x20','\x70','\x75','\x62','\x6C','\x69','\x73','\x68','\x2C','\x0D','\x0A','\x64','\x69','\x73','\x74','\x72','\x69','\x62','\x75','\x74','\x65','\x2C','\x20','\x73','\x75','\x62','\x6C','\x69','\x63','\x65','\x6E','\x73','\x65','\x2C','\x20','\x61','\x6E','\x64','\x2F','\x6F','\x72','\x20','\x73','\x65','\x6C','\x6C','\x20','\x63','\x6F','\x70','\x69','\x65','\x73','\x20','\x6F','\x66','\x20','\x74','\x68','\x65','\x20','\x53','\x6F','\x66','\x74','\x77','\x61','\x72','\x65','\x2C','\x20','\x61','\x6E','\x64','\x20','\x74','\x6F','\x0D','\x0A','\x70','\x65','\x72','\x6D','\x69','\x74','\x20','\x70','\x65','\x72','\x73','\x6F','\x6E','\x73','\x20','\x74','\x6F','\x20','\x77','\x68','\x6F','\x6D','\x20','\x74','\x68','\x65','\x20','\x53','\x6F','\x66','\x74','\x77','\x61','\x72','\x65','\x20','\x69','\x73','\x20','\x66','\x75','\x72','\x6E','\x69','\x73','\x68','\x65','\x64','\x20','\x74','\x6F','\x20','\x64','\x6F','\x20','\x73','\x6F','\x2C','\x20','\x73','\x75','\x62','\x6A','\x65','\x63','\x74','\x20','\x74','\x6F','\x0D','\x0A','\x74','\x68','\x65','\x20','\x66','\x6F','\x6C','\x6C','\x6F','\x77','\x69','\x6E','\x67','\x20','\x63','\x6F','\x6E','\x64','\x69','\x74','\x69','\x6F','\x6E','\x73','\x3A','\x0D','\x0A','\x0D','\x0A','\x54','\x68','\x65','\x20','\x61','\x62','\x6F','\x76','\x65','\x20','\x63','\x6F','\x70','\x79','\x72','\x69','\x67','\x68','\x74','\x20','\x6E','\x6F','\x74','\x69','\x63','\x65','\x20','\x61','\x6E','\x64','\x20','\x74','\x68','\x69','\x73','\x20','\x70','\x65','\x72','\x6D','\x69','\x73','\x73','\x69','\x6F','\x6E','\x20','\x6E','\x6F','\x74','\x69','\x63','\x65','\x20','\x73','\x68','\x61','\x6C','\x6C','\x20','\x62','\x65','\x0D','\x0A','\x69','\x6E','\x63','\x6C','\x75','\x64','\x65','\x64','\x20','\x69','\x6E','\x20','\x61','\x6C','\x6C','\x20','\x63','\x6F','\x70','\x69','\x65','\x73','\x20','\x6F','\x72','\x20','\x73','\x75','\x62','\x73','\x74','\x61','\x6E','\x74','\x69','\x61','\x6C','\x20','\x70','\x6F','\x72','\x74','\x69','\x6F','\x6E','\x73','\x20','\x6F','\x66','\x20','\x74','\x68','\x65','\x20','\x53','\x6F','\x66','\x74','\x77','\x61','\x72','\x65','\x2E','\x0D','\x0A','\x0D','\x0A','\x54','\x48','\x45','\x20','\x53','\x4F','\x46','\x54','\x57','\x41','\x52','\x45','\x20','\x49','\x53','\x20','\x50','\x52','\x4F','\x56','\x49','\x44','\x45','\x44','\x20','\x22','\x41','\x53','\x20','\x49','\x53','\x22','\x2C','\x20','\x57','\x49','\x54','\x48','\x4F','\x55','\x54','\x20','\x57','\x41','\x52','\x52','\x41','\x4E','\x54','\x59','\x20','\x4F','\x46','\x20','\x41','\x4E','\x59','\x20','\x4B','\x49','\x4E','\x44','\x2C','\x0D','\x0A','\x45','\x58','\x50','\x52','\x45','\x53','\x53','\x20','\x4F','\x52','\x20','\x49','\x4D','\x50','\x4C','\x49','\x45','\x44','\x2C','\x20','\x49','\x4E','\x43','\x4C','\x55','\x44','\x49','\x4E','\x47','\x20','\x42','\x55','\x54','\x20','\x4E','\x4F','\x54','\x20','\x4C','\x49','\x4D','\x49','\x54','\x45','\x44','\x20','\x54','\x4F','\x20','\x54','\x48','\x45','\x20','\x57','\x41','\x52','\x52','\x41','\x4E','\x54','\x49','\x45','\x53','\x20','\x4F','\x46','\x0D','\x0A','\x4D','\x45','\x52','\x43','\x48','\x41','\x4E','\x54','\x41','\x42','\x49','\x4C','\x49','\x54','\x59','\x2C','\x20','\x46','\x49','\x54','\x4E','\x45','\x53','\x53','\x20','\x46','\x4F','\x52','\x20','\x41','\x20','\x50','\x41','\x52','\x54','\x49','\x43','\x55','\x4C','\x41','\x52','\x20','\x50','\x55','\x52','\x50','\x4F','\x53','\x45','\x20','\x41','\x4E','\x44','\x20','\x4E','\x4F','\x4E','\x49','\x4E','\x46','\x52','\x49','\x4E','\x47','\x45','\x4D','\x45','\x4E','\x54','\x2E','\x0D','\x0A','\x49','\x4E','\x20','\x4E','\x4F','\x20','\x45','\x56','\x45','\x4E','\x54','\x20','\x53','\x48','\x41','\x4C','\x4C','\x20','\x54','\x48','\x45','\x20','\x41','\x55','\x54','\x48','\x4F','\x52','\x53','\x20','\x4F','\x52','\x20','\x43','\x4F','\x50','\x59','\x52','\x49','\x47','\x48','\x54','\x20','\x48','\x4F','\x4C','\x44','\x45','\x52','\x53','\x20','\x42','\x45','\x20','\x4C','\x49','\x41','\x42','\x4C','\x45','\x20','\x46','\x4F','\x52','\x20','\x41','\x4E','\x59','\x0D','\x0A','\x43','\x4C','\x41','\x49','\x4D','\x2C','\x20','\x44','\x41','\x4D','\x41','\x47','\x45','\x53','\x20','\x4F','\x52','\x20','\x4F','\x54','\x48','\x45','\x52','\x20','\x4C','\x49','\x41','\x42','\x49','\x4C','\x49','\x54','\x59','\x2C','\x20','\x57','\x48','\x45','\x54','\x48','\x45','\x52','\x20','\x49','\x4E','\x20','\x41','\x4E','\x20','\x41','\x43','\x54','\x49','\x4F','\x4E','\x20','\x4F','\x46','\x20','\x43','\x4F','\x4E','\x54','\x52','\x41','\x43','\x54','\x2C','\x0D','\x0A','\x54','\x4F','\x52','\x54','\x20','\x4F','\x52','\x20','\x4F','\x54','\x48','\x45','\x52','\x57','\x49','\x53','\x45','\x2C','\x20','\x41','\x52','\x49','\x53','\x49','\x4E','\x47','\x20','\x46','\x52','\x4F','\x4D','\x2C','\x20','\x4F','\x55','\x54','\x20','\x4F','\x46','\x20','\x4F','\x52','\x20','\x49','\x4E','\x20','\x43','\x4F','\x4E','\x4E','\x45','\x43','\x54','\x49','\x4F','\x4E','\x20','\x57','\x49','\x54','\x48','\x20','\x54','\x48','\x45','\x0D','\x0A','\x53','\x4F','\x46','\x54','\x57','\x41','\x52','\x45','\x20','\x4F','\x52','\x20','\x54','\x48','\x45','\x20','\x55','\x53','\x45','\x20','\x4F','\x52','\x20','\x4F','\x54','\x48','\x45','\x52','\x20','\x44','\x45','\x41','\x4C','\x49','\x4E','\x47','\x53','\x20','\x49','\x4E','\x20','\x54','\x48','\x45','\x20','\x53','\x4F','\x46','\x54','\x57','\x41','\x52','\x45','\x2E', -------------------------------------------------------------------------------- /licences/freetype-pcf.txt: -------------------------------------------------------------------------------- 1 | Copyright (C) 2000 by Francesco Zappa Nardelli 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 18 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 19 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 20 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /licences/freetype.txt: -------------------------------------------------------------------------------- 1 | The FreeType Project LICENSE 2 | ---------------------------- 3 | 4 | 2006-Jan-27 5 | 6 | Copyright 1996-2002, 2006 by 7 | David Turner, Robert Wilhelm, and Werner Lemberg 8 | 9 | 10 | 11 | Introduction 12 | ============ 13 | 14 | The FreeType Project is distributed in several archive packages; 15 | some of them may contain, in addition to the FreeType font engine, 16 | various tools and contributions which rely on, or relate to, the 17 | FreeType Project. 18 | 19 | This license applies to all files found in such packages, and 20 | which do not fall under their own explicit license. The license 21 | affects thus the FreeType font engine, the test programs, 22 | documentation and makefiles, at the very least. 23 | 24 | This license was inspired by the BSD, Artistic, and IJG 25 | (Independent JPEG Group) licenses, which all encourage inclusion 26 | and use of free software in commercial and freeware products 27 | alike. As a consequence, its main points are that: 28 | 29 | o We don't promise that this software works. However, we will be 30 | interested in any kind of bug reports. (`as is' distribution) 31 | 32 | o You can use this software for whatever you want, in parts or 33 | full form, without having to pay us. (`royalty-free' usage) 34 | 35 | o You may not pretend that you wrote this software. If you use 36 | it, or only parts of it, in a program, you must acknowledge 37 | somewhere in your documentation that you have used the 38 | FreeType code. (`credits') 39 | 40 | We specifically permit and encourage the inclusion of this 41 | software, with or without modifications, in commercial products. 42 | We disclaim all warranties covering The FreeType Project and 43 | assume no liability related to The FreeType Project. 44 | 45 | 46 | Finally, many people asked us for a preferred form for a 47 | credit/disclaimer to use in compliance with this license. We thus 48 | encourage you to use the following text: 49 | 50 | """ 51 | Portions of this software are copyright © The FreeType 52 | Project (www.freetype.org). All rights reserved. 53 | """ 54 | 55 | Please replace with the value from the FreeType version you 56 | actually use. 57 | 58 | 59 | Legal Terms 60 | =========== 61 | 62 | 0. Definitions 63 | -------------- 64 | 65 | Throughout this license, the terms `package', `FreeType Project', 66 | and `FreeType archive' refer to the set of files originally 67 | distributed by the authors (David Turner, Robert Wilhelm, and 68 | Werner Lemberg) as the `FreeType Project', be they named as alpha, 69 | beta or final release. 70 | 71 | `You' refers to the licensee, or person using the project, where 72 | `using' is a generic term including compiling the project's source 73 | code as well as linking it to form a `program' or `executable'. 74 | This program is referred to as `a program using the FreeType 75 | engine'. 76 | 77 | This license applies to all files distributed in the original 78 | FreeType Project, including all source code, binaries and 79 | documentation, unless otherwise stated in the file in its 80 | original, unmodified form as distributed in the original archive. 81 | If you are unsure whether or not a particular file is covered by 82 | this license, you must contact us to verify this. 83 | 84 | The FreeType Project is copyright (C) 1996-2000 by David Turner, 85 | Robert Wilhelm, and Werner Lemberg. All rights reserved except as 86 | specified below. 87 | 88 | 1. No Warranty 89 | -------------- 90 | 91 | THE FREETYPE PROJECT IS PROVIDED `AS IS' WITHOUT WARRANTY OF ANY 92 | KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 93 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 94 | PURPOSE. IN NO EVENT WILL ANY OF THE AUTHORS OR COPYRIGHT HOLDERS 95 | BE LIABLE FOR ANY DAMAGES CAUSED BY THE USE OR THE INABILITY TO 96 | USE, OF THE FREETYPE PROJECT. 97 | 98 | 2. Redistribution 99 | ----------------- 100 | 101 | This license grants a worldwide, royalty-free, perpetual and 102 | irrevocable right and license to use, execute, perform, compile, 103 | display, copy, create derivative works of, distribute and 104 | sublicense the FreeType Project (in both source and object code 105 | forms) and derivative works thereof for any purpose; and to 106 | authorize others to exercise some or all of the rights granted 107 | herein, subject to the following conditions: 108 | 109 | o Redistribution of source code must retain this license file 110 | (`FTL.TXT') unaltered; any additions, deletions or changes to 111 | the original files must be clearly indicated in accompanying 112 | documentation. The copyright notices of the unaltered, 113 | original files must be preserved in all copies of source 114 | files. 115 | 116 | o Redistribution in binary form must provide a disclaimer that 117 | states that the software is based in part of the work of the 118 | FreeType Team, in the distribution documentation. We also 119 | encourage you to put an URL to the FreeType web page in your 120 | documentation, though this isn't mandatory. 121 | 122 | These conditions apply to any software derived from or based on 123 | the FreeType Project, not just the unmodified files. If you use 124 | our work, you must acknowledge us. However, no fee need be paid 125 | to us. 126 | 127 | 3. Advertising 128 | -------------- 129 | 130 | Neither the FreeType authors and contributors nor you shall use 131 | the name of the other for commercial, advertising, or promotional 132 | purposes without specific prior written permission. 133 | 134 | We suggest, but do not require, that you use one or more of the 135 | following phrases to refer to this software in your documentation 136 | or advertising materials: `FreeType Project', `FreeType Engine', 137 | `FreeType library', or `FreeType Distribution'. 138 | 139 | As you have not signed this license, you are not required to 140 | accept it. However, as the FreeType Project is copyrighted 141 | material, only this license, or another one contracted with the 142 | authors, grants you the right to use, distribute, and modify it. 143 | Therefore, by using, distributing, or modifying the FreeType 144 | Project, you indicate that you understand and accept all the terms 145 | of this license. 146 | 147 | 4. Contacts 148 | ----------- 149 | 150 | There are two mailing lists related to FreeType: 151 | 152 | o freetype@nongnu.org 153 | 154 | Discusses general use and applications of FreeType, as well as 155 | future and wanted additions to the library and distribution. 156 | If you are looking for support, start in this list if you 157 | haven't found anything to help you in the documentation. 158 | 159 | o freetype-devel@nongnu.org 160 | 161 | Discusses bugs, as well as engine internals, design issues, 162 | specific licenses, porting, etc. 163 | 164 | Our home page can be found at 165 | 166 | https://www.freetype.org 167 | 168 | 169 | --- end of FTL.TXT --- -------------------------------------------------------------------------------- /licences/inconsolata.txt: -------------------------------------------------------------------------------- 1 | Copyright 2006 The Inconsolata Project Authors 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | This license is copied below, and is also available with a FAQ at: 5 | http://scripts.sil.org/OFL 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. -------------------------------------------------------------------------------- /licences/noto-sans.txt: -------------------------------------------------------------------------------- 1 | Copyright 2015-2021 Google LLC. All Rights Reserved. 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | This license is copied below, and is also available with a FAQ at: 5 | http://scripts.sil.org/OFL 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #ifdef __EMSCRIPTEN__ 4 | #include 5 | #include 6 | #endif 7 | 8 | #ifndef __EMSCRIPTEN__ 9 | #define SDL_MAIN_USE_CALLBACKS 10 | #endif 11 | #include 12 | 13 | #include "frontend.h" 14 | 15 | #ifdef __EMSCRIPTEN__ 16 | static double next_time; 17 | static double time_delta; 18 | 19 | static void Terminate() 20 | { 21 | Frontend::Deinitialise(); 22 | emscripten_cancel_main_loop(); 23 | 24 | // Deinitialise persistent storage. 25 | EM_ASM({ 26 | FS.syncfs(false, function (err) {}); 27 | }, 0); 28 | } 29 | 30 | static void FrameRateCallback(const bool pal_mode) 31 | { 32 | time_delta = pal_mode ? Frontend::DivideByPALFramerate(1000.0) : Frontend::DivideByNTSCFramerate(1000.0); 33 | } 34 | 35 | static bool EventFilter(void* /*const userdata*/, SDL_Event* const event) 36 | { 37 | // The event loop will never have time to catch this, so we 38 | // must use this callback to intercept it as soon as possible. 39 | if (event->type == SDL_EVENT_TERMINATING) 40 | Terminate(); 41 | 42 | return 0; 43 | } 44 | 45 | extern "C" EMSCRIPTEN_KEEPALIVE void StorageLoaded() 46 | { 47 | const auto callback = []() 48 | { 49 | const double current_time = emscripten_get_now(); 50 | 51 | if (current_time >= next_time) 52 | { 53 | // If massively delayed, resynchronise to avoid fast-forwarding. 54 | if (current_time >= next_time + 100.0) 55 | next_time = current_time; 56 | 57 | next_time += time_delta; 58 | 59 | SDL_Event event; 60 | while (SDL_PollEvent(&event)) 61 | Frontend::HandleEvent(event); 62 | 63 | Frontend::Update(); 64 | 65 | if (Frontend::WantsToQuit()) 66 | Terminate(); 67 | } 68 | }; 69 | 70 | next_time = emscripten_get_now(); 71 | 72 | emscripten_set_main_loop(callback, 0, 0); 73 | 74 | if (Frontend::Initialise(0, nullptr, FrameRateCallback)) 75 | SDL_AddEventWatch(EventFilter, nullptr); 76 | } 77 | 78 | int main([[maybe_unused]] const int argc, [[maybe_unused]] char** const argv) 79 | { 80 | // Initialise persistent storage. 81 | EM_ASM({ 82 | FS.mount(IDBFS, {}, UTF8ToString($0)); 83 | 84 | FS.syncfs(true, function(err) { 85 | Module._StorageLoaded(); 86 | }); 87 | }, Frontend::GetConfigurationDirectoryPath().u8string().c_str()); 88 | return EXIT_SUCCESS; 89 | } 90 | 91 | #else 92 | static Uint64 time_delta; 93 | 94 | static void FrameRateCallback(const bool pal_mode) 95 | { 96 | if (pal_mode) 97 | { 98 | // Run at 50FPS 99 | time_delta = Frontend::DivideByPALFramerate(SDL_NS_PER_SECOND); 100 | } 101 | else 102 | { 103 | // Run at roughly 59.94FPS (60 divided by 1.001) 104 | time_delta = Frontend::DivideByNTSCFramerate(SDL_NS_PER_SECOND); 105 | } 106 | } 107 | 108 | SDL_AppResult SDL_AppInit([[maybe_unused]] void** const appstate, const int argc, char** const argv) 109 | { 110 | return Frontend::Initialise(argc, argv, FrameRateCallback) ? SDL_APP_CONTINUE : SDL_APP_FAILURE; 111 | } 112 | 113 | SDL_AppResult SDL_AppIterate([[maybe_unused]] void* const appstate) 114 | { 115 | static Uint64 next_time; 116 | 117 | const Uint64 current_time = SDL_GetTicksNS(); 118 | 119 | if (current_time < next_time) 120 | return SDL_APP_CONTINUE; 121 | 122 | // If massively delayed, resynchronise to avoid fast-forwarding. 123 | if (current_time >= next_time + SDL_NS_PER_SECOND / 10) 124 | next_time = current_time; 125 | 126 | next_time += time_delta; 127 | 128 | Frontend::Update(); 129 | return Frontend::WantsToQuit() ? SDL_APP_SUCCESS : SDL_APP_CONTINUE; 130 | } 131 | 132 | SDL_AppResult SDL_AppEvent([[maybe_unused]] void* const appstate, SDL_Event* const event) 133 | { 134 | Frontend::HandleEvent(*event); 135 | return Frontend::WantsToQuit() ? SDL_APP_SUCCESS : SDL_APP_CONTINUE; 136 | } 137 | 138 | void SDL_AppQuit([[maybe_unused]] void* const appstate, [[maybe_unused]] const SDL_AppResult result) 139 | { 140 | Frontend::Deinitialise(); 141 | } 142 | #endif 143 | -------------------------------------------------------------------------------- /raii-wrapper.h: -------------------------------------------------------------------------------- 1 | #ifndef RAII_WRAPPER_H 2 | #define RAII_WRAPPER_H 3 | 4 | #include 5 | 6 | #define MAKE_RAII_POINTER(NAME, TYPE, DELETER) \ 7 | struct NAME##Deleter \ 8 | { \ 9 | void operator()(TYPE* const pointer) { DELETER(pointer); } \ 10 | }; \ 11 | \ 12 | class NAME : public std::unique_ptr \ 13 | { \ 14 | public: \ 15 | using std::unique_ptr::unique_ptr; \ 16 | \ 17 | operator TYPE*() { return get(); } \ 18 | operator const TYPE*() const { return get(); } \ 19 | } 20 | 21 | #endif // RAII_WRAPPER_H 22 | -------------------------------------------------------------------------------- /scripts/AppImage/.gitignore: -------------------------------------------------------------------------------- 1 | # AppImage build directory. 2 | /build 3 | 4 | # Produced files. 5 | /clownmdemu*-x86_64.AppImage* 6 | -------------------------------------------------------------------------------- /scripts/AppImage/makeAppImage.sh: -------------------------------------------------------------------------------- 1 | mkdir -p build 2 | 3 | # Detect architecture. 4 | ARCH=$(uname -m) 5 | 6 | # Set LinuxDeploy filename based on architecture. 7 | LINUXDEPLOY_FILENAME=linuxdeploy-${ARCH}.AppImage 8 | 9 | # Download LinuxDeploy if not already downloaded. 10 | if [ ! -f "build/$LINUXDEPLOY_FILENAME" ]; then 11 | wget -O "build/$LINUXDEPLOY_FILENAME" "https://github.com/linuxdeploy/linuxdeploy/releases/download/1-alpha-20240109-1/$LINUXDEPLOY_FILENAME" 12 | if [ $? -ne 0 ]; then 13 | curl -L -o "build/$LINUXDEPLOY_FILENAME" "https://github.com/linuxdeploy/linuxdeploy/releases/download/1-alpha-20240109-1/$LINUXDEPLOY_FILENAME" 14 | fi 15 | fi 16 | 17 | # Make LinuxDeploy executable. 18 | chmod +x "build/$LINUXDEPLOY_FILENAME" 19 | 20 | # We want... 21 | # - An optimised Release build. 22 | # - For the program to not be installed to '/usr/local', so that LinuxDeploy detects it. 23 | # - Link-time optimisation, for improved optimisation. 24 | # - To set the CMake policy that normally prevents link-time optimisation. 25 | # - FreeType font rendering, since using a system library means less bloat in the executable. 26 | cmake -B build ../../ \ 27 | -DCMAKE_BUILD_TYPE=Release \ 28 | -DCMAKE_INSTALL_PREFIX=/usr \ 29 | -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON \ 30 | -DCMAKE_POLICY_DEFAULT_CMP0069=NEW \ 31 | -DCLOWNMDEMU_FRONTEND_FREETYPE=ON 32 | 33 | # Once again specify the Release build, for generators that require it be done this way. 34 | # Build in parallel to speed-up compilation greatly. 35 | cmake --build build --config Release --parallel $(nproc) 36 | 37 | # Make a temporary directory to install the built files into. 38 | # Make sure that it is fresh and empty, in case this script was ran before; we do not want old, leftover files. 39 | rm -rf build/AppDir 40 | mkdir -p build/AppDir 41 | 42 | # Install into the temporary directory. 43 | DESTDIR=AppDir cmake --build build --target install 44 | 45 | # Produce the AppImage, and bundle it with update metadata. 46 | LINUXDEPLOY_OUTPUT_VERSION=v1.4 \ 47 | LDAI_UPDATE_INFORMATION="gh-releases-zsync|Clownacy|clownmdemu-frontend|latest|ClownMDEmu-*${ARCH}.AppImage.zsync" \ 48 | "build/$LINUXDEPLOY_FILENAME" --appdir build/AppDir --output appimage 49 | -------------------------------------------------------------------------------- /sdl-wrapper.h: -------------------------------------------------------------------------------- 1 | #ifndef SDL_WRAPPER_H 2 | #define SDL_WRAPPER_H 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include "debug-log.h" 10 | #include "raii-wrapper.h" 11 | 12 | namespace SDL 13 | { 14 | struct FreeFunctor 15 | { 16 | void operator()(void* const pointer) { return SDL_free(pointer); } 17 | }; 18 | 19 | template 20 | using Pointer = std::unique_ptr; 21 | 22 | MAKE_RAII_POINTER(Window, SDL_Window, SDL_DestroyWindow ); 23 | MAKE_RAII_POINTER(Renderer, SDL_Renderer, SDL_DestroyRenderer ); 24 | MAKE_RAII_POINTER(Texture, SDL_Texture, SDL_DestroyTexture ); 25 | MAKE_RAII_POINTER(IOStream, SDL_IOStream, SDL_CloseIO ); 26 | MAKE_RAII_POINTER(AudioStream, SDL_AudioStream, SDL_DestroyAudioStream); 27 | MAKE_RAII_POINTER(SharedObject, SDL_SharedObject, SDL_UnloadObject ); 28 | 29 | template 30 | auto PathFunction(const char* const path, Args &&...args) 31 | { 32 | return Function(path, std::forward(args)...); 33 | }; 34 | 35 | template 36 | auto PathFunction(const std::string &path, Args &&...args) 37 | { 38 | return Function(path.c_str(), std::forward(args)...); 39 | }; 40 | 41 | template 42 | auto PathFunction(const std::filesystem::path &path, Args &&...args) 43 | { 44 | return Function(reinterpret_cast(path.u8string().c_str()), std::forward(args)...); 45 | }; 46 | 47 | template 48 | IOStream IOFromFile(const T &path, const char* const mode) { return IOStream(PathFunction(path, mode)); } 49 | 50 | template 51 | bool RemovePath(const T &path) { return PathFunction(path); } 52 | 53 | template 54 | bool GetPathInfo(const T &path, SDL_PathInfo* const info) { return PathFunction(path, info); } 55 | 56 | using Pixel = Uint32; 57 | 58 | inline Texture CreateTexture(Renderer &renderer, const SDL_TextureAccess access, const int width, const int height, const SDL_ScaleMode scale_mode, const bool blending = false) 59 | { 60 | // We're using ARGB8888 because it's more likely to be supported natively by the GPU, avoiding the need for constant conversions 61 | Texture texture(SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, access, width, height)); 62 | 63 | if (!texture) 64 | { 65 | Frontend::debug_log.Log("SDL_CreateTexture failed with the following message - '{}'", SDL_GetError()); 66 | } 67 | else 68 | { 69 | // Disable blending, since we don't need it 70 | if (!SDL_SetTextureBlendMode(texture, blending ? SDL_BLENDMODE_BLEND : SDL_BLENDMODE_NONE)) 71 | Frontend::debug_log.Log("SDL_SetTextureBlendMode failed with the following message - '{}'", SDL_GetError()); 72 | 73 | if (!SDL_SetTextureScaleMode(texture, scale_mode)) 74 | Frontend::debug_log.Log("SDL_SetTextureScaleMode failed with the following message - '{}'", SDL_GetError()); 75 | } 76 | 77 | return texture; 78 | } 79 | 80 | inline Texture CreateTextureWithBlending(Renderer &renderer, const SDL_TextureAccess access, const int width, const int height, const SDL_ScaleMode scale_mode) 81 | { 82 | return CreateTexture(renderer, access, width, height, scale_mode, true); 83 | } 84 | } 85 | 86 | #endif /* SDL_WRAPPER_H */ 87 | -------------------------------------------------------------------------------- /text-encoding.h: -------------------------------------------------------------------------------- 1 | #ifndef TEXT_ENCODING_H 2 | #define TEXT_ENCODING_H 3 | 4 | #include 5 | #include 6 | 7 | #include "common/core/clowncommon/clowncommon.h" 8 | 9 | /* Returns UTF-32 codepoint. */ 10 | /* Reads a maximum of two bytes. */ 11 | cc_u16l ShiftJISToUTF32(const unsigned char* const in_buffer, cc_u8f* const bytes_read); 12 | 13 | /* Returns number of bytes written (maximum 4). */ 14 | std::optional UTF32ToUTF8(const cc_u32f utf32_codepoint); 15 | 16 | #endif /* TEXT_ENCODING_H */ 17 | -------------------------------------------------------------------------------- /windows/common/winapi.cpp: -------------------------------------------------------------------------------- 1 | #include "winapi.h" 2 | 3 | #include 4 | 5 | #ifdef SDL_PLATFORM_WIN32 6 | #define WIN32_LEAN_AND_MEAN 7 | #include 8 | #endif 9 | 10 | #ifdef SDL_PLATFORM_WIN32 11 | using DwmSetWindowAttribute_Type = HRESULT(WINAPI*)(HWND hwnd, DWORD dwAttribute, LPCVOID pvAttribute, DWORD cbAttribute); 12 | 13 | static DwmSetWindowAttribute_Type GetDwmSetWindowAttribute() 14 | { 15 | static DwmSetWindowAttribute_Type DwmSetWindowAttribute_Function; 16 | 17 | if (DwmSetWindowAttribute_Function == nullptr) 18 | { 19 | static SDL::SharedObject dwmapi_dll; 20 | 21 | dwmapi_dll = SDL::SharedObject(SDL_LoadObject("dwmapi.dll")); 22 | 23 | if (dwmapi_dll != nullptr) 24 | DwmSetWindowAttribute_Function = reinterpret_cast(SDL_LoadFunction(dwmapi_dll, "DwmSetWindowAttribute")); 25 | } 26 | 27 | return DwmSetWindowAttribute_Function; 28 | } 29 | #endif 30 | 31 | void SetWindowTitleBarColour([[maybe_unused]] SDL_Window* const window, [[maybe_unused]] const unsigned char red, [[maybe_unused]] const unsigned char green, [[maybe_unused]] const unsigned char blue) 32 | { 33 | #ifdef SDL_PLATFORM_WIN32 34 | const HWND hwnd = static_cast(SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_WIN32_HWND_POINTER, nullptr)); 35 | 36 | if (hwnd == 0) 37 | return; 38 | 39 | const auto DwmSetWindowAttribute_Function = GetDwmSetWindowAttribute(); 40 | 41 | if (DwmSetWindowAttribute_Function != nullptr) 42 | { 43 | // Colour the title bar. 44 | const COLORREF winapi_colour = RGB(red, green, blue); 45 | DwmSetWindowAttribute_Function(hwnd, 35/*DWMWA_CAPTION_COLOR*/, &winapi_colour, sizeof(winapi_colour)); 46 | } 47 | #endif 48 | } 49 | 50 | void DisableWindowRounding([[maybe_unused]] SDL_Window* const window) 51 | { 52 | #ifdef SDL_PLATFORM_WIN32 53 | const HWND hwnd = static_cast(SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_WIN32_HWND_POINTER, nullptr)); 54 | 55 | if (hwnd == 0) 56 | return; 57 | 58 | const auto DwmSetWindowAttribute_Function = GetDwmSetWindowAttribute(); 59 | 60 | if (DwmSetWindowAttribute_Function == nullptr) 61 | return; 62 | 63 | // Disable the dumbass window rounding. 64 | // TODO: This function should probably be renamed. 65 | const int rounding_mode = 1; // DWMWCP_DONOTROUND 66 | DwmSetWindowAttribute_Function(hwnd, 33/*DWMWA_WINDOW_CORNER_PREFERENCE*/, &rounding_mode, sizeof(rounding_mode)); 67 | #endif 68 | } 69 | -------------------------------------------------------------------------------- /windows/common/winapi.h: -------------------------------------------------------------------------------- 1 | #ifndef WINAPI_H 2 | #define WINAPI_H 3 | 4 | #include "../../sdl-wrapper.h" 5 | 6 | void SetWindowTitleBarColour(SDL_Window *window, unsigned char red, unsigned char green, unsigned char blue); 7 | void DisableWindowRounding(SDL_Window *window); 8 | 9 | #endif /* WINAPI_H */ 10 | -------------------------------------------------------------------------------- /windows/common/window-popup.cpp: -------------------------------------------------------------------------------- 1 | #include "window-popup.h" 2 | -------------------------------------------------------------------------------- /windows/common/window-popup.h: -------------------------------------------------------------------------------- 1 | #ifndef WINDOW_POPUP_H 2 | #define WINDOW_POPUP_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | #include "window-with-dear-imgui.h" 11 | 12 | template 13 | class WindowPopup 14 | { 15 | private: 16 | std::optional window; 17 | std::string title; 18 | bool resizeable; 19 | WindowWithDearImGui *host_window; 20 | int dear_imgui_window_width, dear_imgui_window_height; 21 | 22 | bool Begin(bool* const open = nullptr, ImGuiWindowFlags window_flags = 0) 23 | { 24 | if (window.has_value()) 25 | { 26 | window->StartDearImGuiFrame(); 27 | 28 | const ImGuiViewport *viewport = ImGui::GetMainViewport(); 29 | 30 | // Maximise the Dear ImGui window. 31 | ImGui::SetNextWindowPos(viewport->WorkPos); 32 | ImGui::SetNextWindowSize(viewport->WorkSize); 33 | 34 | window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings; 35 | } 36 | else 37 | { 38 | ImGui::SetNextWindowSize(ImVec2(dear_imgui_window_width, dear_imgui_window_height), ImGuiCond_FirstUseEver); 39 | } 40 | 41 | if (!resizeable) 42 | window_flags |= ImGuiWindowFlags_AlwaysAutoResize; 43 | 44 | //ImGui::PushID(this); 45 | return ImGui::Begin(title.c_str(), open, window_flags); 46 | } 47 | 48 | void End() 49 | { 50 | ImGui::End(); 51 | //ImGui::PopID(); 52 | 53 | if (window.has_value()) 54 | window->FinishDearImGuiFrame(); 55 | } 56 | 57 | protected: 58 | void DoTable(const char* const name, const std::function &callback) 59 | { 60 | // Don't bother with IDs that have no visible part. 61 | if (name[0] != '#' || name[1] != '#') 62 | ImGui::SeparatorText(name); 63 | 64 | if (ImGui::BeginTable(name, 2, ImGuiTableFlags_Borders)) 65 | { 66 | ImGui::TableSetupColumn("Property"); 67 | ImGui::TableSetupColumn("Value"); 68 | ImGui::TableHeadersRow(); 69 | 70 | callback(); 71 | 72 | ImGui::EndTable(); 73 | } 74 | }; 75 | 76 | void DoProperty(ImFont* const font, const char* const label, const std::function &callback) 77 | { 78 | ImGui::TableNextColumn(); 79 | ImGui::TextUnformatted(label); 80 | ImGui::TableNextColumn(); 81 | if (font != nullptr) 82 | ImGui::PushFont(font); 83 | callback(); 84 | if (font != nullptr) 85 | ImGui::PopFont(); 86 | }; 87 | 88 | template 89 | void DoProperty(ImFont* const font, const char* const label, fmt::format_string format, T &&...args) 90 | { 91 | DoProperty(font, label, 92 | [&]() 93 | { 94 | ImGui::TextFormatted(format, std::forward(args)...); 95 | } 96 | ); 97 | }; 98 | 99 | public: 100 | WindowPopup(const char* const window_title, const int window_width, const int window_height, const bool resizeable, Window &parent_window, const std::optional forced_scale, WindowWithDearImGui* const host_window = nullptr) 101 | : title(window_title) 102 | , resizeable(resizeable) 103 | , host_window(host_window) 104 | { 105 | const auto dpi_scale = host_window != nullptr ? host_window->GetDPIScale() : parent_window.GetDPIScale(); 106 | const auto scaled_window_width = window_width * forced_scale.value_or(dpi_scale); 107 | const auto scaled_window_height = window_height * forced_scale.value_or(dpi_scale); 108 | 109 | if (host_window != nullptr) 110 | { 111 | dear_imgui_window_width = scaled_window_width; 112 | dear_imgui_window_height = scaled_window_height; 113 | } 114 | else 115 | { 116 | window.emplace(window_title, static_cast(scaled_window_width / dpi_scale), static_cast(scaled_window_height / dpi_scale), resizeable); 117 | SDL_SetWindowParent(window->GetSDLWindow(), parent_window.GetSDLWindow()); 118 | SDL_ShowWindow(window->GetSDLWindow()); 119 | } 120 | } 121 | 122 | template 123 | bool Display(Ts&&... arguments) 124 | { 125 | const auto derived = static_cast(this); 126 | 127 | bool alive = true; 128 | 129 | if (Begin(&alive, derived->window_flags)) 130 | derived->DisplayInternal(std::forward(arguments)...); 131 | 132 | End(); 133 | 134 | return alive; 135 | } 136 | 137 | bool IsWindow(const SDL_Window* const other_window) const 138 | { 139 | if (!window.has_value()) 140 | return false; 141 | 142 | return window->GetSDLWindow() == other_window; 143 | } 144 | 145 | void ProcessEvent(const SDL_Event &event) 146 | { 147 | const auto &previous_context = ImGui::GetCurrentContext(); 148 | window->MakeDearImGuiContextCurrent(); 149 | 150 | ImGui_ImplSDL3_ProcessEvent(&event); 151 | 152 | ImGui::SetCurrentContext(previous_context); 153 | } 154 | 155 | WindowWithDearImGui& GetWindow() 156 | { 157 | if (window.has_value()) 158 | return *window; 159 | else 160 | return *host_window; 161 | } 162 | 163 | ImFont* GetMonospaceFont() 164 | { 165 | return GetWindow().monospace_font; 166 | } 167 | 168 | bool IsResizeable() const { return resizeable; } 169 | }; 170 | 171 | #endif /* WINDOW_POPUP_H */ 172 | -------------------------------------------------------------------------------- /windows/common/window-with-dear-imgui.cpp: -------------------------------------------------------------------------------- 1 | #include "window-with-dear-imgui.h" 2 | 3 | #include 4 | 5 | #include "inconsolata-regular.h" 6 | #include "noto-sans-regular.h" 7 | 8 | 9 | /////////// 10 | // Fonts // 11 | /////////// 12 | 13 | static constexpr float UNSCALED_FONT_SIZE = 16.0f; 14 | 15 | float WindowWithDearImGui::GetFontScale() 16 | { 17 | return CalculateFontSize() / UNSCALED_FONT_SIZE; 18 | } 19 | 20 | unsigned int WindowWithDearImGui::CalculateFontSize() 21 | { 22 | // Note that we are purposefully flooring, as Dear ImGui's docs recommend. 23 | return static_cast(UNSCALED_FONT_SIZE * dpi_scale); 24 | } 25 | 26 | void WindowWithDearImGui::ReloadFonts(const unsigned int font_size) 27 | { 28 | ImGuiIO &io = ImGui::GetIO(); 29 | 30 | io.Fonts->Clear(); 31 | ImGui_ImplSDLRenderer3_DestroyFontsTexture(); 32 | 33 | ImFontConfig font_cfg; 34 | *fmt::format_to_n(font_cfg.Name, std::size(font_cfg.Name) - 1, "Noto Sans Regular, {}px", font_size).out = '\0'; 35 | io.Fonts->AddFontFromMemoryCompressedTTF(noto_sans_regular_compressed_data, noto_sans_regular_compressed_size, static_cast(font_size), &font_cfg); 36 | *fmt::format_to_n(font_cfg.Name, std::size(font_cfg.Name) - 1, "Inconsolata Regular, {}px", font_size).out = '\0'; 37 | monospace_font = io.Fonts->AddFontFromMemoryCompressedTTF(inconsolata_regular_compressed_data, inconsolata_regular_compressed_size, static_cast(font_size), &font_cfg); 38 | } 39 | 40 | 41 | /////////////// 42 | // Not Fonts // 43 | /////////////// 44 | 45 | WindowWithDearImGui::WindowWithDearImGui(const char* const window_title, const int window_width, const int window_height, const bool resizeable) 46 | : Window(window_title, window_width, window_height, resizeable) 47 | , dear_imgui_context(ImGui::CreateContext()) 48 | , dpi_scale(GetDPIScale()) 49 | { 50 | const auto &previous_context = ImGui::GetCurrentContext(); 51 | ImGui::SetCurrentContext(dear_imgui_context); 52 | 53 | ImGuiIO &io = ImGui::GetIO(); 54 | ImGuiStyle &style = ImGui::GetStyle(); 55 | io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls 56 | io.IniFilename = nullptr; // Disable automatic loading/saving so we can do it ourselves. 57 | 58 | // Setup Dear ImGui style 59 | ImGui::StyleColorsDark(); 60 | //ImGui::StyleColorsLight(); 61 | //ImGui::StyleColorsClassic(); 62 | 63 | style.WindowBorderSize = 0.0f; 64 | style.PopupBorderSize = 0.0f; 65 | style.ChildBorderSize = 0.0f; 66 | style.WindowTitleAlign = ImVec2(0.5f, 0.5f); 67 | style.TabRounding = 0.0f; 68 | style.ScrollbarRounding = 0.0f; 69 | 70 | ImVec4* colors = style.Colors; 71 | colors[ImGuiCol_Text] = ImVec4(0.94f, 0.94f, 0.94f, 1.00f); 72 | colors[ImGuiCol_WindowBg] = ImVec4(0.13f, 0.13f, 0.13f, 0.94f); 73 | colors[ImGuiCol_FrameBg] = ImVec4(0.35f, 0.35f, 0.35f, 0.54f); 74 | colors[ImGuiCol_FrameBgHovered] = ImVec4(0.69f, 0.69f, 0.69f, 0.40f); 75 | colors[ImGuiCol_FrameBgActive] = ImVec4(0.69f, 0.69f, 0.69f, 0.67f); 76 | colors[ImGuiCol_Border] = ImVec4(0.43f, 0.43f, 0.43f, 0.50f); 77 | colors[ImGuiCol_TitleBg] = ImVec4(0.09f, 0.09f, 0.09f, 1.00f); 78 | colors[ImGuiCol_TitleBgActive] = ImVec4(0.06f, 0.06f, 0.06f, 1.00f); 79 | colors[ImGuiCol_MenuBarBg] = ImVec4(0.09f, 0.09f, 0.09f, 1.00f); 80 | colors[ImGuiCol_CheckMark] = ImVec4(0.63f, 0.63f, 0.63f, 1.00f); 81 | colors[ImGuiCol_SliderGrab] = ImVec4(0.50f, 0.50f, 0.50f, 1.00f); 82 | colors[ImGuiCol_SliderGrabActive] = ImVec4(0.56f, 0.56f, 0.56f, 1.00f); 83 | colors[ImGuiCol_Button] = ImVec4(0.41f, 0.41f, 0.41f, 0.63f); 84 | colors[ImGuiCol_ButtonHovered] = ImVec4(0.38f, 0.38f, 0.38f, 1.00f); 85 | colors[ImGuiCol_ButtonActive] = ImVec4(0.35f, 0.35f, 0.35f, 1.00f); 86 | colors[ImGuiCol_Header] = ImVec4(0.38f, 0.38f, 0.38f, 0.39f); 87 | colors[ImGuiCol_HeaderHovered] = ImVec4(0.38f, 0.38f, 0.38f, 0.80f); 88 | colors[ImGuiCol_HeaderActive] = ImVec4(0.38f, 0.38f, 0.38f, 1.00f); 89 | colors[ImGuiCol_Separator] = ImVec4(0.43f, 0.43f, 0.43f, 0.50f); 90 | colors[ImGuiCol_SeparatorHovered] = ImVec4(0.56f, 0.56f, 0.56f, 0.78f); 91 | colors[ImGuiCol_SeparatorActive] = ImVec4(0.63f, 0.63f, 0.63f, 1.00f); 92 | colors[ImGuiCol_ResizeGrip] = ImVec4(0.51f, 0.51f, 0.51f, 0.43f); 93 | colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.51f, 0.51f, 0.51f, 0.79f); 94 | colors[ImGuiCol_ResizeGripActive] = ImVec4(0.51f, 0.51f, 0.51f, 0.95f); 95 | colors[ImGuiCol_Tab] = ImVec4(0.31f, 0.31f, 0.31f, 0.86f); 96 | colors[ImGuiCol_TabHovered] = ImVec4(0.51f, 0.51f, 0.51f, 0.80f); 97 | colors[ImGuiCol_TabSelected] = ImVec4(0.41f, 0.41f, 0.41f, 1.00f); 98 | 99 | style_backup = style; 100 | 101 | // Apply DPI scale. 102 | style.ScaleAllSizes(dpi_scale); 103 | 104 | const auto &menu_bar_bg_colour = colors[ImGuiCol_MenuBarBg]; 105 | SetTitleBarColour(menu_bar_bg_colour.x * 0xFF, menu_bar_bg_colour.y * 0xFF, menu_bar_bg_colour.z * 0xFF); 106 | 107 | // Setup Platform/Renderer backends 108 | ImGui_ImplSDL3_InitForSDLRenderer(GetSDLWindow(), GetRenderer()); 109 | ImGui_ImplSDLRenderer3_Init(GetRenderer()); 110 | 111 | // Load fonts 112 | ReloadFonts(CalculateFontSize()); 113 | 114 | ImGui::SetCurrentContext(previous_context); 115 | } 116 | 117 | WindowWithDearImGui::~WindowWithDearImGui() 118 | { 119 | const auto &previous_context = ImGui::GetCurrentContext(); 120 | ImGui::SetCurrentContext(dear_imgui_context); 121 | 122 | ImGui_ImplSDLRenderer3_Shutdown(); 123 | ImGui_ImplSDL3_Shutdown(); 124 | 125 | ImGui::SetCurrentContext(previous_context); 126 | } 127 | 128 | void WindowWithDearImGui::StartDearImGuiFrame() 129 | { 130 | previous_dear_imgui_context = ImGui::GetCurrentContext(); 131 | ImGui::SetCurrentContext(dear_imgui_context); 132 | 133 | // Handle dynamic DPI support 134 | const float new_dpi = GetDPIScale(); 135 | 136 | if (dpi_scale != new_dpi) 137 | { 138 | dpi_scale = new_dpi; 139 | 140 | auto& style = ImGui::GetStyle(); 141 | style = style_backup; 142 | style.ScaleAllSizes(dpi_scale); 143 | ReloadFonts(CalculateFontSize()); 144 | } 145 | 146 | // Start the Dear ImGui frame 147 | ImGui_ImplSDLRenderer3_NewFrame(); 148 | ImGui_ImplSDL3_NewFrame(); 149 | ImGui::NewFrame(); 150 | } 151 | 152 | void WindowWithDearImGui::FinishDearImGuiFrame() 153 | { 154 | SDL_RenderClear(GetRenderer()); 155 | 156 | // Render Dear ImGui. 157 | ImGui::Render(); 158 | ImGui_ImplSDLRenderer3_RenderDrawData(ImGui::GetDrawData(), GetRenderer()); 159 | 160 | // Finally display the rendered frame to the user. 161 | SDL_RenderPresent(GetRenderer()); 162 | 163 | ImGui::SetCurrentContext(previous_dear_imgui_context); 164 | } 165 | 166 | float WindowWithDearImGui::GetMenuBarSize() 167 | { 168 | const auto &previous_context = ImGui::GetCurrentContext(); 169 | ImGui::SetCurrentContext(dear_imgui_context); 170 | 171 | const float menu_bar_size = UNSCALED_FONT_SIZE + ImGui::GetStyle().FramePadding.y * 2.0f; // An inlined ImGui::GetFrameHeight that actually works 172 | 173 | ImGui::SetCurrentContext(previous_context); 174 | 175 | return menu_bar_size; 176 | } 177 | -------------------------------------------------------------------------------- /windows/common/window-with-dear-imgui.h: -------------------------------------------------------------------------------- 1 | #ifndef WINDOW_WITH_DEAR_IMGUI_H 2 | #define WINDOW_WITH_DEAR_IMGUI_H 3 | 4 | #include 5 | 6 | #include 7 | 8 | #include "../../sdl-wrapper.h" 9 | 10 | #include "../../libraries/imgui/imgui.h" 11 | #include "../../libraries/imgui/backends/imgui_impl_sdl3.h" 12 | #include "../../libraries/imgui/backends/imgui_impl_sdlrenderer3.h" 13 | 14 | #include "../../raii-wrapper.h" 15 | #include "window.h" 16 | 17 | namespace ImGui 18 | { 19 | template 20 | void TextUnformatted(const T &string) 21 | { 22 | ImGui::TextUnformatted(&string.front(), &string.back() + 1); 23 | } 24 | 25 | template <> 26 | inline void TextUnformatted(const std::filesystem::path &path) 27 | { 28 | const auto &string = path.u8string(); 29 | ImGui::TextUnformatted(reinterpret_cast(&string.front()), reinterpret_cast(&string.back()) + 1); 30 | } 31 | 32 | template 33 | void TextFormatted(fmt::format_string format, T&&... args) 34 | { 35 | const auto string = fmt::format(format, std::forward(args)...); 36 | ImGui::TextUnformatted(string); 37 | } 38 | } 39 | 40 | class WindowWithDearImGui : public Window 41 | { 42 | private: 43 | MAKE_RAII_POINTER(DearImGuiContext, ImGuiContext, ImGui::DestroyContext); 44 | 45 | DearImGuiContext dear_imgui_context; 46 | ImGuiContext *previous_dear_imgui_context; 47 | ImGuiStyle style_backup; 48 | float dpi_scale; 49 | 50 | float GetFontScale(); 51 | unsigned int CalculateFontSize(); 52 | void ReloadFonts(unsigned int font_size); 53 | 54 | public: 55 | // TODO: Make this private. 56 | ImFont *monospace_font; 57 | 58 | WindowWithDearImGui(const char *window_title, int window_width, int window_height, bool resizeable); 59 | ~WindowWithDearImGui(); 60 | void MakeDearImGuiContextCurrent() { ImGui::SetCurrentContext(dear_imgui_context); } 61 | void StartDearImGuiFrame(); 62 | void FinishDearImGuiFrame(); 63 | float GetMenuBarSize(); 64 | }; 65 | 66 | #endif /* WINDOW_WITH_DEAR_IMGUI_H */ 67 | -------------------------------------------------------------------------------- /windows/common/window-with-framebuffer.cpp: -------------------------------------------------------------------------------- 1 | #include "window-with-framebuffer.h" 2 | 3 | #include 4 | 5 | #include "../../frontend.h" 6 | 7 | SDL::Texture WindowWithFramebuffer::CreateFramebufferTexture(SDL::Renderer &renderer, const int framebuffer_width, const int framebuffer_height) 8 | { 9 | auto texture = SDL::CreateTexture(renderer, SDL_TEXTUREACCESS_STREAMING, framebuffer_width, framebuffer_height, SDL_SCALEMODE_NEAREST); 10 | 11 | // Try to use SDL3's fancy new 'pixel-art' scaling mode. 12 | // The cast is to support older versions of SDL3 (pre-3.4). 13 | SDL_SetTextureScaleMode(texture, static_cast(2)/*SDL_SCALEMODE_PIXELART*/); 14 | 15 | return texture; 16 | } 17 | -------------------------------------------------------------------------------- /windows/common/window-with-framebuffer.h: -------------------------------------------------------------------------------- 1 | #ifndef WINDOW_WITH_FRAMEBUFFER_H 2 | #define WINDOW_WITH_FRAMEBUFFER_H 3 | 4 | #include "../../sdl-wrapper.h" 5 | 6 | #include "window-with-dear-imgui.h" 7 | 8 | class WindowWithFramebuffer : public WindowWithDearImGui 9 | { 10 | private: 11 | static SDL::Texture CreateFramebufferTexture(SDL::Renderer &renderer, const int framebuffer_width, const int framebuffer_height); 12 | 13 | public: 14 | SDL::Texture framebuffer_texture; 15 | 16 | WindowWithFramebuffer(const char* const window_title, const int window_width, const int window_height, const int framebuffer_width, const int framebuffer_height, const bool resizeable) 17 | : WindowWithDearImGui(window_title, window_width, window_height, resizeable) 18 | , framebuffer_texture(CreateFramebufferTexture(GetRenderer(), framebuffer_width, framebuffer_height)) 19 | { 20 | 21 | } 22 | }; 23 | 24 | #endif /* WINDOW_WITH_FRAMEBUFFER_H */ 25 | -------------------------------------------------------------------------------- /windows/common/window.cpp: -------------------------------------------------------------------------------- 1 | #include "window.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "../../common/core/clowncommon/clowncommon.h" 7 | 8 | #include "../../frontend.h" 9 | #include "winapi.h" 10 | 11 | static float HandleDPIError(const float dpi_scale) 12 | { 13 | // Prevent any insanity if we somehow get bad values. 14 | if (dpi_scale == 0.0f) 15 | return 1.0f; 16 | 17 | return dpi_scale; 18 | } 19 | 20 | static float GetDisplayDPIScale() 21 | { 22 | const SDL_DisplayID display_index = SDL_GetPrimaryDisplay(); 23 | 24 | if (display_index == 0) 25 | { 26 | Frontend::debug_log.Log("SDL_GetPrimaryDisplay failed with the following message - '{}'", SDL_GetError()); 27 | return 1.0f; 28 | } 29 | 30 | return HandleDPIError(SDL_GetDisplayContentScale(display_index)); 31 | } 32 | 33 | float Window::GetSizeScale() 34 | { 35 | const SDL_DisplayID display_index = SDL_GetDisplayForWindow(GetSDLWindow()); 36 | 37 | if (display_index == 0) 38 | { 39 | Frontend::debug_log.Log("SDL_GetDisplayForWindow failed with the following message - '{}'", SDL_GetError()); 40 | return 1.0f; 41 | } 42 | 43 | return HandleDPIError(SDL_GetDisplayContentScale(display_index)); 44 | } 45 | 46 | float Window::GetDPIScale() 47 | { 48 | return HandleDPIError(SDL_GetWindowDisplayScale(GetSDLWindow())); 49 | } 50 | 51 | Window::Window(const char* const window_title, const int window_width, const int window_height, const bool resizeable) 52 | { 53 | const float scale = GetDisplayDPIScale(); 54 | 55 | Uint32 window_flags = SDL_WINDOW_HIDDEN | SDL_WINDOW_HIGH_PIXEL_DENSITY; 56 | 57 | if (resizeable) 58 | window_flags |= SDL_WINDOW_RESIZABLE; 59 | 60 | // SDL3's documentation currently advices using this instead of 'SDL_CreateWindow' 61 | // and 'SDL_CreateRenderer' separately to avoid "window flicker". 62 | // https://wiki.libsdl.org/SDL3/SDL_CreateWindow 63 | SDL_Window *window; 64 | SDL_Renderer *renderer; 65 | if (!SDL_CreateWindowAndRenderer(window_title, static_cast(window_width * scale), static_cast(window_height * scale), window_flags, &window, &renderer)) 66 | throw std::runtime_error(std::string("SDL_CreateWindowAndRenderer failed with the following message - '") + SDL_GetError() + "'"); 67 | 68 | sdl_window = SDL::Window(window); 69 | this->renderer = SDL::Renderer(renderer); 70 | 71 | DisableRounding(); 72 | } 73 | 74 | void Window::SetTitleBarColour(const unsigned char red, const unsigned char green, const unsigned char blue) 75 | { 76 | SetWindowTitleBarColour(sdl_window, red, green, blue); 77 | } 78 | 79 | void Window::DisableRounding() 80 | { 81 | DisableWindowRounding(sdl_window); 82 | } 83 | 84 | void Window::ShowWarningMessageBox(const char* const message) 85 | { 86 | SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_WARNING, "Warning", message, GetSDLWindow()); 87 | } 88 | 89 | void Window::ShowErrorMessageBox(const char* const message) 90 | { 91 | SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", message, GetSDLWindow()); 92 | } 93 | 94 | void Window::ShowFatalMessageBox(const char* const message) 95 | { 96 | SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Fatal Error", message, GetSDLWindow()); 97 | } 98 | -------------------------------------------------------------------------------- /windows/common/window.h: -------------------------------------------------------------------------------- 1 | #ifndef WINDOW_H 2 | #define WINDOW_H 3 | 4 | #include 5 | 6 | #include "../../sdl-wrapper.h" 7 | 8 | class Window 9 | { 10 | private: 11 | SDL::Window sdl_window; 12 | SDL::Renderer renderer; 13 | 14 | public: 15 | Window(const char *window_title, int window_width, int window_height, bool resizeable); 16 | 17 | float GetSizeScale(); 18 | float GetDPIScale(); 19 | void SetFullscreen(const bool enabled) 20 | { 21 | SDL_SetWindowFullscreen(GetSDLWindow(), enabled ? SDL_WINDOW_FULLSCREEN : 0); 22 | if (!enabled) 23 | DisableRounding(); 24 | } 25 | bool GetFullscreen() { return (SDL_GetWindowFlags(GetSDLWindow()) & SDL_WINDOW_FULLSCREEN) != 0; } 26 | void ToggleFullscreen() { SetFullscreen(!GetFullscreen()); } 27 | SDL::Window& GetSDLWindow() { return sdl_window; } 28 | const SDL::Window& GetSDLWindow() const { return sdl_window; } 29 | SDL::Renderer& GetRenderer() { return renderer; } 30 | const SDL::Renderer& GetRenderer() const { return renderer; } 31 | bool GetVSync() 32 | { 33 | int vsync; 34 | 35 | if (!SDL_GetRenderVSync(renderer, &vsync)) 36 | return false; 37 | 38 | return vsync != 0; 39 | } 40 | void SetVSync(const bool enabled) 41 | { 42 | if (enabled) 43 | { 44 | // Try enabling adaptive V-sync, and fall-back on regular V-sync if it fails. 45 | if (!SDL_SetRenderVSync(renderer, SDL_RENDERER_VSYNC_ADAPTIVE)) 46 | SDL_SetRenderVSync(renderer, 1); 47 | } 48 | else 49 | { 50 | SDL_SetRenderVSync(renderer, SDL_RENDERER_VSYNC_DISABLED); 51 | } 52 | } 53 | void SetTitleBarColour(unsigned char red, unsigned char green, unsigned char blue); 54 | void DisableRounding(); 55 | void ShowWarningMessageBox(const char *message); 56 | void ShowErrorMessageBox(const char *message); 57 | void ShowFatalMessageBox(const char *message); 58 | }; 59 | 60 | #endif /* WINDOW_H */ 61 | -------------------------------------------------------------------------------- /windows/debug-fm.cpp: -------------------------------------------------------------------------------- 1 | #include "debug-fm.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "../frontend.h" 7 | 8 | void DebugFM::Registers::DisplayInternal() 9 | { 10 | const std::array, 2> pannings = {{ 11 | {"Mute", "R"}, 12 | {"L", "L+R"} 13 | }}; 14 | 15 | const FM_State &fm = Frontend::emulator->CurrentState().clownmdemu.fm; 16 | const auto monospace_font = GetMonospaceFont(); 17 | 18 | ImGui::SeparatorText("FM Channels"); 19 | 20 | if (ImGui::BeginTable("FM Channel Table", 1 + std::size(fm.channels), ImGuiTableFlags_Borders | ImGuiTableFlags_SizingStretchSame)) 21 | { 22 | ImGui::TableSetupColumn("Register", ImGuiTableColumnFlags_WidthFixed); 23 | ImGui::TableSetupColumn("FM1"); 24 | ImGui::TableSetupColumn("FM2"); 25 | ImGui::TableSetupColumn("FM3"); 26 | ImGui::TableSetupColumn("FM4"); 27 | ImGui::TableSetupColumn("FM5"); 28 | ImGui::TableSetupColumn("FM6"); 29 | ImGui::TableHeadersRow(); 30 | 31 | ImGui::TableNextColumn(); 32 | ImGui::TextUnformatted("Frequency"); 33 | 34 | ImGui::PushFont(monospace_font); 35 | for (const auto &channel : fm.channels) 36 | { 37 | ImGui::TableNextColumn(); 38 | ImGui::TextFormatted("0x{:04X}", channel.state.operators[0].phase.f_number_and_block); 39 | } 40 | ImGui::PopFont(); 41 | 42 | ImGui::TableNextColumn(); 43 | ImGui::TextUnformatted("Feedback"); 44 | 45 | ImGui::PushFont(monospace_font); 46 | for (const auto &channel : fm.channels) 47 | { 48 | ImGui::TableNextColumn(); 49 | ImGui::TextFormatted("{}", 9 - channel.state.feedback_divisor); 50 | } 51 | ImGui::PopFont(); 52 | 53 | ImGui::TableNextColumn(); 54 | ImGui::TextUnformatted("Algorithm"); 55 | 56 | ImGui::PushFont(monospace_font); 57 | for (const auto &channel : fm.channels) 58 | { 59 | ImGui::TableNextColumn(); 60 | ImGui::TextFormatted("{}", channel.state.algorithm); 61 | DoToolTip( 62 | [&]() -> std::string_view 63 | { 64 | switch (channel.state.algorithm) 65 | { 66 | case 0: return "Four serial connection mode."; 67 | case 1: return "Three double modulation serial connection mode."; 68 | case 2: return "Double modulation mode (1)."; 69 | case 3: return "Double modulation mode (2)."; 70 | case 4: return "Two serial connection and two parallel modes."; 71 | case 5: return "Common modulation 3 parallel mode."; 72 | case 6: return "Two serial connection + two sine mode."; 73 | case 7: return "Four parallel sine synthesis mode."; 74 | default: return "ERROR"; 75 | } 76 | }() 77 | ); 78 | } 79 | ImGui::PopFont(); 80 | 81 | ImGui::TableNextColumn(); 82 | ImGui::TextUnformatted("Panning"); 83 | 84 | for (const auto &channel : fm.channels) 85 | { 86 | ImGui::TableNextColumn(); 87 | ImGui::TextUnformatted(pannings[channel.pan_left][channel.pan_right]); 88 | } 89 | 90 | ImGui::TableNextColumn(); 91 | ImGui::TextUnformatted("Amplitude Modulation Sensitivity"); 92 | 93 | ImGui::PushFont(monospace_font); 94 | for (const auto &channel : fm.channels) 95 | { 96 | ImGui::TableNextColumn(); 97 | switch (channel.state.amplitude_modulation_shift) 98 | { 99 | case 0: 100 | ImGui::TextUnformatted("3"); 101 | break; 102 | case 1: 103 | ImGui::TextUnformatted("2"); 104 | break; 105 | case 3: 106 | ImGui::TextUnformatted("1"); 107 | break; 108 | case 7: 109 | ImGui::TextUnformatted("0"); 110 | break; 111 | default: 112 | ImGui::TextUnformatted("ERROR"); 113 | break; 114 | } 115 | } 116 | ImGui::PopFont(); 117 | 118 | ImGui::TableNextColumn(); 119 | ImGui::TextUnformatted("Phase Modulation Sensitivity"); 120 | 121 | ImGui::PushFont(monospace_font); 122 | for (const auto &channel : fm.channels) 123 | { 124 | ImGui::TableNextColumn(); 125 | ImGui::TextFormatted("{}", channel.state.phase_modulation_sensitivity); 126 | } 127 | ImGui::PopFont(); 128 | 129 | ImGui::EndTable(); 130 | } 131 | 132 | ImGui::SeparatorText("FM Operators"); 133 | 134 | if (ImGui::BeginTabBar("FM Channels Tab Bar")) 135 | { 136 | char window_name_buffer[] = "FM1"; 137 | 138 | for (std::size_t channel_index = 0; channel_index != std::size(fm.channels); ++channel_index) 139 | { 140 | const auto &channel = fm.channels[channel_index]; 141 | 142 | window_name_buffer[2] = '1' + channel_index; 143 | 144 | if (ImGui::BeginTabItem(window_name_buffer)) 145 | { 146 | if (ImGui::BeginTable("FM Operator Table", 5, ImGuiTableFlags_Borders | ImGuiTableFlags_SizingStretchSame)) 147 | { 148 | ImGui::TableSetupColumn("Register", ImGuiTableColumnFlags_WidthFixed); 149 | ImGui::TableSetupColumn("Operator 1"); 150 | ImGui::TableSetupColumn("Operator 2"); 151 | ImGui::TableSetupColumn("Operator 3"); 152 | ImGui::TableSetupColumn("Operator 4"); 153 | ImGui::TableHeadersRow(); 154 | 155 | ImGui::TableNextColumn(); 156 | ImGui::TextUnformatted("Key On"); 157 | 158 | for (const auto &op : channel.state.operators) 159 | { 160 | ImGui::TableNextColumn(); 161 | ImGui::TextUnformatted(op.key_on ? "On" : "Off"); 162 | } 163 | 164 | ImGui::TableNextRow(); 165 | ImGui::TableNextColumn(); 166 | ImGui::TextUnformatted("Detune"); 167 | 168 | ImGui::PushFont(monospace_font); 169 | for (const auto &op : channel.state.operators) 170 | { 171 | ImGui::TableNextColumn(); 172 | ImGui::TextFormatted("{}", op.phase.detune); 173 | } 174 | ImGui::PopFont(); 175 | 176 | ImGui::TableNextRow(); 177 | ImGui::TableNextColumn(); 178 | ImGui::TextUnformatted("Multiplier"); 179 | 180 | ImGui::PushFont(monospace_font); 181 | for (const auto &op : channel.state.operators) 182 | { 183 | ImGui::TableNextColumn(); 184 | ImGui::TextFormatted("{}", op.phase.multiplier / 2); 185 | } 186 | ImGui::PopFont(); 187 | 188 | ImGui::TableNextRow(); 189 | ImGui::TableNextColumn(); 190 | ImGui::TextUnformatted("Total Level"); 191 | 192 | ImGui::PushFont(monospace_font); 193 | for (const auto &op : channel.state.operators) 194 | { 195 | ImGui::TableNextColumn(); 196 | ImGui::TextFormatted("0x{:02X}", op.total_level >> 3); 197 | } 198 | ImGui::PopFont(); 199 | 200 | ImGui::TableNextRow(); 201 | ImGui::TableNextColumn(); 202 | ImGui::TextUnformatted("Key Scale"); 203 | 204 | ImGui::PushFont(monospace_font); 205 | for (const auto &op : channel.state.operators) 206 | { 207 | static const std::array decode = {3, 2, 0xFF, 1, 0xFF, 0xFF, 0xFF, 0}; 208 | 209 | ImGui::TableNextColumn(); 210 | ImGui::TextFormatted("{}", decode[op.key_scale - 1]); 211 | } 212 | ImGui::PopFont(); 213 | 214 | ImGui::TableNextRow(); 215 | ImGui::TableNextColumn(); 216 | ImGui::TextUnformatted("Attack Rate"); 217 | 218 | ImGui::PushFont(monospace_font); 219 | for (const auto &op : channel.state.operators) 220 | { 221 | ImGui::TableNextColumn(); 222 | ImGui::TextFormatted("0x{:02X}", op.rates[FM_OPERATOR_ENVELOPE_MODE_ATTACK]); 223 | } 224 | ImGui::PopFont(); 225 | 226 | ImGui::TableNextRow(); 227 | ImGui::TableNextColumn(); 228 | ImGui::TextUnformatted("Decay Rate"); 229 | 230 | ImGui::PushFont(monospace_font); 231 | for (const auto &op : channel.state.operators) 232 | { 233 | ImGui::TableNextColumn(); 234 | ImGui::TextFormatted("0x{:02X}", op.rates[FM_OPERATOR_ENVELOPE_MODE_DECAY]); 235 | } 236 | ImGui::PopFont(); 237 | 238 | ImGui::TableNextRow(); 239 | ImGui::TableNextColumn(); 240 | ImGui::TextUnformatted("Sustain Rate"); 241 | 242 | ImGui::PushFont(monospace_font); 243 | for (const auto &op : channel.state.operators) 244 | { 245 | ImGui::TableNextColumn(); 246 | ImGui::TextFormatted("0x{:02X}", op.rates[FM_OPERATOR_ENVELOPE_MODE_SUSTAIN]); 247 | } 248 | ImGui::PopFont(); 249 | 250 | ImGui::TableNextRow(); 251 | ImGui::TableNextColumn(); 252 | ImGui::TextUnformatted("Release Rate"); 253 | 254 | ImGui::PushFont(monospace_font); 255 | for (const auto &op : channel.state.operators) 256 | { 257 | ImGui::TableNextColumn(); 258 | ImGui::TextFormatted("0x{:X}", op.rates[FM_OPERATOR_ENVELOPE_MODE_RELEASE] >> 1); 259 | } 260 | ImGui::PopFont(); 261 | 262 | ImGui::TableNextRow(); 263 | ImGui::TableNextColumn(); 264 | ImGui::TextUnformatted("Sustain Level"); 265 | 266 | ImGui::PushFont(monospace_font); 267 | for (const auto &op : channel.state.operators) 268 | { 269 | ImGui::TableNextColumn(); 270 | ImGui::TextFormatted("0x{:X}", (op.sustain_level / 0x20) & 0xF); 271 | } 272 | ImGui::PopFont(); 273 | 274 | ImGui::TableNextRow(); 275 | ImGui::TableNextColumn(); 276 | ImGui::TextUnformatted("Amplitude Modulation"); 277 | 278 | for (const auto &op : channel.state.operators) 279 | { 280 | ImGui::TableNextColumn(); 281 | ImGui::TextUnformatted(op.amplitude_modulation_on ? "On" : "Off"); 282 | } 283 | 284 | const auto DoSSGEG = [&](const char* const label, const std::function &function) 285 | { 286 | ImGui::TableNextRow(); 287 | ImGui::TableNextColumn(); 288 | ImGui::TextUnformatted(label); 289 | 290 | ImGui::PushFont(monospace_font); 291 | for (const auto &op : channel.state.operators) 292 | { 293 | ImGui::TableNextColumn(); 294 | ImGui::TextUnformatted(function(op) ? "Yes" : "No"); 295 | } 296 | ImGui::PopFont(); 297 | }; 298 | 299 | DoSSGEG("SSG-EG Enabled", [](const FM_Operator_State &op) { return op.ssgeg.enabled; }); 300 | DoSSGEG("SSG-EG Attack", [](const FM_Operator_State &op) { return op.ssgeg.attack; }); 301 | DoSSGEG("SSG-EG Alternate", [](const FM_Operator_State &op) { return op.ssgeg.alternate; }); 302 | DoSSGEG("SSG-EG Hold", [](const FM_Operator_State &op) { return op.ssgeg.hold; }); 303 | 304 | if (channel_index == 2) 305 | { 306 | ImGui::TableNextRow(); 307 | ImGui::TableNextColumn(); 308 | ImGui::TextUnformatted("Frequency"); 309 | 310 | ImGui::PushFont(monospace_font); 311 | for (const auto &frequency : fm.channel_3_metadata.frequencies) 312 | { 313 | ImGui::TableNextColumn(); 314 | ImGui::TextFormatted("0x{:04X}", frequency); 315 | } 316 | ImGui::PopFont(); 317 | } 318 | 319 | ImGui::EndTable(); 320 | } 321 | 322 | ImGui::EndTabItem(); 323 | } 324 | } 325 | 326 | ImGui::EndTabBar(); 327 | } 328 | 329 | if (ImGui::BeginTable("Table Table", 2, ImGuiTableFlags_SizingStretchSame)) 330 | { 331 | ImGui::TableNextColumn(); 332 | ImGui::SeparatorText("DAC Channel"); 333 | 334 | if (ImGui::BeginTable("DAC Register Table", 2, ImGuiTableFlags_Borders)) 335 | { 336 | ImGui::TableSetupColumn("Register"); 337 | ImGui::TableSetupColumn("Value"); 338 | ImGui::TableHeadersRow(); 339 | 340 | ImGui::TableNextColumn(); 341 | ImGui::TextUnformatted("Enabled"); 342 | 343 | ImGui::TableNextColumn(); 344 | ImGui::TextUnformatted(fm.dac_enabled ? "Yes" : "No"); 345 | 346 | ImGui::TableNextColumn(); 347 | ImGui::TextUnformatted("Sample"); 348 | 349 | ImGui::PushFont(monospace_font); 350 | ImGui::TableNextColumn(); 351 | ImGui::TextFormatted("0x{:03X}", fm.dac_sample); 352 | ImGui::PopFont(); 353 | 354 | ImGui::TableNextColumn(); 355 | ImGui::TextUnformatted("Test"); 356 | 357 | ImGui::TableNextColumn(); 358 | ImGui::TextUnformatted(fm.dac_test ? "On" : "Off"); 359 | 360 | ImGui::EndTable(); 361 | } 362 | 363 | ImGui::TableNextColumn(); 364 | ImGui::SeparatorText("Timers"); 365 | 366 | if (ImGui::BeginTable("Timer Table", 3, ImGuiTableFlags_Borders)) 367 | { 368 | ImGui::TableSetupColumn("Property"); 369 | ImGui::TableSetupColumn("Timer A"); 370 | ImGui::TableSetupColumn("Timer B"); 371 | ImGui::TableHeadersRow(); 372 | 373 | ImGui::TableNextColumn(); 374 | ImGui::TextUnformatted("Enabled"); 375 | 376 | for (const auto &timer : fm.timers) 377 | { 378 | ImGui::TableNextColumn(); 379 | ImGui::TextUnformatted(timer.enabled ? "Yes" : "No"); 380 | } 381 | 382 | ImGui::TableNextColumn(); 383 | ImGui::TextUnformatted("Cached Counter"); 384 | 385 | ImGui::PushFont(monospace_font); 386 | for (const auto &timer : fm.timers) 387 | { 388 | ImGui::TableNextColumn(); 389 | ImGui::TextFormatted("0x{:03X}", timer.value); 390 | } 391 | ImGui::PopFont(); 392 | 393 | ImGui::TableNextColumn(); 394 | ImGui::TextUnformatted("Active Counter"); 395 | 396 | ImGui::PushFont(monospace_font); 397 | for (const auto &timer : fm.timers) 398 | { 399 | ImGui::TableNextColumn(); 400 | ImGui::TextFormatted("0x{:03X}", timer.counter); 401 | } 402 | ImGui::PopFont(); 403 | 404 | ImGui::TableNextColumn(); 405 | ImGui::TextUnformatted("Flag"); 406 | 407 | for (cc_u8f i = 0; i < std::size(fm.timers); ++i) 408 | { 409 | ImGui::TableNextColumn(); 410 | ImGui::TextUnformatted((fm.status & (1 << i)) != 0 ? "Set" : "Cleared"); 411 | } 412 | 413 | ImGui::EndTable(); 414 | } 415 | 416 | ImGui::TableNextColumn(); 417 | ImGui::SeparatorText("Other"); 418 | 419 | if (ImGui::BeginTable("Other Table", 2, ImGuiTableFlags_Borders)) 420 | { 421 | ImGui::TableSetupColumn("Property"); 422 | ImGui::TableSetupColumn("Value"); 423 | ImGui::TableHeadersRow(); 424 | 425 | ImGui::TableNextColumn(); 426 | ImGui::TextUnformatted("Latched Address"); 427 | 428 | ImGui::PushFont(monospace_font); 429 | ImGui::TableNextColumn(); 430 | ImGui::TextFormatted("0x{:02X}", fm.address); 431 | ImGui::PopFont(); 432 | 433 | ImGui::TableNextColumn(); 434 | ImGui::TextUnformatted("Latched Port"); 435 | 436 | ImGui::PushFont(monospace_font); 437 | ImGui::TableNextColumn(); 438 | ImGui::TextFormatted("{}", fm.port / 3); 439 | ImGui::PopFont(); 440 | 441 | ImGui::TableNextColumn(); 442 | ImGui::TextUnformatted("Status"); 443 | 444 | ImGui::PushFont(monospace_font); 445 | ImGui::TableNextColumn(); 446 | ImGui::TextFormatted("0x{:02X}", fm.status); 447 | ImGui::PopFont(); 448 | 449 | ImGui::TableNextColumn(); 450 | ImGui::TextUnformatted("FM3 Mode"); 451 | ImGui::TableNextColumn(); 452 | ImGui::TextUnformatted(fm.channel_3_metadata.csm_mode_enabled ? "CSM" : fm.channel_3_metadata.per_operator_frequencies_enabled ? "Multifrequency" : "Normal"); 453 | 454 | ImGui::TableNextColumn(); 455 | ImGui::TextUnformatted("Low Frequency Oscillator"); 456 | ImGui::TableNextColumn(); 457 | ImGui::TextUnformatted(fm.lfo.enabled ? "Enabled" : "Disabled"); 458 | 459 | ImGui::TableNextColumn(); 460 | ImGui::TextUnformatted("LFO Frequency"); 461 | 462 | ImGui::PushFont(monospace_font); 463 | ImGui::TableNextColumn(); 464 | ImGui::TextFormatted("{}", fm.lfo.frequency); 465 | ImGui::PopFont(); 466 | 467 | ImGui::TableNextColumn(); 468 | ImGui::TextUnformatted("Frequency Cache"); 469 | 470 | ImGui::PushFont(monospace_font); 471 | ImGui::TableNextColumn(); 472 | ImGui::TextFormatted("0x{:02X}", fm.cached_upper_frequency_bits); 473 | ImGui::PopFont(); 474 | 475 | ImGui::TableNextColumn(); 476 | ImGui::TextUnformatted("Multi-Frequency Cache"); 477 | 478 | ImGui::PushFont(monospace_font); 479 | ImGui::TableNextColumn(); 480 | ImGui::TextFormatted("0x{:02X}", fm.cached_upper_frequency_bits_fm3_multi_frequency); 481 | ImGui::PopFont(); 482 | 483 | ImGui::EndTable(); 484 | } 485 | 486 | ImGui::EndTable(); 487 | } 488 | } 489 | -------------------------------------------------------------------------------- /windows/debug-fm.h: -------------------------------------------------------------------------------- 1 | #ifndef DEBUG_FM_H 2 | #define DEBUG_FM_H 3 | 4 | #include "common/window-popup.h" 5 | 6 | namespace DebugFM 7 | { 8 | class Registers : public WindowPopup 9 | { 10 | private: 11 | using Base = WindowPopup; 12 | 13 | static constexpr Uint32 window_flags = 0; 14 | 15 | void DisplayInternal(); 16 | 17 | public: 18 | using Base::WindowPopup; 19 | 20 | friend Base; 21 | }; 22 | } 23 | 24 | #endif /* DEBUG_FM_H */ 25 | -------------------------------------------------------------------------------- /windows/debug-frontend.cpp: -------------------------------------------------------------------------------- 1 | #include "debug-frontend.h" 2 | 3 | #include "../frontend.h" 4 | 5 | void DebugFrontend::DisplayInternal() 6 | { 7 | ImGui::SeparatorText("SDL Drivers"); 8 | 9 | if (ImGui::BeginTable("SDL Drivers", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit)) 10 | { 11 | // Render 12 | ImGui::TableNextColumn(); 13 | ImGui::TextUnformatted("Render"); 14 | 15 | ImGui::TableNextColumn(); 16 | 17 | const char* const renderer_name = SDL_GetRendererName(GetWindow().GetRenderer()); 18 | ImGui::TextUnformatted(renderer_name == nullptr ? "unknown" : renderer_name); 19 | 20 | // GPU 21 | const char* const gpu_name = [&]() -> const char* 22 | { 23 | #if SDL_VERSION_ATLEAST(3, 1, 6) 24 | const auto property_id = SDL_GetRendererProperties(GetWindow().GetRenderer()); 25 | 26 | if (property_id == 0) 27 | return "unknown"; 28 | 29 | const auto device_pointer = static_cast(SDL_GetPointerProperty(property_id, SDL_PROP_RENDERER_GPU_DEVICE_POINTER, nullptr)); 30 | 31 | if (device_pointer == nullptr) 32 | return "none"; 33 | 34 | const auto name = SDL_GetGPUDeviceDriver(device_pointer); 35 | 36 | if (name == nullptr) 37 | return "unknown"; 38 | 39 | return name; 40 | #else 41 | return "unknown"; 42 | #endif 43 | }(); 44 | ImGui::TableNextColumn(); 45 | ImGui::TextUnformatted("GPU"); 46 | 47 | ImGui::TableNextColumn(); 48 | ImGui::TextUnformatted(gpu_name); 49 | 50 | // Video 51 | ImGui::TableNextColumn(); 52 | ImGui::TextUnformatted("Video"); 53 | 54 | ImGui::TableNextColumn(); 55 | const char* const video_driver_name = SDL_GetCurrentVideoDriver(); 56 | ImGui::TextUnformatted(video_driver_name != nullptr ? video_driver_name : "none"); 57 | 58 | // Audio 59 | ImGui::TableNextColumn(); 60 | ImGui::TextUnformatted("Audio"); 61 | 62 | ImGui::TableNextColumn(); 63 | const char* const audio_driver_name = SDL_GetCurrentAudioDriver(); 64 | ImGui::TextUnformatted(audio_driver_name != nullptr ? audio_driver_name : "none"); 65 | 66 | ImGui::EndTable(); 67 | } 68 | 69 | ImGui::SeparatorText("Audio"); 70 | 71 | if (ImGui::BeginTable("Audio", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit)) 72 | { 73 | ImGui::TableNextColumn(); 74 | ImGui::TextUnformatted("Sample Rate"); 75 | DoToolTip("The number of audio frames played per second."); 76 | ImGui::TableNextColumn(); 77 | ImGui::TextFormatted("{}", Frontend::emulator->GetAudioSampleRate()); 78 | 79 | ImGui::TableNextColumn(); 80 | ImGui::TextUnformatted("Buffer Frames"); 81 | DoToolTip("The number of audio frames that are pulled from the buffer in a single batch."); 82 | ImGui::TableNextColumn(); 83 | ImGui::TextFormatted("{}", Frontend::emulator->GetAudioTotalBufferFrames()); 84 | 85 | ImGui::TableNextColumn(); 86 | ImGui::TextUnformatted("Target Frames"); 87 | DoToolTip("The number of buffered audio frames that the audio system tries to maintain."); 88 | ImGui::TableNextColumn(); 89 | ImGui::TextFormatted("{}", Frontend::emulator->GetAudioTargetFrames()); 90 | 91 | ImGui::TableNextColumn(); 92 | ImGui::TextUnformatted("Average Frames"); 93 | DoToolTip("The current average number of buffered audio frames."); 94 | ImGui::TableNextColumn(); 95 | ImGui::TextFormatted("{}", Frontend::emulator->GetAudioAverageFrames()); 96 | 97 | ImGui::EndTable(); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /windows/debug-frontend.h: -------------------------------------------------------------------------------- 1 | #ifndef DEBUG_FRONTEND_H 2 | #define DEBUG_FRONTEND_H 3 | 4 | #include "common/window-popup.h" 5 | 6 | class DebugFrontend : public WindowPopup 7 | { 8 | private: 9 | using Base = WindowPopup; 10 | 11 | static constexpr Uint32 window_flags = 0; 12 | 13 | void DisplayInternal(); 14 | 15 | public: 16 | using Base::WindowPopup; 17 | 18 | friend Base; 19 | }; 20 | 21 | #endif /* DEBUG_FRONTEND_H */ 22 | -------------------------------------------------------------------------------- /windows/debug-log-viewer.cpp: -------------------------------------------------------------------------------- 1 | #include "debug-log-viewer.h" 2 | 3 | #include "../frontend.h" 4 | 5 | void DebugLogViewer::DisplayInternal() 6 | { 7 | ImGui::Checkbox("Enable Logging", &Frontend::debug_log.logging_enabled); 8 | ImGui::SameLine(); 9 | ImGui::Checkbox("Log to Console", &Frontend::debug_log.log_to_console); 10 | ImGui::SameLine(); 11 | if (ImGui::Button("Clear")) 12 | Frontend::debug_log.lines.clear(); 13 | 14 | ImGui::PushFont(GetMonospaceFont()); 15 | ImGui::InputTextMultiline("##log", &Frontend::debug_log.lines[0], Frontend::debug_log.lines.length() + 1, ImVec2(-FLT_MIN, -FLT_MIN), ImGuiInputTextFlags_ReadOnly); 16 | 17 | ImGui::PopFont(); 18 | } 19 | -------------------------------------------------------------------------------- /windows/debug-log-viewer.h: -------------------------------------------------------------------------------- 1 | #ifndef DEBUG_LOG_VIEWER_H 2 | #define DEBUG_LOG_VIEWER_H 3 | 4 | #include "common/window-popup.h" 5 | 6 | class DebugLogViewer : public WindowPopup 7 | { 8 | private: 9 | using Base = WindowPopup; 10 | 11 | static constexpr Uint32 window_flags = 0; 12 | 13 | void DisplayInternal(); 14 | 15 | public: 16 | using Base::WindowPopup; 17 | 18 | friend Base; 19 | }; 20 | 21 | #endif /* DEBUG_LOG_VIEWER_H */ 22 | -------------------------------------------------------------------------------- /windows/debug-m68k.cpp: -------------------------------------------------------------------------------- 1 | #include "debug-m68k.h" 2 | 3 | #include "../libraries/imgui/imgui.h" 4 | #include "../common/core/clowncommon/clowncommon.h" 5 | #include "../common/core/clownmdemu.h" 6 | 7 | void DebugM68k::Registers::DisplayInternal(const Clown68000_State &m68k) 8 | { 9 | ImGui::PushFont(GetMonospaceFont()); 10 | 11 | for (cc_u8f i = 0; i < 8; ++i) 12 | { 13 | if (i != 0 && i != 4) 14 | ImGui::SameLine(); 15 | 16 | ImGui::TextFormatted("D{:X}:{:08X}", i, m68k.data_registers[i]); 17 | } 18 | 19 | ImGui::Separator(); 20 | 21 | for (cc_u8f i = 0; i < 8; ++i) 22 | { 23 | if (i != 0 && i != 4) 24 | ImGui::SameLine(); 25 | 26 | ImGui::TextFormatted("A{:X}:{:08X}", i, m68k.address_registers[i]); 27 | } 28 | 29 | ImGui::Separator(); 30 | 31 | ImGui::TextFormatted("PC:{:08X}", m68k.program_counter); 32 | ImGui::SameLine(); 33 | ImGui::TextFormatted("SR:{:04X}", m68k.status_register); 34 | ImGui::SameLine(); 35 | ImGui::TextUnformatted(" "); 36 | ImGui::SameLine(); 37 | if ((m68k.status_register & 0x2000) != 0) 38 | ImGui::TextFormatted("USP:{:08X}", m68k.user_stack_pointer); 39 | else 40 | ImGui::TextFormatted("SSP:{:08X}", m68k.supervisor_stack_pointer); 41 | 42 | ImGui::PopFont(); 43 | } 44 | -------------------------------------------------------------------------------- /windows/debug-m68k.h: -------------------------------------------------------------------------------- 1 | #ifndef DEBUG_M68K_H 2 | #define DEBUG_M68K_H 3 | 4 | #include "../common/core/clown68000/interpreter/clown68000.h" 5 | 6 | #include "common/window-popup.h" 7 | 8 | namespace DebugM68k 9 | { 10 | class Registers : public WindowPopup 11 | { 12 | private: 13 | using Base = WindowPopup; 14 | 15 | static constexpr Uint32 window_flags = 0; 16 | 17 | void DisplayInternal(const Clown68000_State &m68k); 18 | 19 | public: 20 | using Base::WindowPopup; 21 | 22 | friend Base; 23 | }; 24 | } 25 | 26 | #endif /* DEBUG_M68K_H */ 27 | -------------------------------------------------------------------------------- /windows/debug-memory.cpp: -------------------------------------------------------------------------------- 1 | #include "debug-memory.h" 2 | 3 | #include 4 | 5 | #include "../libraries/imgui/imgui.h" 6 | #include "../common/core/clowncommon/clowncommon.h" 7 | 8 | #if 0 9 | void DebugMemory::DisplayInternal(const cc_u8l* const buffer, const std::size_t buffer_length) 10 | { 11 | #if 0 12 | ImGui::PushFont(window.GetMonospaceFont()); 13 | 14 | // Fit window's width to text, and disable horizontal resizing. 15 | const ImGuiStyle &style = ImGui::GetStyle(); 16 | const float width = style.ScrollbarSize + style.WindowPadding.x * 2 + ImGui::CalcTextSize("0000: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F").x; 17 | ImGui::SetNextWindowSizeConstraints(ImVec2(width, 0), ImVec2(width, FLT_MAX)); 18 | 19 | ImGui::SetNextWindowSize(ImVec2(width, width), ImGuiCond_FirstUseEver); 20 | 21 | ImGui::PopFont(); 22 | #endif 23 | 24 | ImGui::PushFont(GetMonospaceFont()); 25 | 26 | ImGuiListClipper clipper; 27 | clipper.Begin(buffer_length / 0x10); 28 | while (clipper.Step()) 29 | { 30 | for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; ++i) 31 | { 32 | const int offset = i * 0x10; 33 | const cc_u8l* const bytes = &buffer[offset]; 34 | 35 | ImGui::TextFormatted("{:04X}: {:02X} {:02X} {:02X} {:02X}" 36 | " {:02X} {:02X} {:02X} {:02X}" 37 | " {:02X} {:02X} {:02X} {:02X}" 38 | " {:02X} {:02X} {:02X} {:02X}", offset, 39 | bytes[0x0], bytes[0x1], bytes[0x2], bytes[0x3], bytes[0x4], bytes[0x5], bytes[0x6], bytes[0x7], 40 | bytes[0x8], bytes[0x9], bytes[0xA], bytes[0xB], bytes[0xC], bytes[0xD], bytes[0xE], bytes[0xF]); 41 | } 42 | } 43 | 44 | ImGui::PopFont(); 45 | } 46 | 47 | void DebugMemory::DisplayInternal(const cc_u16l* const buffer, const std::size_t buffer_length) 48 | { 49 | // TODO: Remove or reimplement this. 50 | #if 0 51 | ImGui::PushFont(window.GetMonospaceFont()); 52 | 53 | // Fit window's width to text, and disable horizontal resizing. 54 | const ImGuiStyle &style = ImGui::GetStyle(); 55 | const float width = style.ScrollbarSize + style.WindowPadding.x * 2 + ImGui::CalcTextSize("00000: 0001 0203 0405 0607 0809 0A0B 0C0D 0E0F").x; 56 | ImGui::SetNextWindowSizeConstraints(ImVec2(width, 0), ImVec2(width, FLT_MAX)); 57 | 58 | ImGui::SetNextWindowSize(ImVec2(width, width), ImGuiCond_FirstUseEver); 59 | 60 | ImGui::PopFont(); 61 | #endif 62 | 63 | ImGui::PushFont(GetMonospaceFont()); 64 | 65 | ImGuiListClipper clipper; 66 | clipper.Begin(buffer_length / 8); 67 | while (clipper.Step()) 68 | { 69 | for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; ++i) 70 | { 71 | const cc_u16l* const words = &buffer[i * 8]; 72 | 73 | ImGui::TextFormatted("{:05X}: {:04X} {:04X} {:04X} {:04X} {:04X} {:04X} {:04X} {:04X}", i * 0x10, 74 | words[0], words[1], words[2], words[3], words[4], words[5], words[6], words[7]); 75 | } 76 | } 77 | 78 | ImGui::PopFont(); 79 | } 80 | #endif 81 | 82 | template<> 83 | void DebugMemory::PrintLine(const cc_u8l* const buffer, const std::size_t buffer_length_digits, const int index) 84 | { 85 | const int offset = index * 0x10; 86 | const cc_u8l* const bytes = &buffer[offset]; 87 | 88 | ImGui::TextFormatted("{:0{}X}: {:02X} {:02X} {:02X} {:02X}" 89 | " {:02X} {:02X} {:02X} {:02X}" 90 | " {:02X} {:02X} {:02X} {:02X}" 91 | " {:02X} {:02X} {:02X} {:02X}", offset, buffer_length_digits, 92 | bytes[0x0], bytes[0x1], bytes[0x2], bytes[0x3], bytes[0x4], bytes[0x5], bytes[0x6], bytes[0x7], 93 | bytes[0x8], bytes[0x9], bytes[0xA], bytes[0xB], bytes[0xC], bytes[0xD], bytes[0xE], bytes[0xF]); 94 | } 95 | 96 | template<> 97 | void DebugMemory::PrintLine(const cc_u16l* const buffer, const std::size_t buffer_length_digits, const int index) 98 | { 99 | const cc_u16l* const words = &buffer[index * 8]; 100 | 101 | ImGui::TextFormatted("{:0{}X}: {:04X} {:04X} {:04X} {:04X} {:04X} {:04X} {:04X} {:04X}", index * 0x10, buffer_length_digits, 102 | words[0], words[1], words[2], words[3], words[4], words[5], words[6], words[7]); 103 | } 104 | -------------------------------------------------------------------------------- /windows/debug-memory.h: -------------------------------------------------------------------------------- 1 | #ifndef DEBUG_MEMORY_H 2 | #define DEBUG_MEMORY_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "../common/core/clowncommon/clowncommon.h" 10 | 11 | #include "../frontend.h" 12 | #include "common/window-popup.h" 13 | 14 | class DebugMemory : public WindowPopup 15 | { 16 | private: 17 | using Base = WindowPopup; 18 | 19 | static constexpr Uint32 window_flags = 0; 20 | 21 | template 22 | static constexpr std::size_t IntegerSize() 23 | { 24 | using Type = std::remove_cvref_t; 25 | if constexpr (std::is_same_v || std::is_same_v) 26 | { 27 | return 1; 28 | } 29 | else if constexpr (std::is_same_v || std::is_same_v) 30 | { 31 | return 2; 32 | } 33 | else if constexpr (std::is_same_v || std::is_same_v) 34 | { 35 | return 4; 36 | } 37 | else 38 | { 39 | static_assert(!sizeof(Type), "Unsupported type passed to IntegerSize"); 40 | return 0; 41 | } 42 | } 43 | 44 | template 45 | void PrintLine(const T *buffer, std::size_t offset_digits, int index); 46 | 47 | template 48 | void DisplayInternal(const T &buffer) 49 | { 50 | #if 0 51 | ImGui::PushFont(window.GetMonospaceFont()); 52 | 53 | // Fit window's width to text, and disable horizontal resizing. 54 | const ImGuiStyle &style = ImGui::GetStyle(); 55 | const float width = style.ScrollbarSize + style.WindowPadding.x * 2 + ImGui::CalcTextSize("0000: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F").x; 56 | ImGui::SetNextWindowSizeConstraints(ImVec2(width, 0), ImVec2(width, FLT_MAX)); 57 | 58 | ImGui::SetNextWindowSize(ImVec2(width, width), ImGuiCond_FirstUseEver); 59 | 60 | ImGui::PopFont(); 61 | #endif 62 | 63 | const auto bytes_per_value = IntegerSize(); 64 | 65 | if (ImGui::Button("Save to File")) 66 | { 67 | std::vector save_buffer; 68 | save_buffer.reserve(std::size(buffer) * bytes_per_value); 69 | 70 | for (const auto &value : buffer) 71 | { 72 | auto working_value = +value; // The '+' is to promote this to 'int' if needed, so that the below shift by 8 is valid. 73 | for (std::size_t i = 0; i < bytes_per_value; ++i) 74 | { 75 | save_buffer.push_back(working_value >> ((bytes_per_value - 1) * 8) & 0xFF); 76 | working_value <<= 8; 77 | } 78 | } 79 | 80 | Frontend::file_utilities.SaveFile(GetWindow(), "Save RAM Dump", 81 | [save_buffer](const FileUtilities::SaveFileInnerCallback &callback) 82 | { 83 | return callback(std::data(save_buffer), std::size(save_buffer)); 84 | }); 85 | } 86 | 87 | if (IsResizeable()) 88 | ImGui::BeginChild("Memory contents"); 89 | 90 | ImGui::PushFont(GetMonospaceFont()); 91 | 92 | const auto bytes_per_line = 0x10; 93 | const auto values_per_line = bytes_per_line / bytes_per_value; 94 | const auto total_lines = std::size(buffer) / values_per_line; 95 | const auto maximum_index = (total_lines - 1) * bytes_per_line; 96 | const auto maximum_index_bits = 1 + static_cast(std::log2(maximum_index)); 97 | const auto maximum_index_digits = CC_DIVIDE_CEILING(maximum_index_bits, 4); 98 | 99 | ImGuiListClipper clipper; 100 | clipper.Begin(total_lines); 101 | while (clipper.Step()) 102 | for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; ++i) 103 | PrintLine(std::data(buffer), maximum_index_digits, i); 104 | 105 | ImGui::PopFont(); 106 | 107 | if (IsResizeable()) 108 | ImGui::EndChild(); 109 | } 110 | 111 | public: 112 | using Base::WindowPopup; 113 | 114 | friend Base; 115 | }; 116 | 117 | template<> 118 | void DebugMemory::PrintLine(const cc_u8l *buffer, std::size_t buffer_length_digits, int index); 119 | 120 | template<> 121 | void DebugMemory::PrintLine(const cc_u16l *buffer, std::size_t buffer_length_digits, int index); 122 | 123 | #endif /* DEBUG_MEMORY_H */ 124 | -------------------------------------------------------------------------------- /windows/debug-pcm.cpp: -------------------------------------------------------------------------------- 1 | #include "debug-pcm.h" 2 | 3 | #include "../libraries/imgui/imgui.h" 4 | 5 | #include "../frontend.h" 6 | 7 | void DebugPCM::Registers::DisplayInternal() 8 | { 9 | if (ImGui::BeginTable("Channels", 8, ImGuiTableFlags_Borders)) 10 | { 11 | ImGui::TableSetupColumn("Channel"); 12 | ImGui::TableSetupColumn("Enabled"); 13 | ImGui::TableSetupColumn("Frequency"); 14 | ImGui::TableSetupColumn("Volume"); 15 | ImGui::TableSetupColumn("Panning"); 16 | ImGui::TableSetupColumn("Address"); 17 | ImGui::TableSetupColumn("Start Address"); 18 | ImGui::TableSetupColumn("Loop Address"); 19 | ImGui::TableHeadersRow(); 20 | 21 | const PCM_State &pcm = Frontend::emulator->CurrentState().clownmdemu.mega_cd.pcm; 22 | 23 | for (std::size_t i = 0; i != std::size(pcm.channels); ++i) 24 | { 25 | const auto &channel = pcm.channels[i]; 26 | 27 | ImGui::TableNextColumn(); 28 | ImGui::TextFormatted("{}", i + 1); 29 | 30 | ImGui::TableNextColumn(); 31 | ImGui::TextUnformatted(channel.disabled ? "No" : "Yes"); 32 | 33 | ImGui::PushFont(GetMonospaceFont()); 34 | 35 | ImGui::TableNextColumn(); 36 | ImGui::TextFormatted("0x{:04X} ({:5}Hz)", channel.frequency, channel.frequency * CLOWNMDEMU_PCM_SAMPLE_RATE / 0x800); 37 | 38 | ImGui::TableNextColumn(); 39 | ImGui::TextFormatted("0x{:02X}", channel.volume); 40 | 41 | ImGui::TableNextColumn(); 42 | ImGui::TextFormatted("0x{:02X}", (channel.panning[0] << 0) | (channel.panning[1] << 4)); 43 | 44 | ImGui::TableNextColumn(); 45 | ImGui::TextFormatted("0x{:04X}.{:03X}", channel.address / (1 << 11), (channel.address % (1 << 11)) * 2); 46 | 47 | ImGui::TableNextColumn(); 48 | ImGui::TextFormatted("0x{:02X}00", channel.start_address); 49 | 50 | ImGui::TableNextColumn(); 51 | ImGui::TextFormatted("0x{:04X}", channel.loop_address); 52 | 53 | ImGui::PopFont(); 54 | } 55 | 56 | ImGui::EndTable(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /windows/debug-pcm.h: -------------------------------------------------------------------------------- 1 | #ifndef DEBUG_PCM_H 2 | #define DEBUG_PCM_H 3 | 4 | #include "common/window-popup.h" 5 | 6 | namespace DebugPCM 7 | { 8 | class Registers : public WindowPopup 9 | { 10 | private: 11 | using Base = WindowPopup; 12 | 13 | static constexpr Uint32 window_flags = 0; 14 | 15 | void DisplayInternal(); 16 | 17 | public: 18 | using Base::WindowPopup; 19 | 20 | friend Base; 21 | }; 22 | } 23 | 24 | #endif /* DEBUG_PCM_H */ 25 | -------------------------------------------------------------------------------- /windows/debug-psg.cpp: -------------------------------------------------------------------------------- 1 | #include "debug-psg.h" 2 | 3 | #include 4 | 5 | #include "../frontend.h" 6 | 7 | void DebugPSG::Registers::DisplayInternal() 8 | { 9 | const PSG_State &psg = Frontend::emulator->CurrentState().clownmdemu.psg; 10 | const auto monospace_font = GetMonospaceFont(); 11 | 12 | // Latched command. 13 | ImGui::SeparatorText("Latched Command"); 14 | if (ImGui::BeginTable("Latched Command", 2, ImGuiTableFlags_Borders)) 15 | { 16 | ImGui::TableSetupColumn("Channel"); 17 | ImGui::TableSetupColumn("Type"); 18 | ImGui::TableHeadersRow(); 19 | 20 | ImGui::TableNextColumn(); 21 | 22 | if (psg.latched_command.channel == 3) 23 | ImGui::TextUnformatted("Noise"); 24 | else 25 | ImGui::TextFormatted("Tone {}", psg.latched_command.channel + 1); 26 | 27 | ImGui::TableNextColumn(); 28 | 29 | ImGui::TextUnformatted(psg.latched_command.is_volume_command ? "Attenuation" : "Frequency"); 30 | 31 | ImGui::EndTable(); 32 | } 33 | 34 | // Channels. 35 | const cc_u32f psg_clock = Frontend::emulator->GetPALMode() ? CLOWNMDEMU_PSG_SAMPLE_RATE_PAL : CLOWNMDEMU_PSG_SAMPLE_RATE_NTSC; 36 | 37 | // Tone channels. 38 | ImGui::SeparatorText("Tone Channels"); 39 | if (ImGui::BeginTable("Tone Channels", 3, ImGuiTableFlags_Borders)) 40 | { 41 | ImGui::TableSetupColumn("Channel"); 42 | ImGui::TableSetupColumn("Frequency"); 43 | ImGui::TableSetupColumn("Attentuation"); 44 | ImGui::TableHeadersRow(); 45 | 46 | for (std::size_t i = 0; i != std::size(psg.tones); ++i) 47 | { 48 | const auto &tone = psg.tones[i]; 49 | 50 | ImGui::TableNextColumn(); 51 | ImGui::TextFormatted("Tone {}", i + 1); 52 | 53 | ImGui::PushFont(monospace_font); 54 | 55 | ImGui::TableNextColumn(); 56 | ImGui::TextFormatted("0x{:03X} ({:6}Hz)", tone.countdown_master, tone.countdown_master == 0 ? 0 : psg_clock / tone.countdown_master / 2); 57 | 58 | ImGui::TableNextColumn(); 59 | if (tone.attenuation == 15) 60 | ImGui::TextUnformatted("0xF (Mute)"); 61 | else 62 | ImGui::TextFormatted("0x{:X} ({:2}db)", tone.attenuation, tone.attenuation * 2); 63 | 64 | ImGui::PopFont(); 65 | } 66 | 67 | ImGui::EndTable(); 68 | } 69 | 70 | // Noise channel. 71 | ImGui::SeparatorText("Noise Channel"); 72 | if (ImGui::BeginTable("Noise Channel", 3, ImGuiTableFlags_Borders)) 73 | { 74 | ImGui::TableSetupColumn("Noise Type"); 75 | ImGui::TableSetupColumn("Frequency Mode"); 76 | ImGui::TableSetupColumn("Attentuation"); 77 | ImGui::TableHeadersRow(); 78 | 79 | ImGui::TableNextColumn(); 80 | 81 | switch (psg.noise.type) 82 | { 83 | case PSG_NOISE_TYPE_PERIODIC: 84 | ImGui::TextUnformatted("Periodic"); 85 | break; 86 | 87 | case PSG_NOISE_TYPE_WHITE: 88 | ImGui::TextUnformatted("White"); 89 | break; 90 | } 91 | 92 | ImGui::PushFont(monospace_font); 93 | 94 | ImGui::TableNextColumn(); 95 | 96 | if (psg.noise.frequency_mode == 3) 97 | ImGui::TextUnformatted("Tone 3"); 98 | else 99 | ImGui::TextFormatted("{} ({:4}Hz)", psg.noise.frequency_mode, psg_clock / (0x10 << psg.noise.frequency_mode) / 2); 100 | 101 | ImGui::TableNextColumn(); 102 | 103 | if (psg.noise.attenuation == 15) 104 | ImGui::TextUnformatted("0xF (Mute)"); 105 | else 106 | ImGui::TextFormatted("0x{:X} ({:2}db)", psg.noise.attenuation, psg.noise.attenuation * 2); 107 | 108 | ImGui::PopFont(); 109 | 110 | ImGui::EndTable(); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /windows/debug-psg.h: -------------------------------------------------------------------------------- 1 | #ifndef DEBUG_PSG_H 2 | #define DEBUG_PSG_H 3 | 4 | #include "common/window-popup.h" 5 | 6 | namespace DebugPSG 7 | { 8 | class Registers : public WindowPopup 9 | { 10 | private: 11 | using Base = WindowPopup; 12 | 13 | static constexpr Uint32 window_flags = 0; 14 | 15 | void DisplayInternal(); 16 | 17 | public: 18 | using Base::WindowPopup; 19 | 20 | friend Base; 21 | }; 22 | } 23 | 24 | #endif /* DEBUG_PSG_H */ 25 | -------------------------------------------------------------------------------- /windows/debug-vdp.h: -------------------------------------------------------------------------------- 1 | #ifndef DEBUG_VDP_H 2 | #define DEBUG_VDP_H 3 | 4 | #include 5 | #include 6 | 7 | #include "../sdl-wrapper.h" 8 | 9 | #include "../common/core/clowncommon/clowncommon.h" 10 | #include "../common/core/vdp.h" 11 | 12 | #include "common/window-popup.h" 13 | 14 | namespace DebugVDP 15 | { 16 | constexpr cc_u8f TOTAL_SPRITES = 80; 17 | 18 | using ReadTileWord = std::function; 19 | using DrawMapPiece = std::function; 20 | using MapPieceTooltip = std::function; 21 | using RenderPiece = std::function; 22 | 23 | struct BrightnessAndPaletteLineSettings 24 | { 25 | int brightness_option_index = 0, palette_line_option_index = 0; 26 | 27 | bool DisplayBrightnessAndPaletteLineSettings(); 28 | }; 29 | 30 | struct RegeneratingTexturesBase 31 | { 32 | private: 33 | unsigned int cache_frame_counter = 0; 34 | 35 | public: 36 | // TODO: Any way to share this between the two sprite viewers again when using Dear ImGui windows? 37 | std::vector textures; 38 | 39 | void RegenerateTexturesIfNeeded(const std::function &callback, bool force_regenerate = false); 40 | }; 41 | 42 | struct RegeneratingTextures : private RegeneratingTexturesBase 43 | { 44 | using RegeneratingTexturesBase::textures; 45 | 46 | void RegenerateTexturesIfNeeded(const std::function &callback, bool force_regenerate = false); 47 | }; 48 | 49 | struct RegeneratingTexturesHardwareAccelerated : private RegeneratingTexturesBase 50 | { 51 | using RegeneratingTexturesBase::textures; 52 | 53 | void RegenerateTexturesIfNeeded(SDL::Renderer &renderer, const std::function &callback, bool force_regenerate = false); 54 | }; 55 | 56 | 57 | struct RegeneratingPieces : public RegeneratingTextures 58 | { 59 | protected: 60 | std::size_t texture_width = 0; 61 | std::size_t texture_height = 0; 62 | 63 | public: 64 | void RegenerateIfNeeded( 65 | SDL::Renderer &renderer, 66 | std::size_t piece_width, 67 | std::size_t piece_height, 68 | std::size_t maximum_piece_width, 69 | std::size_t maximum_piece_height, 70 | std::size_t piece_buffer_size_in_pixels, 71 | bool multiple_palette_lines, 72 | const RenderPiece &render_piece_callback, 73 | bool force_regenerate = false); 74 | 75 | auto GetTextureWidth() const 76 | { 77 | return texture_width; 78 | } 79 | 80 | auto GetTextureHeight() const 81 | { 82 | return texture_height; 83 | } 84 | 85 | SDL_FRect GetPieceRect(const std::size_t piece_index, std::size_t piece_width, std::size_t piece_height, std::size_t piece_buffer_size_in_pixels, cc_u8f palette_line_index) const; 86 | 87 | void Draw(SDL::Renderer &renderer, VDP_TileMetadata piece_metadata, std::size_t piece_width, std::size_t piece_height, std::size_t piece_buffer_size_in_pixels, cc_u16f x, cc_u16f y, cc_u8f brightness_index, bool transparency, bool swap_coordinates = false); 88 | }; 89 | 90 | struct SpriteCommon : protected RegeneratingTextures 91 | { 92 | protected: 93 | void DisplaySpriteCommon(Window &window); 94 | }; 95 | 96 | class SpriteViewer: public WindowPopup, public SpriteCommon 97 | { 98 | private: 99 | using Base = WindowPopup; 100 | 101 | static constexpr Uint32 window_flags = 0; 102 | 103 | int scale = 0; 104 | SDL::Texture texture = nullptr; 105 | 106 | void DisplayInternal(); 107 | 108 | public: 109 | using Base::WindowPopup; 110 | 111 | friend Base; 112 | }; 113 | 114 | class SpriteList : public WindowPopup, public SpriteCommon 115 | { 116 | private: 117 | using Base = WindowPopup; 118 | 119 | static constexpr Uint32 window_flags = 0; 120 | 121 | void DisplayInternal(); 122 | 123 | public: 124 | using Base::WindowPopup; 125 | 126 | friend Base; 127 | }; 128 | 129 | template 130 | class MapViewer : public WindowPopup, protected RegeneratingTexturesHardwareAccelerated 131 | { 132 | private: 133 | static constexpr Uint32 window_flags = 0; 134 | 135 | int scale = 0; 136 | 137 | protected: 138 | void DisplayMap( 139 | std::size_t map_width_in_pieces, 140 | std::size_t map_height_in_pieces, 141 | std::size_t maximum_map_width_in_pixels, 142 | std::size_t maximum_map_height_in_pixels, 143 | std::size_t piece_width, 144 | std::size_t piece_height, 145 | const DrawMapPiece &draw_piece, 146 | const MapPieceTooltip &piece_tooltip, 147 | bool force_regenerate = false); 148 | 149 | public: 150 | using Base = WindowPopup; 151 | using Base::WindowPopup; 152 | 153 | friend Base; 154 | }; 155 | 156 | class PlaneViewer : public MapViewer 157 | { 158 | private: 159 | using Base = MapViewer; 160 | 161 | RegeneratingPieces regenerating_pieces; 162 | 163 | void DisplayInternal(cc_u16l plane_address, std::size_t plane_width, std::size_t plane_height); 164 | 165 | public: 166 | using Base::MapViewer; 167 | 168 | friend Base; 169 | friend Base::Base; 170 | }; 171 | 172 | class StampMapViewer : public MapViewer, protected BrightnessAndPaletteLineSettings 173 | { 174 | private: 175 | using Base = MapViewer; 176 | 177 | RegeneratingPieces regenerating_pieces; 178 | 179 | void DisplayInternal(); 180 | 181 | public: 182 | using Base::MapViewer; 183 | 184 | friend Base; 185 | friend Base::Base; 186 | }; 187 | 188 | template 189 | class GridViewer : public WindowPopup, protected BrightnessAndPaletteLineSettings 190 | { 191 | private: 192 | static constexpr Uint32 window_flags = 0; 193 | 194 | protected: 195 | RegeneratingPieces regenerating_pieces; 196 | 197 | void DisplayGrid( 198 | std::size_t piece_width, 199 | std::size_t piece_height, 200 | std::size_t total_pieces, 201 | std::size_t maximum_piece_width, 202 | std::size_t maximum_piece_height, 203 | std::size_t piece_buffer_size_in_pixels, 204 | const RenderPiece &render_piece_callback, 205 | const char *label_singular, 206 | const char *label_plural); 207 | 208 | public: 209 | using Base = WindowPopup; 210 | using Base::WindowPopup; 211 | 212 | friend Base; 213 | }; 214 | 215 | class VRAMViewer : public GridViewer 216 | { 217 | private: 218 | using Base = GridViewer; 219 | 220 | void DisplayInternal(); 221 | 222 | public: 223 | using Base::GridViewer; 224 | 225 | friend Base; 226 | friend Base::Base; 227 | }; 228 | 229 | class StampViewer : public GridViewer 230 | { 231 | private: 232 | using Base = GridViewer; 233 | 234 | void DisplayInternal(); 235 | 236 | public: 237 | using Base::GridViewer; 238 | 239 | friend Base; 240 | friend Base::Base; 241 | }; 242 | 243 | class CRAMViewer : public WindowPopup 244 | { 245 | private: 246 | using Base = WindowPopup; 247 | 248 | static constexpr Uint32 window_flags = 0; 249 | 250 | int brightness = 1; 251 | 252 | void DisplayInternal(); 253 | 254 | public: 255 | using Base::WindowPopup; 256 | 257 | friend Base; 258 | }; 259 | 260 | class Registers : public WindowPopup 261 | { 262 | private: 263 | using Base = WindowPopup; 264 | 265 | static constexpr Uint32 window_flags = 0; 266 | 267 | std::size_t selected_tab = 0; 268 | 269 | void DisplayInternal(); 270 | 271 | public: 272 | using Base::WindowPopup; 273 | 274 | friend Base; 275 | }; 276 | } 277 | 278 | #endif /* DEBUG_VDP_H */ 279 | -------------------------------------------------------------------------------- /windows/debug-z80.cpp: -------------------------------------------------------------------------------- 1 | #include "debug-z80.h" 2 | 3 | #include "../libraries/imgui/imgui.h" 4 | 5 | #include "../frontend.h" 6 | 7 | void DebugZ80::Registers::DisplayInternal() 8 | { 9 | const Z80_State &z80 = Frontend::emulator->CurrentState().clownmdemu.z80.state; 10 | 11 | ImGui::PushFont(GetMonospaceFont()); 12 | 13 | ImGui::TextFormatted("Normal: A:{:02X} B:{:02X} C:{:02X} D:{:02X} E:{:02X} F:{:02X} H:{:02X} L:{:02X}", z80.a, z80.b, z80.c, z80.d, z80.e, z80.f, z80.h, z80.l); 14 | ImGui::Separator(); 15 | ImGui::TextFormatted("Shadow: A:{:02X} B:{:02X} C:{:02X} D:{:02X} E:{:02X} F:{:02X} H:{:02X} L:{:02X}", z80.a_, z80.b_, z80.c_, z80.d_, z80.e_, z80.f_, z80.h_, z80.l_); 16 | ImGui::Separator(); 17 | ImGui::TextFormatted("Other: I:{:02X} R:{:02X} IX:{:04X} IY:{:04X} SP:{:04X} PC:{:04X}", z80.i, z80.r, (static_cast(z80.ixh) << 8) | z80.ixl, (static_cast(z80.iyh) << 8) | z80.iyl, z80.stack_pointer, z80.program_counter); 18 | 19 | ImGui::PopFont(); 20 | } 21 | -------------------------------------------------------------------------------- /windows/debug-z80.h: -------------------------------------------------------------------------------- 1 | #ifndef DEBUG_Z80_H 2 | #define DEBUG_Z80_H 3 | 4 | #include "common/window-popup.h" 5 | 6 | namespace DebugZ80 7 | { 8 | class Registers : public WindowPopup 9 | { 10 | private: 11 | using Base = WindowPopup; 12 | 13 | static constexpr Uint32 window_flags = 0; 14 | 15 | void DisplayInternal(); 16 | 17 | public: 18 | using Base::WindowPopup; 19 | 20 | friend Base; 21 | }; 22 | } 23 | 24 | #endif /* DEBUG_Z80_H */ 25 | -------------------------------------------------------------------------------- /windows/disassembler.cpp: -------------------------------------------------------------------------------- 1 | #include "disassembler.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "../common/core/clown68000/disassembler/disassembler.h" 7 | #include "../libraries/imgui/imgui.h" 8 | 9 | #include "../emulator-instance.h" 10 | #include "../frontend.h" 11 | 12 | static unsigned int address; 13 | static int current_memory; 14 | static std::string assembly; 15 | 16 | static long ReadCallback(void* const user_data) 17 | { 18 | const EmulatorInstance* const emulator = static_cast(user_data); 19 | const ClownMDEmu_State &clownmdemu = emulator->CurrentState().clownmdemu; 20 | 21 | long value; 22 | 23 | switch (current_memory) 24 | { 25 | default: 26 | case 0: 27 | { 28 | // ROM 29 | const std::vector &rom_buffer = emulator->GetROMBuffer(); 30 | 31 | if (rom_buffer.empty()) 32 | { 33 | value = 0; 34 | } 35 | else 36 | { 37 | address %= rom_buffer.size(); 38 | value = (rom_buffer[address + 0] << 8) | rom_buffer[address + 1]; 39 | } 40 | 41 | break; 42 | } 43 | 44 | case 1: 45 | // WORK-RAM 46 | address %= std::size(clownmdemu.m68k.ram) * 2; 47 | value = clownmdemu.m68k.ram[address / 2]; 48 | break; 49 | 50 | case 2: 51 | // PRG-RAM 52 | address %= std::size(clownmdemu.mega_cd.prg_ram.buffer) * 2; 53 | value = clownmdemu.mega_cd.prg_ram.buffer[address / 2]; 54 | break; 55 | 56 | case 3: 57 | // WORD-RAM (1M) Bank 1 58 | address %= std::size(clownmdemu.mega_cd.word_ram.buffer) * 2 / 2; 59 | value = clownmdemu.mega_cd.word_ram.buffer[address / 2 * 2 + 0]; 60 | break; 61 | 62 | case 4: 63 | // WORD-RAM (1M) Bank 2 64 | address %= std::size(clownmdemu.mega_cd.word_ram.buffer) * 2 / 2; 65 | value = clownmdemu.mega_cd.word_ram.buffer[address / 2 * 2 + 1]; 66 | break; 67 | 68 | case 5: 69 | // WORD-RAM (2M) 70 | address %= std::size(clownmdemu.mega_cd.word_ram.buffer) * 2; 71 | value = clownmdemu.mega_cd.word_ram.buffer[address / 2]; 72 | break; 73 | } 74 | 75 | address += 2; 76 | 77 | return value; 78 | } 79 | 80 | static void PrintCallback(void* /*const user_data*/, const char* const string) 81 | { 82 | assembly += string; 83 | assembly += '\n'; 84 | } 85 | 86 | void Disassembler::DisplayInternal() 87 | { 88 | static const std::array memories = {"ROM", "WORK-RAM", "PRG-RAM", "WORD-RAM (1M) Bank 1", "WORD-RAM (1M) Bank 2", "WORD-RAM (2M)"}; 89 | 90 | ImGui::Combo("Memory", ¤t_memory, memories.data(), memories.size()); 91 | 92 | static int address_imgui; 93 | 94 | ImGui::InputInt("Address", &address_imgui, 0, 0, ImGuiInputTextFlags_CharsHexadecimal); 95 | 96 | if (ImGui::Button("Disassemble")) 97 | { 98 | assembly.clear(); 99 | 100 | address = address_imgui; 101 | Clown68000_Disassemble(address, 0x1000, ReadCallback, PrintCallback, &*Frontend::emulator); 102 | 103 | if (assembly[assembly.length() - 1] == '\n') 104 | assembly.pop_back(); 105 | } 106 | 107 | ImGui::PushFont(GetMonospaceFont()); 108 | ImGui::InputTextMultiline("##code", &assembly[0], assembly.length() + 1, ImVec2(-FLT_MIN, -FLT_MIN), ImGuiInputTextFlags_ReadOnly); // '+1' to count the null character. 109 | ImGui::PopFont(); 110 | } 111 | -------------------------------------------------------------------------------- /windows/disassembler.h: -------------------------------------------------------------------------------- 1 | #ifndef DISASSEMBLER_H 2 | #define DISASSEMBLER_H 3 | 4 | #include "common/window-popup.h" 5 | 6 | class Disassembler : public WindowPopup 7 | { 8 | private: 9 | using Base = WindowPopup; 10 | 11 | static constexpr Uint32 window_flags = 0; 12 | 13 | void DisplayInternal(); 14 | 15 | public: 16 | using Base::WindowPopup; 17 | 18 | friend Base; 19 | }; 20 | 21 | #endif /* DISASSEMBLER_H */ 22 | --------------------------------------------------------------------------------