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