
extern "C" {
 int __report_rangecheckfailure = 0;
}

#include <windows.h>

#include "common.inc"
#include "coro2b.inc"
#include "file_api.inc"
#include "coro_fhp2.inc"

#include "crypt.inc"

uint f_dump = 1;

enum{ bufsize=1<<25 };
byte buf[bufsize];

//    goto -0x2d
struct footer {
  byte f_enc;
  uint sign;   // idstring "\xe1\x12\x6f\x5a" # 0x5a6f12e1
  uint vers;
  qword toc_offs;
  qword toc_size;
  byte toc_sha1[20]; // sha1 of unpacked toc

  void Dump( void ) {
    if( f_dump==0 ) return;
    uint i;
    printf( "   f_enc = %i\n", f_enc );
    printf( "   sign  = %08X\n", sign );
    printf( "   vers  = %i\n", vers );
    printf( "toc_offs = %I64X\n", toc_offs );
    printf( "toc_size = %I64X\n", toc_size );
    printf( "toc_size = " );
    for( i=0; i<sizeof(toc_sha1); i++ ) printf( "%02X", toc_sha1[i] );
    printf( "\n\n" );
  }

  uint f_valid( void ) {
    return /*(f_enc==1) &&*/ (sign==0x5a6f12e1) && ((vers==4)||(vers==3));
  }

} hdr;

struct filename {
  uint len; // including zero terminator
  char s[0x10000-sizeof(len)];

  uint read( filehandle0 f ) {
    uint r = 0;
    r = f.read(len);
    if( r==0 ) if( len>sizeof(s) ) r=1; else {
      r = (f.read(s,len)!=len);
    }
    return r;
  }

  void Dump( void ) {
    if( f_dump==0 ) return;
    printf( "%i: \"", len );
    printf( "%s", s );
    printf( "\"\n" );
  }

} nam;

struct fileheader0 {
  qword copy_offs;
  qword csize;
  qword usize;
  uint f_zip;
  byte hash[20]; // of what?

  uint n_chunks;
};

struct fileheader : fileheader0 {
  qword ptr[0x10000][2]; // start/end of each chunk
  byte buf[1<<17];

  byte f_enc;
  uint chunksize; // buffer size to unpacking?

  uint read( filehandle0 f ) {
    uint r = 0, l;
    if( r==0 ) r=f.read( *(fileheader0*)this );
    if( f_zip ) {
      l = ((byte*)&ptr[n_chunks][0])-((byte*)&ptr[0][0]);
      if( r==0 ) r=(f.read((byte*)ptr,l)!=l);
    }
    if( r==0 ) r=f.read(f_enc);
    if( f_zip ) {
      if( r==0 ) r=f.read(chunksize);
    }
    return r;
  }

  uint dumpfile( filehandle0 f, filehandle0 g ) {
    uint i,l1; qword l,pos;
    pos = f.tell();
    for( i=0; i<n_chunks; i++ ) {
      f.seek( ptr[i][0] ); 

      l = ptr[i][1]-ptr[i][0]; if( l>sizeof(buf) ) return 1;
      l1 = AlignUp( (l1=l), 16 );

      if( f.read(buf,l1)!=l1 ) return 2;

aes_crypt( 1, buf, l1 );

      g.writ( buf, l1 );
    }
    f.seek( pos );
    return 0;
  }

  uint updatefile( uint f_DEC, filehandle0 f ) {
    uint i,l1; qword l,pos;
    for( i=0; i<n_chunks; i++ ) {
      f.seek( ptr[i][0] ); 

      l = ptr[i][1]-ptr[i][0]; if( l>sizeof(buf) ) return 1;
      l1 = AlignUp( (l1=l), 16 );

      if( f.read(buf,l1)!=l1 ) return 2;

      aes_crypt( f_DEC, buf, l1 );

      f.seek( ptr[i][0] ); 
      f.writ( buf, l1 );
    }
    return 0;
  }

  void Dump( void ) {
    if( f_dump==0 ) return;
    uint i; qword n;
    printf( "copy_offs=%I64X; ", copy_offs );
    n=csize; if( f_zip ) for( i=0,n=0; i<n_chunks; i++ ) n+=ptr[i][1]-ptr[i][0];
    printf( "csize=%I64i/%I64i; ", csize, n );
    printf( "usize=%I64i; ", usize );
    if( f_zip )
    printf( "chunksize=%i; ", chunksize );
    printf( "f_zip=%i; ", f_zip );
    printf( "f_enc=%i; ", f_enc );
    printf( "n_chunks=%i;\n", n_chunks );
    printf( "hash=" ); for( i=0; i<sizeof(hash); i++ ) printf( "%02X", hash[i] ); printf( "\n" );
    if( f_zip )
    for( i=0; i<n_chunks; i++ ) {
      printf( "%09I64X..%09I64X = %I64i\n", ptr[i][0], ptr[i][1], ptr[i][1]-ptr[i][0] );
    }
    printf( "\n" );
  }
  
} fhdr, fhdr1;

uint n_files;

int main( int argc, char** argv ) {
//  if( argc<4 ) return 1;

  uint i,l, f_DEC = (argv[1][0]=='d'); f_dump=atoi(&argv[1][1]);
//  uint i,l, f_DEC = 0;/*(argv[1][0]=='d'); f_dump=atoi(&argv[1][1]);*/
  qword ii;

  file_open_mode_rw();
  filehandle f(argv[2],0); if( f==0 ) return 2;
  filehandle g;
  filehandle g1;

  if( argc>3 ) strcpy( g_key, argv[3] );

//  filehandle g(argv[3],1); if( g==0 ) return 3;
//  l = f.read( buf, bufsize );
//  l = aes_crypt( f_DEC, buf, l );
//  g.writ( buf, l );

  f.seek( f.size() - sizeof(footer) );

  f.read( hdr );

  hdr.Dump();

  printf( "f_valid = %i; key = <%s>\n", hdr.f_valid(), g_key );
  if( hdr.f_valid()==0 ) return 3;

  byte* toc = new byte[hdr.toc_size]; if( toc==0 ) { printf( "toc alloc error\n" ); return 4; }
  f.seek( hdr.toc_offs );
  if( f.read( toc, hdr.toc_size )!=hdr.toc_size )  { printf( "toc read error\n" ); return 5; }

  if( hdr.f_enc ) {
    g.make("toc_dump");
    if( f_DEC==0 ) g.writ( toc, hdr.toc_size );
    if( hdr.f_enc ) aes_crypt( f_DEC, toc, hdr.toc_size );
    if( f_DEC==1 ) g.writ( toc, hdr.toc_size );
    g.close();
    // write back re-crypted
    f.seek( hdr.toc_offs );
    f.writ( toc, hdr.toc_size ); 
  }

  if( hdr.f_enc ) {
    g.open("toc_dump");
  } else {
    g.open(argv[2]); g.seek( hdr.toc_offs );
  }

  if( nam.read(g) ) { printf( "name read error\n" ); return 6; }
  nam.Dump();

  if( g.read(n_files) ) { printf( "n_files read error\n" ); return 6; }

  printf( "\nn_files = %i\n\n", n_files );

  for( ii=0; ii<n_files; ii++ ) {

    if( f_dump ) printf( "!%09I64X!\n", g.tell() );
    if( !f_dump ) if( ii%256==0 ) printf( "\r%.1lf%%", double(g.tell())/double(hdr.toc_size)*100 );

    if( nam.read(g) ) { printf( "name read error\n" ); return 6; }
    nam.Dump();

    if( fhdr.read(g) ) { printf( "name read error\n" ); return 6; }
    fhdr.Dump();

    if( fhdr.f_enc ) fhdr.updatefile( f_DEC, f );
  }

  if( f_dump ) printf( "!%09I64X!\n", g.tell() );
  if( !f_dump ) printf( "\r%.1lf%%\n\n", double(100) );

//  g1.make( "file_dump" ); fhdr.dumpfile( f, g1 ); g1.close();

  return 0;
}

