⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 multiio.c

📁 NES game Emulator in Linux.c and asm codes.
💻 C
字号:
/*

SNEeSe, an Open Source Super NES emulator.


Copyright (c) 1998-2004 Charles Bilyue'.
Portions Copyright (c) 2003-2004 Daniel Horchner.

This is free software.  See 'LICENSE' for details.
You must read and accept the license prior to use.

multiio.c - transparent support for gzipped and zipped files.

*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "multiio.h"
#include "map.h"

#ifdef ZLIB
#include <zlib.h>
#include "unzip.h"
#endif

st_map_t *fh_map = NULL;                        // associative array: file handle -> file mode

typedef enum { FM_NORMAL, FM_GZIP, FM_ZIP, FM_UNDEF } fmode2_t;

typedef struct st_finfo
{
 fmode2_t fmode;
 int compressed;
} st_finfo_t;

static st_finfo_t finfo_list[6] =
{
 {FM_NORMAL, 0},
 {FM_NORMAL, 1},                                // should never be used
 {FM_GZIP, 0},
 {FM_GZIP, 1},
 {FM_ZIP, 0},                                   // should never be used
 {FM_ZIP, 1}
};

#ifdef ZLIB
int unzip_current_file_nr = 0;
#endif

static void init_fh_map(void)
{
 fh_map = map_create(20);                       // 20 simultaneous open files
 map_put(fh_map, stdin, &finfo_list[0]);        //  should be enough to start with
 map_put(fh_map, stdout, &finfo_list[0]);
 map_put(fh_map, stderr, &finfo_list[0]);
}

static st_finfo_t *get_finfo(FILE *file)
{
 st_finfo_t *finfo;

 if (fh_map == NULL)
  init_fh_map();
 if ((finfo = (st_finfo_t *) map_get(fh_map, file)) == NULL)
 {
  fprintf(stderr, "\nINTERNAL ERROR: File pointer was not present in map (%p)\n", file);
  map_dump(fh_map);
  exit(1);
 }
 return finfo;
}

static fmode2_t get_fmode(FILE *file)
{
 return get_finfo(file)->fmode;
}

#ifdef ZLIB
int unzip_get_number_entries(const char *filename)
{
 FILE *file;
 unsigned char magic[4] = { 0 };

 if ((file = fopen(filename, "rb")) == NULL)
  return -1;
 fread(magic, 1, sizeof (magic), file);
 fclose(file);

 if (magic[0] == 'P' && magic[1] == 'K' && magic[2] == 0x03 && magic[3] == 0x04)
 {
  unz_global_info info;

  file = (FILE *) unzOpen(filename);
  unzGetGlobalInfo(file, &info);
  unzClose(file);
  return info.number_entry;
 }
 else
  return -1;
}

int unzip_goto_file(unzFile file, int file_index)
{
 int retval = unzGoToFirstFile(file), n = 0;

 if (file_index > 0)
  while (n < file_index)
  {
   retval = unzGoToNextFile(file);
   n++;
  }
 return retval;
}

static int unzip_seek_helper(FILE *file, int offset)
{
#define MAXBUFSIZE 32768
 char buffer[MAXBUFSIZE];
 int n, tmp, pos = unztell(file);               // returns ftell() of the "current file"

 if (pos == offset)
  return 0;
 else if (pos > offset)
 {
  unzCloseCurrentFile(file);
  unzip_goto_file(file, unzip_current_file_nr);
  unzOpenCurrentFile(file);
  pos = 0;
 }
 n = offset - pos;
 while (n > 0 && !unzeof(file))
 {
  tmp = unzReadCurrentFile(file, buffer, n > MAXBUFSIZE ? MAXBUFSIZE : n);
  if (tmp < 0)
   return -1;
  n -= tmp;
 }
 return n > 0 ? -1 : 0;
}
#endif // ZLIB

FILE *fopen2(const char *filename, const char *mode)
{
 int n, len = strlen(mode), read = 0, compressed = 0;
 fmode2_t fmode = FM_UNDEF;
 st_finfo_t *finfo;
 FILE *file = NULL;

 if (fh_map == NULL)
  init_fh_map();

 for (n = 0; n < len; n++)
 {
  switch (mode[n])
  {
   case 'r':
    read = 1;
    break;
#ifdef ZLIB
   case 'f':
   case 'h':
   case '1':
   case '2':
   case '3':
   case '4':
   case '5':
   case '6':
   case '7':
   case '8':
   case '9':
    fmode = FM_GZIP;
    break;
#endif
   case 'w':
   case 'a':
    fmode = FM_NORMAL;
    break;
   case '+':
    if (fmode == FM_UNDEF)
     fmode = FM_NORMAL;
    break;
  }
 }

 if (read)
 {
  unsigned char magic[4] = { 0 };
  FILE *fh;

  // TODO?: check if mode is valid for fopen(), i.e., no 'f', 'h' or number
  if ((fh = fopen(filename, mode)) != NULL)
  {
   fread(magic, sizeof (magic), 1, fh);
#ifdef ZLIB
   if (magic[0] == 0x1f && magic[1] == 0x8b && magic[2] == 0x08)
   {                                    // ID1, ID2 and CM. gzip uses Compression Method 8
    fmode = FM_GZIP;
    compressed = 1;
   }
   else if (magic[0] == 'P' && magic[1] == 'K' && magic[2] == 0x03 && magic[3] == 0x04)
   {
    fmode = FM_ZIP;
    compressed = 1;
   }
   else
#endif
    /*
      Files that are opened with mode "r+" will probably be written to.
      zlib doesn't support mode "r+", so we have to use FM_NORMAL.
      Mode "r" doesn't require FM_NORMAL and FM_GZIP works, but we
      shouldn't introduce needless overhead.
    */
    fmode = FM_NORMAL;
    fclose(fh);
  }
 }

 if (fmode == FM_NORMAL)
  file = fopen(filename, mode);
#ifdef ZLIB
 else if (fmode == FM_GZIP)
  file = (FILE *) gzopen(filename, mode);
 else if (fmode == FM_ZIP)
 {
  file = (FILE *) unzOpen(filename);
  if (file != NULL)
  {
   unzip_goto_file(file, unzip_current_file_nr);
   unzOpenCurrentFile(file);
  }
 }
#endif

 if (file == NULL)
  return NULL;

 finfo = &finfo_list[fmode * 2 + compressed];
 fh_map = map_put(fh_map, file, finfo);

 return file;
}

int fclose2(FILE *file)
{
 fmode2_t fmode = get_fmode(file);

 map_del(fh_map, file);
 if (fmode == FM_NORMAL)
  return fclose(file);
#ifdef ZLIB
 else if (fmode == FM_GZIP)
  return gzclose(file);
 else if (fmode == FM_ZIP)
  {
   unzCloseCurrentFile(file);
   return unzClose(file);
  }
#endif
 else
  return EOF;
}

int fseek2(FILE *file, long offset, int mode)
{
 st_finfo_t *finfo = get_finfo(file);

 if (finfo->fmode == FM_NORMAL)
  return fseek(file, offset, mode);
#ifdef ZLIB
 else if (finfo->fmode == FM_GZIP)
 {
  if (mode == SEEK_END)                         // zlib doesn't support SEEK_END
  {
   // Note that this is _slow_...
   while (!gzeof(file))
   {
    gzgetc(file); // necessary for _uncompressed_ files in order to set EOF
    gzseek(file, 1024 * 1024, SEEK_CUR);
   }
   offset += gztell(file);
   mode = SEEK_SET;
  }
  /*
    The zlib documentation contains a major error. From the doc:
      gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET)
    That is not true for uncompressed files. gzrewind() doesn't change the
    file pointer for uncompressed files in the ports I (dbjh) tested
    (zlib 1.1.3, DJGPP, Cygwin & GNU/Linux). It clears the EOF indicator.
  */
  if (!finfo->compressed)
   gzrewind(file);
  return gzseek(file, offset, mode) == -1 ? -1 : 0;
 }
 else if (finfo->fmode == FM_ZIP)
 {
  int base;
  if (mode != SEEK_SET && mode != SEEK_CUR && mode != SEEK_END)
   return -1;
  if (mode == SEEK_SET)
   base = 0;
  else if (mode == SEEK_CUR)
   base = unztell(file);
  else // mode == SEEK_END
  {
   unz_file_info info;

   unzip_goto_file(file, unzip_current_file_nr);
   unzGetCurrentFileInfo(file, &info, NULL, 0, NULL, 0, NULL, 0);
   base = info.uncompressed_size;
  }
  return unzip_seek_helper(file, base + offset);
 }
#endif // ZLIB
 return -1;
}

size_t fread2(void *buffer, size_t size, size_t number, FILE *file)
{
 fmode2_t fmode = get_fmode(file);

 if (size == 0 || number == 0)
  return 0;

 if (fmode == FM_NORMAL)
  return fread(buffer, size, number, file);
#ifdef ZLIB
 else if (fmode == FM_GZIP)
 {
  int n = gzread(file, buffer, number * size);
  return n / size;
 }
 else if (fmode == FM_ZIP)
 {
  int n = unzReadCurrentFile(file, buffer, number * size);
  return n / size;
 }
#endif
 return 0;
}

int fgetc2(FILE *file)
{
 fmode2_t fmode = get_fmode(file);

 if (fmode == FM_NORMAL)
  return fgetc(file);
#ifdef ZLIB
 else if (fmode == FM_GZIP)
  return gzgetc(file);
 else if (fmode == FM_ZIP)
 {
  char c;
  int retval = unzReadCurrentFile(file, &c, 1);
  return retval <= 0 ? EOF : c & 0xff;          // avoid sign bit extension
 }
#endif
 else
  return EOF;
}

char *fgets2(char *buffer, int maxlength, FILE *file)
{
 fmode2_t fmode = get_fmode(file);

 if (fmode == FM_NORMAL)
  return fgets(buffer, maxlength, file);
#ifdef ZLIB
 else if (fmode == FM_GZIP)
 {
  char *retval = gzgets(file, buffer, maxlength);
  return retval == Z_NULL ? NULL : retval;
 }
 else if (fmode == FM_ZIP)
 {
  int n = 0, c = 0;
  while (n < maxlength - 1 && (c = fgetc2(file)) != EOF)
  {
   buffer[n] = c;                               // '\n' must also be stored in buffer
   n++;
   if (c == '\n')
   {
    buffer[n] = 0;
    break;
   }
  }
  if (n >= maxlength - 1 || c == EOF)
   buffer[n] = 0;
  return n > 0 ? buffer : NULL;
 }
#endif
 else
  return NULL;
}

int feof2(FILE *file)
{
 fmode2_t fmode = get_fmode(file);

 if (fmode == FM_NORMAL)
  return feof(file);
#ifdef ZLIB
 else if (fmode == FM_GZIP)
  return gzeof(file);
 else if (fmode == FM_ZIP)
  return unzeof(file);                          // returns feof() of the "current file"
#endif
 else
  return -1;
}

size_t fwrite2(const void *buffer, size_t size, size_t number, FILE *file)
{
 fmode2_t fmode = get_fmode(file);

 if (size == 0 || number == 0)
  return 0;

 if (fmode == FM_NORMAL)
  return fwrite(buffer, size, number, file);
#ifdef ZLIB
 else if (fmode == FM_GZIP)
 {
  int n = gzwrite(file, (void *) buffer, number * size);
  return n / size;
 }
#endif
 else
  return 0;                                     // writing to zip files is not supported
}

int fputc2(int character, FILE *file)
{
 fmode2_t fmode = get_fmode(file);

 if (fmode == FM_NORMAL)
  return fputc(character, file);
#ifdef ZLIB
 else if (fmode == FM_GZIP)
  return gzputc(file, character);
#endif
 else
  return EOF;                                   // writing to zip files is not supported
}

long ftell2(FILE *file)
{
 fmode2_t fmode = get_fmode (file);

 if (fmode == FM_NORMAL)
  return ftell(file);
#ifdef ZLIB
 else if (fmode == FM_GZIP)
  return gztell(file);
 else if (fmode == FM_ZIP)
  return unztell(file);                         // returns ftell() of the "current file"
#endif
 else
  return -1;
}

void rewind2(FILE *file)
{
  fseek2(file, 0, SEEK_SET);
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -