| 1 | // MIT License |
| 2 | |
| 3 | // Copyright (c) 2017 Vadim Grigoruk @nesbox // grigoruk@gmail.com |
| 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 | |
| 23 | #include "start.h" |
| 24 | #include "studio/fs.h" |
| 25 | #include "cart.h" |
| 26 | |
| 27 | #if defined(__TIC_WINDOWS__) |
| 28 | #include <windows.h> |
| 29 | #else |
| 30 | #include <unistd.h> |
| 31 | #endif |
| 32 | |
| 33 | static void reset(Start* start) |
| 34 | { |
| 35 | u8* tile = (u8*)start->tic->ram->tiles.data; |
| 36 | |
| 37 | tic_api_cls(start->tic, tic_color_black); |
| 38 | |
| 39 | static const u8 Reset[] = {0x0, 0x2, 0x42, 0x00}; |
| 40 | u8 val = Reset[sizeof(Reset) * (start->ticks % TIC80_FRAMERATE) / TIC80_FRAMERATE]; |
| 41 | |
| 42 | for(s32 i = 0; i < sizeof(tic_tile); i++) tile[i] = val; |
| 43 | |
| 44 | tic_api_map(start->tic, 0, 0, TIC_MAP_SCREEN_WIDTH, TIC_MAP_SCREEN_HEIGHT + (TIC80_HEIGHT % TIC_SPRITESIZE ? 1 : 0), 0, 0, 0, 0, 1, NULL, NULL); |
| 45 | } |
| 46 | |
| 47 | static void (Start* start) |
| 48 | { |
| 49 | for(s32 i = 0; i < STUDIO_TEXT_BUFFER_SIZE; i++) |
| 50 | tic_api_print(start->tic, (char[]){start->text[i], '\0'}, |
| 51 | (i % STUDIO_TEXT_BUFFER_WIDTH) * STUDIO_TEXT_WIDTH, |
| 52 | (i / STUDIO_TEXT_BUFFER_WIDTH) * STUDIO_TEXT_HEIGHT, |
| 53 | start->color[i], true, 1, false); |
| 54 | } |
| 55 | |
| 56 | static void chime(Start* start) |
| 57 | { |
| 58 | playSystemSfx(start->studio, 1); |
| 59 | } |
| 60 | |
| 61 | static void stop_chime(Start* start) |
| 62 | { |
| 63 | sfx_stop(start->tic, 0); |
| 64 | } |
| 65 | |
| 66 | static void (Start* start) |
| 67 | { |
| 68 | drawHeader(start); |
| 69 | } |
| 70 | |
| 71 | static void start_console(Start* start) |
| 72 | { |
| 73 | drawHeader(start); |
| 74 | setStudioMode(start->studio, TIC_CONSOLE_MODE); |
| 75 | } |
| 76 | |
| 77 | static void tick(Start* start) |
| 78 | { |
| 79 | // stages that have a tick count of 0 run in zero time |
| 80 | // (typically this is only used to start/stop audio) |
| 81 | while (start->stages[start->stage].ticks == 0) { |
| 82 | start->stages[start->stage].fn(start); |
| 83 | start->stage++; |
| 84 | } |
| 85 | |
| 86 | tic_api_cls(start->tic, TIC_COLOR_BG); |
| 87 | |
| 88 | Stage *stage = &start->stages[start->stage]; |
| 89 | stage->fn(start); |
| 90 | if (stage->ticks > 0) stage->ticks--; |
| 91 | if (stage->ticks == 0) start->stage++; |
| 92 | |
| 93 | start->ticks++; |
| 94 | } |
| 95 | |
| 96 | static void* _memmem(const void* haystack, size_t hlen, const void* needle, size_t nlen) |
| 97 | { |
| 98 | const u8* p = haystack; |
| 99 | size_t plen = hlen; |
| 100 | |
| 101 | if (!nlen) return NULL; |
| 102 | |
| 103 | s32 needle_first = *(u8*)needle; |
| 104 | |
| 105 | while (plen >= nlen && (p = memchr(p, needle_first, plen - nlen + 1))) |
| 106 | { |
| 107 | if (!memcmp(p, needle, nlen)) |
| 108 | return (void*)p; |
| 109 | |
| 110 | p++; |
| 111 | plen = hlen - (p - (const u8*)haystack); |
| 112 | } |
| 113 | |
| 114 | return NULL; |
| 115 | } |
| 116 | |
| 117 | void initStart(Start* start, Studio* studio, const char* cart) |
| 118 | { |
| 119 | enum duration { |
| 120 | immediate = 0, |
| 121 | one_second = TIC80_FRAMERATE, |
| 122 | forever = -1 |
| 123 | }; |
| 124 | |
| 125 | *start = (Start) |
| 126 | { |
| 127 | .studio = studio, |
| 128 | .tic = getMemory(studio), |
| 129 | .initialized = true, |
| 130 | .tick = tick, |
| 131 | .embed = false, |
| 132 | .ticks = 0, |
| 133 | .stage = 0, |
| 134 | .stages = |
| 135 | { |
| 136 | { reset, .ticks = one_second }, |
| 137 | { chime, .ticks = immediate }, |
| 138 | { header, .ticks = one_second }, |
| 139 | { stop_chime, .ticks = immediate }, |
| 140 | { start_console, .ticks = forever }, |
| 141 | } |
| 142 | }; |
| 143 | |
| 144 | static const char* [] = |
| 145 | { |
| 146 | "" , |
| 147 | " " TIC_NAME_FULL, |
| 148 | " version " TIC_VERSION, |
| 149 | " " TIC_COPYRIGHT, |
| 150 | }; |
| 151 | |
| 152 | for(s32 i = 0; i < COUNT_OF(Header); i++) |
| 153 | strcpy(&start->text[i * STUDIO_TEXT_BUFFER_WIDTH], Header[i]); |
| 154 | |
| 155 | for(s32 i = 0; i < STUDIO_TEXT_BUFFER_SIZE; i++) |
| 156 | start->color[i] = CLAMP(((i % STUDIO_TEXT_BUFFER_WIDTH) + (i / STUDIO_TEXT_BUFFER_WIDTH)) / 2, |
| 157 | tic_color_black, tic_color_dark_grey); |
| 158 | |
| 159 | #if defined(__EMSCRIPTEN__) |
| 160 | |
| 161 | if (cart) |
| 162 | { |
| 163 | s32 size = 0; |
| 164 | void* data = fs_read(cart, &size); |
| 165 | |
| 166 | if(data) SCOPE(free(data)) |
| 167 | { |
| 168 | tic_cart_load(&start->tic->cart, data, size); |
| 169 | tic_api_reset(start->tic); |
| 170 | start->embed = true; |
| 171 | } |
| 172 | } |
| 173 | |
| 174 | #else |
| 175 | |
| 176 | { |
| 177 | char appPath[TICNAME_MAX]; |
| 178 | |
| 179 | # if defined(__TIC_WINDOWS__) |
| 180 | { |
| 181 | wchar_t wideAppPath[TICNAME_MAX]; |
| 182 | GetModuleFileNameW(NULL, wideAppPath, sizeof wideAppPath); |
| 183 | WideCharToMultiByte(CP_UTF8, 0, wideAppPath, COUNT_OF(wideAppPath), appPath, COUNT_OF(appPath), 0, 0); |
| 184 | } |
| 185 | # elif defined(__TIC_LINUX__) |
| 186 | s32 size = readlink("/proc/self/exe" , appPath, sizeof appPath); |
| 187 | appPath[size] = '\0'; |
| 188 | # elif defined(__TIC_MACOSX__) |
| 189 | s32 size = sizeof appPath; |
| 190 | _NSGetExecutablePath(appPath, &size); |
| 191 | # endif |
| 192 | |
| 193 | s32 appSize = 0; |
| 194 | u8* app = fs_read(appPath, &appSize); |
| 195 | |
| 196 | if(app) SCOPE(free(app)) |
| 197 | { |
| 198 | s32 size = appSize; |
| 199 | const u8* ptr = app; |
| 200 | |
| 201 | while(true) |
| 202 | { |
| 203 | const EmbedHeader* = (const EmbedHeader*)_memmem(ptr, size, CART_SIG, STRLEN(CART_SIG)); |
| 204 | |
| 205 | if(header) |
| 206 | { |
| 207 | if(appSize == header->appSize + sizeof(EmbedHeader) + header->cartSize) |
| 208 | { |
| 209 | u8* data = calloc(1, sizeof(tic_cartridge)); |
| 210 | |
| 211 | if(data) |
| 212 | { |
| 213 | s32 dataSize = tic_tool_unzip(data, sizeof(tic_cartridge), app + header->appSize + sizeof(EmbedHeader), header->cartSize); |
| 214 | |
| 215 | if(dataSize) |
| 216 | { |
| 217 | tic_cart_load(&start->tic->cart, data, dataSize); |
| 218 | tic_api_reset(start->tic); |
| 219 | start->embed = true; |
| 220 | } |
| 221 | |
| 222 | free(data); |
| 223 | } |
| 224 | |
| 225 | break; |
| 226 | } |
| 227 | else |
| 228 | { |
| 229 | ptr = (const u8*)header + STRLEN(CART_SIG); |
| 230 | size = appSize - (s32)(ptr - app); |
| 231 | } |
| 232 | } |
| 233 | else break; |
| 234 | } |
| 235 | } |
| 236 | } |
| 237 | |
| 238 | #endif |
| 239 | } |
| 240 | |
| 241 | void freeStart(Start* start) |
| 242 | { |
| 243 | free(start); |
| 244 | } |
| 245 | |