/* ------------------------------------------------------------------------- */ /* "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. */ /* */ /* Part of Inform 6.34 */ /* copyright (c) Graham Nelson 1993 - 2020 */ /* */ /* ------------------------------------------------------------------------- */ #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 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("", total_files); debug_file_printf(""); debug_file_print_with_entities(filename_given); debug_file_printf(""); #ifdef HAS_REALPATH if (realpath(name, absolute_name)) { debug_file_printf(""); debug_file_print_with_entities(absolute_name); debug_file_printf(""); } #endif debug_file_printf("Inform 6"); debug_file_printf(""); } 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 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); debug_file_printf(""); debug_file_print_with_entities(filename); debug_file_printf(""); debug_file_printf("Inform 7"); debug_file_printf(""); } 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= 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 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 (j0) { 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(""); 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(""); } #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> 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> 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 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; bxb[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."); } /* (5) Output static arrays (if any). */ { /* We have to backpatch entries mentioned in staticarray_backpatch_table. */ int32 size_before_arrays = size; int32 val, ix, jx; for (ix=0, jx=0; ix> 24) & 0xFF); sf_put((val >> 16) & 0xFF); sf_put((val >> 8) & 0xFF); sf_put((val) & 0xFF); size += 4; } /* Flush out the last bit of static_array_area, after the last backpatch marker. */ offset = static_array_area_size; while (jx> 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(""); 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(""); } 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(""); debug_file_printf("", (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("%d", location.file_index - 1); debug_file_printf ("%d", location.beginning_byte_index); debug_file_printf ("%d", location.beginning_line_number); debug_file_printf ("%d", 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 ("%d", location.end_byte_index); debug_file_printf ("%d", location.end_line_number); debug_file_printf ("%d", location.end_character_number); } } static void write_debug_location_origsource_internals(debug_location location) { debug_file_printf ("%d", location.orig_file_index - 1); if (location.orig_beg_line_number) debug_file_printf ("%d", location.orig_beg_line_number); if (location.orig_beg_char_number) debug_file_printf ("%d", location.orig_beg_char_number); } extern void write_debug_location(debug_location location) { if (location.file_index && location.file_index != 255) { debug_file_printf(""); write_debug_location_internals(location); debug_file_printf(""); } if (location.orig_file_index) { debug_file_printf(""); write_debug_location_origsource_internals(location); debug_file_printf(""); } } 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("", index); write_debug_location_internals(current->location); debug_file_printf(""); } if (locations.location.orig_file_index) { debug_file_printf(""); write_debug_location_origsource_internals(locations.location); debug_file_printf(""); } } 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 ("%s " "(superseded replacement)", 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("%s", 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(""); fgetpos(Debug_fp, &symbol_debug_backpatch_positions[symbol_index].position); symbol_debug_backpatch_positions[symbol_index].valid = TRUE; debug_file_printf("*BACKPATCH*"); } 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(""); debug_file_printf("header"); debug_file_printf("
0
"); } extern void write_debug_section(const char*name, int32 beginning_address) { debug_file_printf("%d", beginning_address); debug_file_printf("
"); debug_file_printf(""); debug_file_printf(""); debug_file_print_with_entities(name); debug_file_printf(""); debug_file_printf("
%d
", beginning_address); } extern void end_writing_debug_sections(int32 end_address) { debug_file_printf("%d", end_address); debug_file_printf("
"); } 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 ``''. */ if (fseek(Debug_fp, -7L, SEEK_CUR)) { fatalerror("I/O failure: can't seek in debugging information file"); } /* Overwrite: *BACKPATCH* */ 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(""); debug_file_printf ("#%s", system_constants.keywords[system_constant]); debug_file_printf ("%d", value_of_system_constant(system_constant)); debug_file_printf(""); } } extern void end_debug_file() { write_debug_system_constants(); debug_file_printf("
\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); } } /* ========================================================================= */