s1fwx 2.6源代码分享

s1fwx 2.6版本源代码,包括以下文件:

crc32.cpp
crc32.h
crc32.o
CVS
decrypt.cpp
decrypt.h
decrypt.o
device.cpp
device.h
device_lin.cpp
device_lin.o
dump.cpp
dump.h
dump.o
file.txt
flash.cpp
flash.h
flash.o
heads.h
include.h
main.cpp
main.ico
main.o
Makefile
md5.cpp
md5.h
md5.o
parser.cpp
parser.h
parser.o
resource.h
s1fwx
s1fwx.aps
s1fwx.ncb
s1fwx.rc
s1fwx.script
s1fwx.sln
s1fwx.suo
s1fwx.vcproj
sum16.cpp
sum16.h
sum16.o
sum32.cpp
sum32.h
sum32.o

其中main.cpp文件内容如下:

const char szAppInfo[] = 

// -----------------------------------------------------------------------------------------------
"s1fwx v2.6 - copyright (c) 2005 wiRe (http://www.s1mp3.de/)\n";
// -----------------------------------------------------------------------------------------------
// v2.6
// -new script command "DATE"
// -removed some bugs inside archive function
// -flash-read autodetects firmware size, no buffer-overrun anymore
// -----------------------------------------------------------------------------------------------

#include "include.h"
#include "decrypt.h"
#include "sum16.h"
#include "sum32.h"
#include "crc32.h"
#include "md5.h"
#include "flash.h"
#include "parser.h"

#include "heads.h"

#ifdef LINUX
#define _stricmp stricmp
#include <unistd.h>
#include <errno.h>
static int GetLastError() {
  return errno;
}
#define DeleteFile unlink
#define BOOL bool
#endif


// -----------------------------------------------------------------------------------------------
enum FILETYPE {
  FILETYPE_ENC,
  FILETYPE_FW,
  FILETYPE_AFI,
  FILETYPE_UNKNOWN=127,
  FILETYPE_OVERWRITE=128
};

uint8 g_filetype = FILETYPE_UNKNOWN;




// -----------------------------------------------------------------------------------------------
char trunc_buf[256];
char *trunc(char *str, size_t n = 0)
{
  ZeroMemory(trunc_buf, sizeof(trunc_buf));
  if(!n) n = strlen(str);
  if(n >= sizeof(trunc_buf)) n = sizeof(trunc_buf)-1;
  strncpy(trunc_buf, str, n);
  str = trunc_buf;
  char *end = str + n-1;
  while(*str == ' ' || *str == '\t') str++;
  while((end >= str) && (*end == ' ' || *end == '\t')) *end-- = 0;
  return str;
}


// -----------------------------------------------------------------------------------------------
char fsize_buf[256];
char *fsize(uint32 fsize)
{
  if(fsize == 1) sprintf(fsize_buf, "1byte");
  else if(fsize < 1024) sprintf(fsize_buf, "%ubytes", fsize);
  else {
    if(fsize < 1048576) {
      if(fsize % 1024 == 0) sprintf(fsize_buf, "%ukb", fsize/1024);
      else if((fsize*10) % 1024 == 0) sprintf(fsize_buf, "%.01fkb", fsize/1024.0);
      else sprintf(fsize_buf, "%.02fkb", fsize/1024.0);
    } else {
      if(fsize % 1048576 == 0) sprintf(fsize_buf, "%umb", fsize/1048576);
      else if((fsize*10) % 1048576 == 0) sprintf(fsize_buf, "%.01fmb", fsize/1048576.0);
      else sprintf(fsize_buf, "%.02fmb", fsize/1048576.0);
    }
  }
  return fsize_buf;
}


#ifdef LINUX
int display_error(int result) {
  printf("Error: %s\n",strerror(result));
  return result;
}
#else
// -----------------------------------------------------------------------------------------------
int display_error(int result)
{
  if(result == ERROR_SUCCESS) {
    fprintf(stderr, "success\n");
    return result;
  }

  LPVOID pTextBuffer = NULL;
  if(FormatMessage(
    FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_IGNORE_INSERTS|FORMAT_MESSAGE_FROM_SYSTEM, 
    NULL, (DWORD)result, 0, (LPTSTR)&pTextBuffer, 0, NULL) != 0)
  {
    fprintf(stderr, "error: %s", (LPCSTR)pTextBuffer);
    LocalFree(pTextBuffer);
  }
  else
  {    
    fprintf(stderr, "error: unknown error (0x%04X)\n", result);
  }
  return result;
}
#endif


// -----------------------------------------------------------------------------------------------
int detect_filetype(char *filename, bool fFileExists = true)
{
  if(g_filetype != FILETYPE_UNKNOWN) return(g_filetype);

  char *ext = strrchr(filename, ':');
  if(ext) {
    if(_stricmp(ext+1, "afi") == 0) {*ext = 0; return(g_filetype=FILETYPE_AFI|FILETYPE_OVERWRITE);}
    else if(_stricmp(ext+1, "fw") == 0) {*ext = 0; return(g_filetype=FILETYPE_FW|FILETYPE_OVERWRITE);}
    else if(_stricmp(ext+1, "enc") == 0) {*ext = 0; return(g_filetype=FILETYPE_ENC|FILETYPE_OVERWRITE);}
  }

  if(!fFileExists && g_filetype == FILETYPE_UNKNOWN) return FILETYPE_UNKNOWN;

  FILE *fh = fopen(filename, "rb");
  if(!fh) return GetLastError();
  union {
    char    str[16];
    uint8   db[16];
    uint16  dh[8];
    uint32  dw[4];
  } id;
  if(fread(&id, sizeof(id), 1, fh) != 1) {
    fclose(fh);
    return ERROR_READ_FAULT;
  }
  fclose(fh);

  if(strncmp("Enciphered", id.str, 10)==0) return(g_filetype=FILETYPE_ENC);
  else if(strcmp(AFI_ID, id.str) == 0) return(g_filetype=FILETYPE_AFI);
  else if(id.dw[0] == FIRMWARE_ID) return(g_filetype=FILETYPE_FW);
  else return ERROR_INVALID_DATA;
}


// -----------------------------------------------------------------------------------------------
int display_fileinfo(char *filename)
{
  md5_context ctx;
  uint8 md5[16];
  uint8 buf[512];
  uint32 len;

  printf("\n---[ info ]--------------------------------------------------------------------\n");
  int ret = detect_filetype(filename);  //to correct filename, removes :afi or :fw
  printf("filename = '%s'\nfiletype = ", filename);
  if(ret<0 && ret != ERROR_INVALID_DATA) return ret;
  switch(ret&FILETYPE_UNKNOWN) {
    case FILETYPE_ENC: printf("encoded"); break;
    case FILETYPE_AFI: printf("afi"); break;
    case FILETYPE_FW: printf("fw"); break;
    default: printf("unknown");
  }
  if(ret&FILETYPE_OVERWRITE) printf(" (overwritten)");
  printf("\n");

  FILE *fh = fopen(filename, "rb");
  if(!fh) return GetLastError();
  md5_starts(&ctx);
  for(;!feof(fh);) {
    len = (uint32)fread(buf, 1, sizeof(buf), fh);
    if(len > 0) md5_update(&ctx, buf, len);
  }
  md5_finish(&ctx, md5);
  printf("md5 = ");
  for(len=0; len<16; len++) printf("%02X", md5[len]);
  printf("\n");
  /*fseek(fh, 32, SEEK_SET);
  uint32 sum32 = calc_sum32(fh);
  if(sum32 != 0) printf("checksum = 0x%08X\n", sum32);*/
  fclose(fh);

  return ERROR_SUCCESS;
}


// -----------------------------------------------------------------------------------------------
int decrypt(char *filename)
{
  display_fileinfo(filename);
  printf("decrypting file...\n");

  int ret = ERROR_SUCCESS;

  struct stat fs;
  if(stat(filename, &fs) != 0) return GetLastError();
  if(fs.st_size <= 10) return ERROR_BAD_FORMAT;
  FILE *fh_in = fopen(filename, "rb");
  if(!fh_in) return GetLastError();  

  uint8 *buf_in = new uint8[fs.st_size];
  if(!buf_in) ret = ERROR_NOT_ENOUGH_MEMORY;
  else {
    uint8 *buf_out = new uint8[fs.st_size];
    if(!buf_out) ret = ERROR_NOT_ENOUGH_MEMORY;
    else {
      if(fread(buf_in, fs.st_size, 1, fh_in) != 1) ret = ERROR_READ_FAULT;
      else if(strncmp((char*)buf_in, "Enciphered", 10) != 0) ret = ERROR_BAD_FORMAT;
      else {
        fs.st_size -= 10;
        fs.st_size = (fs.st_size >> 9) << 9;

        char key[32];
        extract_key(key, buf_in, fs.st_size, buf_out);
        key[20] = 0;
        printf("key = '%s'\n", key);
        memcpy(buf_in, buf_out, fs.st_size);

        if(filename[0]) filename[strlen(filename)-1] = '~';   //generate new filename
        FILE *fh_out = fopen(filename, "wb");
        if(!fh_out) ret = GetLastError();
        else {

          memset(buf_out, 0, fs.st_size);
          decrypt(buf_out, buf_in, fs.st_size, key, (uint32)strlen(key));
          if(fwrite(buf_out, fs.st_size, 1, fh_out) != 1) ret = ERROR_WRITE_FAULT;

        }
        fclose(fh_out);
        if(ret != ERROR_SUCCESS) DeleteFile(filename);
      }
      delete buf_out;
    }
    delete buf_in;
  }
  fclose(fh_in);
  if(ret != ERROR_SUCCESS) return ret;

  g_filetype = FILETYPE_UNKNOWN;
  ret = detect_filetype(filename);
  return (ret<0)?ret:ERROR_SUCCESS;
}




// -----------------------------------------------------------------------------------------------
int display_afi(char *filename)
{
  display_fileinfo(filename);

  FILE *fh = fopen(filename, "rb");
  if(!fh) return GetLastError();
  AFI_HEAD afi;
  if(fread(&afi, sizeof(afi), 1, fh) != 1) {
    fclose(fh);
    return ERROR_READ_FAULT;
  }

  printf("\n---[ info:afi ]----------------------------------------------------------------\n");
  if(afi.eoh.sum32 != calc_sum32(&afi, sizeof(AFI_HEAD)-4)) 
    printf("invalid checksum, file header maybe damaged\n");
  printf("file id = '%c%c%c%c'%s\n",
    afi.id[0], afi.id[1], afi.id[2], afi.id[3], (strcmp(AFI_ID, afi.id)==0)?"":" (unknown)");
  printf("vendor id = 0x%04X\n", afi.vendor_id);
  printf("product id = 0x%04X\n", afi.product_id);
  printf("version = %u.%u.%02X\n", afi.version_id[0]>>4, afi.version_id[0]&15, afi.version_id[1]);
  printf("date = %02X/%02X/%02X%02X\n", afi.date.month, afi.date.day, afi.date.year[0], afi.date.year[1]);

  printf("\n+---filename---+ +---size---+ +-type-+ +---+ +--chksum--+\n");
  for(int n=0; n<AFI_ENTRIES; n++) if(afi.entry[n].filename[0]) {
    int x=0;
    x += printf("| %s", trunc(afi.entry[n].filename, 8));
    x += printf(".%s", trunc(afi.entry[n].extension, 3));
    for(; x<15; x++) printf(" ");
    x += printf("| | %s", fsize(afi.entry[n].fsize));
    for(; x<28; x++) printf(" ");
    printf("| | %c%c%c%c ", afi.entry[n].desc[0], afi.entry[n].desc[1], afi.entry[n].desc[2], afi.entry[n].desc[3]);    
    printf("| | %c ", afi.entry[n].content);
    uint32 crc32 = calc_crc32(fh, afi.entry[n].fofs, afi.entry[n].fsize);
    uint32 sum32 = calc_sum32(fh, afi.entry[n].fofs, afi.entry[n].fsize);
    printf("| | %08X |", crc32);
    /*if(afi.entry[n].sum32 != 0)*/ if(sum32 != afi.entry[n].sum32) printf("  <- invalid checksum");
    printf("\n");
  }
  printf("+--------------+ +----------+ +------+ +---+ +----------+\n");

  fclose(fh);
  return ERROR_SUCCESS;
}


// -----------------------------------------------------------------------------------------------
int display_fw(char *filename)
{
  display_fileinfo(filename);

  FILE *fh = fopen(filename, "rb");
  if(!fh) return GetLastError();
  FW_HEAD fw;
  if(fread(&fw, sizeof(fw), 1, fh) != 1) {
    fclose(fh);
    return ERROR_READ_FAULT;
  }

  printf("\n---[ info:fw ]-----------------------------------------------------------------\n");
  if(fw.sum16 != calc_sum16(&fw, 0x1FE) || fw.sum32 != calc_sum32(fh, 0x200, sizeof(FW_HEAD)-0x200))
    printf("invalid checksum, file header maybe damaged\n");
  printf("file id = 0x%08X%s\n", fw.id, (fw.id==FIRMWARE_ID)?"":" (unknown)");
  printf("vendor id = 0x%04X\n", fw.vendor_id);
  printf("product id = 0x%04X\n", fw.product_id);
  printf("version = %u.%u.%02X\n", fw.version_id[0]>>4, fw.version_id[0]&15, fw.version_id[1]);
  printf("date = %02X/%02X/%02X%02X\n", fw.date.month, fw.date.day, fw.date.year[0], fw.date.year[1]);
  printf("info = '%s'\n", trunc(fw.info.global, 32));
  printf("manufacturer = '%s'\n", trunc(fw.info.manufacturer, 32));
  printf("device name = '%s'\n", trunc(fw.info.device, 32));
  //printf("usb desc = '%s'\n", trunc(fw.usb.desc, 23));
  printf("usb attr = '%s'\n", trunc(fw.usb.attr, 8));
  printf("usb ident = '%s'\n", trunc(fw.usb.ident, 16));
  printf("usb rev = '%s'\n", trunc(fw.usb.rev, 4));
  //print comval entries
  printf("rtc rate = 0x%02X\n", fw.comval.rtc_rate);
  printf("display contrast = %u\n", fw.comval.display_contrast);
  printf("light time = %u\n", fw.comval.light_time);
  printf("standby time = %u\n", fw.comval.standby_time);
  printf("sleep time = %u\n", fw.comval.sleep_time);
  printf("language = ");
  const char *language[] = {"simple chinese", "english", "traditional chinese"};
  if(fw.comval.language_id>2) printf("unknown (0x%02X)\n", fw.comval.language_id);
    else printf("%s\n", language[fw.comval.language_id]);
  printf("replay mode = 0x%02X\n", fw.comval.replay_mode);
  printf("online mode = 0x%02X\n", fw.comval.online_mode);
  printf("battery type = ");
  const char *battery[] = {"1.5V alkaline", "1.2V Ni-H", "3.6V li-polymer"};
  if(fw.comval.battery_type>2) printf("unknown (0x%02X)\n", fw.comval.battery_type);
    else printf("%s\n", battery[fw.comval.battery_type]);
  printf("radio/fm support = %s\n", (fw.comval.radio_disable == 0)?"yes":"no");

  printf("\n+---filename---+ +---size---+ +--chksum--+\n");
  for(int n=0; n<AFI_ENTRIES; n++) if(fw.entry[n].filename[0]) {
    int x=0;
    x += printf("| %s", trunc(fw.entry[n].filename, 8));
    x += printf(".%s", trunc(fw.entry[n].extension, 3));
    for(; x<15; x++) printf(" ");
    x += printf("| | %s", fsize(fw.entry[n].fsize));
    for(; x<28; x++) printf(" ");
    uint32 crc32 = calc_crc32(fh, ((size_t)fw.entry[n].fofs_s9)<<9, fw.entry[n].fsize);
    uint32 sum32 = calc_sum32(fh, ((size_t)fw.entry[n].fofs_s9)<<9, fw.entry[n].fsize);
    printf("| | %08X |", crc32);
    /*if(afi.entry[n].sum32 != 0)*/ if(sum32 != fw.entry[n].sum32) printf("  <- invalid checksum");
    printf("\n");
  }
  printf("+--------------+ +----------+ +----------+\n");

  fclose(fh);
  return ERROR_SUCCESS;
}


// -----------------------------------------------------------------------------------------------
int display(char *filename)
{
  int ret = detect_filetype(filename);
  switch(ret&FILETYPE_UNKNOWN) {
    case FILETYPE_FW:  return display_fw(filename);
    case FILETYPE_AFI: return display_afi(filename);
    case FILETYPE_ENC: ret = decrypt(filename); if(ret == ERROR_SUCCESS) return display(filename);
    case ERROR_INVALID_DATA: return display_fileinfo(filename);
    default: return ret;
  }
}




// -----------------------------------------------------------------------------------------------
int extract_to_file(char *filename, FILE *fh_src, size_t fofs, size_t fsize, uint32 *sum32p/*=NULL*/)
{
  // by converting fofs to long we support only files up to 2GB!
  if(fseek(fh_src, (long)fofs, SEEK_SET) != 0) return GetLastError();

  FILE *fh = fopen(filename, "wb");
  if(!fh) return GetLastError();   

  int ret = ERROR_SUCCESS;
  uint8 *buf = new uint8[fsize];
  if(!buf) ret = ERROR_NOT_ENOUGH_MEMORY;
  else {
  
    if(fread(buf, fsize, 1, fh_src) != 1) ret = ERROR_READ_FAULT;
    if(fwrite(buf, fsize, 1, fh) != 1) ret = ERROR_WRITE_FAULT;
    if(sum32p != NULL) *sum32p = calc_sum32(buf, fsize);

    delete buf;
  }

  fclose(fh);
  return ret;
}


// -----------------------------------------------------------------------------------------------
int extract_from_file(char *filename, FILE *fh_dest, uint32 *fsizep, uint32 *sum32p)
{
  struct stat fs;
  if(stat(filename, &fs) != 0) return GetLastError();
  if(fsizep) *fsizep = (uint32)fs.st_size;

  FILE *fh = fopen(filename, "rb");
  if(!fh) return GetLastError();

  int ret = ERROR_SUCCESS;
  uint8 *buf = new uint8[fs.st_size];
  if(!buf) ret = ERROR_NOT_ENOUGH_MEMORY;
  else {
  
    if(fread(buf, fs.st_size, 1, fh) != 1) ret = ERROR_READ_FAULT;
    if(fwrite(buf, fs.st_size, 1, fh_dest) != 1) ret = ERROR_WRITE_FAULT;
    if(sum32p != NULL) *sum32p = calc_sum32(buf, fs.st_size);

    delete buf;
  }

  fclose(fh);
  return ret;
}




// -----------------------------------------------------------------------------------------------
int extract_afi(char *filename)
{
  display_fileinfo(filename);

  FILE *fh = fopen(filename, "rb");
  if(!fh) return GetLastError();

  AFI_HEAD afi;
  if(fread(&afi, sizeof(afi), 1, fh) != 1) {
    fclose(fh);
    return ERROR_READ_FAULT;
  }

  printf("\n---[ extract:afi ]-------------------------------------------------------------\n");
  int cnt=0, err=0, wrn=0;
  if(afi.eoh.sum32 != calc_sum32(&afi, sizeof(AFI_HEAD)-4)) {
    wrn++;
    printf("warning: invalid checksum, file header maybe damaged\n");
  }
  for(int n=0; n<AFI_ENTRIES; n++) if(afi.entry[n].filename[0]) {
    cnt++;
    char xfile[16];
    strcpy(xfile, trunc(afi.entry[n].filename, 8));
    strcat(xfile, ".");
    strcat(xfile, trunc(afi.entry[n].extension, 3));
    printf("extracting '%s'\n", xfile);
    uint32 sum32;
    int ret = extract_to_file(xfile, fh, afi.entry[n].fofs, afi.entry[n].fsize, &sum32);
    if(ret != ERROR_SUCCESS) {
      err++;
      printf("  -> ");
      display_error(ret);
    } else if(sum32 != afi.entry[n].sum32) {
      wrn++;
      printf("  -> warning: invalid checksum\n");
    }
  }
  printf("extracted %u file%s: %u error%s, %u warning%s\n",
    cnt, (cnt==1)?"":"s", err, (err==1)?"":"s", wrn, (wrn==1)?"":"s");

  fclose(fh);
  return ERROR_SUCCESS;
}


// -----------------------------------------------------------------------------------------------
int extract_fw(char *filename)
{
  display_fileinfo(filename);

  FILE *fh = fopen(filename, "rb");
  if(!fh) return GetLastError();

  FW_HEAD fw;
  if(fread(&fw, sizeof(fw), 1, fh) != 1) {
    fclose(fh);
    return ERROR_READ_FAULT;
  }

  printf("\n---[ extract:fw ]--------------------------------------------------------------\n");
  int cnt=0, err=0, wrn=0;
  if(fw.sum16 != calc_sum16(&fw, 0x1FE) || fw.sum32 != calc_sum32(fh, 0x200, sizeof(FW_HEAD)-0x200)) {
    wrn++;
    printf("warning: invalid checksum, file header maybe damaged\n");
  }
  for(int n=0; n<FIRMWARE_ENTRIES; n++) if(fw.entry[n].filename[0]) {
    cnt++;
    char xfile[16];
    strcpy(xfile, trunc(fw.entry[n].filename, 8));
    strcat(xfile, ".");
    strcat(xfile, trunc(fw.entry[n].extension, 3));
    printf("extracting '%s'\n", xfile);
    uint32 sum32;
    int ret = extract_to_file(xfile, fh, ((size_t)fw.entry[n].fofs_s9)<<9, fw.entry[n].fsize, &sum32);
    if(ret != ERROR_SUCCESS) {
      err++;
      printf("  -> ");
      display_error(ret);
    } else if(sum32 != fw.entry[n].sum32) {
      wrn++;
      printf("  -> warning: invalid checksum\n");
    }
  }
  printf("extracted %u file%s: %u error%s, %u warning%s\n",
    cnt, (cnt==1)?"":"s", err, (err==1)?"":"s", wrn, (wrn==1)?"":"s");

  fclose(fh);
  return ERROR_SUCCESS;
}


// -----------------------------------------------------------------------------------------------
int extract(char *filename)
{
  int ret = detect_filetype(filename);
  switch(ret&FILETYPE_UNKNOWN) {
    case FILETYPE_FW:  return extract_fw(filename);
    case FILETYPE_AFI: return extract_afi(filename);
    case FILETYPE_ENC: ret = decrypt(filename); if(ret == ERROR_SUCCESS) return extract(filename);
    default: return ret;
  }
}




// -----------------------------------------------------------------------------------------------
int extract_script_afi(char *filename)
{
  fprintf(stderr, "\n---[ script:afi ]--------------------------------------------------------------\n");
  printf("//%s", szAppInfo);
  printf("//auto-generated script from \"%s:afi\"\n", filename);

  FILE *fh = fopen(filename, "rb");
  if(!fh) return GetLastError();

  AFI_HEAD afi;
  if(fread(&afi, sizeof(afi), 1, fh) != 1) {
    fclose(fh);
    return ERROR_READ_FAULT;
  }

  printf("\n[AFI]\nVID = 0x%04X\nPID = 0x%04X\nVER = %u.%u.%02X\nDATE = %02X/%02X/%02X%02X\n\nPATH = \".\\\"\n",
    afi.vendor_id, afi.product_id, afi.version_id[0]>>4, afi.version_id[0]&15, afi.version_id[1],
    afi.date.month, afi.date.day, afi.date.year[0], afi.date.year[1]);

  for(int n=0; n<AFI_ENTRIES; n++) if(afi.entry[n].filename[0]) {
    char xfile[16];
    strcpy(xfile, trunc(afi.entry[n].filename, 8));
    strcat(xfile, ".");
    strcat(xfile, trunc(afi.entry[n].extension, 3));
    switch(afi.entry[n].content) {
      case 'B': printf("BREC = \"%s\", \"%s\"\n", xfile, trunc(afi.entry[n].desc, 4)); break;
      case 'F': printf("FWSC = \"%s\", \"%s\"\n", xfile, trunc(afi.entry[n].desc, 4)); break;
      case 'U': printf("ADFU = \"%s\", \"%s\"\n", xfile, trunc(afi.entry[n].desc, 4)); break;
      case 'I': printf("FWIMAGE = \"%s\"\n", xfile); break;
      default:
      printf("FILE = \"%s\", \"%s\", \"%c\", 0x%04X\n", xfile, trunc(afi.entry[n].desc, 4),
        afi.entry[n].content, afi.entry[n].type);
    }
  }

  printf("\n[EOF]\n");
  fprintf(stderr, "done\n");

  fclose(fh);
  return ERROR_SUCCESS;
}


// -----------------------------------------------------------------------------------------------
int extract_script_fw(char *filename)
{
  fprintf(stderr, "\n---[ script:fw ]---------------------------------------------------------------\n");
  printf("//%s", szAppInfo);
  printf("//auto-generated script from \"%s:fw\"\n", filename);

  FILE *fh = fopen(filename, "rb");
  if(!fh) return GetLastError();

  FW_HEAD fw;
  if(fread(&fw, sizeof(fw), 1, fh) != 1) {
    fclose(fh);
    return ERROR_READ_FAULT;
  }

  printf("\n[FW]\nVID = 0x%04X\nPID = 0x%04X\nVER = %u.%u.%02X\nDATE = %02X/%02X/%02X%02X\n\n",
    fw.vendor_id, fw.product_id, fw.version_id[0]>>4, fw.version_id[0]&15, fw.version_id[1],
    fw.date.month, fw.date.day, fw.date.year[0], fw.date.year[1]);
  printf("INF = \"%s\"\n", trunc(fw.info.global, 32));
  printf("INF_DEVICE = \"%s\"\n", trunc(fw.info.device, 32));
  printf("INF_MANUFACTURER = \"%s\"\n\n", trunc(fw.info.manufacturer, 32));
  //printf("USB_DESC = \"%s\"\n", trunc(fw.usb.desc, 23));
  printf("USB_ATTR = \"%s\"\n", trunc(fw.usb.attr, 8));
  printf("USB_IDENT = \"%s\"\n", trunc(fw.usb.ident, 16));
  printf("USB_REV = \"%s\"\n\n", trunc(fw.usb.rev, 4));
  printf("SET_RTCRATE = 0x%04X\nSET_DISPLAYCONTRAST = %u\nSET_LIGHTTIME = %u\nSET_STANDBYTIME = %u\nSET_SLEEPTIME = %u\n" \
    "SET_LANGUAGEID = %u\nSET_REPLAYMODE = %u\nSET_ONLINEMODE = %u\nSET_BATTERYTYPE = %u\nSET_RADIODISABLE = %u\n\n", 
    fw.comval.rtc_rate, fw.comval.display_contrast, fw.comval.light_time, fw.comval.standby_time,
    fw.comval.sleep_time, fw.comval.language_id, fw.comval.replay_mode, fw.comval.online_mode, 
    fw.comval.battery_type, fw.comval.radio_disable);
  printf("PATH = \".\\\"\n");

  for(int n=0; n<FIRMWARE_ENTRIES; n++) if(fw.entry[n].filename[0]) {
    char xfile[16];
    strcpy(xfile, trunc(fw.entry[n].filename, 8));
    strcat(xfile, ".");
    strcat(xfile, trunc(fw.entry[n].extension, 3));
    printf("FILE = \"%s\"\n", xfile);
  }

  printf("\n[EOF]\n");
  fprintf(stderr, "done\n");

  fclose(fh);
  return ERROR_SUCCESS;
}


// -----------------------------------------------------------------------------------------------
int extract_script(char *filename)
{
  int ret = detect_filetype(filename);
  switch(ret&FILETYPE_UNKNOWN) {
    case FILETYPE_FW:  return extract_script_fw(filename);
    case FILETYPE_AFI: return extract_script_afi(filename);
    case FILETYPE_ENC: ret = decrypt(filename); if(ret == ERROR_SUCCESS) return extract_script(filename);
    default: return ret;
  }
}




// -----------------------------------------------------------------------------------------------
int archive_afi(char *filename)
{
  printf("\n---[ archive:afi ]-------------------------------------------------------------\n");

  int ret = ERROR_SUCCESS;
  FILE *fh = NULL;

  LPSTR lppFileList[AFI_ENTRIES];
  ZeroMemory(lppFileList, sizeof(lppFileList));

  do {
    AFI_HEAD ah;
    ZeroMemory(&ah, sizeof(ah));

    // run parser
    PARSER Parser(stdin);
    ret = Parser.parse_script(&ah, lppFileList, NULL, NULL);
    if(ret != ERROR_SUCCESS) break;

    //create archive and write beta-header
    fh = fopen(filename, "wb");
    if(!fh) {ret = GetLastError(); break;}
    if(fwrite(&ah, sizeof(ah), 1, fh) != 1) {ret = ERROR_WRITE_FAULT; break;}

    // write files
    size_t fofs = sizeof(ah);
    for(int file_cnt=0; file_cnt<AFI_ENTRIES; file_cnt++) if(lppFileList[file_cnt])
    {
      printf("add file \"%s\"\n", lppFileList[file_cnt]);

      //align file to 16
      while(fofs % 16) {
        char zero = 0;
        if(fwrite(&zero, 1, 1, fh) != 1) {ret = ERROR_WRITE_FAULT; break;}
        fofs++;
      }
      if(ret != ERROR_SUCCESS) break;

      //add new file
      ah.entry[file_cnt].fofs = (uint32)(fofs);
      ret = extract_from_file(lppFileList[file_cnt], fh, &ah.entry[file_cnt].fsize, &ah.entry[file_cnt].sum32);
      if(ret != ERROR_SUCCESS) break;
      fofs += ah.entry[file_cnt].fsize;
    }
    if(ret != ERROR_SUCCESS) break;

    // calc checksum
    ah.eoh.sum32 = calc_sum32(&ah, sizeof(AFI_HEAD)-4);

    //write new header
    if(fseek(fh, 0, SEEK_SET) != 0) {ret = GetLastError(); break;}
    else if(fwrite(&ah, sizeof(ah), 1, fh) != 1) {ret = ERROR_WRITE_FAULT; break;}
  } while(0);

  for(int n=0; n<AFI_ENTRIES; n++) if(lppFileList[n]) {delete lppFileList[n]; break;}
  if(fh) fclose(fh);
  if(ret == ERROR_SUCCESS) display_afi(filename);
  return ret;
}


// -----------------------------------------------------------------------------------------------
int archive_fw(char *filename)
{
  printf("\n---[ archive:fw ]--------------------------------------------------------------\n");

  int ret = ERROR_SUCCESS;
  FILE *fh = NULL;

  LPSTR lppFileList[FIRMWARE_ENTRIES];
  ZeroMemory(lppFileList, sizeof(lppFileList));

  do {
    FW_HEAD fwh;
    ZeroMemory(&fwh, sizeof(fwh));

    // run parser
    PARSER Parser(stdin);
    ret = Parser.parse_script(NULL, NULL, &fwh, lppFileList);
    if(ret != ERROR_SUCCESS) break;

    // create wide character string fwh.desc
    fwh.usb.desc[0] = 0x0330;
    for(int c=0; c<23; c++) fwh.usb.desc[1+c] = (uint16)fwh.usb.attr[c];

    //create archive and write beta-header
    fh = fopen(filename, "wb");
    if(!fh) {ret = GetLastError(); break;}
    if(fwrite(&fwh, sizeof(fwh), 1, fh) != 1) {ret = ERROR_WRITE_FAULT; break;}

    // write files
    size_t fofs = sizeof(fwh);
    for(int file_cnt=0; file_cnt<FIRMWARE_ENTRIES; file_cnt++) if(lppFileList[file_cnt])
    {
      printf("add file \"%s\"\n", lppFileList[file_cnt]);

      //align file to 512
      while(fofs % 512) {
        char zero = 0;
        if(fwrite(&zero, 1, 1, fh) != 1) {ret = ERROR_WRITE_FAULT; break;}
        fofs++;
      }
      if(ret != ERROR_SUCCESS) break;

      //add new file
      fwh.entry[file_cnt].fofs_s9 = (uint32)(fofs >> 9);
      ret = extract_from_file(lppFileList[file_cnt], fh, &fwh.entry[file_cnt].fsize, &fwh.entry[file_cnt].sum32);
      if(ret != ERROR_SUCCESS) break;
      fofs += fwh.entry[file_cnt].fsize;
    }
    if(ret != ERROR_SUCCESS) break;

    // calc checksum
    fwh.sum32 = calc_sum32(&fwh.entry, sizeof(FW_HEAD)-0x200);  // 32bit checksum across entries
    fwh.sum16 = calc_sum16(&fwh, 0x1FE);                        // 16bit checksum across header

    //write new header
    if(fseek(fh, 0, SEEK_SET) != 0) {ret = GetLastError(); break;}
    else if(fwrite(&fwh, sizeof(fwh), 1, fh) != 1) {ret = ERROR_WRITE_FAULT; break;}
  } while(0);

  for(int n=0; n<FIRMWARE_ENTRIES; n++) if(lppFileList[n]) delete lppFileList[n];
  if(fh) fclose(fh);
  if(ret == ERROR_SUCCESS) display_fw(filename);
  return ret;
}


// -----------------------------------------------------------------------------------------------
int archive(char *filename)
{
  int ret = detect_filetype(filename, false);
  switch(ret&FILETYPE_UNKNOWN) {
    case FILETYPE_UNKNOWN: return ERROR_BAD_COMMAND;
    case FILETYPE_FW:  return archive_fw(filename);
    case FILETYPE_AFI: return archive_afi(filename);
    case FILETYPE_ENC: printf("error: writing encrypted files is not supported\n"); return ERROR_SUCCESS;
    default: return ret;
  }  
}




// -----------------------------------------------------------------------------------------------
int list_fw(char *filename)
{
  FILE *fh = fopen(filename, "rb");
  if(!fh) return GetLastError();

  AFI_HEAD afi;
  if(fread(&afi, sizeof(afi), 1, fh) != 1) {
    fclose(fh);
    return ERROR_READ_FAULT;
  }

  for(int n=0; n<AFI_ENTRIES; n++) if(afi.entry[n].filename[0] && afi.entry[n].content == 'I') {
    char xfile[1024];
    strcpy(xfile, filename);
    strcat(xfile, ".");
    strcat(xfile, trunc(afi.entry[n].filename, 8));
    strcat(xfile, ".");
    strcat(xfile, trunc(afi.entry[n].extension, 3));
    int ret = extract_to_file(xfile, fh, afi.entry[n].fofs, afi.entry[n].fsize);
    g_filetype = FILETYPE_UNKNOWN;
    if(ret == ERROR_SUCCESS) display(xfile);
    else display_error(ret);
    DeleteFile(xfile);
  }

  fclose(fh);
  return ERROR_SUCCESS;
}


// -----------------------------------------------------------------------------------------------
int list(char *filename)
{
  int ret = display(filename);
  if(ret != ERROR_SUCCESS) return ret;
  if((g_filetype&FILETYPE_UNKNOWN) == FILETYPE_AFI) return list_fw(filename);
  return ERROR_SUCCESS;
}




// -----------------------------------------------------------------------------------------------
int info(char *filename)
{
#ifndef LINUX
  int ret;
  BOOL fNext;
  WIN32_FIND_DATA fd;
  if(!filename || !*filename) filename = "*.bin";
  printf("show file info (%s)...\n", filename);
  HANDLE hFF = FindFirstFile(filename, &fd);
  if(hFF == INVALID_HANDLE_VALUE) return GetLastError();
  do {
    g_filetype = FILETYPE_UNKNOWN;
    ret = display(fd.cFileName);
    if(ret != ERROR_SUCCESS) display_error(ret);

    if((g_filetype&FILETYPE_UNKNOWN) == FILETYPE_AFI) {
      ret = list_fw(fd.cFileName);
      if(ret != ERROR_SUCCESS) display_error(ret);
    }
    fNext = FindNextFile(hFF, &fd);
    if(fNext) printf("\n===============================================================================\n");
  } while(fNext);
  CloseHandle(hFF);
#else
  printf("ToDo\n");
#endif
  return ERROR_SUCCESS;
}




// -----------------------------------------------------------------------------------------------
int _execute(char cmd, char *filename)
{
#ifdef LINUX
  switch(cmd) {
#else
  switch(tolower(cmd)) {
#endif
    case 0: return display(filename);
    case 'i': return display_fileinfo(filename);
    case 'x': return extract(filename);
    case 'l': return list(filename);    
    default: return ERROR_BAD_COMMAND;
  }
}

// -----------------------------------------------------------------------------------------------
int execute_mul(char cmd, char *filename)
{
#ifndef LINUX
  bool first=true;
  WIN32_FIND_DATA fd;
  HANDLE hFF = FindFirstFile(filename, &fd);
  if(hFF == INVALID_HANDLE_VALUE) return GetLastError();

  //get rel. path from filename  
  char *path = strrchr(filename, '\\');
  if(!path) path = strrchr(filename, ':');
  if(path) {
    *(++path) = 0;
    path = new char[MAX_PATH + strlen(filename)];
    if(!path) return ERROR_NOT_ENOUGH_MEMORY;
  }

  do {
    if(!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
    {
      if(!first) printf("\n===============================================================================\n");
      first = false;
      g_filetype = FILETYPE_UNKNOWN;
      int ret;
      if(!path) ret = _execute(cmd, fd.cFileName);
      else {
        strcpy(path, filename);
        strcat(path, fd.cFileName);
        ret = _execute(cmd, path);
      }
      if(ret == ERROR_BAD_COMMAND) {if(path) delete path; CloseHandle(hFF); return ret;}
      if(ret != ERROR_SUCCESS) display_error(ret);
    }
  } while(FindNextFile(hFF, &fd));

  if(path) delete path;
  CloseHandle(hFF);
#else
  printf("ToDo\n");
#endif
  return ERROR_SUCCESS;
}


// -----------------------------------------------------------------------------------------------
int execute(char cmd, char *filename)
{
#ifdef LINUX
  switch(cmd) {
#else
  switch(tolower(cmd)) {
#endif
    case 'f': return flash_dump(filename);
    case 'r': return flash_repair(filename);
    case 'a': return archive(filename);
    case 's': return extract_script(filename);
  }
  detect_filetype(filename);
  if(g_filetype & FILETYPE_OVERWRITE) return _execute(cmd, filename);
  return execute_mul(cmd, filename);
}


// -----------------------------------------------------------------------------------------------
int syntax()
{
  fprintf(stderr,
    "\n"\
    "show s1 firmware file (i)nfo or e(x)tract, (l)ist and (a)rchive files\n"\
    "use command (f) to extract the firmware from your connected s1 player device\n"\
    "\n"\
    "usage:\n"\
    "  s1fwx {i|x|s|a|l|f|r} {filename{:[afi|fw|enc]}}\n"\
    "\n"\
    "examples:\n"\
    "  display file info                s1fwx i fname.ext\n"\
    "  extract firmware file            s1fwx x fname.ext\n"\
    "  generate script from file        s1fwx s fname.ext > new.script\n"\
    "  create firmware file by script   s1fwx a new.fw:fw < def_fw.script\n"\
    "  create afi file by script        s1fwx a new.bin:afi < def_afi.script\n"\
    "  list entire content of any file  s1fwx l *.bin\n"\
    "  extract firmware from player     s1fwx f dump.bin\n"\
    "  repair dumped firmware files     s1fwx r dump.bin\n"
  );
  return ERROR_SUCCESS;
}


// -----------------------------------------------------------------------------------------------
int main(int argc, char *argv[])
{
  fprintf(stderr, szAppInfo);

  int ret = ERROR_SUCCESS;
  if(argc < 2) return syntax();
  else if(argc == 2) ret = execute_mul(0, argv[1]);
  else if(argc > 3) ret = ERROR_BAD_COMMAND;
  else if(argv[1][1] != 0) ret = ERROR_BAD_COMMAND;
  else ret = execute(argv[1][0], argv[2]);
  if(ret != ERROR_SUCCESS) return display_error(ret);
  //printf("-------------------------------------------------------------------------------\n");
  return(0);
}