4 * Copyright (C) 2015 Alexander Andrejevic <theflash AT sdf DOT lonestar DOT org>
5 * Copyright (C) 2015, 2019 Jason Self <j@jxself.org>
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Affero General Public License as
9 * published by the Free Software Foundation, either version 3 of the
10 * License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Affero General Public License for more details.
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>
20 * SPDX-License-Identifier: AGPL-3.0-or-later
46 const int DEFAULT_ZVERSION = 6;
49 { ZVERSION = 11, ZORKID, ZSERIAL };
52 { FAIL = -1, OK = 0, NEED_RESTART = 1 };
54 static struct option const long_options[] = {
55 {"help", no_argument, NULL, 'h'},
56 {"version", no_argument, NULL, 'V'},
57 {"output", required_argument, NULL, 'o'},
58 {"zversion", required_argument, NULL, ZVERSION},
59 {"zorkid", required_argument, NULL, ZORKID},
60 {"serial", required_argument, NULL, ZSERIAL},
73 int zversion; /* 0 - 8 */
74 int zorkid; /* 0 - 65535 */
75 char zserial[7]; /* YYMMDD */
76 Opcode_dict *opcode_dict;
80 struct String_Table_Elem
82 string value; // value in ASCII format
92 void fill_config (void);
93 void get_arguments (int argc, char *argv[], char *envp[]);
95 char *get_output_file_name ();
98 list < String_Table_Elem > m_string_table;
102 char *build_output_filename (const char basename[], const char *suffix);
103 void fill_zserial (void);
104 void new_file_suffix (char *result, size_t maxlen, const char *src,
105 const char *newsuffix);
107 void output_code_section ();
109 void parse_intarg (int *dest, const char name[], int min, int max,
111 void parse_zserial (void);
112 void print_usage (int failed);
113 void print_version ();
114 void wrong_arg (const char *err, ...);
119 CMain::CMain ():m_output_file (NULL)
126 FILE *file = fopen (m_output_file, "wb");
129 program_header_reset (6);
130 int size = sizeof (Program_header);
132 Program_header.mode = 0;
133 Program_header.release = 1; // game version
135 int code_start_offset = 64;
136 Program_header.startPC = code_start_offset >> 2;
139 ZMemblock *zmem_code = zmem_init (65536);
141 /// write zero number of local variables
142 zmem_putbyte (zmem_code, 0); // number of local variables
145 // write instructions' codes
146 for (int i = 0; i < g_numberOfInstructions; ++i)
148 for (int j = 0; j < g_codes[i]->used_size; ++j)
150 zmem_putbyte (zmem_code, g_codes[i]->contents[j]);
156 m_code_size += 8 - (m_code_size & 7);
158 Program_header.dynamic_size = 8;
160 //Program_header.h_file_size = 33; //sizeof(Program_header) + zmb->used_size;
164 Word stringTableOffset = m_code_size;
165 Program_header.H_STRINGS_OFFSET = (64 + stringTableOffset) >> 3;
167 int stringTableSize = 64;
168 Program_header.h_file_size =
169 (code_start_offset + m_code_size + stringTableSize) >> 3;
170 ZMemblock *zmb = zmem_init (Program_header.h_file_size * 8);
172 for (int i = 0; i < m_code_size; ++i)
173 zmem_putbyte (zmb, zmem_code->contents[i]);
175 zmem_destroy (zmem_code);
177 //zmem_putbyte(zmb, 0); // number of local variables
178 //zmem_putbyte(zmb, 141); // print addr command
181 //zmem_putbyte(zmb, (offset >> 8) & 255);
182 //zmem_putbyte(zmb, offset & 255);
184 //zmem_putbyte(zmb, 186); // quit command
186 // output zeros until string table begins
187 while (zmb->used_size < stringTableOffset)
188 zmem_putbyte (zmb, 0);
190 //// fill string table with one string
191 //add_string_to_string_table("Hello, World!", zmb);
193 outputToFile (&Program_header, file);
194 fwrite (zmb->contents, zmb->allocated_size, 1, file);
203 CMain::get_output_file_name ()
205 return m_output_file;
210 CMain::get_arguments (int argc, char *argv[], char *envp[])
213 while ((opt = getopt_long (argc, argv, "hVo:", long_options, NULL)) != -1)
223 wrong_arg ("Output file must be given once\n");
224 m_output_file = optarg;
227 parse_intarg (&Config.zversion, "zversion", 1, 8, 1);
230 parse_intarg (&Config.zorkid, "zorkid", 0, 0xFFFF, 0);
240 int first_input_file = optind;
241 if (first_input_file >= argc)
242 wrong_arg ("Missing input file\n");
244 m_output_file = build_output_filename (argv[first_input_file], ".dat");
246 // TODO: Everything :)
249 printf ("Input files:\n");
250 for (int i = optind; i < argc; i++)
251 printf ("\t%s\n", argv[i]);
253 printf ("Output file: %s\n\n", m_output_file);
258 "- ZSerial: %s\n", Config.zversion, Config.zorkid, Config.zserial);
264 CMain::wrong_arg (const char *err, ...)
270 vfprintf (stderr, err, ap);
273 fprintf (stderr, "Try `" PACKAGE_NAME " --help' for more information.\n");
279 CMain::print_version ()
281 printf (PACKAGE_STRING "\n"
282 "License AGPLv3+: GNU AGPL version 3 or later\n"
283 "<http://gnu.org/licenses/agpl.html>\n"
284 "This is free software: you are free to change and redistribute it.\n"
285 "There is NO WARRANTY, to the extent permitted by law.\n");
291 CMain::print_usage (int failed)
293 printf ("Usage: " PACKAGE_NAME " [OPTION...] [FILES...]\n"
295 "--version Display program version and exit\n"
296 "--help Display this help\n"
298 "--zversion (accepts numbers 1 - 8, defaults to %d if not specified)\n"
299 "--zorkid (integer between 0 and 65535, defaults to 0 if not specified)\n"
300 "--serial (six characters of ASCII, defaults to current date\n"
301 " in the form YYMMDD if not specified)\n",
307 CMain::fill_zserial (void)
312 timeinfo = localtime (&t);
313 strftime (Config.zserial, sizeof (Config.zserial), "%y%m%d", timeinfo);
318 CMain::fill_config (void)
320 bzero (&Config, sizeof (Config));
321 Config.zversion = DEFAULT_ZVERSION;
327 CMain::parse_intarg (int *dest, const char name[], int min, int max,
335 int n = atoi (optarg);
336 if (n >= min && n <= max)
341 wrong_arg ("Wrong %s value %s, must be integer between %d and %d\n",
342 name, optarg, min, max);
347 CMain::parse_zserial (void)
354 size_t n = strlen (optarg);
355 if (n == sizeof (Config.zserial) - 1)
358 while (*p && isalnum (*p))
361 { /* ..optarg contains alphanumeric only? */
362 strncpy (Config.zserial, optarg, sizeof (Config.zserial));
366 wrong_arg ("Wrong zserial value %s, must be 6 ascii characters\n", optarg);
371 CMain::new_file_suffix (char *result, size_t maxlen, const char *src,
372 const char *newsuffix)
374 strncpy (result, src, maxlen);
375 char *p = strrchr (result, '.');
376 if (p && strchr (p, '/'))
380 strncpy (p, newsuffix, maxlen - (p - result));
384 strncat (result, newsuffix, maxlen);
391 CMain::build_output_filename (const char basename[], const char *suffix)
393 int n = strlen (basename) + strlen (suffix);
394 char *ofile = (char *) malloc (n + 1); /* todo!!! check for NULL. free. */
395 new_file_suffix (ofile, n, basename, suffix);
409 CMain::output_code_section ()
417 main (int argc, char *argv[], char *envp[])
422 main.get_arguments (argc, argv, envp);
423 init_opcodes (Config.zversion, 0);
427 for (int i = optind; i < argc; i++)
428 parse_file (argv[i]);
431 /* TODO! List global symbols */
432 /* TODO! Find abbreviations */