Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | Download | RSS feed

  1. // DGen/SDL 1.17
  2. // by Joe Groff <joe@pknet.com>
  3. // Read LICENSE for copyright etc., but if you've seen one BSDish license,
  4. // you've seen them all ;)
  5.  
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #include <unistd.h>
  9. #include <sys/stat.h>
  10. #include <sys/types.h>
  11. #include <fcntl.h>
  12. #include <string.h>
  13. #include <stdint.h>
  14. #include <limits.h>
  15. #include <errno.h>
  16.  
  17. #ifdef __MINGW32__
  18. #include <windows.h>
  19. #include <wincon.h>
  20. #endif
  21.  
  22. #define IS_MAIN_CPP
  23. #include "system.h"
  24. #include "md.h"
  25. #include "pd.h"
  26. #include "pd-defs.h"
  27. #include "rc.h"
  28. #include "rc-vars.h"
  29.  
  30. #ifdef __BEOS__
  31. #include <OS.h>
  32. #endif
  33.  
  34. #ifdef __MINGW32__
  35. static long dgen_mingw_detach = 1;
  36. #endif
  37.  
  38. // Defined in ras.cpp, and set to true if the Genesis palette's changed.
  39. extern int pal_dirty;
  40.  
  41. FILE *debug_log = NULL;
  42.  
  43. // Do a demo frame, if active
  44. enum demo_status {
  45.         DEMO_OFF,
  46.         DEMO_RECORD,
  47.         DEMO_PLAY
  48. };
  49.  
  50. static inline void do_demo(md& megad, FILE* demo, enum demo_status* status)
  51. {
  52.         uint32_t pad[2];
  53.  
  54.         switch (*status) {
  55.         case DEMO_OFF:
  56.                 break;
  57.         case DEMO_RECORD:
  58.                 pad[0] = h2be32(megad.pad[0]);
  59.                 pad[1] = h2be32(megad.pad[1]);
  60.                 fwrite(&pad, sizeof(pad), 1, demo);
  61.                 break;
  62.         case DEMO_PLAY:
  63.                 if (fread(&pad, sizeof(pad), 1, demo) == 1) {
  64.                         megad.pad[0] = be2h32(pad[0]);
  65.                         megad.pad[1] = be2h32(pad[1]);
  66.                 }
  67.                 else {
  68.                         if (feof(demo))
  69.                                 pd_message("Demo finished.");
  70.                         else
  71.                                 pd_message("Demo finished (read error).");
  72.                         *status = DEMO_OFF;
  73.                 }
  74.                 break;
  75.         }
  76. }
  77.  
  78. // Temporary garbage can string :)
  79. static char temp[65536] = "";
  80.  
  81. // Show help and exit with code 2
  82. static void help()
  83. {
  84.   printf(
  85.   "DGen/SDL v"VER"\n"
  86.   "Usage: dgen [options] [romname [...]]\n\n"
  87.   "Where options are:\n"
  88.   "    -v              Print version number and exit.\n"
  89.   "    -r RCFILE       Read in the file RCFILE after parsing\n"
  90.   "                    $HOME/.dgen/dgenrc.\n"
  91.   "    -n USEC         Causes DGen to sleep USEC microseconds per frame, to\n"
  92.   "                    be nice to other processes.\n"
  93.   "    -p CODE,CODE... Takes a comma-delimited list of Game Genie (ABCD-EFGH)\n"
  94.   "                    or Hex (123456:ABCD) codes to patch the ROM with.\n"
  95.   "    -R (J|X|U|E| )  Force emulator region. Affects vertical resolution,\n"
  96.   "                    frame rate and ROM operation.\n"
  97.   "                    J: Japan (NTSC), X: Japan (PAL), U: America (NTSC)\n"
  98.   "                    E: Europe (PAL), ' ': auto (default).\n"
  99.   "    -N              Use NTSC mode (60Hz).\n"
  100.   "    -P              Use PAL mode (50Hz).\n"
  101.   "    -H HZ           Use a custom frame rate.\n"
  102.   "    -d DEMONAME     Record a demo of the game you are playing.\n"
  103.   "    -D DEMONAME     Play back a previously recorded demo.\n"
  104.   "    -s SLOT         Load the saved state from the given slot at startup.\n"
  105. #ifdef __MINGW32__
  106.   "    -m              Do not detach from console.\n"
  107. #endif
  108.   );
  109.   // Display platform-specific options
  110.   pd_help();
  111.   exit(2);
  112. }
  113.  
  114. // Save/load states
  115. // It is externed from your implementation to change the current slot
  116. // (I know this is a hack :)
  117. int slot = 0;
  118. void md_save(md& megad)
  119. {
  120.         FILE *save;
  121.         char file[64];
  122.  
  123.         if (!megad.plugged) {
  124.                 pd_message("Cannot save state when no ROM is loaded.");
  125.                 return;
  126.         }
  127.         if (((size_t)snprintf(file,
  128.                               sizeof(file),
  129.                               "%s.gs%d",
  130.                               megad.romname,
  131.                               slot) >= sizeof(file)) ||
  132.             ((save = dgen_fopen("saves", file, DGEN_WRITE)) == NULL)) {
  133.                 snprintf(temp, sizeof(temp),
  134.                          "Couldn't save state to slot %d!", slot);
  135.                 pd_message(temp);
  136.                 return;
  137.         }
  138.         megad.export_gst(save);
  139.         fclose(save);
  140.         snprintf(temp, sizeof(temp), "Saved state to slot %d.", slot);
  141.         pd_message(temp);
  142. }
  143.  
  144. void md_load(md& megad)
  145. {
  146.         FILE *load;
  147.         char file[64];
  148.  
  149.         if (!megad.plugged) {
  150.                 pd_message("Cannot restore state when no ROM is loaded.");
  151.                 return;
  152.         }
  153.         if (((size_t)snprintf(file,
  154.                               sizeof(file),
  155.                               "%s.gs%d",
  156.                               megad.romname,
  157.                               slot) >= sizeof(file)) ||
  158.             ((load = dgen_fopen("saves", file, DGEN_READ)) == NULL)) {
  159.                 snprintf(temp, sizeof(temp),
  160.                          "Couldn't load state from slot %d!", slot);
  161.                 pd_message(temp);
  162.                 return;
  163.         }
  164.         megad.import_gst(load);
  165.         fclose(load);
  166.         snprintf(temp, sizeof(temp), "Loaded state from slot %d.", slot);
  167.         pd_message(temp);
  168. }
  169.  
  170. // Load/save states from file
  171. void ram_save(md& megad)
  172. {
  173.         FILE *save;
  174.         int ret;
  175.  
  176.         if (!megad.has_save_ram())
  177.                 return;
  178.         save = dgen_fopen("ram", megad.romname, DGEN_WRITE);
  179.         if (save == NULL)
  180.                 goto fail;
  181.         ret = megad.put_save_ram(save);
  182.         fclose(save);
  183.         if (ret == 0)
  184.                 return;
  185. fail:
  186.         fprintf(stderr, "Couldn't save battery RAM to `%s'\n", megad.romname);
  187. }
  188.  
  189. void ram_load(md& megad)
  190. {
  191.         FILE *load;
  192.         int ret;
  193.  
  194.         if (!megad.has_save_ram())
  195.                 return;
  196.         load = dgen_fopen("ram", megad.romname, DGEN_READ);
  197.         if (load == NULL)
  198.                 goto fail;
  199.         ret = megad.get_save_ram(load);
  200.         fclose(load);
  201.         if (ret == 0)
  202.                 return;
  203. fail:
  204.         fprintf(stderr, "Couldn't load battery RAM from `%s'\n",
  205.                 megad.romname);
  206. }
  207.  
  208. int main(int argc, char *argv[])
  209. {
  210.   int c = 0, stop = 0, usec = 0, start_slot = -1;
  211.   unsigned long frames, frames_old, fps;
  212.   char *patches = NULL, *rom = NULL;
  213.   unsigned long oldclk, newclk, startclk, fpsclk;
  214.   FILE *file = NULL;
  215.   enum demo_status demo_status = DEMO_OFF;
  216.   unsigned int samples;
  217.   class md *megad;
  218.   bool first = true;
  219.   bool forced_hz = false;
  220.   bool forced_pal = false;
  221.  
  222.         // Parse the RC file
  223.         if ((dgen_autoconf) &&
  224.             ((file = dgen_fopen_autorc(DGEN_READ)) != NULL)) {
  225.                 parse_rc(file, DGEN_AUTORC);
  226.                 fclose(file);
  227.         }
  228.         if ((file = dgen_fopen_rc(DGEN_READ)) != NULL) {
  229.                 parse_rc(file, DGEN_RC);
  230.                 fclose(file);
  231.                 file = NULL;
  232.                 pd_rc();
  233.         }
  234.         else if (errno == ENOENT) {
  235.                 if ((file = dgen_fopen_rc(DGEN_APPEND)) != NULL) {
  236.                         fprintf(file,
  237.                                 "# DGen " VER " configuration file.\n"
  238.                                 "# See dgenrc(5) for more information.\n");
  239.                         fclose(file);
  240.                         file = NULL;
  241.                 }
  242.         }
  243.         else
  244.                 fprintf(stderr, "rc: %s: %s\n", DGEN_RC, strerror(errno));
  245.  
  246.   // Check all our options
  247.   snprintf(temp, sizeof(temp), "%s%s",
  248. #ifdef __MINGW32__
  249.            "m"
  250. #endif
  251.            "s:hvr:n:p:R:NPH:d:D:",
  252.            pd_options);
  253.   while((c = getopt(argc, argv, temp)) != EOF)
  254.     {
  255.       switch(c)
  256.         {
  257.         case 'v':
  258.           // Show version and exit
  259.           printf("DGen/SDL version "VER"\n");
  260.           return 0;
  261.         case 'r':
  262.           // Parse another RC file or stdin
  263.           if ((strcmp(optarg, "-") == 0) ||
  264.               ((file = dgen_fopen(NULL, optarg,
  265.                                   (DGEN_READ | DGEN_CURRENT))) != NULL)) {
  266.             if (file == NULL)
  267.               parse_rc(stdin, "(stdin)");
  268.             else {
  269.               parse_rc(file, optarg);
  270.               fclose(file);
  271.               file = NULL;
  272.             }
  273.             pd_rc();
  274.           }
  275.           else
  276.             fprintf(stderr, "rc: %s: %s\n", optarg, strerror(errno));
  277.           break;
  278.         case 'n':
  279.           // Sleep for n microseconds
  280.           dgen_nice = atoi(optarg);
  281.           break;
  282.         case 'p':
  283.           // Game Genie patches
  284.           patches = optarg;
  285.           break;
  286.         case 'R':
  287.                 // Region selection
  288.                 if (strlen(optarg) != 1)
  289.                         goto bad_region;
  290.                 switch (optarg[0] | 0x20) {
  291.                 case 'j':
  292.                 case 'x':
  293.                 case 'u':
  294.                 case 'e':
  295.                 case ' ':
  296.                         break;
  297.                 default:
  298.                 bad_region:
  299.                         fprintf(stderr, "main: invalid region `%s'.\n",
  300.                                 optarg);
  301.                         return EXIT_FAILURE;
  302.                 }
  303.                 dgen_region = (optarg[0] & ~(0x20));
  304.                 // Override PAL and Hz settings if region is specified.
  305.                 if (dgen_region) {
  306.                         int hz;
  307.                         int pal;
  308.  
  309.                         md::region_info(dgen_region, &pal, &hz, 0, 0, 0);
  310.                         dgen_hz = hz;
  311.                         dgen_pal = pal;
  312.                 }
  313.                 forced_pal = false;
  314.                 forced_hz = false;
  315.                 break;
  316.         case 'N':
  317.                 // NTSC mode
  318.                 dgen_hz = NTSC_HZ;
  319.                 dgen_pal = 0;
  320.                 forced_pal = true;
  321.                 break;
  322.         case 'P':
  323.                 // PAL mode
  324.                 dgen_hz = PAL_HZ;
  325.                 dgen_pal = 1;
  326.                 forced_pal = true;
  327.                 break;
  328.         case 'H':
  329.                 // Custom frame rate
  330.                 dgen_hz = atoi(optarg);
  331.                 if ((dgen_hz <= 0) || (dgen_hz > 1000)) {
  332.                         fprintf(stderr, "main: invalid frame rate (%ld).\n",
  333.                                 (long)dgen_hz);
  334.                         dgen_hz = (dgen_pal ? 50 : 60);
  335.                         forced_hz = false;
  336.                 }
  337.                 else
  338.                         forced_hz = true;
  339.                 break;
  340. #ifdef __MINGW32__
  341.         case 'm':
  342.                 dgen_mingw_detach = 0;
  343.                 break;
  344. #endif
  345.         case 'd':
  346.           // Record demo
  347.           if(file)
  348.             {
  349.               fprintf(stderr,"main: Can't record and play at the same time!\n");
  350.               break;
  351.             }
  352.           if(!(file = dgen_fopen("demos", optarg, DGEN_WRITE)))
  353.             {
  354.               fprintf(stderr, "main: Can't record demo file %s!\n", optarg);
  355.               break;
  356.             }
  357.           demo_status = DEMO_RECORD;
  358.           break;
  359.         case 'D':
  360.           // Play demo
  361.           if(file)
  362.             {
  363.               fprintf(stderr,"main: Can't record and play at the same time!\n");
  364.               break;
  365.             }
  366.           if(!(file = dgen_fopen("demos", optarg, (DGEN_READ | DGEN_CURRENT))))
  367.             {
  368.               fprintf(stderr, "main: Can't play demo file %s!\n", optarg);
  369.               break;
  370.             }
  371.           demo_status = DEMO_PLAY;
  372.           break;
  373.         case '?': // Bad option!
  374.         case 'h': // A cry for help :)
  375.           help();
  376.         case 's':
  377.           // Pick a savestate to autoload
  378.           start_slot = atoi(optarg);
  379.           break;
  380.         default:
  381.           // Pass it on to platform-dependent stuff
  382.           pd_option(c, optarg);
  383.           break;
  384.         }
  385.     }
  386.  
  387. #ifdef __BEOS__
  388.   // BeOS snooze() sleeps in milliseconds, not microseconds
  389.   dgen_nice /= 1000;
  390. #endif
  391.  
  392. #ifdef __MINGW32__
  393.         if (dgen_mingw_detach) {
  394.                 FILE *cons;
  395.  
  396.                 fprintf(stderr,
  397.                         "main: Detaching from console, use -m to prevent"
  398.                         " this.\n");
  399.                 // Console isn't needed anymore. Redirect output to log file.
  400.                 cons = dgen_fopen(NULL, "log.txt", (DGEN_WRITE | DGEN_TEXT));
  401.                 if (cons != NULL) {
  402.                         fflush(stdout);
  403.                         fflush(stderr);
  404.                         dup2(fileno(cons), fileno(stdout));
  405.                         dup2(fileno(cons), fileno(stderr));
  406.                         fclose(cons);
  407.                         setvbuf(stdout, NULL, _IONBF, 0);
  408.                         setvbuf(stderr, NULL, _IONBF, 0);
  409.                         cons = NULL;
  410.                 }
  411.                 FreeConsole();
  412.         }
  413. #endif
  414.  
  415.   // Initialize the platform-dependent stuff.
  416.   if (!pd_graphics_init(dgen_sound, dgen_pal, dgen_hz))
  417.     {
  418.       fprintf(stderr, "main: Couldn't initialize graphics!\n");
  419.       return 1;
  420.     }
  421.   if(dgen_sound)
  422.     {
  423.       long rate = dgen_soundrate;
  424.  
  425.       if (dgen_soundsegs < 0)
  426.               dgen_soundsegs = 0;
  427.       samples = (dgen_soundsegs * (rate / dgen_hz));
  428.       pd_sound_init(rate, samples);
  429.     }
  430.  
  431.         rom = argv[optind];
  432.         // Create the megadrive object.
  433.         megad = new md(dgen_pal, dgen_region);
  434.         if ((megad == NULL) || (!megad->okay())) {
  435.                 fprintf(stderr, "main: Mega Drive initialization failed.\n");
  436.                 goto clean_up;
  437.         }
  438. next_rom:
  439.         // Load the requested ROM.
  440.         if (rom != NULL) {
  441.                 if (megad->load(rom)) {
  442.                         pd_message("Unable to load \"%s\".", rom);
  443.                         if ((first) && ((optind + 1) == argc))
  444.                                 goto clean_up;
  445.                 }
  446.                 else
  447.                         pd_message("Loaded \"%s\".", rom);
  448.         }
  449.         else
  450.                 pd_message("No cartridge.");
  451.         first = false;
  452.         // Set untouched pads.
  453.         megad->pad[0] = MD_PAD_UNTOUCHED;
  454.         megad->pad[1] = MD_PAD_UNTOUCHED;
  455. #ifdef WITH_JOYSTICK
  456.         if (dgen_joystick)
  457.                 megad->init_joysticks();
  458. #endif
  459.         // Load patches, if given.
  460.         if (patches) {
  461.                 printf("main: Using patch codes \"%s\".\n", patches);
  462.                 megad->patch(patches, NULL, NULL, NULL);
  463.                 // Use them only once.
  464.                 patches = NULL;
  465.         }
  466.         // Reset
  467.         megad->reset();
  468.  
  469.         // Automatic region settings from ROM header.
  470.         if (!dgen_region) {
  471.                 uint8_t c = megad->region_guess();
  472.                 int hz;
  473.                 int pal;
  474.  
  475.                 md::region_info(c, &pal, &hz, 0, 0, 0);
  476.                 if (forced_hz)
  477.                         hz = dgen_hz;
  478.                 if (forced_pal)
  479.                         pal = dgen_pal;
  480.                 if ((hz != dgen_hz) || (pal != dgen_pal) ||
  481.                     (c != megad->region)) {
  482.                         megad->region = c;
  483.                         dgen_hz = hz;
  484.                         dgen_pal = pal;
  485.                         printf("main: reconfiguring for region \"%c\": "
  486.                                "%dHz (%s)\n", c, hz, (pal ? "PAL" : "NTSC"));
  487.                         pd_graphics_reinit(dgen_sound, dgen_pal, dgen_hz);
  488.                         if (dgen_sound) {
  489.                                 long rate = dgen_soundrate;
  490.  
  491.                                 pd_sound_deinit();
  492.                                 samples = (dgen_soundsegs * (rate / dgen_hz));
  493.                                 pd_sound_init(rate, samples);
  494.                         }
  495.                         megad->pal = pal;
  496.                         megad->init_pal();
  497.                         megad->init_sound();
  498.                 }
  499.         }
  500.  
  501.         // Load up save RAM
  502.         ram_load(*megad);
  503.         // If -s option was given, load the requested slot
  504.         if (start_slot >= 0) {
  505.                 slot = start_slot;
  506.                 md_load(*megad);
  507.         }
  508.         // If autoload is on, load save state 0
  509.         else if (dgen_autoload) {
  510.                 slot = 0;
  511.                 md_load(*megad);
  512.         }
  513.  
  514.         // Start the timing refs
  515.         startclk = pd_usecs();
  516.         oldclk = startclk;
  517.         fpsclk = startclk;
  518.  
  519.         // Show cartridge header
  520.         if (dgen_show_carthead)
  521.                 pd_show_carthead(*megad);
  522.  
  523.         // Go around, and around, and around, and around... ;)
  524.         frames = 0;
  525.         frames_old = 0;
  526.         fps = 0;
  527.         while (!stop) {
  528.                 const unsigned int usec_frame = (1000000 / dgen_hz);
  529.                 unsigned long tmp;
  530.                 int frames_todo;
  531.  
  532.                 newclk = pd_usecs();
  533.  
  534.                 if (pd_stopped()) {
  535.                         // Fix FPS count.
  536.                         tmp = (newclk - oldclk);
  537.                         startclk += tmp;
  538.                         fpsclk += tmp;
  539.                         oldclk = newclk;
  540.                 }
  541.  
  542.                 // Update FPS count.
  543.                 tmp = ((newclk - fpsclk) & 0x3fffff);
  544.                 if (tmp >= 1000000) {
  545.                         fpsclk = newclk;
  546.                         if (frames_old > frames)
  547.                                 fps = (frames_old - frames);
  548.                         else
  549.                                 fps = (frames - frames_old);
  550.                         frames_old = frames;
  551.                 }
  552.  
  553.                 if (dgen_frameskip == 0) {
  554.                         // Check whether megad->one_frame() must be called.
  555.                         if (pd_freeze)
  556.                                 goto frozen;
  557.                         goto do_not_skip;
  558.                 }
  559.  
  560.                 // Measure how many frames to do this round.
  561.                 usec += ((newclk - oldclk) & 0x3fffff); // no more than 4 secs
  562.                 frames_todo = (usec / usec_frame);
  563.                 usec %= usec_frame;
  564.                 oldclk = newclk;
  565.  
  566.                 if (frames_todo == 0) {
  567.                         // No frame to do yet, relax the CPU until next one.
  568.                         tmp = (usec_frame - usec);
  569.                         if (tmp > 1000) {
  570.                                 // Never sleep for longer than the 50Hz value
  571.                                 // so events are checked often enough.
  572.                                 if (tmp > (1000000 / 50))
  573.                                         tmp = (1000000 / 50);
  574.                                 tmp -= 1000;
  575. #ifdef __BEOS__
  576.                                 snooze(tmp / 1000);
  577. #else
  578.                                 usleep(tmp);
  579. #endif
  580.                         }
  581.                 }
  582.                 else {
  583.                         // Check whether megad->one_frame() must be called.
  584.                         if (pd_freeze)
  585.                                 goto frozen;
  586.  
  587.                         // Draw frames.
  588.                         while (frames_todo != 1) {
  589.                                 do_demo(*megad, file, &demo_status);
  590.                                 if (dgen_sound) {
  591.                                         // Skip this frame, keep sound going.
  592.                                         megad->one_frame(NULL, NULL, &sndi);
  593.                                         pd_sound_write();
  594.                                 }
  595.                                 else
  596.                                         megad->one_frame(NULL, NULL, NULL);
  597.                                 --frames_todo;
  598.                                 stop |= (pd_handle_events(*megad) ^ 1);
  599.                         }
  600.                         --frames_todo;
  601.                 do_not_skip:
  602.                         do_demo(*megad, file, &demo_status);
  603.                         if (dgen_sound) {
  604.                                 megad->one_frame(&mdscr, mdpal, &sndi);
  605.                                 pd_sound_write();
  606.                         }
  607.                         else
  608.                                 megad->one_frame(&mdscr, mdpal, NULL);
  609.                 frozen:
  610.                         if ((mdpal) && (pal_dirty)) {
  611.                                 pd_graphics_palette_update();
  612.                                 pal_dirty = 0;
  613.                         }
  614.                         pd_graphics_update(megad->plugged);
  615.                         ++frames;
  616.                 }
  617.  
  618.                 stop |= (pd_handle_events(*megad) ^ 1);
  619.  
  620.                 if (dgen_nice) {
  621. #ifdef __BEOS__
  622.                         snooze(dgen_nice);
  623. #else
  624.                         usleep(dgen_nice);
  625. #endif
  626.                 }
  627.         }
  628.  
  629. #ifdef WITH_JOYSTICK
  630.         if (dgen_joystick)
  631.                 megad->deinit_joysticks();
  632. #endif
  633.  
  634.         // Print fps
  635.         fpsclk = ((pd_usecs() - startclk) / 1000000);
  636.         if (fpsclk == 0)
  637.                 fpsclk = 1;
  638. #ifdef WITH_DEBUGGER
  639.         megad->debug_leave();
  640. #endif
  641.         printf("%lu frames per second (average %lu, optimal %ld)\n",
  642.                fps, (frames / fpsclk), (long)dgen_hz);
  643.  
  644.         ram_save(*megad);
  645.         if (dgen_autosave) {
  646.                 slot = 0;
  647.                 md_save(*megad);
  648.         }
  649.         megad->unplug();
  650.         if (file) {
  651.                 fclose(file);
  652.                 file = NULL;
  653.         }
  654.         if ((++optind) < argc) {
  655.                 rom = argv[optind];
  656.                 stop = 0;
  657.                 goto next_rom;
  658.         }
  659. clean_up:
  660.         // Cleanup
  661.         delete megad;
  662.         pd_sound_deinit();
  663.         pd_quit();
  664.         // Save configuration.
  665.         if (dgen_autoconf) {
  666.                 if ((file = dgen_fopen_autorc(DGEN_WRITE)) == NULL)
  667.                         fputs("main: can't write " DGEN_AUTORC ".\n", stderr);
  668.                 else {
  669.                         fprintf(file,
  670.                                 "# DGen/SDL v" VER "\n"
  671.                                 "# This file is automatically overwritten.\n"
  672.                                 "\n");
  673.                         dump_rc(file);
  674.                         fclose(file);
  675.                         file = NULL;
  676.                 }
  677.         }
  678.         // Come back anytime :)
  679.         return 0;
  680. }
  681.