| | 1 | /* FrobTadsApplication class implementation. |
| | 2 | */ |
| | 3 | #include "common.h" |
| | 4 | |
| | 5 | #include <stdio.h> |
| | 6 | #include <string.h> |
| | 7 | #include <stdlib.h> |
| | 8 | #include <signal.h> |
| | 9 | |
| | 10 | #include "os.h" |
| | 11 | #include "trd.h" |
| | 12 | #include "t3std.h" |
| | 13 | #include "vmmain.h" |
| | 14 | #include "vmvsn.h" |
| | 15 | #include "vmmaincn.h" |
| | 16 | #include "vmhostsi.h" |
| | 17 | extern "C" |
| | 18 | { |
| | 19 | #include "osgen.h" |
| | 20 | } |
| | 21 | |
| | 22 | #include "frobtadsapp.h" |
| | 23 | #include "frobappctx.h" |
| | 24 | |
| | 25 | |
| | 26 | FrobTadsApplication* globalApp; |
| | 27 | |
| | 28 | |
| | 29 | /* Wee need a signal handler to handle window resizes when running |
| | 30 | * inside a terminal. |
| | 31 | */ |
| | 32 | RETSIGTYPE winResizeHandler( int ) |
| | 33 | { |
| | 34 | // Re-initialize the screen. |
| | 35 | globalApp->resizeEvent(); |
| | 36 | |
| | 37 | // Change the corresponding global variables that TADS uses to |
| | 38 | // tell the size. |
| | 39 | G_oss_screen_width = globalApp->width(); |
| | 40 | G_oss_screen_height = globalApp->height(); |
| | 41 | |
| | 42 | // Tell TADS that the size just changed. |
| | 43 | osssb_on_resize_screen(); |
| | 44 | // Flush the screen just in case. |
| | 45 | globalApp->flush(); |
| | 46 | |
| | 47 | // Restore the timeout. |
| | 48 | // FIXME: This resets the timeout to its initial |
| | 49 | // value. We want to actually set it to the |
| | 50 | // remaining timeout. |
| | 51 | //globalApp->fGameWindow->setTimeout(0); |
| | 52 | |
| | 53 | // Not sure why, but on some systems the handler has to be |
| | 54 | // installed over and over again after the signal is handled. |
| | 55 | #ifdef HAVE_SIGWINCH |
| | 56 | signal(SIGWINCH, winResizeHandler); |
| | 57 | #endif |
| | 58 | } |
| | 59 | |
| | 60 | |
| | 61 | FrobTadsApplication::FrobTadsApplication( const FrobOptions& opts ) |
| | 62 | : options(opts), fRemainingTimeout(0), fColorsEnabled(false) |
| | 63 | { |
| | 64 | // Initialize the global pointer to us. |
| | 65 | globalApp = this; |
| | 66 | |
| | 67 | // Install our window-resize signal handler. |
| | 68 | #ifdef HAVE_SIGWINCH |
| | 69 | signal(SIGWINCH, winResizeHandler); |
| | 70 | #endif |
| | 71 | } |
| | 72 | |
| | 73 | |
| | 74 | int |
| | 75 | FrobTadsApplication::fRunTads2( char* filename ) |
| | 76 | { |
| | 77 | // Create the Tads 2 application container context (appctx). We |
| | 78 | // make it static for no other reason than automatic null |
| | 79 | // initialization of the fields (C++ guarantees that); unused |
| | 80 | // fields *must* be 0, or else the VM would crash. |
| | 81 | static appctxdef appctx; |
| | 82 | |
| | 83 | // Set the file I/O safety level callback. |
| | 84 | appctx.get_io_safety_level = getIoSafetyLevel; |
| | 85 | |
| | 86 | // trdmain() requires argc/argv style arguments. We create some |
| | 87 | // to make it happy. |
| | 88 | char argv0[] = "frob"; |
| | 89 | char* argv[2] = {argv0, filename}; |
| | 90 | |
| | 91 | // Run the Tads 2 VM. |
| | 92 | char savExt[] = "sav"; |
| | 93 | int vmRet = trdmain(2, argv, &appctx, savExt); |
| | 94 | |
| | 95 | return vmRet; |
| | 96 | } |
| | 97 | |
| | 98 | |
| | 99 | int |
| | 100 | FrobTadsApplication::fRunTads3( char* filename ) |
| | 101 | { |
| | 102 | // Create the Tads 3 host and client services interfaces. |
| | 103 | CVmMainClientConsole clientifc; |
| | 104 | CVmHostIfc* hostifc = new CVmHostIfcStdio(""); |
| | 105 | |
| | 106 | // Set the file I/O safety level. |
| | 107 | hostifc->set_io_safety(this->options.safetyLevel); |
| | 108 | |
| | 109 | // Run the Tads 3 VM. |
| | 110 | int vmRet = vm_run_image(&clientifc, filename, hostifc, 0, 0, 0, false, 0, 0, false, false, 0, 0, 0, 0); |
| | 111 | |
| | 112 | delete hostifc; |
| | 113 | return vmRet; |
| | 114 | } |
| | 115 | |
| | 116 | |
| | 117 | int |
| | 118 | FrobTadsApplication::runTads( const char* filename, int vm ) |
| | 119 | { |
| | 120 | // We might strip the path from the filename later, in case we |
| | 121 | // change the current directory. |
| | 122 | size_t filenameLen = strlen(filename); |
| | 123 | char* finalFilenamePtr; |
| | 124 | char* finalFilename = new char[filenameLen + 1]; |
| | 125 | strcpy(finalFilename, filename); |
| | 126 | finalFilenamePtr = finalFilename; |
| | 127 | |
| | 128 | // We'll try setting the current directory to the game's |
| | 129 | // directory (if the user didn't tell us not to). |
| | 130 | if (this->options.changeDir) { |
| | 131 | char* gameDirBuf = new char[filenameLen + 1]; |
| | 132 | gameDirBuf[0] = '\0'; // Paranoia. |
| | 133 | os_get_path_name(gameDirBuf, filenameLen, filename); |
| | 134 | // Try changing the current directory. |
| | 135 | if (this->changeDirectory(gameDirBuf)) { |
| | 136 | // Success. Since we changed the current |
| | 137 | // directory, we must adapt the filename by |
| | 138 | // stripping it from its path. For this |
| | 139 | // purpose, we'll just store the position of the |
| | 140 | // filename's first character. |
| | 141 | finalFilenamePtr = os_get_root_name(finalFilename); |
| | 142 | } |
| | 143 | // Done with the directory-name buffer. |
| | 144 | delete[] gameDirBuf; |
| | 145 | } |
| | 146 | |
| | 147 | // Initialize the screen. |
| | 148 | this->init(); |
| | 149 | |
| | 150 | // The osgen layer uses these global variables to determine the |
| | 151 | // size of the screen; set them to the width and height of our |
| | 152 | // game window. |
| | 153 | G_oss_screen_width = this->width(); |
| | 154 | G_oss_screen_height = this->height(); |
| | 155 | |
| | 156 | // Initialize the osgen scrollback buffer. |
| | 157 | osssbini(this->options.scrollBufSize); |
| | 158 | |
| | 159 | // A kludge to circumvent a curses color problem; display one |
| | 160 | // character in reverse video then one in normal colors. This |
| | 161 | // avoids the problem where the first block of text is shown in |
| | 162 | // wrong colors. |
| | 163 | int tmpColor = ossgetcolor(OSGEN_COLOR_STATUSLINE, OSGEN_COLOR_STATUSBG, 0, 0); |
| | 164 | ossdsp(0, 0, tmpColor, " "); |
| | 165 | globalApp->flush(); |
| | 166 | tmpColor = ossgetcolor(OSGEN_COLOR_TEXT, OSGEN_COLOR_TEXTBG, 0, 0); |
| | 167 | ossdsp(0, 0, tmpColor, " "); |
| | 168 | globalApp->flush(); |
| | 169 | |
| | 170 | // Run the VM. |
| | 171 | int vmRet = vm == 0 ? this->fRunTads2(finalFilenamePtr) : this->fRunTads3(finalFilenamePtr); |
| | 172 | |
| | 173 | // Done with the filename. |
| | 174 | delete[] finalFilename; |
| | 175 | |
| | 176 | // Pause. |
| | 177 | os_expause(); |
| | 178 | |
| | 179 | // Delete the scrollback buffer. |
| | 180 | osssbdel(); |
| | 181 | |
| | 182 | // Return the VM's exit code. |
| | 183 | return vmRet; |
| | 184 | } |
| | 185 | |
| | 186 | |
| | 187 | /* We implement changeDirectory() as a wrapper around chdir() or |
| | 188 | * SetCurrentDirectory(); whichever is available. The former is in |
| | 189 | * <unistd.h>, the latter in <windows.h>. If the system lacks both, |
| | 190 | * we'll always return false to indicate failure. |
| | 191 | */ |
| | 192 | #ifdef HAVE_CHDIR |
| | 193 | #include <unistd.h> |
| | 194 | #endif |
| | 195 | #ifdef HAVE_SETCURRENTDIRECTORY |
| | 196 | #include <windows.h> |
| | 197 | #endif |
| | 198 | bool |
| | 199 | FrobTadsApplication::changeDirectory( const char* dir ) |
| | 200 | { |
| | 201 | #ifdef HAVE_CHDIR |
| | 202 | if (chdir(dir) == 0) return true; |
| | 203 | return false; |
| | 204 | #else |
| | 205 | #ifdef HAVE_SETCURRENTDIRECTORY |
| | 206 | return SetCurrentDirectory(dir); |
| | 207 | #else |
| | 208 | return false; |
| | 209 | #endif |
| | 210 | #endif |
| | 211 | } |