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 | |