asar coverage - build #279


src/asar/
File: src/asar/interface-cli.cpp
Date: 2025-03-06 00:06:53
Lines:
103/299
34.4%
Functions:
2/9
22.2%
Branches:
147/762
19.3%

Line Branch Exec Source
1 #include "asar.h"
2 #include "libcon.h"
3 #include "platform/file-helpers.h"
4 #include "virtualfile.h"
5 #include "interface-shared.h"
6 #include "assembleblock.h"
7 #include "asar_math.h"
8 #include "unicode.h"
9 #include "platform/thread-helpers.h"
10
11 #if defined(windows)
12 # define NOMINMAX
13 # include <windows.h>
14 # include <io.h>
15 # include <fcntl.h>
16 #endif
17
18 #ifdef TIMELIMIT
19 # if defined(linux)
20 # include <sys/resource.h>
21 # include <signal.h>
22 # elif defined(_WIN32)
23 //WARNING: The Windows equivalent of SIGXCPU, job limits, is very poorly suited for short-running
24 // tasks like this; it's only checked approximately every seven seconds on the machine I tested on,
25 // and it kills the process instantly once this happens. (Additionally, due to an implementation
26 // quirk, it'll bug up if you ask for anything above about seven minutes, so don't do that.)
27 # define NOMINMAX
28 # include <windows.h>
29 # else
30 # error Time limits not configured for this OS.
31 # endif
32 #endif
33
34 #if defined(linux)
35 # include <unistd.h>
36 #elif defined(_WIN32)
37 # include <windows.h>
38 # include <io.h>
39 #endif
40
41 void print(const char * str)
42 {
43 puts(str);
44 }
45
46 static FILE * errloc=stderr;
47 static int errnum=0;
48
49 namespace ansi_text_color {
50 enum e : int {
51 BRIGHT_RED,
52 BRIGHT_YELLOW,
53 };
54 }
55
56 #if defined(_WIN32)
57 static bool has_windows_screen_info = false;
58 static DWORD windows_screen_attributes = 0u;
59 #endif
60
61 static void set_text_color(FILE* output_loc, string* in_out_str, ansi_text_color::e color)
62 {
63 #if defined(linux)
64 if (isatty(fileno(output_loc)))
65 {
66 switch (color)
67 {
68 case ansi_text_color::BRIGHT_RED:
69 *in_out_str = STR "\u001b[31;1m" + *in_out_str;
70 break;
71 case ansi_text_color::BRIGHT_YELLOW:
72 *in_out_str = STR "\u001b[33;1m" + *in_out_str;
73 break;
74 }
75 }
76 #elif defined(_WIN32)
77 // Currently using SetConsoleTextAttribute() approach over an ASCII escape character
78 // approach because it makes the output text easier to parse. Unfortunately, this
79 // also currently makes this a Windows-only solution.
80 CONSOLE_SCREEN_BUFFER_INFO screenInfo;
81 HANDLE win_handle = (HANDLE)_get_osfhandle(fileno(output_loc));
82 if (GetConsoleScreenBufferInfo(win_handle, &screenInfo) == TRUE)
83 {
84 DWORD color = 0u;
85 switch (color)
86 {
87 case ansi_text_color::BRIGHT_RED:
88 color = FOREGROUND_RED;
89 break;
90 case ansi_text_color::BRIGHT_YELLOW:
91 color = FOREGROUND_RED | FOREGROUND_GREEN;
92 break;
93 }
94
95 windows_screen_attributes = screenInfo.wAttributes;
96 has_windows_screen_info = true;
97 SetConsoleTextAttribute(win_handle, (windows_screen_attributes & 0x00F0) | FOREGROUND_INTENSITY | color);
98 }
99 #endif
100 }
101
102 static void reset_text_color(FILE* output_loc, string* in_out_str)
103 {
104 #if defined(linux)
105 if (isatty(fileno(output_loc)))
106 {
107 *in_out_str = STR "\u001b[0m" + *in_out_str;
108 }
109 #elif defined(_WIN32)
110 if (has_windows_screen_info)
111 {
112 HANDLE win_handle = (HANDLE)_get_osfhandle(fileno(output_loc));
113 SetConsoleTextAttribute(win_handle, windows_screen_attributes);
114 }
115 #endif
116 }
117
118 static int max_num_errors = 20;
119
120 void error_interface(const char* errid, int whichpass, const char * e_)
121 {
122 errored = true;
123 if (pass == whichpass)
124 {
125 errnum++;
126 const char* current_block = get_current_block();
127 // don't show current block if the error came from an error command or limit reached
128 bool show_block = (current_block && (strcmp(errid, "error_command") != 0 && strcmp(errid, "limit_reached") != 0));
129 bool show_stack = strcmp(errid, "limit_reached") != 0;
130 string location;
131 string details;
132 get_current_line_details(&location, &details, !show_block);
133 string error_string = (show_stack ? location+": " : STR "") + "error: (" + errid + "): " + e_;
134 string details_string = (show_stack ? details + get_callstack() : "") + "\n";
135 set_text_color(errloc, &error_string, ansi_text_color::BRIGHT_RED);
136 fputs(error_string, errloc);
137 reset_text_color(errloc, &details_string);
138 fputs(details_string, errloc);
139 if (errnum == max_num_errors + 1) throw_err_fatal(pass, err_limit_reached, max_num_errors);
140 }
141 }
142
143 static bool werror=false;
144 static bool warned=false;
145
146 void warn(int errid, const char * e_)
147 {
148 const char* current_block = get_current_block();
149 // don't show current block if the warning came from a warn command
150 bool show_block = (current_block && (errid != warn_id_warn_command));
151 string location;
152 string details;
153 get_current_line_details(&location, &details, !show_block);
154 string warning_string = location+": warning: (" + get_warning_name((asar_warning_id)errid) + "): " + e_;
155 string details_string = details + get_callstack() + "\n";
156 set_text_color(errloc, &warning_string, ansi_text_color::BRIGHT_YELLOW);
157 fputs(warning_string, errloc);
158 reset_text_color(errloc, &details_string);
159 fputs(details_string, errloc);
160 warned=true;
161 }
162
163 #ifdef TIMELIMIT
164 #if defined(linux)
165 void onsigxcpu(int ignored)
166 {
167 error<errnull>(pass, "Time limit exceeded.");
168 exit(1);
169 }
170 #elif defined(_WIN32)
171 //null
172 #endif
173 #endif
174
175 3 int main(int argc, const char * argv[])
176 {
177 #ifdef TIMELIMIT
178 #if defined(linux)
179 rlimit lim;
180 lim.rlim_cur=TIMELIMIT;
181 lim.rlim_max=RLIM_INFINITY;
182 setrlimit(RLIMIT_CPU, &lim);
183 signal(SIGXCPU, onsigxcpu);
184 #elif defined(_WIN32)
185 HANDLE hjob=CreateJobObject(NULL, nullptr);
186 AssignProcessToJobObject(hjob, GetCurrentProcess());
187 JOBOBJECT_BASIC_LIMIT_INFORMATION jbli;
188 jbli.LimitFlags=JOB_OBJECT_LIMIT_PROCESS_TIME;
189 jbli.PerProcessUserTimeLimit.LowPart=10*1000*1000*TIMELIMIT;
190 jbli.PerProcessUserTimeLimit.HighPart=0;
191 SetInformationJobObject(hjob, JobObjectBasicLimitInformation, &jbli, sizeof(jbli));
192 #endif
193 #endif
194
195 #define pause(sev) do { if (pause>=pause_##sev) libcon_pause(); } while(0)
196
197 enum {
198 pause_no,
199 pause_err,
200 pause_warn,
201 pause_yes,
202 3 } pause=pause_no;
203
204 enum cmdlparam
205 {
206 cmdlparam_none,
207
208 cmdlparam_addincludepath,
209 cmdlparam_adddefine,
210
211 cmdlparam_count
212 };
213
214 #if defined(windows)
215 // RPG Hacker: MinGW compatibility hack.
216 # if !defined(_O_U16TEXT)
217 # define _O_U16TEXT 0x20000
218 # endif
219
220 _setmode(_fileno(stdin), _O_U16TEXT);
221 // RPG Hacker: These would currently break Asar, because we're using narrow print functions everywhere.
222 //_setmode(_fileno(stdout), _O_U16TEXT);
223 //_setmode(_fileno(stderr), _O_U16TEXT);
224
225 SetConsoleOutputCP(CP_UTF8);
226 SetConsoleCP(CP_UTF8);
227
228
229 // RPG Hacker: Full Unicode support on Windows requires using a wchar_t command line.
230 // This means we have to convert our arguments from UTF-16 to UTF-8 here.
231 LPWSTR * argv_w = CommandLineToArgvW(GetCommandLineW(), &argc);
232
233 autoarray<string> u8_argv_arr;
234 autoarray<const char *> raw_argv_arr;
235
236 for (int i = 0; i < argc; ++i)
237 {
238 if (!utf16_to_utf8(&u8_argv_arr[i], argv_w[i]))
239 {
240 throw_err_null(pass, err_cmdl_utf16_to_utf8_failed, "Command line arguments on Windows must be valid UTF-16.");
241 pause(err);
242 return 1;
243 }
244
245 raw_argv_arr[i] = u8_argv_arr[i];
246 }
247
248 argv = (const char**)raw_argv_arr;
249 #endif
250
251 try
252 {
253 3 romdata_r = nullptr;
254
12/24
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 3 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 3 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 3 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 3 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 3 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 3 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 3 times.
✗ Branch 19 not taken.
✓ Branch 20 taken 3 times.
✗ Branch 21 not taken.
✓ Branch 22 taken 3 times.
✗ Branch 23 not taken.
9 string version=STR"Asar "+dec(asarver_maj)+"."+dec(asarver_min)+"."+dec(asarver_bug)+(asarver_beta?"pre":"")
255
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
12 +", originally developed by Alcaro, maintained by Asar devs.\n"+
256
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
6 "Source code: https://github.com/RPGHacker/asar\n";
257 3 const char * myname=argv[0];
258
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 3 times.
3 if (strrchr(myname, '/')) myname=strrchr(myname, '/')+1;
259 //char * dot=strrchr(myname, '.');
260 //if (dot) *dot='\0';
261 //if (dot) *dot='.';
262
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 libcon_init(argc, argv,
263 "[options] asm_file [rom_file]\n\n"
264 "Supported options:\n\n"
265 " --version \n"
266 " Display version information.\n\n"
267 " -v, --verbose \n"
268 " Enable verbose mode.\n\n"
269 " --symbols=<none/wla/nocash>\n"
270 " Specifies the format of the symbols output file. (Default is none for no symbols file)\n\n"
271 " --symbols-path=<filename>\n"
272 " Override the default path to the symbols output file. The default is the ROM's base name with an\n"
273 " extension of '.sym'.\n\n"
274 " --no-title-check\n"
275 " Disable verifying ROM title. (Note that irresponsible use will likely corrupt your ROM)\n\n"
276 " --pause-mode=<never/on-error/on-warning/always>\n"
277 " Specify when Asar should pause the application. (Never, on error, on warning or always)\n\n"
278 " --fix-checksum=<on/off>\n"
279 " Override Asar's checksum generation, allowing you to manually enable/disable generating a checksum\n\n"
280 " -I<path> \n"
281 " --include <path> \n"
282 " Add an include search path to Asar.\n\n"
283 " -D<def>[=<val>] \n"
284 " --define <def>[=<val>]\n"
285 " Add a define (optionally with a value) to Asar.\n\n"
286 " -werror \n"
287 " Treat warnings as errors.\n\n"
288 " -w<name> \n"
289 " Enable a specific warning.\n\n"
290 " -wno<name> \n"
291 " Disable a specific warning.\n\n"
292 " --full-error-stack\n"
293 " Enables detailed call stack information for warnings and errors.\n\n"
294 " --error-limit=<N> \n"
295 " Stop after encountering this many errors, instead of the default 20\n\n"
296 );
297 3 ignoretitleerrors=false;
298 3 string par;
299
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 bool verbose=libcon_interactive;
300
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 string symbols="";
301
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 string symfilename="";
302
303 3 autoarray<string> includepaths;
304 3 autoarray<const char*> includepath_cstrs;
305 // need to do this before option processing since the option parser already does set_warning_enabled
306
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 reset_warnings_to_default();
307
308
4/6
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 3 times.
6 while ((par=libcon_option()))
309 {
310 3 cmdlparam postprocess_param = cmdlparam_none;
311 3 const char* postprocess_arg = nullptr;
312
313 #define checkstartmatch(arg, stringliteral) (!strncmp(arg, stringliteral, strlen(stringliteral)))
314
315
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (par=="--no-title-check") ignoretitleerrors=true;
316
3/6
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 3 times.
3 else if (par == "-v" || par=="--verbose") verbose=true;
317
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
3 else if (checkstartmatch(par, "--symbols="))
318 {
319 if (par == "--symbols=none") symbols = "";
320 else if (par=="--symbols=wla") symbols="wla";
321 else if (par=="--symbols=nocash") symbols="nocash";
322 else libcon_badusage();
323 }
324
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
3 else if (checkstartmatch(par, "--symbols-path=")) {
325 symfilename=((const char*)par) + strlen("--symbols-path=");
326 }
327
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
3 else if (checkstartmatch(par, "--error-limit="))
328 {
329 char* out;
330 long lim = strtol((const char*)par + strlen("--error-limit="), &out, 10);
331 max_num_errors = lim;
332 }
333
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 else if (par=="--version")
334 {
335 puts(version);
336 return 0;
337 }
338
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
3 else if (checkstartmatch(par, "--pause-mode="))
339 {
340 if (par=="--pause-mode=never") pause=pause_no;
341 else if (par=="--pause-mode=on-error") pause=pause_err;
342 else if (par=="--pause-mode=on-warning") pause=pause_warn;
343 else if (par=="--pause-mode=always") pause=pause_yes;
344 else libcon_badusage();
345 }
346
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
3 else if(checkstartmatch(par, "--fix-checksum=")) {
347 if(par=="--fix-checksum=on") {
348 force_checksum_fix = true;
349 checksum_fix_enabled = true;
350 } else if(par=="--fix-checksum=off") {
351 force_checksum_fix = true;
352 checksum_fix_enabled = false;
353 } else libcon_badusage();
354 }
355
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
3 else if (checkstartmatch(par, "-I"))
356 {
357 postprocess_param = cmdlparam_addincludepath;
358 postprocess_arg = ((const char*)par) + strlen("-I");
359 }
360
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 1 times.
3 else if (checkstartmatch(par, "-D"))
361 {
362 2 postprocess_param = cmdlparam_adddefine;
363 2 postprocess_arg = ((const char*)par) + strlen("-D");
364 }
365
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 else if (par == "--include")
366 {
367 postprocess_arg = libcon_option_value();
368 if (postprocess_arg != nullptr)
369 {
370 postprocess_param = cmdlparam_addincludepath;
371 }
372 }
373
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 else if (par == "--define")
374 {
375
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 postprocess_arg = libcon_option_value();
376
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (postprocess_arg != nullptr)
377 {
378 1 postprocess_param = cmdlparam_adddefine;
379 }
380 }
381 else if (checkstartmatch(par, "-w"))
382 {
383 const char* w_param = ((const char*)par) + strlen("-w");
384
385 if (checkstartmatch(w_param, "error"))
386 {
387 werror = true;
388 }
389 else if (checkstartmatch(w_param, "no"))
390 {
391 const char* name_start = w_param + strlen("no");
392 asar_warning_id warnid = parse_warning_id_from_string(name_start);
393
394 if (warnid != warning_id_end)
395 {
396 set_warning_enabled(warnid, false);
397 }
398 else
399 {
400 throw_warning(pass, warn_invalid_warning_id, name_start, "-wno");
401 }
402 }
403 else
404 {
405 asar_warning_id warnid = parse_warning_id_from_string(w_param);
406
407 if (warnid != warning_id_end)
408 {
409 set_warning_enabled(warnid, true);
410 }
411 else
412 {
413 throw_warning(pass, warn_invalid_warning_id, w_param, "-w");
414 }
415 }
416
417 }
418 else if (par=="--full-error-stack") simple_callstacks=false;
419 else libcon_badusage();
420
421
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (postprocess_param == cmdlparam_addincludepath)
422 {
423 includepaths.append(postprocess_arg);
424 }
425
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 else if (postprocess_param == cmdlparam_adddefine)
426 {
427
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 1 times.
3 if (strchr(postprocess_arg, '=') != nullptr)
428 {
429 // argument contains value, not only name
430
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 const char* eq_loc = strchr(postprocess_arg, '=');
431
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 string name = string(postprocess_arg, (int)(eq_loc - postprocess_arg));
432
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 strip_whitespace(name);
433
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 name.strip_prefix('!'); // remove leading ! if present
434
435
2/6
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
2 if (!validatedefinename(name)) throw_err_null(pass, err_cmdl_define_invalid, "command line defines", name.data());
436
437
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 if (clidefines.exists(name)) {
438 throw_err_null(pass, err_cmdl_define_override, "Command line define", name.data());
439 pause(err);
440 return 1;
441 }
442
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 clidefines.create(name) = eq_loc + 1;
443
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 }
444 else
445 {
446 // argument doesn't have a value, only name
447
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 string name = postprocess_arg;
448
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 strip_whitespace(name);
449
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 name.strip_prefix('!'); // remove leading ! if present
450
451
2/6
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
1 if (!validatedefinename(name)) throw_err_null(pass, err_cmdl_define_invalid, "command line defines", name.data());
452
453
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 if (clidefines.exists(name)) {
454 throw_err_null(pass, err_cmdl_define_override, "Command line define", name.data());
455 pause(err);
456 return 1;
457 }
458
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 clidefines.create(name) = "";
459
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 }
460 }
461 }
462
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
3 if (verbose)
463 {
464
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 puts(version);
465 }
466
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3 string asmname=libcon_require_filename("Enter patch name:");
467
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3 string romname=libcon_optional_filename("Enter ROM name:", nullptr);
468 //char * outname=libcon_optional_filename("Enter output ROM name:", nullptr);
469
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 libcon_end();
470
3/12
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 3 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
3 if (!strchr(asmname, '.') && !file_exists(asmname)) asmname+=".asm";
471
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (!romname)
472 {
473 string romnametmp = get_base_name(asmname);
474 if (file_exists(romnametmp+".sfc")) romname=romnametmp+".sfc";
475 else if (file_exists(romnametmp+".smc")) romname=romnametmp+".smc";
476 else romname=romnametmp+".sfc";
477 }
478
3/10
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 3 times.
3 else if (!strchr(romname, '.') && !file_exists(romname))
479 {
480 if (file_exists(romname+".sfc")) romname+=".sfc";
481 else if (file_exists(romname+".smc")) romname+=".smc";
482 }
483
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3 if (!file_exists(romname))
484 {
485
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 FileHandleType f = open_file(romname, FileOpenMode_Write);
486
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (f == InvalidFileHandle)
487 {
488 throw_err_fatal(pass, err_create_rom_failed);
489 }
490
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 close_file(f);
491 }
492
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
3 if (!openrom(romname, false))
493 {
494 pause(err);
495 return 1;
496 }
497 //check if the ROM title and checksum looks sane
498
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
3 if (romlen>=32768 && !ignoretitleerrors)
499 {
500 bool validtitle=setmapper();
501 if (!validtitle)
502 {
503 string title;
504 for (int i=0;i<21;i++)
505 {
506 unsigned char c=romdata[snestopc(0x00FFC0+i)];
507 if (c==7) c=14;
508 if (c==8) c=27;//to not generate more hard-to-print characters than needed
509 if (c==9) c=26;//random characters are picked in accordance with the charset Windows-1252, but they should be garbage in all charsets
510 if (c=='\r') c=17;
511 if (c=='\n') c=25;
512 if (c=='\0') c=155;
513 title+=(char)c;
514 }
515 if (libcon_interactive)
516 {
517 if (!libcon_question_bool(STR"Warning: The ROM title appears to be \""+title+"\", which looks like garbage. "
518 "Is this your ROM title? (Note that improperly answering \"yes\" will crash your ROM.)", false))
519 {
520 puts("Assembling aborted. snespurify should be able to fix your ROM.");
521 return 1;
522 }
523 }
524 else
525 {
526 puts(STR"Error: The ROM title appears to be \""+title+"\", which looks like garbage. "
527 "If this is the ROM title, add --no-title-check to the command line options. If the ROM title is something else, use snespurify on your ROM.");
528 pause(err);
529 return 1;
530 }
531 }
532 }
533
534
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3 string stdincludespath = STR dir(argv[0]) + "stdincludes.txt";
535
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 parse_std_includes(stdincludespath, includepaths);
536
537
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 for (int i = 0; i < includepaths.count; ++i)
538 {
539 includepath_cstrs.append((const char*)includepaths[i]);
540 }
541
542 3 size_t includepath_count = (size_t)includepath_cstrs.count;
543
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 virtual_filesystem new_filesystem;
544
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3 new_filesystem.initialize(&includepath_cstrs[0], includepath_count);
545 3 filesystem = &new_filesystem;
546
547
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3 string stddefinespath = STR dir(argv[0]) + "stddefines.txt";
548
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 parse_std_defines(stddefinespath);
549
550 3 auto execute_patch = [&]()
551 {
552 try {
553
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 3 times.
12 for (pass=0;pass<3;pass++)
554 {
555 //pass 1: find which bank all labels are in, for label optimizations
556 // freespaces are listed as above 0xFFFFFF, to find if it's in the ROM or if it's dynamic
557 //pass 2: find where exactly all labels are
558 //pass 3: assemble it all
559
1/2
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
9 initstuff();
560
1/2
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
9 assemblefile(asmname);
561 // RPG Hacker: Necessary, because finishpass() can throws warning and errors.
562
1/2
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
9 string asmpath = filesystem->create_absolute_path(nullptr, asmname);
563
1/2
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
9 callstack_push cs_push(callstack_entry_type::FILE, asmpath);
564
1/2
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
9 finishpass();
565 9 }
566 3 return true;
567 } catch(errfatal&) {
568 return false;
569 }
570 3 };
571 #if defined(RUN_VIA_FIBER)
572 run_as_fiber(execute_patch);
573 #elif defined(RUN_VIA_THREAD)
574
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 run_as_thread(execute_patch);
575 #else
576 execute_patch();
577 #endif
578
579
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 closecachedfiles(); // this needs the vfs so do it before destroying it
580
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 new_filesystem.destroy();
581 3 filesystem = nullptr;
582
583
2/10
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
3 if (werror && warned) throw_err_null(pass, err_werror);
584
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 if (checksum_fix_enabled) fixchecksum();
585 //if (pcpos>romlen) romlen=pcpos;
586
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
3 if (errored)
587 {
588 if (errnum==0)
589 throw_err_null(pass, err_internal_error, "phantom error");
590 puts("Errors were detected while assembling the patch. Assembling aborted. Your ROM has not been modified.");
591 closerom(false);
592 reseteverything();
593 pause(err);
594 return 1;
595 }
596
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
3 if (warned)
597 {
598 if (libcon_interactive)
599 {
600 if (!libcon_question_bool("One or more warnings were detected while assembling the patch. "
601 "Do you want insert the patch anyways? (Default: yes)", true))
602 {
603 puts("ROM left unchanged.");
604 closerom(false);
605 reseteverything();
606 return 1;
607 }
608 }
609 else
610 {
611 if (verbose) puts("Assembling completed, but one or more warnings were detected.");
612 pause(warn);
613 }
614 }
615 else
616 {
617
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
3 if (verbose) puts("Assembling completed without problems.");
618
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
3 pause(yes);
619 }
620
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 unsigned int romCrc = closerom();
621
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (symbols)
622 {
623 if (!symfilename) symfilename = get_base_name(romname)+".sym";
624 string contents = create_symbols_file(symbols, romCrc).convert_line_endings_to_native();
625
626 FileHandleType symfile = open_file(symfilename, FileOpenMode_Write);
627
628 if (symfile == InvalidFileHandle)
629 {
630 puts(STR"Failed to create symbols file: \""+symfilename+"\".");
631 pause(err);
632 return 1;
633 }
634 else
635 {
636 write_file(symfile, (const char*)contents, (uint32_t)contents.length());
637 close_file(symfile);
638 }
639 }
640
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 reseteverything();
641
11/22
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 3 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 3 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 3 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 3 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 3 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 3 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 3 times.
✗ Branch 19 not taken.
✓ Branch 20 taken 3 times.
✗ Branch 21 not taken.
3 }
642 catch(errfatal&)
643 {
644 puts("A fatal error was detected while assembling the patch. Assembling aborted. Your ROM has not been modified.");
645 closerom(false);
646 reseteverything();
647 pause(err);
648 return 1;
649 }
650 3 return 0;
651 }
652