--- /dev/null
+/* ------------------------------------------------------------------------- */
+/* "files" : File handling for source code, the transcript file and the */
+/* debugging information file; file handling and splicing of */
+/* the output file. */
+/* */
+/* Note that filenaming conventions are left to the top-level */
+/* routines in "inform.c", since they are tied up with ICL */
+/* settings and are very host OS-dependent. */
+/* */
+/* Copyright (c) Graham Nelson 1993 - 2018 */
+/* */
+/* This file is part of Inform. */
+/* */
+/* Inform is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 3 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* Inform is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with Inform. If not, see https://gnu.org/licenses/ */
+/* */
+/* ------------------------------------------------------------------------- */
+
+#include "header.h"
+
+int total_files; /* Number of files so far, including
+ #include and #origsource files */
+int total_input_files; /* Number of source files so far
+ (excludes #origsource) */
+int current_input_file; /* Most recently-opened source file */
+static int current_origsource_file; /* Most recently-used #origsource */
+
+int32 total_chars_read; /* Characters read in (from all
+ source files put together) */
+
+static int checksum_low_byte, /* For calculating the Z-machine's */
+ checksum_high_byte; /* "verify" checksum */
+
+static int32 checksum_long; /* For the Glulx checksum, */
+static int checksum_count; /* similarly */
+
+/* ------------------------------------------------------------------------- */
+/* Most of the information about source files is kept by "lexer.c"; this */
+/* level is only concerned with file names and handles. */
+/* ------------------------------------------------------------------------- */
+
+FileId *InputFiles=NULL; /* Ids for all the source files */
+static char *filename_storage, /* Translated filenames */
+ *filename_storage_p;
+static int filename_storage_left;
+
+/* ------------------------------------------------------------------------- */
+/* When emitting debug information, we won't have addresses of routines, */
+/* sequence points, Glulx objects (addresses of Z-machine objects aren't */
+/* needed), globals, arrays, or grammar lines. We only have their */
+/* offsets from base addresses, which won't be known until the end of */
+/* compilation. Since everything else in the relevant debug records is */
+/* known much earlier and is less convenient to store up, we emit the */
+/* debug records with a placeholder value and then backpatch these */
+/* placeholders. The following structs each store either an offset or a */
+/* symbol index and the point in the debug information file where the */
+/* corresponding address should be written once the base address is known. */
+/* ------------------------------------------------------------------------- */
+
+#define INITIAL_DEBUG_INFORMATION_BACKPATCH_ALLOCATION 65536
+
+typedef struct value_and_backpatch_position_struct
+{ int32 value;
+ fpos_t backpatch_position;
+} value_and_backpatch_position;
+
+typedef struct debug_backpatch_accumulator_struct
+{ int32 number_of_values_to_backpatch;
+ int32 number_of_available_backpatches;
+ value_and_backpatch_position *values_and_backpatch_positions;
+ int32 (* backpatching_function)(int32);
+} debug_backpatch_accumulator;
+
+static debug_backpatch_accumulator object_backpatch_accumulator;
+static debug_backpatch_accumulator packed_code_backpatch_accumulator;
+static debug_backpatch_accumulator code_backpatch_accumulator;
+static debug_backpatch_accumulator global_backpatch_accumulator;
+static debug_backpatch_accumulator array_backpatch_accumulator;
+static debug_backpatch_accumulator grammar_backpatch_accumulator;
+
+/* ------------------------------------------------------------------------- */
+/* File handles and names for temporary files. */
+/* ------------------------------------------------------------------------- */
+
+FILE *Temp1_fp=NULL, *Temp2_fp=NULL, *Temp3_fp=NULL;
+char Temp1_Name[PATHLEN], Temp2_Name[PATHLEN], Temp3_Name[PATHLEN];
+
+/* ------------------------------------------------------------------------- */
+/* Opening and closing source code files */
+/* ------------------------------------------------------------------------- */
+
+#if defined(PC_WIN32) && defined(HAS_REALPATH)
+#include <windows.h>
+char *realpath(const char *path, char *resolved_path)
+{
+ return GetFullPathNameA(path,PATHLEN,resolved_path,NULL) != 0 ? resolved_path : 0;
+}
+#endif
+
+extern void load_sourcefile(char *filename_given, int same_directory_flag)
+{
+ /* Meaning: open a new file of Inform source. (The lexer picks up on
+ this by noticing that input_file has increased.) */
+
+ char name[PATHLEN];
+#ifdef HAS_REALPATH
+ char absolute_name[PATHLEN];
+#endif
+ int x = 0;
+ FILE *handle;
+
+ if (total_files == MAX_SOURCE_FILES)
+ memoryerror("MAX_SOURCE_FILES", MAX_SOURCE_FILES);
+
+ do
+ { x = translate_in_filename(x, name, filename_given, same_directory_flag,
+ (total_files==0)?1:0);
+ handle = fopen(name,"r");
+ } while ((handle == NULL) && (x != 0));
+
+ if (filename_storage_left <= (int)strlen(name))
+ memoryerror("MAX_SOURCE_FILES", MAX_SOURCE_FILES);
+
+ filename_storage_left -= strlen(name)+1;
+ strcpy(filename_storage_p, name);
+ InputFiles[total_files].filename = filename_storage_p;
+
+ filename_storage_p += strlen(name)+1;
+
+ if (debugfile_switch)
+ { debug_file_printf("<source index=\"%d\">", total_files);
+ debug_file_printf("<given-path>");
+ debug_file_print_with_entities(filename_given);
+ debug_file_printf("</given-path>");
+#ifdef HAS_REALPATH
+ if (realpath(name, absolute_name))
+ { debug_file_printf("<resolved-path>");
+ debug_file_print_with_entities(absolute_name);
+ debug_file_printf("</resolved-path>");
+ }
+#endif
+ debug_file_printf("<language>Inform 6</language>");
+ debug_file_printf("</source>");
+ }
+
+ InputFiles[total_files].handle = handle;
+ if (InputFiles[total_files].handle==NULL)
+ fatalerror_named("Couldn't open source file", name);
+
+ InputFiles[total_files].is_input = TRUE;
+
+ if (line_trace_level > 0) printf("\nOpening file \"%s\"\n",name);
+
+ total_files++;
+ total_input_files++;
+ current_input_file = total_files;
+}
+
+static void close_sourcefile(int file_number)
+{
+ if (InputFiles[file_number-1].handle == NULL) return;
+
+ /* Close this file. */
+
+ if (ferror(InputFiles[file_number-1].handle))
+ fatalerror_named("I/O failure: couldn't read from source file",
+ InputFiles[file_number-1].filename);
+
+ fclose(InputFiles[file_number-1].handle);
+
+ InputFiles[file_number-1].handle = NULL;
+
+ if (line_trace_level > 0) printf("\nClosing file\n");
+}
+
+extern void close_all_source(void)
+{ int i;
+ for (i=0; i<total_files; i++) close_sourcefile(i+1);
+}
+
+/* ------------------------------------------------------------------------- */
+/* Register an #origsource filename. This goes in the InputFiles table, */
+/* but we do not open the file or advance current_input_file. */
+/* ------------------------------------------------------------------------- */
+
+extern int register_orig_sourcefile(char *filename)
+{
+ int ix;
+
+ /* If the filename has already been used as an origsource filename,
+ return that entry. We check the most-recently-used file first, and
+ then search the list. */
+ if (current_origsource_file > 0 && current_origsource_file <= total_files) {
+ if (!strcmp(filename, InputFiles[current_origsource_file-1].filename))
+ return current_origsource_file;
+ }
+
+ for (ix=0; ix<total_files; ix++) {
+ if (InputFiles[ix].is_input)
+ continue;
+ if (!strcmp(filename, InputFiles[ix].filename)) {
+ current_origsource_file = ix+1;
+ return current_origsource_file;
+ }
+ }
+
+ /* This filename has never been used before. Allocate a new InputFiles
+ entry. */
+
+ char *name = filename; /* no translation */
+
+ if (total_files == MAX_SOURCE_FILES)
+ memoryerror("MAX_SOURCE_FILES", MAX_SOURCE_FILES);
+
+ if (filename_storage_left <= (int)strlen(name))
+ memoryerror("MAX_SOURCE_FILES", MAX_SOURCE_FILES);
+
+ filename_storage_left -= strlen(name)+1;
+ strcpy(filename_storage_p, name);
+ InputFiles[total_files].filename = filename_storage_p;
+
+ filename_storage_p += strlen(name)+1;
+
+ if (debugfile_switch)
+ { debug_file_printf("<source index=\"%d\">", total_files);
+ debug_file_printf("<given-path>");
+ debug_file_print_with_entities(filename);
+ debug_file_printf("</given-path>");
+ debug_file_printf("<language>Inform 7</language>");
+ debug_file_printf("</source>");
+ }
+
+ InputFiles[total_files].handle = NULL;
+ InputFiles[total_files].is_input = FALSE;
+
+ total_files++;
+ current_origsource_file = total_files;
+ return current_origsource_file;
+}
+
+/* ------------------------------------------------------------------------- */
+/* Feeding source code up into the lexical analyser's buffer */
+/* (see "lexer.c" for its specification) */
+/* ------------------------------------------------------------------------- */
+
+extern int file_load_chars(int file_number, char *buffer, int length)
+{
+ int read_in; FILE *handle;
+
+ if (file_number-1 > total_files)
+ { buffer[0] = 0; return 1; }
+
+ handle = InputFiles[file_number-1].handle;
+ if (handle == NULL)
+ { buffer[0] = 0; return 1; }
+
+ read_in = fread(buffer, 1, length, handle);
+ total_chars_read += read_in;
+
+ if (read_in == length) return length;
+
+ close_sourcefile(file_number);
+
+ if (file_number == 1)
+ { buffer[read_in] = 0;
+ buffer[read_in+1] = 0;
+ buffer[read_in+2] = 0;
+ buffer[read_in+3] = 0;
+ }
+ else
+ { buffer[read_in] = '\n';
+ buffer[read_in+1] = ' ';
+ buffer[read_in+2] = ' ';
+ buffer[read_in+3] = ' ';
+ }
+
+ return(-(read_in+4));
+}
+
+/* ------------------------------------------------------------------------- */
+/* Final assembly and output of the story file/module. */
+/* ------------------------------------------------------------------------- */
+
+FILE *sf_handle;
+
+static void sf_put(int c)
+{
+ if (!glulx_mode) {
+
+ /* The checksum is the unsigned sum mod 65536 of the bytes in the
+ story file from 0x0040 (first byte after header) to the end.
+
+ The link data does not contribute to the checksum of a module. */
+
+ checksum_low_byte += c;
+ if (checksum_low_byte>=256)
+ { checksum_low_byte-=256;
+ if (++checksum_high_byte==256) checksum_high_byte=0;
+ }
+
+ }
+ else {
+
+ /* The checksum is the unsigned 32-bit sum of the entire story file,
+ considered as a list of 32-bit words, with the checksum field
+ being zero. */
+
+ switch (checksum_count) {
+ case 0:
+ checksum_long += (((int32)(c & 0xFF)) << 24);
+ break;
+ case 1:
+ checksum_long += (((int32)(c & 0xFF)) << 16);
+ break;
+ case 2:
+ checksum_long += (((int32)(c & 0xFF)) << 8);
+ break;
+ case 3:
+ checksum_long += ((int32)(c & 0xFF));
+ break;
+ }
+
+ checksum_count = (checksum_count+1) & 3;
+
+ }
+
+ fputc(c, sf_handle);
+}
+
+/* Recursive procedure to generate the Glulx compression table. */
+
+static void output_compression(int entnum, int32 *size, int *count)
+{
+ huffentity_t *ent = &(huff_entities[entnum]);
+ int32 val;
+ char *cx;
+
+ sf_put(ent->type);
+ (*size)++;
+ (*count)++;
+
+ switch (ent->type) {
+ case 0:
+ val = Write_Strings_At + huff_entities[ent->u.branch[0]].addr;
+ sf_put((val >> 24) & 0xFF);
+ sf_put((val >> 16) & 0xFF);
+ sf_put((val >> 8) & 0xFF);
+ sf_put((val) & 0xFF);
+ (*size) += 4;
+ val = Write_Strings_At + huff_entities[ent->u.branch[1]].addr;
+ sf_put((val >> 24) & 0xFF);
+ sf_put((val >> 16) & 0xFF);
+ sf_put((val >> 8) & 0xFF);
+ sf_put((val) & 0xFF);
+ (*size) += 4;
+ output_compression(ent->u.branch[0], size, count);
+ output_compression(ent->u.branch[1], size, count);
+ break;
+ case 1:
+ /* no data */
+ break;
+ case 2:
+ sf_put(ent->u.ch);
+ (*size) += 1;
+ break;
+ case 3:
+ cx = (char *)abbreviations_at + ent->u.val*MAX_ABBREV_LENGTH;
+ while (*cx) {
+ sf_put(*cx);
+ cx++;
+ (*size) += 1;
+ }
+ sf_put('\0');
+ (*size) += 1;
+ break;
+ case 4:
+ val = unicode_usage_entries[ent->u.val].ch;
+ sf_put((val >> 24) & 0xFF);
+ sf_put((val >> 16) & 0xFF);
+ sf_put((val >> 8) & 0xFF);
+ sf_put((val) & 0xFF);
+ (*size) += 4;
+ break;
+ case 9:
+ val = abbreviations_offset + 4 + ent->u.val*4;
+ sf_put((val >> 24) & 0xFF);
+ sf_put((val >> 16) & 0xFF);
+ sf_put((val >> 8) & 0xFF);
+ sf_put((val) & 0xFF);
+ (*size) += 4;
+ break;
+ }
+}
+
+static void output_file_z(void)
+{ FILE *fin=NULL; char new_name[PATHLEN];
+ int32 length, blanks=0, size, i, j, offset;
+ uint32 code_length, size_before_code, next_cons_check;
+ int use_function;
+
+ ASSERT_ZCODE();
+
+ /* At this point, construct_storyfile() has just been called. */
+
+ /* Enter the length information into the header. */
+
+ length=((int32) Write_Strings_At) + static_strings_extent;
+ if (module_switch) length += link_data_size +
+ zcode_backpatch_size +
+ zmachine_backpatch_size;
+
+ while ((length%length_scale_factor)!=0) { length++; blanks++; }
+ length=length/length_scale_factor;
+ zmachine_paged_memory[26]=(length & 0xff00)/0x100;
+ zmachine_paged_memory[27]=(length & 0xff);
+
+ /* To assist interpreters running a paged virtual memory system, Inform
+ writes files which are padded with zeros to the next multiple of
+ 0.5K. This calculates the number of bytes of padding needed: */
+
+ while (((length_scale_factor*length)+blanks-1)%512 != 511) blanks++;
+
+ translate_out_filename(new_name, Code_Name);
+
+ sf_handle = fopen(new_name,"wb");
+ if (sf_handle == NULL)
+ fatalerror_named("Couldn't open output file", new_name);
+
+#ifdef MAC_MPW
+ /* Set the type and creator to Andrew Plotkin's MaxZip, a popular
+ Z-code interpreter on the Macintosh */
+
+ if (!module_switch) fsetfileinfo(new_name, 'mxZR', 'ZCOD');
+#endif
+
+ /* (1) Output the paged memory. */
+
+ for (i=0;i<64;i++)
+ fputc(zmachine_paged_memory[i], sf_handle);
+ size = 64;
+ checksum_low_byte = 0;
+ checksum_high_byte = 0;
+
+ for (i=64; i<Write_Code_At; i++)
+ { sf_put(zmachine_paged_memory[i]); size++;
+ }
+
+ /* (2) Output the compiled code area. */
+
+ if (temporary_files_switch)
+ { fclose(Temp2_fp);
+ Temp2_fp = NULL;
+ fin=fopen(Temp2_Name,"rb");
+ if (fin==NULL)
+ fatalerror("I/O failure: couldn't reopen temporary file 2");
+ }
+
+ if (!OMIT_UNUSED_ROUTINES) {
+ /* This is the old-fashioned case, which is easy. All of zcode_area
+ (zmachine_pc bytes) will be output. next_cons_check will be
+ ignored, because j will never reach it. */
+ code_length = zmachine_pc;
+ use_function = TRUE;
+ next_cons_check = code_length+1;
+ }
+ else {
+ /* With dead function stripping, life is more complicated.
+ j will run from 0 to zmachine_pc, but only code_length of
+ those should be output. next_cons_check is the location of
+ the next function break; that's where we check whether
+ we're in a live function or a dead one.
+ (This logic is simplified by the assumption that a backpatch
+ marker will never straddle a function break.) */
+ if (zmachine_pc != df_total_size_before_stripping)
+ compiler_error("Code size does not match (zmachine_pc and df_total_size).");
+ code_length = df_total_size_after_stripping;
+ use_function = TRUE;
+ next_cons_check = 0;
+ df_prepare_function_iterate();
+ }
+ size_before_code = size;
+
+ j=0;
+ if (!module_switch)
+ for (i=0; i<zcode_backpatch_size; i=i+3)
+ { int long_flag = TRUE;
+ offset
+ = 256*read_byte_from_memory_block(&zcode_backpatch_table, i+1)
+ + read_byte_from_memory_block(&zcode_backpatch_table, i+2);
+ backpatch_error_flag = FALSE;
+ backpatch_marker
+ = read_byte_from_memory_block(&zcode_backpatch_table, i);
+ if (backpatch_marker >= 0x80) long_flag = FALSE;
+ backpatch_marker &= 0x7f;
+ offset = offset + (backpatch_marker/32)*0x10000;
+ while (offset+0x30000 < j) {
+ offset += 0x40000;
+ long_flag = !long_flag;
+ }
+ backpatch_marker &= 0x1f;
+
+ /* All code up until the next backpatch marker gets flushed out
+ as-is. (Unless we're in a stripped-out function.) */
+ while (j<offset) {
+ if (!use_function) {
+ while (j<offset && j<next_cons_check) {
+ /* get dummy value */
+ ((temporary_files_switch)?fgetc(fin):
+ read_byte_from_memory_block(&zcode_area, j));
+ j++;
+ }
+ }
+ else {
+ while (j<offset && j<next_cons_check) {
+ size++;
+ sf_put((temporary_files_switch)?fgetc(fin):
+ read_byte_from_memory_block(&zcode_area, j));
+ j++;
+ }
+ }
+ if (j == next_cons_check)
+ next_cons_check = df_next_function_iterate(&use_function);
+ }
+
+ if (long_flag)
+ { int32 v = (temporary_files_switch)?fgetc(fin):
+ read_byte_from_memory_block(&zcode_area, j);
+ v = 256*v + ((temporary_files_switch)?fgetc(fin):
+ read_byte_from_memory_block(&zcode_area, j+1));
+ j += 2;
+ if (use_function) {
+ v = backpatch_value(v);
+ sf_put(v/256); sf_put(v%256);
+ size += 2;
+ }
+ }
+ else
+ { int32 v = (temporary_files_switch)?fgetc(fin):
+ read_byte_from_memory_block(&zcode_area, j);
+ j++;
+ if (use_function) {
+ v = backpatch_value(v);
+ sf_put(v);
+ size++;
+ }
+ }
+
+ if (j > next_cons_check)
+ compiler_error("Backpatch appears to straddle function break");
+
+ if (backpatch_error_flag)
+ { printf("*** %s zcode offset=%08lx backpatch offset=%08lx ***\n",
+ (long_flag)?"long":"short", (long int) j, (long int) i);
+ }
+ }
+
+ /* Flush out the last bit of zcode_area, after the last backpatch
+ marker. */
+ offset = zmachine_pc;
+ while (j<offset) {
+ if (!use_function) {
+ while (j<offset && j<next_cons_check) {
+ /* get dummy value */
+ ((temporary_files_switch)?fgetc(fin):
+ read_byte_from_memory_block(&zcode_area, j));
+ j++;
+ }
+ }
+ else {
+ while (j<offset && j<next_cons_check) {
+ size++;
+ sf_put((temporary_files_switch)?fgetc(fin):
+ read_byte_from_memory_block(&zcode_area, j));
+ j++;
+ }
+ }
+ if (j == next_cons_check)
+ next_cons_check = df_next_function_iterate(&use_function);
+ }
+
+ if (temporary_files_switch)
+ { if (ferror(fin))
+ fatalerror("I/O failure: couldn't read from temporary file 2");
+ fclose(fin);
+ fin = NULL;
+ }
+
+ if (size_before_code + code_length != size)
+ compiler_error("Code output length did not match");
+
+ /* (3) Output any null bytes (required to reach a packed address)
+ before the strings area. */
+
+ while (size<Write_Strings_At) { sf_put(0); size++; }
+
+ /* (4) Output the static strings area. */
+
+ if (temporary_files_switch)
+ { fclose(Temp1_fp);
+ Temp1_fp = NULL;
+ fin=fopen(Temp1_Name,"rb");
+ if (fin==NULL)
+ fatalerror("I/O failure: couldn't reopen temporary file 1");
+ for (i=0; i<static_strings_extent; i++) sf_put(fgetc(fin));
+ if (ferror(fin))
+ fatalerror("I/O failure: couldn't read from temporary file 1");
+ fclose(fin);
+ fin = NULL;
+ remove(Temp1_Name); remove(Temp2_Name);
+ }
+ else
+ for (i=0; i<static_strings_extent; i++) {
+ sf_put(read_byte_from_memory_block(&static_strings_area,i));
+ size++;
+ }
+
+ /* (5) Output the linking data table (in the case of a module). */
+
+ if (temporary_files_switch)
+ { if (module_switch)
+ { fclose(Temp3_fp);
+ Temp3_fp = NULL;
+ fin=fopen(Temp3_Name,"rb");
+ if (fin==NULL)
+ fatalerror("I/O failure: couldn't reopen temporary file 3");
+ for (j=0; j<link_data_size; j++) sf_put(fgetc(fin));
+ if (ferror(fin))
+ fatalerror("I/O failure: couldn't read from temporary file 3");
+ fclose(fin);
+ fin = NULL;
+ remove(Temp3_Name);
+ }
+ }
+ else
+ if (module_switch)
+ for (i=0; i<link_data_size; i++)
+ sf_put(read_byte_from_memory_block(&link_data_area,i));
+
+ if (module_switch)
+ { for (i=0; i<zcode_backpatch_size; i++)
+ sf_put(read_byte_from_memory_block(&zcode_backpatch_table, i));
+ for (i=0; i<zmachine_backpatch_size; i++)
+ sf_put(read_byte_from_memory_block(&zmachine_backpatch_table, i));
+ }
+
+ /* (6) Output null bytes to reach a multiple of 0.5K. */
+
+ while (blanks>0) { sf_put(0); blanks--; }
+
+ if (ferror(sf_handle))
+ fatalerror("I/O failure: couldn't write to story file");
+
+ fseek(sf_handle, 28, SEEK_SET);
+ fputc(checksum_high_byte, sf_handle);
+ fputc(checksum_low_byte, sf_handle);
+
+ if (ferror(sf_handle))
+ fatalerror("I/O failure: couldn't backtrack on story file for checksum");
+
+ fclose(sf_handle);
+
+ /* Write a copy of the header into the debugging information file
+ (mainly so that it can be used to identify which story file matches
+ with which debugging info file). */
+
+ if (debugfile_switch)
+ { debug_file_printf("<story-file-prefix>");
+ for (i = 0; i < 63; i += 3)
+ { if (i == 27)
+ { debug_file_print_base_64_triple
+ (zmachine_paged_memory[27],
+ checksum_high_byte,
+ checksum_low_byte);
+ } else
+ { debug_file_print_base_64_triple
+ (zmachine_paged_memory[i],
+ zmachine_paged_memory[i + 1],
+ zmachine_paged_memory[i + 2]);
+ }
+ }
+ debug_file_print_base_64_single(zmachine_paged_memory[63]);
+ debug_file_printf("</story-file-prefix>");
+ }
+
+#ifdef ARCHIMEDES
+ { char settype_command[PATHLEN];
+ sprintf(settype_command, "settype %s %s",
+ new_name, riscos_file_type());
+ system(settype_command);
+ }
+#endif
+#ifdef MAC_FACE
+ if (module_switch)
+ InformFiletypes (new_name, INF_MODULE_TYPE);
+ else
+ InformFiletypes (new_name, INF_ZCODE_TYPE);
+#endif
+}
+
+static void output_file_g(void)
+{ FILE *fin=NULL; char new_name[PATHLEN];
+ int32 size, i, j, offset;
+ int32 VersionNum;
+ uint32 code_length, size_before_code, next_cons_check;
+ int use_function;
+ int first_byte_of_triple, second_byte_of_triple, third_byte_of_triple;
+
+ ASSERT_GLULX();
+
+ /* At this point, construct_storyfile() has just been called. */
+
+ translate_out_filename(new_name, Code_Name);
+
+ sf_handle = fopen(new_name,"wb+");
+ if (sf_handle == NULL)
+ fatalerror_named("Couldn't open output file", new_name);
+
+#ifdef MAC_MPW
+ /* Set the type and creator to Andrew Plotkin's MaxZip, a popular
+ Z-code interpreter on the Macintosh */
+
+ if (!module_switch) fsetfileinfo(new_name, 'mxZR', 'ZCOD');
+#endif
+
+ checksum_long = 0;
+ checksum_count = 0;
+
+ /* Determine the version number. */
+
+ VersionNum = 0x00020000;
+
+ /* Increase for various features the game may have used. */
+ if (no_unicode_chars != 0 || (uses_unicode_features)) {
+ VersionNum = 0x00030000;
+ }
+ if (uses_memheap_features) {
+ VersionNum = 0x00030100;
+ }
+ if (uses_acceleration_features) {
+ VersionNum = 0x00030101;
+ }
+ if (uses_float_features) {
+ VersionNum = 0x00030102;
+ }
+
+ /* And check if the user has requested a specific version. */
+ if (requested_glulx_version) {
+ if (requested_glulx_version < VersionNum) {
+ static char error_message_buff[256];
+ sprintf(error_message_buff, "Version 0x%08lx requested, but \
+game features require version 0x%08lx", (long)requested_glulx_version, (long)VersionNum);
+ warning(error_message_buff);
+ }
+ else {
+ VersionNum = requested_glulx_version;
+ }
+ }
+
+ /* (1) Output the header. We use sf_put here, instead of fputc,
+ because the header is included in the checksum. */
+
+ /* Magic number */
+ sf_put('G');
+ sf_put('l');
+ sf_put('u');
+ sf_put('l');
+ /* Version number. */
+ sf_put((VersionNum >> 24));
+ sf_put((VersionNum >> 16));
+ sf_put((VersionNum >> 8));
+ sf_put((VersionNum));
+ /* RAMSTART */
+ sf_put((Write_RAM_At >> 24));
+ sf_put((Write_RAM_At >> 16));
+ sf_put((Write_RAM_At >> 8));
+ sf_put((Write_RAM_At));
+ /* EXTSTART, or game file size */
+ sf_put((Out_Size >> 24));
+ sf_put((Out_Size >> 16));
+ sf_put((Out_Size >> 8));
+ sf_put((Out_Size));
+ /* ENDMEM, which the game file size plus MEMORY_MAP_EXTENSION */
+ i = Out_Size + MEMORY_MAP_EXTENSION;
+ sf_put((i >> 24));
+ sf_put((i >> 16));
+ sf_put((i >> 8));
+ sf_put((i));
+ /* STACKSIZE */
+ sf_put((MAX_STACK_SIZE >> 24));
+ sf_put((MAX_STACK_SIZE >> 16));
+ sf_put((MAX_STACK_SIZE >> 8));
+ sf_put((MAX_STACK_SIZE));
+ /* Initial function to call. Inform sets things up so that this
+ is the start of the executable-code area. */
+ sf_put((Write_Code_At >> 24));
+ sf_put((Write_Code_At >> 16));
+ sf_put((Write_Code_At >> 8));
+ sf_put((Write_Code_At));
+ /* String-encoding table. */
+ sf_put((Write_Strings_At >> 24));
+ sf_put((Write_Strings_At >> 16));
+ sf_put((Write_Strings_At >> 8));
+ sf_put((Write_Strings_At));
+ /* Checksum -- zero for the moment. */
+ sf_put(0x00);
+ sf_put(0x00);
+ sf_put(0x00);
+ sf_put(0x00);
+
+ size = GLULX_HEADER_SIZE;
+
+ /* (1a) Output the eight-byte memory layout identifier. */
+
+ sf_put('I'); sf_put('n'); sf_put('f'); sf_put('o');
+ sf_put(0); sf_put(1); sf_put(0); sf_put(0);
+
+ /* (1b) Output the rest of the Inform-specific data. */
+
+ /* Inform version number */
+ sf_put('0' + ((RELEASE_NUMBER/100)%10));
+ sf_put('.');
+ sf_put('0' + ((RELEASE_NUMBER/10)%10));
+ sf_put('0' + RELEASE_NUMBER%10);
+ /* Glulx back-end version number */
+ sf_put('0' + ((GLULX_RELEASE_NUMBER/100)%10));
+ sf_put('.');
+ sf_put('0' + ((GLULX_RELEASE_NUMBER/10)%10));
+ sf_put('0' + GLULX_RELEASE_NUMBER%10);
+ /* Game release number */
+ sf_put((release_number>>8) & 0xFF);
+ sf_put(release_number & 0xFF);
+ /* Game serial number */
+ {
+ char serialnum[8];
+ write_serial_number(serialnum);
+ for (i=0; i<6; i++)
+ sf_put(serialnum[i]);
+ }
+ size += GLULX_STATIC_ROM_SIZE;
+
+ /* (2) Output the compiled code area. */
+
+ if (temporary_files_switch)
+ { fclose(Temp2_fp);
+ Temp2_fp = NULL;
+ fin=fopen(Temp2_Name,"rb");
+ if (fin==NULL)
+ fatalerror("I/O failure: couldn't reopen temporary file 2");
+ }
+
+ if (!OMIT_UNUSED_ROUTINES) {
+ /* This is the old-fashioned case, which is easy. All of zcode_area
+ (zmachine_pc bytes) will be output. next_cons_check will be
+ ignored, because j will never reach it. */
+ code_length = zmachine_pc;
+ use_function = TRUE;
+ next_cons_check = code_length+1;
+ }
+ else {
+ /* With dead function stripping, life is more complicated.
+ j will run from 0 to zmachine_pc, but only code_length of
+ those should be output. next_cons_check is the location of
+ the next function break; that's where we check whether
+ we're in a live function or a dead one.
+ (This logic is simplified by the assumption that a backpatch
+ marker will never straddle a function break.) */
+ if (zmachine_pc != df_total_size_before_stripping)
+ compiler_error("Code size does not match (zmachine_pc and df_total_size).");
+ code_length = df_total_size_after_stripping;
+ use_function = TRUE;
+ next_cons_check = 0;
+ df_prepare_function_iterate();
+ }
+ size_before_code = size;
+
+ j=0;
+ if (!module_switch)
+ for (i=0; i<zcode_backpatch_size; i=i+6) {
+ int data_len;
+ int32 v;
+ offset =
+ (read_byte_from_memory_block(&zcode_backpatch_table, i+2) << 24)
+ | (read_byte_from_memory_block(&zcode_backpatch_table, i+3) << 16)
+ | (read_byte_from_memory_block(&zcode_backpatch_table, i+4) << 8)
+ | (read_byte_from_memory_block(&zcode_backpatch_table, i+5));
+ backpatch_error_flag = FALSE;
+ backpatch_marker =
+ read_byte_from_memory_block(&zcode_backpatch_table, i);
+ data_len =
+ read_byte_from_memory_block(&zcode_backpatch_table, i+1);
+
+ /* All code up until the next backpatch marker gets flushed out
+ as-is. (Unless we're in a stripped-out function.) */
+ while (j<offset) {
+ if (!use_function) {
+ while (j<offset && j<next_cons_check) {
+ /* get dummy value */
+ ((temporary_files_switch)?fgetc(fin):
+ read_byte_from_memory_block(&zcode_area, j));
+ j++;
+ }
+ }
+ else {
+ while (j<offset && j<next_cons_check) {
+ size++;
+ sf_put((temporary_files_switch)?fgetc(fin):
+ read_byte_from_memory_block(&zcode_area, j));
+ j++;
+ }
+ }
+ if (j == next_cons_check)
+ next_cons_check = df_next_function_iterate(&use_function);
+ }
+
+ /* Write out the converted value of the backpatch marker.
+ (Unless we're in a stripped-out function.) */
+ switch (data_len) {
+
+ case 4:
+ v = ((temporary_files_switch)?fgetc(fin):
+ read_byte_from_memory_block(&zcode_area, j));
+ v = (v << 8) | ((temporary_files_switch)?fgetc(fin):
+ read_byte_from_memory_block(&zcode_area, j+1));
+ v = (v << 8) | ((temporary_files_switch)?fgetc(fin):
+ read_byte_from_memory_block(&zcode_area, j+2));
+ v = (v << 8) | ((temporary_files_switch)?fgetc(fin):
+ read_byte_from_memory_block(&zcode_area, j+3));
+ j += 4;
+ if (!use_function)
+ break;
+ v = backpatch_value(v);
+ sf_put((v >> 24) & 0xFF);
+ sf_put((v >> 16) & 0xFF);
+ sf_put((v >> 8) & 0xFF);
+ sf_put((v) & 0xFF);
+ size += 4;
+ break;
+
+ case 2:
+ v = ((temporary_files_switch)?fgetc(fin):
+ read_byte_from_memory_block(&zcode_area, j));
+ v = (v << 8) | ((temporary_files_switch)?fgetc(fin):
+ read_byte_from_memory_block(&zcode_area, j+1));
+ j += 2;
+ if (!use_function)
+ break;
+ v = backpatch_value(v);
+ if (v >= 0x10000) {
+ printf("*** backpatch value does not fit ***\n");
+ backpatch_error_flag = TRUE;
+ }
+ sf_put((v >> 8) & 0xFF);
+ sf_put((v) & 0xFF);
+ size += 2;
+ break;
+
+ case 1:
+ v = ((temporary_files_switch)?fgetc(fin):
+ read_byte_from_memory_block(&zcode_area, j));
+ j += 1;
+ if (!use_function)
+ break;
+ v = backpatch_value(v);
+ if (v >= 0x100) {
+ printf("*** backpatch value does not fit ***\n");
+ backpatch_error_flag = TRUE;
+ }
+ sf_put((v) & 0xFF);
+ size += 1;
+ break;
+
+ default:
+ printf("*** unknown backpatch data len = %d ***\n",
+ data_len);
+ backpatch_error_flag = TRUE;
+ }
+
+ if (j > next_cons_check)
+ compiler_error("Backpatch appears to straddle function break");
+
+ if (backpatch_error_flag) {
+ printf("*** %d bytes zcode offset=%08lx backpatch offset=%08lx ***\n",
+ data_len, (long int) j, (long int) i);
+ }
+ }
+
+ /* Flush out the last bit of zcode_area, after the last backpatch
+ marker. */
+ offset = zmachine_pc;
+ while (j<offset) {
+ if (!use_function) {
+ while (j<offset && j<next_cons_check) {
+ /* get dummy value */
+ ((temporary_files_switch)?fgetc(fin):
+ read_byte_from_memory_block(&zcode_area, j));
+ j++;
+ }
+ }
+ else {
+ while (j<offset && j<next_cons_check) {
+ size++;
+ sf_put((temporary_files_switch)?fgetc(fin):
+ read_byte_from_memory_block(&zcode_area, j));
+ j++;
+ }
+ }
+ if (j == next_cons_check)
+ next_cons_check = df_next_function_iterate(&use_function);
+ }
+
+ if (temporary_files_switch)
+ { if (ferror(fin))
+ fatalerror("I/O failure: couldn't read from temporary file 2");
+ fclose(fin);
+ fin = NULL;
+ }
+
+ if (size_before_code + code_length != size)
+ compiler_error("Code output length did not match");
+
+ /* (4) Output the static strings area. */
+
+ if (temporary_files_switch) {
+ fseek(Temp1_fp, 0, SEEK_SET);
+ }
+ {
+ int32 ix, lx;
+ int ch, jx, curbyte, bx;
+ int depth, checkcount;
+ huffbitlist_t *bits;
+ int32 origsize;
+
+ origsize = size;
+
+ if (compression_switch) {
+
+ /* The 12-byte table header. */
+ lx = compression_table_size;
+ sf_put((lx >> 24) & 0xFF);
+ sf_put((lx >> 16) & 0xFF);
+ sf_put((lx >> 8) & 0xFF);
+ sf_put((lx) & 0xFF);
+ size += 4;
+ sf_put((no_huff_entities >> 24) & 0xFF);
+ sf_put((no_huff_entities >> 16) & 0xFF);
+ sf_put((no_huff_entities >> 8) & 0xFF);
+ sf_put((no_huff_entities) & 0xFF);
+ size += 4;
+ lx = Write_Strings_At + 12;
+ sf_put((lx >> 24) & 0xFF);
+ sf_put((lx >> 16) & 0xFF);
+ sf_put((lx >> 8) & 0xFF);
+ sf_put((lx) & 0xFF);
+ size += 4;
+
+ checkcount = 0;
+ output_compression(huff_entity_root, &size, &checkcount);
+ if (checkcount != no_huff_entities)
+ compiler_error("Compression table count mismatch.");
+ }
+
+ if (size - origsize != compression_table_size)
+ compiler_error("Compression table size mismatch.");
+
+ origsize = size;
+
+ for (lx=0, ix=0; lx<no_strings; lx++) {
+ int escapelen=0, escapetype=0;
+ int done=FALSE;
+ int32 escapeval=0;
+ if (compression_switch)
+ sf_put(0xE1); /* type byte -- compressed string */
+ else
+ sf_put(0xE0); /* type byte -- non-compressed string */
+ size++;
+ jx = 0;
+ curbyte = 0;
+ while (!done) {
+ if (temporary_files_switch)
+ ch = fgetc(Temp1_fp);
+ else
+ ch = read_byte_from_memory_block(&static_strings_area, ix);
+ ix++;
+ if (ix > static_strings_extent || ch < 0)
+ compiler_error("Read too much not-yet-compressed text.");
+
+ if (escapelen == -1) {
+ escapelen = 0;
+ if (ch == '@') {
+ ch = '@';
+ }
+ else if (ch == '0') {
+ ch = '\0';
+ }
+ else if (ch == 'A' || ch == 'D' || ch == 'U') {
+ escapelen = 4;
+ escapetype = ch;
+ escapeval = 0;
+ continue;
+ }
+ else {
+ compiler_error("Strange @ escape in processed text.");
+ }
+ }
+ else if (escapelen) {
+ escapeval = (escapeval << 4) | ((ch-'A') & 0x0F);
+ escapelen--;
+ if (escapelen == 0) {
+ if (escapetype == 'A') {
+ ch = huff_abbrev_start+escapeval;
+ }
+ else if (escapetype == 'D') {
+ ch = huff_dynam_start+escapeval;
+ }
+ else if (escapetype == 'U') {
+ ch = huff_unicode_start+escapeval;
+ }
+ else {
+ compiler_error("Strange @ escape in processed text.");
+ }
+ }
+ else
+ continue;
+ }
+ else {
+ if (ch == '@') {
+ escapelen = -1;
+ continue;
+ }
+ if (ch == 0) {
+ ch = 256;
+ done = TRUE;
+ }
+ }
+
+ if (compression_switch) {
+ bits = &(huff_entities[ch].bits);
+ depth = huff_entities[ch].depth;
+ for (bx=0; bx<depth; bx++) {
+ if (bits->b[bx / 8] & (1 << (bx % 8)))
+ curbyte |= (1 << jx);
+ jx++;
+ if (jx == 8) {
+ sf_put(curbyte);
+ size++;
+ curbyte = 0;
+ jx = 0;
+ }
+ }
+ }
+ else {
+ if (ch >= huff_dynam_start) {
+ sf_put(' '); sf_put(' '); sf_put(' ');
+ size += 3;
+ }
+ else if (ch >= huff_abbrev_start) {
+ /* nothing */
+ }
+ else {
+ /* 256, the string terminator, comes out as zero */
+ sf_put(ch & 0xFF);
+ size++;
+ }
+ }
+ }
+ if (compression_switch && jx) {
+ sf_put(curbyte);
+ size++;
+ }
+ }
+
+ if (size - origsize != compression_string_size)
+ compiler_error("Compression string size mismatch.");
+
+ }
+
+ /* (4.5) Output any null bytes (required to reach a GPAGESIZE address)
+ before RAMSTART. */
+
+ while (size % GPAGESIZE) { sf_put(0); size++; }
+
+ /* (5) Output RAM. */
+
+ for (i=0; i<RAM_Size; i++)
+ { sf_put(zmachine_paged_memory[i]); size++;
+ }
+
+ if (ferror(sf_handle))
+ fatalerror("I/O failure: couldn't write to story file");
+
+ fseek(sf_handle, 32, SEEK_SET);
+ fputc((checksum_long >> 24) & 0xFF, sf_handle);
+ fputc((checksum_long >> 16) & 0xFF, sf_handle);
+ fputc((checksum_long >> 8) & 0xFF, sf_handle);
+ fputc((checksum_long) & 0xFF, sf_handle);
+
+ if (ferror(sf_handle))
+ fatalerror("I/O failure: couldn't backtrack on story file for checksum");
+
+ /* Write a copy of the first 64 bytes into the debugging information file
+ (mainly so that it can be used to identify which story file matches with
+ which debugging info file). */
+
+ if (debugfile_switch)
+ { fseek(sf_handle, 0L, SEEK_SET);
+ debug_file_printf("<story-file-prefix>");
+ for (i = 0; i < 63; i += 3)
+ { first_byte_of_triple = fgetc(sf_handle);
+ second_byte_of_triple = fgetc(sf_handle);
+ third_byte_of_triple = fgetc(sf_handle);
+ debug_file_print_base_64_triple
+ (first_byte_of_triple,
+ second_byte_of_triple,
+ third_byte_of_triple);
+ }
+ debug_file_print_base_64_single(fgetc(sf_handle));
+ debug_file_printf("</story-file-prefix>");
+ }
+
+ fclose(sf_handle);
+
+#ifdef ARCHIMEDES
+ { char settype_command[PATHLEN];
+ sprintf(settype_command, "settype %s %s",
+ new_name, riscos_file_type());
+ system(settype_command);
+ }
+#endif
+#ifdef MAC_FACE
+ if (module_switch)
+ InformFiletypes (new_name, INF_MODULE_TYPE);
+ else
+ InformFiletypes (new_name, INF_ZCODE_TYPE);
+#endif
+}
+
+extern void output_file(void)
+{
+ if (!glulx_mode)
+ output_file_z();
+ else
+ output_file_g();
+}
+
+/* ------------------------------------------------------------------------- */
+/* Output the text transcript file (only called if there is to be one). */
+/* ------------------------------------------------------------------------- */
+
+FILE *transcript_file_handle; int transcript_open;
+
+extern void write_to_transcript_file(char *text)
+{ fputs(text, transcript_file_handle);
+ fputc('\n', transcript_file_handle);
+}
+
+extern void open_transcript_file(char *what_of)
+{ char topline_buffer[256];
+
+ transcript_file_handle = fopen(Transcript_Name,"w");
+ if (transcript_file_handle==NULL)
+ fatalerror_named("Couldn't open transcript file",
+ Transcript_Name);
+
+ transcript_open = TRUE;
+
+ sprintf(topline_buffer, "Transcript of the text of \"%s\"\n\
+[From %s]\n", what_of, banner_line);
+ write_to_transcript_file(topline_buffer);
+}
+
+extern void abort_transcript_file(void)
+{ if (transcript_switch && transcript_open)
+ fclose(transcript_file_handle);
+ transcript_open = FALSE;
+}
+
+extern void close_transcript_file(void)
+{ char botline_buffer[256];
+ char sn_buffer[7];
+
+ write_serial_number(sn_buffer);
+ sprintf(botline_buffer, "\n[End of transcript: release %d.%s]\n",
+ release_number, sn_buffer);
+ write_to_transcript_file(botline_buffer);
+
+ if (ferror(transcript_file_handle))
+ fatalerror("I/O failure: couldn't write to transcript file");
+ fclose(transcript_file_handle);
+ transcript_open = FALSE;
+
+#ifdef ARCHIMEDES
+ { char settype_command[PATHLEN];
+ sprintf(settype_command, "settype %s text",
+ Transcript_Name);
+ system(settype_command);
+ }
+#endif
+#ifdef MAC_FACE
+ InformFiletypes (Transcript_Name, INF_TEXT_TYPE);
+#endif
+}
+
+/* ------------------------------------------------------------------------- */
+/* Access to the debugging information file. */
+/* ------------------------------------------------------------------------- */
+
+static FILE *Debug_fp; /* Handle of debugging info file */
+
+static void open_debug_file(void)
+{ Debug_fp=fopen(Debugging_Name,"wb");
+ if (Debug_fp==NULL)
+ fatalerror_named("Couldn't open debugging information file",
+ Debugging_Name);
+}
+
+extern void nullify_debug_file_position(maybe_file_position *position) {
+ position->valid = 0;
+}
+
+static void close_debug_file(void)
+{ fclose(Debug_fp);
+#ifdef MAC_FACE
+ InformFiletypes (Debugging_Name, INF_DEBUG_TYPE);
+#endif
+}
+
+extern void begin_debug_file(void)
+{ open_debug_file();
+
+ debug_file_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
+ debug_file_printf("<inform-story-file version=\"1.0\" ");
+ debug_file_printf("content-creator=\"Inform\" ");
+ debug_file_printf
+ ("content-creator-version=\"%d.%d%d\">",
+ (VNUMBER / 100) % 10,
+ (VNUMBER / 10) % 10,
+ VNUMBER % 10);
+}
+
+extern void debug_file_printf(const char*format, ...)
+{ va_list argument_pointer;
+ va_start(argument_pointer, format);
+ vfprintf(Debug_fp, format, argument_pointer);
+ va_end(argument_pointer);
+ if (ferror(Debug_fp))
+ { fatalerror("I/O failure: can't write to debugging information file");
+ }
+}
+
+extern void debug_file_print_with_entities(const char*string)
+{ int index = 0;
+ char character;
+ for (character = string[index]; character; character = string[++index])
+ { switch(character)
+ { case '"':
+ debug_file_printf(""");
+ break;
+ case '&':
+ debug_file_printf("&");
+ break;
+ case '\'':
+ debug_file_printf("'");
+ break;
+ case '<':
+ debug_file_printf("<");
+ break;
+ case '>':
+ debug_file_printf(">");
+ break;
+ default:
+ debug_file_printf("%c", character);
+ break;
+ }
+ }
+}
+
+static char base_64_digits[] =
+ { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
+ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
+ 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
+ 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', '+', '/' };
+
+extern void debug_file_print_base_64_triple
+ (uchar first, uchar second, uchar third)
+{ debug_file_printf
+ ("%c%c%c%c",
+ base_64_digits[first >> 2],
+ base_64_digits[((first & 3) << 4) | (second >> 4)],
+ base_64_digits[((second & 15) << 2) | (third >> 6)],
+ base_64_digits[third & 63]);
+}
+
+extern void debug_file_print_base_64_pair(uchar first, uchar second)
+{ debug_file_printf
+ ("%c%c%c=",
+ base_64_digits[first >> 2],
+ base_64_digits[((first & 3) << 4) | (second >> 4)],
+ base_64_digits[(second & 15) << 2]);
+}
+
+extern void debug_file_print_base_64_single(uchar first)
+{ debug_file_printf
+ ("%c%c==",
+ base_64_digits[first >> 2],
+ base_64_digits[(first & 3) << 4]);
+}
+
+static void write_debug_location_internals(debug_location location)
+{ debug_file_printf("<file-index>%d</file-index>", location.file_index - 1);
+ debug_file_printf
+ ("<file-position>%d</file-position>", location.beginning_byte_index);
+ debug_file_printf
+ ("<line>%d</line>", location.beginning_line_number);
+ debug_file_printf
+ ("<character>%d</character>", location.beginning_character_number);
+ if (location.beginning_byte_index != location.end_byte_index ||
+ location.beginning_line_number != location.end_line_number ||
+ location.beginning_character_number != location.end_character_number)
+ { debug_file_printf
+ ("<end-file-position>%d</end-file-position>",
+ location.end_byte_index);
+ debug_file_printf
+ ("<end-line>%d</end-line>", location.end_line_number);
+ debug_file_printf
+ ("<end-character>%d</end-character>",
+ location.end_character_number);
+ }
+}
+
+static void write_debug_location_origsource_internals(debug_location location)
+{ debug_file_printf
+ ("<file-index>%d</file-index>", location.orig_file_index - 1);
+ if (location.orig_beg_line_number)
+ debug_file_printf
+ ("<line>%d</line>", location.orig_beg_line_number);
+ if (location.orig_beg_char_number)
+ debug_file_printf
+ ("<character>%d</character>", location.orig_beg_char_number);
+}
+
+extern void write_debug_location(debug_location location)
+{ if (location.file_index && location.file_index != 255)
+ { debug_file_printf("<source-code-location>");
+ write_debug_location_internals(location);
+ debug_file_printf("</source-code-location>");
+ }
+ if (location.orig_file_index)
+ { debug_file_printf("<source-code-location>");
+ write_debug_location_origsource_internals(location);
+ debug_file_printf("</source-code-location>");
+ }
+}
+
+extern void write_debug_locations(debug_locations locations)
+{ if (locations.next)
+ { const debug_locations*current = &locations;
+ unsigned int index = 0;
+ for (; current; current = current->next, ++index)
+ { debug_file_printf("<source-code-location index=\"%d\">", index);
+ write_debug_location_internals(current->location);
+ debug_file_printf("</source-code-location>");
+ }
+ if (locations.location.orig_file_index)
+ { debug_file_printf("<source-code-location>");
+ write_debug_location_origsource_internals(locations.location);
+ debug_file_printf("</source-code-location>");
+ }
+ }
+ else
+ { write_debug_location(locations.location);
+ }
+}
+
+extern void write_debug_optional_identifier(int32 symbol_index)
+{ if (stypes[symbol_index] != ROUTINE_T)
+ { compiler_error
+ ("Attempt to write a replaceable identifier for a non-routine");
+ }
+ if (replacement_debug_backpatch_positions[symbol_index].valid)
+ { if (fsetpos
+ (Debug_fp,
+ &replacement_debug_backpatch_positions[symbol_index].position))
+ { fatalerror("I/O failure: can't seek in debugging information file");
+ }
+ debug_file_printf
+ ("<identifier artificial=\"true\">%s "
+ "(superseded replacement)</identifier>",
+ symbs[symbol_index]);
+ if (fseek(Debug_fp, 0L, SEEK_END))
+ { fatalerror("I/O failure: can't seek in debugging information file");
+ }
+ }
+ fgetpos
+ (Debug_fp, &replacement_debug_backpatch_positions[symbol_index].position);
+ replacement_debug_backpatch_positions[symbol_index].valid = TRUE;
+ debug_file_printf("<identifier>%s</identifier>", symbs[symbol_index]);
+ /* Space for: artificial="true" (superseded replacement) */
+ debug_file_printf(" ");
+}
+
+extern void write_debug_symbol_backpatch(int32 symbol_index)
+{ if (symbol_debug_backpatch_positions[symbol_index].valid) {
+ compiler_error("Symbol entry incorrectly reused in debug information "
+ "file backpatching");
+ }
+ fgetpos(Debug_fp, &symbol_debug_backpatch_positions[symbol_index].position);
+ symbol_debug_backpatch_positions[symbol_index].valid = TRUE;
+ /* Reserve space for up to 10 digits plus a negative sign. */
+ debug_file_printf("*BACKPATCH*");
+}
+
+extern void write_debug_symbol_optional_backpatch(int32 symbol_index)
+{ if (symbol_debug_backpatch_positions[symbol_index].valid) {
+ compiler_error("Symbol entry incorrectly reused in debug information "
+ "file backpatching");
+ }
+ /* Reserve space for open and close value tags and up to 10 digits plus a
+ negative sign, but take the backpatch position just inside the element,
+ so that we'll be in the same case as above if the symbol is eventually
+ defined. */
+ debug_file_printf("<value>");
+ fgetpos(Debug_fp, &symbol_debug_backpatch_positions[symbol_index].position);
+ symbol_debug_backpatch_positions[symbol_index].valid = TRUE;
+ debug_file_printf("*BACKPATCH*</value>");
+}
+
+static void write_debug_backpatch
+ (debug_backpatch_accumulator *accumulator, int32 value)
+{ if (accumulator->number_of_values_to_backpatch ==
+ accumulator->number_of_available_backpatches)
+ { my_realloc(&accumulator->values_and_backpatch_positions,
+ sizeof(value_and_backpatch_position) *
+ accumulator->number_of_available_backpatches,
+ 2 * sizeof(value_and_backpatch_position) *
+ accumulator->number_of_available_backpatches,
+ "values and debug information backpatch positions");
+ accumulator->number_of_available_backpatches *= 2;
+ }
+ accumulator->values_and_backpatch_positions
+ [accumulator->number_of_values_to_backpatch].value = value;
+ fgetpos
+ (Debug_fp,
+ &accumulator->values_and_backpatch_positions
+ [accumulator->number_of_values_to_backpatch].backpatch_position);
+ ++(accumulator->number_of_values_to_backpatch);
+ /* Reserve space for up to 10 digits plus a negative sign. */
+ debug_file_printf("*BACKPATCH*");
+}
+
+extern void write_debug_object_backpatch(int32 object_number)
+{ if (glulx_mode)
+ { write_debug_backpatch(&object_backpatch_accumulator, object_number - 1);
+ }
+ else
+ { debug_file_printf("%d", object_number);
+ }
+}
+
+static int32 backpatch_object_address(int32 index)
+{ return object_tree_offset + OBJECT_BYTE_LENGTH * index;
+}
+
+extern void write_debug_packed_code_backpatch(int32 offset)
+{ write_debug_backpatch(&packed_code_backpatch_accumulator, offset);
+}
+
+static int32 backpatch_packed_code_address(int32 offset)
+{
+ if (OMIT_UNUSED_ROUTINES) {
+ int stripped;
+ offset = df_stripped_offset_for_code_offset(offset, &stripped);
+ if (stripped)
+ return 0;
+ }
+ return (code_offset + offset) / scale_factor;
+}
+
+extern void write_debug_code_backpatch(int32 offset)
+{ write_debug_backpatch(&code_backpatch_accumulator, offset);
+}
+
+static int32 backpatch_code_address(int32 offset)
+{
+ if (OMIT_UNUSED_ROUTINES) {
+ int stripped;
+ offset = df_stripped_offset_for_code_offset(offset, &stripped);
+ if (stripped)
+ return 0;
+ }
+ return code_offset + offset;
+}
+
+extern void write_debug_global_backpatch(int32 offset)
+{ write_debug_backpatch(&global_backpatch_accumulator, offset);
+}
+
+static int32 backpatch_global_address(int32 offset)
+{ return variables_offset + WORDSIZE * (offset - MAX_LOCAL_VARIABLES);
+}
+
+extern void write_debug_array_backpatch(int32 offset)
+{ write_debug_backpatch(&array_backpatch_accumulator, offset);
+}
+
+static int32 backpatch_array_address(int32 offset)
+{ return (glulx_mode ? arrays_offset : variables_offset) + offset;
+}
+
+extern void write_debug_grammar_backpatch(int32 offset)
+{ write_debug_backpatch(&grammar_backpatch_accumulator, offset);
+}
+
+static int32 backpatch_grammar_address(int32 offset)
+{ return grammar_table_offset + offset;
+}
+
+extern void begin_writing_debug_sections()
+{ debug_file_printf("<story-file-section>");
+ debug_file_printf("<type>header</type>");
+ debug_file_printf("<address>0</address>");
+}
+
+extern void write_debug_section(const char*name, int32 beginning_address)
+{ debug_file_printf("<end-address>%d</end-address>", beginning_address);
+ debug_file_printf("</story-file-section>");
+ debug_file_printf("<story-file-section>");
+ debug_file_printf("<type>");
+ debug_file_print_with_entities(name);
+ debug_file_printf("</type>");
+ debug_file_printf("<address>%d</address>", beginning_address);
+}
+
+extern void end_writing_debug_sections(int32 end_address)
+{ debug_file_printf("<end-address>%d</end-address>", end_address);
+ debug_file_printf("</story-file-section>");
+}
+
+extern void write_debug_undef(int32 symbol_index)
+{ if (!symbol_debug_backpatch_positions[symbol_index].valid)
+ { compiler_error
+ ("Attempt to erase debugging information never written or since "
+ "erased");
+ }
+ if (stypes[symbol_index] != CONSTANT_T)
+ { compiler_error
+ ("Attempt to erase debugging information for a non-constant "
+ "because of an #undef");
+ }
+ if (fsetpos
+ (Debug_fp, &symbol_debug_backpatch_positions[symbol_index].position))
+ { fatalerror("I/O failure: can't seek in debugging information file");
+ }
+ /* There are 7 characters in ``<value>''. */
+ if (fseek(Debug_fp, -7L, SEEK_CUR))
+ { fatalerror("I/O failure: can't seek in debugging information file");
+ }
+ /* Overwrite: <value>*BACKPATCH*</value> */
+ debug_file_printf(" ");
+ nullify_debug_file_position
+ (&symbol_debug_backpatch_positions[symbol_index]);
+ if (fseek(Debug_fp, 0L, SEEK_END))
+ { fatalerror("I/O failure: can't seek in debugging information file");
+ }
+}
+
+static void apply_debug_information_backpatches
+ (debug_backpatch_accumulator *accumulator)
+{ int32 backpatch_index, backpatch_value;
+ for (backpatch_index = accumulator->number_of_values_to_backpatch;
+ backpatch_index--;)
+ { if (fsetpos
+ (Debug_fp,
+ &accumulator->values_and_backpatch_positions
+ [backpatch_index].backpatch_position))
+ { fatalerror
+ ("I/O failure: can't seek in debugging information file");
+ }
+ backpatch_value =
+ (*accumulator->backpatching_function)
+ (accumulator->values_and_backpatch_positions
+ [backpatch_index].value);
+ debug_file_printf
+ ("%11d", /* Space for up to 10 digits plus a negative sign. */
+ backpatch_value);
+ }
+}
+
+static void apply_debug_information_symbol_backpatches()
+{ int backpatch_symbol;
+ for (backpatch_symbol = no_symbols; backpatch_symbol--;)
+ { if (symbol_debug_backpatch_positions[backpatch_symbol].valid)
+ { if (fsetpos(Debug_fp,
+ &symbol_debug_backpatch_positions
+ [backpatch_symbol].position))
+ { fatalerror
+ ("I/O failure: can't seek in debugging information file");
+ }
+ debug_file_printf("%11d", svals[backpatch_symbol]);
+ }
+ }
+}
+
+static void write_debug_system_constants()
+{ int *system_constant_list =
+ glulx_mode ? glulx_system_constant_list : z_system_constant_list;
+ int system_constant_index = 0;
+
+ /* Store system constants. */
+ for (; system_constant_list[system_constant_index] != -1;
+ ++system_constant_index)
+ { int system_constant = system_constant_list[system_constant_index];
+ debug_file_printf("<constant>");
+ debug_file_printf
+ ("<identifier>#%s</identifier>",
+ system_constants.keywords[system_constant]);
+ debug_file_printf
+ ("<value>%d</value>",
+ value_of_system_constant(system_constant));
+ debug_file_printf("</constant>");
+ }
+}
+
+extern void end_debug_file()
+{ write_debug_system_constants();
+ debug_file_printf("</inform-story-file>\n");
+
+ if (glulx_mode)
+ { apply_debug_information_backpatches(&object_backpatch_accumulator);
+ } else
+ { apply_debug_information_backpatches(&packed_code_backpatch_accumulator);
+ }
+ apply_debug_information_backpatches(&code_backpatch_accumulator);
+ apply_debug_information_backpatches(&global_backpatch_accumulator);
+ apply_debug_information_backpatches(&array_backpatch_accumulator);
+ apply_debug_information_backpatches(&grammar_backpatch_accumulator);
+
+ apply_debug_information_symbol_backpatches();
+
+ close_debug_file();
+}
+
+/* ------------------------------------------------------------------------- */
+/* Temporary storage files: */
+/* */
+/* Temp file 1 is used to hold the static strings area, as compiled */
+/* 2 to hold compiled routines of Z-code */
+/* 3 to hold the link data table (but only for modules) */
+/* */
+/* (Though annoying, this procedure typically saves about 200K of memory, */
+/* an important point for Amiga and sub-386 PC users of Inform) */
+/* ------------------------------------------------------------------------- */
+
+extern void open_temporary_files(void)
+{ translate_temp_filename(1);
+ Temp1_fp=fopen(Temp1_Name,"wb");
+ if (Temp1_fp==NULL) fatalerror_named("Couldn't open temporary file 1",
+ Temp1_Name);
+ translate_temp_filename(2);
+ Temp2_fp=fopen(Temp2_Name,"wb");
+ if (Temp2_fp==NULL) fatalerror_named("Couldn't open temporary file 2",
+ Temp2_Name);
+
+ if (!module_switch) return;
+ translate_temp_filename(3);
+ Temp3_fp=fopen(Temp3_Name,"wb");
+ if (Temp3_fp==NULL) fatalerror_named("Couldn't open temporary file 3",
+ Temp3_Name);
+}
+
+extern void check_temp_files(void)
+{
+ if (ferror(Temp1_fp))
+ fatalerror("I/O failure: couldn't write to temporary file 1");
+ if (ferror(Temp2_fp))
+ fatalerror("I/O failure: couldn't write to temporary file 2");
+ if (module_switch && ferror(Temp3_fp))
+ fatalerror("I/O failure: couldn't write to temporary file 3");
+}
+
+extern void remove_temp_files(void)
+{ if (Temp1_fp != NULL) fclose(Temp1_fp);
+ Temp1_fp = NULL;
+ if (Temp2_fp != NULL) fclose(Temp2_fp);
+ Temp2_fp = NULL;
+ remove(Temp1_Name); remove(Temp2_Name);
+ if (module_switch)
+ { if (Temp3_fp != NULL) fclose(Temp3_fp);
+ Temp3_fp = NULL;
+ remove(Temp3_Name);
+ }
+}
+
+/* ========================================================================= */
+/* Data structure management routines */
+/* ------------------------------------------------------------------------- */
+
+extern void init_files_vars(void)
+{ malloced_bytes = 0;
+ checksum_low_byte = 0; /* Z-code */
+ checksum_high_byte = 0;
+ checksum_long = 0; /* Glulx */
+ checksum_count = 0;
+ transcript_open = FALSE;
+}
+
+extern void files_begin_prepass(void)
+{
+ total_files = 0;
+ total_input_files = 0;
+ current_input_file = 0;
+ current_origsource_file = 0;
+}
+
+extern void files_begin_pass(void)
+{ total_chars_read=0;
+ if (temporary_files_switch)
+ open_temporary_files();
+}
+
+static void initialise_accumulator
+ (debug_backpatch_accumulator *accumulator,
+ int32 (* backpatching_function)(int32))
+{ accumulator->number_of_values_to_backpatch = 0;
+ accumulator->number_of_available_backpatches =
+ INITIAL_DEBUG_INFORMATION_BACKPATCH_ALLOCATION;
+ accumulator->values_and_backpatch_positions =
+ my_malloc
+ (sizeof(value_and_backpatch_position) *
+ accumulator->number_of_available_backpatches,
+ "values and debug information backpatch positions");
+ accumulator->backpatching_function = backpatching_function;
+}
+
+extern void files_allocate_arrays(void)
+{ filename_storage = my_malloc(MAX_SOURCE_FILES*64, "filename storage");
+ filename_storage_p = filename_storage;
+ filename_storage_left = MAX_SOURCE_FILES*64;
+ InputFiles = my_malloc(MAX_SOURCE_FILES*sizeof(FileId),
+ "input file storage");
+ if (debugfile_switch)
+ { if (glulx_mode)
+ { initialise_accumulator
+ (&object_backpatch_accumulator, &backpatch_object_address);
+ } else
+ { initialise_accumulator
+ (&packed_code_backpatch_accumulator,
+ &backpatch_packed_code_address);
+ }
+ initialise_accumulator
+ (&code_backpatch_accumulator, &backpatch_code_address);
+ initialise_accumulator
+ (&global_backpatch_accumulator, &backpatch_global_address);
+ initialise_accumulator
+ (&array_backpatch_accumulator, &backpatch_array_address);
+ initialise_accumulator
+ (&grammar_backpatch_accumulator, &backpatch_grammar_address);
+ }
+}
+
+static void tear_down_accumulator(debug_backpatch_accumulator *accumulator)
+{ my_free
+ (&(accumulator->values_and_backpatch_positions),
+ "values and debug information backpatch positions");
+}
+
+extern void files_free_arrays(void)
+{ my_free(&filename_storage, "filename storage");
+ my_free(&InputFiles, "input file storage");
+ if (debugfile_switch)
+ { if (!glulx_mode)
+ { tear_down_accumulator(&object_backpatch_accumulator);
+ } else
+ { tear_down_accumulator(&packed_code_backpatch_accumulator);
+ }
+ tear_down_accumulator(&code_backpatch_accumulator);
+ tear_down_accumulator(&global_backpatch_accumulator);
+ tear_down_accumulator(&array_backpatch_accumulator);
+ tear_down_accumulator(&grammar_backpatch_accumulator);
+ }
+}
+
+/* ========================================================================= */