//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop

#include "vsuwin.h"
#include "uclwin.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)

AnsiString __fastcall GetNewTempDirVSU (void)
{
  GUID a;
  CoInitialize (NULL);
  CoCreateGuid (&a);
  CoUninitialize ();
  return ("C:\\TEMP\\VMXSTOREUCL.TMP."+GUIDToString(a)+"\\");
}

__int64 __fastcall GetFileSize (AnsiString File)
{
  TFileStream *fs = new TFileStream (File, fmOpenRead);
  __int64 a = fs->Size;
  delete fs;
  return a;
}

AnsiString StreamString (TFileStream *fs)
{
  char c;
  fs->Read(&c,1);
  AnsiString A;
  while (c!='\n'&&c!='\r'&&c!='\0'&&fs->Position!=fs->Size)
  {
    A+=c;
    fs->Read(&c,1);
  }
  return A;
}

void __fastcall CreateStore (AnsiString File, TStrings *Files, TCallbackProc *Proc)
{
  TFileStream *fs = new TFileStream (File,fmCreate|fmOpenRead);
  fs->Position=0;
  TFileStream *f2;
  char *sign = new char [4];
  sign = "VSU";
  sign[3] = '\0';
  fs->Write(sign,3);
  int N = Files -> Count;
  fs->Write(&N,4);
  __int64 sall = 0, sc = 0;
  __int64 *sizes = new __int64 [N];
  for (int i = 0; i < Files -> Count; i++)
  {
    f2 = new TFileStream (Files->Strings[i],fmOpenRead);
    __int64 ii = f2 -> Size;
    fs->Write(&ii,8);
    delete f2;
    sall+=ii;
    sizes[i]=ii;
  }
  for (int i = 0; i < Files -> Count; i++)
  {
    AnsiString str = ExtractFileName(Files->Strings[i])+'\n';
    fs->Write(str.c_str(),str.Length());
  }
  // writing directory structure
  // т.е её не наблюдается (всё в корне)
  for (int i = 0; i < Files -> Count; i++)
  {
    AnsiString str = "\\\n";
    fs->Write(str.c_str(),2);
  }
  // end
  for (int i = 0; i < Files -> Count; i++)
  {
    f2 = new TFileStream (Files->Strings[i],fmOpenRead);
    fs->CopyFrom(f2,f2->Size);
    delete f2;
    sc+=sizes[i];
    if (Proc!=NULL)
      Proc(100*sc/sall);
  }
  delete fs;
  delete [] sign;
  delete [] sizes;
}

void __fastcall StoreExtractFiles (AnsiString Store, TStrings *Files, AnsiString Dir, TCallbackProc *Proc, bool KeepDirStruct = false)
{
  if (!DirectoryExists (Dir))
    CreateDirectory (Dir.c_str(),NULL);
  TFileStream *fs = new TFileStream (Store, fmOpenRead);
  char *sign = new char [4];
  fs->Read(sign,3);
  if (sign[0]!='V'||sign[1]!='S'||sign[2]!='U')
  {
    Application->MessageBoxA("Это не файл VMX Store","Ошибка",MB_OK|MB_ICONERROR);
    delete fs;
    return;
  }
  int N;
  fs->Read(&N,4);
  __int64 *sizes = new __int64 [N];
  __int64 sall = 0, sc = 0;
  for (int i = 0; i < N; i++)
  {
    fs->Read(&sizes[i],8);
    sall+=sizes[i];
  }
  AnsiString *names = new AnsiString [N];
  for (int i = 0; i < N; i++)
    names[i] = StreamString(fs);
  // dir struct
  AnsiString *dirs = new AnsiString [N];
  for (int i = 0; i < N; i++)
  {
    dirs[i] = StreamString(fs);
    if (dirs[i]=="\\")
      dirs[i]="";
    else dirs[i]=dirs[i].SubString(2,dirs[i].Length()-1);
  }
  // end reading
  if (KeepDirStruct)
  {
    for (int i = 0; i < N; i++)
    {
      if (dirs[i]!="")
        CreateDirectory ((Dir+dirs[i]).c_str(),NULL);
    }
  }
  TFileStream *f2;
  for (int i = 0; i < N; i++)
  {
    if (Files->IndexOf(names[i])>-1)
    {
      f2=new TFileStream (Dir+(KeepDirStruct?dirs[i]:AnsiString(""))+names[i],fmCreate|fmOpenWrite);
      f2->Position=0;
      f2->CopyFrom(fs,sizes[i]);
      delete f2;
    }
    else
      fs->Seek (sizes[i],soCurrent);
    sc+=sizes[i];
    if (Proc!=NULL)
      Proc (100*sc/sall);
  }
  delete [] sizes;
  delete fs;
  delete [] names;
  delete [] dirs;
}

void __fastcall StoreAddFiles (AnsiString File, AnsiString ToDir, TStrings *AddFiles, TCallbackProc *Proc)
{
  AnsiString TempDir = GetNewTempDirVSU ();
  CreateDirectory (TempDir.c_str(),NULL);
  TFileStream *fs = new TFileStream (File, fmOpenRead);
  char *sign = new char [4];
  fs->Read(sign,3);
  if (sign[0]!='V'||sign[1]!='S'||sign[2]!='U')
  {
    Application->MessageBoxA("Это не файл VMX Store","Ошибка",MB_OK|MB_ICONERROR);
    delete fs;
    return;
  }
  int N;
  fs->Read(&N,4);
  __int64 *sizes = new __int64 [N];
  __int64 sall = 0, sc = 0;
  for (int i = 0; i < N; i++)
  {
    fs->Read(&sizes[i],8);
    sall+=sizes[i];
  }
  AnsiString *names = new AnsiString [N];
  for (int i = 0; i < N; i++)
    names [i] = StreamString(fs);
  // dir struct
  AnsiString *dirs = new AnsiString [N];
  for (int i = 0; i < N; i++)
    dirs[i] = StreamString(fs);
  // end reading
  TFileStream *f2;
  for (int i = 0; i < N; i++)
  {
    f2=new TFileStream (TempDir+names[i],fmCreate|fmOpenWrite);
    f2->Position=0;
    f2->CopyFrom(fs,sizes[i]);
    delete f2;
    sc+=sizes[i];
    if (Proc!=NULL)
      Proc(100*sc/sall);
  }
  delete [] sizes;
  delete fs;
  TStrings *NewFiles = new TStringList;
  for (int i = 0; i < N; i++)
    NewFiles->Add (TempDir+names[i]);
  NewFiles->AddStrings(AddFiles);
  fs = new TFileStream (File,fmCreate|fmOpenWrite);
  fs->Position=0;
  sign = new char [4];
  sign = "VSU";
  sign[3] = '\0';
  fs->Write(sign,3);
  int N2 = NewFiles->Count;
  fs->Write(&N2,4);
  __int64 tmp;
  sall = 0;
  sc = 0;
  for (int i = 0; i < N2; i++)
  {
    f2=new TFileStream (NewFiles->Strings[i],fmOpenRead);
    tmp = f2->Size;
    fs->Write(&tmp,8);
    delete f2;
    sall+=tmp;
  }
  AnsiString s;
  for (int i = 0; i < N2; i++)
  {
    s = ExtractFileName(NewFiles->Strings[i])+'\n';
    fs->Write(s.c_str(),s.Length());
  }
  // writing dir struct
  for (int i = 0; i < N2; i++)
  {
    if (i < N)
      s = dirs[i]+'\n';
    else s = ToDir+'\n';
    fs->Write(s.c_str(),s.Length());
  }
  // end
  for (int i = 0; i < N2; i++)
  {
    f2=new TFileStream (NewFiles->Strings[i],fmOpenRead);
    tmp=f2->Size;
    fs->CopyFrom(f2,f2->Size);
    delete f2;
    sc+=tmp;
    if (Proc!=NULL)
      Proc(100*sc/sall);
  }
  delete fs;
  for (int i = 0; i < N; i++)
    remove ((TempDir+names[i]).c_str());
  RemoveDirectory (TempDir.c_str());
  delete NewFiles;
  delete [] names;
  delete [] dirs;
}

void __fastcall StoreRemoveFiles (AnsiString File, TStrings* RemFiles, TCallbackProc *Proc)
{
  AnsiString TempDir = GetNewTempDirVSU ();
  CreateDirectory (TempDir.c_str(),NULL);
  TFileStream *fs = new TFileStream (File, fmOpenRead);
  char *sign = new char [4];
  fs->Read(sign,3);
  if (sign[0]!='V'||sign[1]!='S'||sign[2]!='U')
  {
    Application->MessageBoxA("Это не файл VMX Store","Ошибка",MB_OK|MB_ICONERROR);
    delete fs;
    return;
  }
  int N;
  fs->Read(&N,4);
  __int64 *sizes = new __int64 [N];
  __int64 sall = 0, sc = 0;
  for (int i = 0; i < N; i++)
  {
    fs->Read(&sizes[i],8);
    sall+=sizes[i];
  }
  AnsiString *names = new AnsiString [N];
  for (int i = 0; i < N; i++)
    names[i]=StreamString(fs);
  // dir struct
  AnsiString *dirs = new AnsiString [N];
  for (int i = 0; i < N; i++)
    dirs[i] = StreamString(fs);
  // end reading
  TFileStream *f2;
  for (int i = 0; i < N; i++)
  {
    f2=new TFileStream (TempDir+names[i],fmCreate|fmOpenWrite);
    f2->Position=0;
    f2->CopyFrom(fs,sizes[i]);
    delete f2;
    sc+=sizes[i];
    if (Proc!=NULL)
      Proc(100*sc/sall);
  }
  delete [] sizes;
  delete fs;
  TStrings *NewFiles = new TStringList;
  TStrings *NewDirs = new TStringList;
  for (int i = 0; i < N; i++)
    if (RemFiles->IndexOf(names[i])==-1)
    {
      NewFiles->Add (TempDir+names[i]);
      NewDirs->Add (dirs[i]);
    }
  fs = new TFileStream (File,fmCreate|fmOpenWrite);
  fs->Position=0;
  sign = new char [4];
  sign = "VSU";
  sign[3] = '\0';
  fs->Write(sign,3);
  int N2 = NewFiles->Count;
  fs->Write(&N2,4);
  __int64 tmp;
  sall = 0;
  sc = 0;
  for (int i = 0; i < N2; i++)
  {
    f2=new TFileStream (NewFiles->Strings[i],fmOpenRead);
    tmp = f2->Size;
    fs->Write(&tmp,8);
    delete f2;
    sall+=tmp;
  }
  AnsiString s;
  for (int i = 0; i < N2; i++)
  {
    s = ExtractFileName(NewFiles->Strings[i])+'\n';
    fs->Write(s.c_str(),s.Length());
  }
  // writing directory structure
  for (int i = 0; i < N2; i++)
  {
    s = NewDirs->Strings[i]+'\n';
    fs->Write(s.c_str(),s.Length());
  }
  // end
  for (int i = 0; i < N2; i++)
  {
    f2=new TFileStream (NewFiles->Strings[i],fmOpenRead);
    tmp=f2->Size;
    fs->CopyFrom(f2,f2->Size);
    delete f2;
    sc+=tmp;
    if (Proc!=NULL)
      Proc(100*sc/sall);
  }
  delete fs;
  for (int i = 0; i < N; i++)
    remove ((TempDir+names[i]).c_str());
  RemoveDirectory (TempDir.c_str());
  delete NewFiles;
  delete [] names;
  delete [] dirs;
  delete NewDirs;
}

// Files is the pointer to the TStringList object
bool __fastcall VSUGetContent (AnsiString File, TStrings *Files, TStrings *Dirs)
{
  TFileStream *fs = new TFileStream (File,fmOpenRead);
  char *sign = new char [4];
  fs->Read(sign,3);
  if (sign[0]!='V'||sign[1]!='S'||sign[2]!='U')
  {
    Application->MessageBoxA("Это не файл VMX Store UCL","Ошибка",MB_OK|MB_ICONERROR);
    delete fs;
    return false;
  }
  int N;
  fs->Read(&N,4);
  __int64 tmp;
  for (int i = 0; i < N; i++)
    fs->Read(&tmp,8);
  for (int i = 0; i < N; i++)
    Files->Add(StreamString(fs));
  for (int i = 0; i < N; i++)
    Dirs->Add(StreamString(fs));
  delete [] sign;
  delete fs;
  return true;
}

void __fastcall VSUExtractFiles (AnsiString File, TStrings *Files, AnsiString Dir, TCallbackProc *Proc, bool KeepDirStruct = false)
{
  TStrings *fs = new TStringList, *ds = new TStringList;
  VSUGetContent (File, fs, ds);
  TStrings *xs = new TStringList;
  for (int i = 0; i < Files->Count; i++)
    if (fs->IndexOf(Files->Strings [i])>-1)
      xs->Add(ds->Strings[fs->IndexOf(Files->Strings [i])]);
  for (int i = 0; i < xs->Count; i++)
  {
    if (xs->Strings[i]=="\\")
      xs->Strings[i]="";
    else xs->Strings[i]=xs->Strings[i].SubString(2,xs->Strings[i].Length()-1);
  }
  StoreExtractFiles (File, Files, Dir, Proc, false);
  for (int i = 0; i < Files->Count; i++)
    rename ((Dir+Files->Strings[i]).c_str(),(Dir+Files->Strings[i]+".tmp").c_str());
  if (KeepDirStruct)
  {
    for (int i = 0; i < xs->Count; i++)
      if (xs->Strings[i]!="")
        CreateDirectory ((Dir+xs->Strings[i]).c_str(),NULL);
  }
  for (int i = 0; i < Files->Count; i++)
    UclDecompressFile (Dir+Files->Strings[i]+".tmp",Dir+(KeepDirStruct?xs->Strings[i]:AnsiString(""))+Files->Strings[i]);
  for (int i = 0; i < Files->Count; i++)
    remove ((Dir+Files->Strings[i]+".tmp").c_str());
  delete fs;
  delete ds;
  delete xs;
}

__int64 cfs = 0;

void __fastcall VSUCreateFile (AnsiString File, TStrings *Files, FeedbackProc *Proc1, TCallbackProc *Proc2)
{
  AnsiString TempDir = GetNewTempDirVSU ();
  CreateDirectory (TempDir.c_str(),NULL);
  StdFeedback = Proc1;
  for (int i = 0; i < Files->Count; i++)
  {
    TFileStream *fs = new TFileStream (Files->Strings[i],fmOpenRead);
    cfs = fs->Size;
    delete fs;
    UclCompressFile (Files->Strings[i],TempDir+ExtractFileName(Files->Strings[i]),0x2b,10);
  }
  TStrings *mFiles = new TStringList;
  for (int i = 0; i < Files->Count; i++)
    mFiles->Add(TempDir+ExtractFileName(Files->Strings[i]));
  CreateStore (File, mFiles, Proc2);
  for (int i = 0; i < Files->Count; i++)
    remove ((TempDir+ExtractFileName(Files->Strings[i])).c_str());
  RemoveDirectory (TempDir.c_str());
  delete mFiles;
}

void __fastcall VSUFileInfo (AnsiString Store, AnsiString File, long& Size, long &CSize)
{
  AnsiString TD = GetNewTempDirVSU ();
  CreateDirectory (TD.c_str(),NULL);
  TStrings *Files = new TStringList;
  Files->Add(File);
  StoreExtractFiles (Store, Files, TD, NULL);
  UclFileInfo (TD+File,Size,CSize);
  delete Files;
  remove ((TD+File).c_str());
  RemoveDirectory (TD.c_str());
}

void __fastcall VSUAddFiles (AnsiString Store, AnsiString ToDir, TStrings *Files, FeedbackProc *Proc1, TCallbackProc *Proc2)
{
  AnsiString TD = GetNewTempDirVSU ();
  CreateDirectory (TD.c_str(),NULL);
  TStrings *IFiles = new TStringList;
  StdFeedback = Proc1;
  for (int i = 0; i < Files->Count; i++)
  {
    TFileStream *fs = new TFileStream (Files->Strings[i],fmOpenRead);
    cfs = fs->Size;
    delete fs;
    UclCompressFile (Files->Strings[i],TD+ExtractFileName(Files->Strings[i]),0x2b,10);
    IFiles->Add(TD+ExtractFileName(Files->Strings[i]));
  }
  StoreAddFiles (Store, ToDir, IFiles, Proc2);
  for (int i = 0; i < IFiles->Count; i++)
    remove (IFiles->Strings[i].c_str());
  RemoveDirectory (TD.c_str());
  delete IFiles;
}

void __fastcall VSUDeleteFiles (AnsiString Store, TStrings *Files, TCallbackProc *Proc2)
{
  StoreRemoveFiles (Store, Files, Proc2);
}
