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
33static 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
47static void drawHeader(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
56static void chime(Start* start)
57{
58 playSystemSfx(start->studio, 1);
59}
60
61static void stop_chime(Start* start)
62{
63 sfx_stop(start->tic, 0);
64}
65
66static void header(Start* start)
67{
68 drawHeader(start);
69}
70
71static void start_console(Start* start)
72{
73 drawHeader(start);
74 setStudioMode(start->studio, TIC_CONSOLE_MODE);
75}
76
77static 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
96static 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
117void 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* Header[] =
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* header = (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
241void freeStart(Start* start)
242{
243 free(start);
244}
245