...use an Exe Internal Virtual File System @ RunTime?

Author: Cybergen

Category: Files

{*********************************************************************
 This Sourcecode is Freeware i.e Credit-Ware:
 you should say e.g. "Thanks to Cybergen"
 if you use it in your software.
 At least, it would be  ^^ nice.

 Cybergen <nope2k@web.de>
*********************************************************************}

{
Reference:

 bool : csi_fat_available
 bool : csi_fat_get_file_list(files:tstringlist)
 cardinal : cis_load_file(fn:string;p:pointer)
 bool : cis_save_file(fn:string)
 bool : cis_delete_file(fn:string)
 bool : cis_file_exists(fn:string)

CIS-FAT - Code: [Cybergen Internal Small - File Allocation Table]
}

(* CSI-FAT - START *)

function RunProg(Cmd, WorkDir: string): string;
var
  
tsi: TStartupInfo;
  tpi: TProcessInformation;
  nRead: DWORD;
  aBuf: array[0..101] of Char;
  sa: TSecurityAttributes;
  hOutputReadTmp, hOutputRead, hOutputWrite, hInputWriteTmp, hInputRead,
  hInputWrite, hErrorWrite: THandle;
  FOutput: string;
begin
  
FOutput := '';

  sa.nLength        := SizeOf(TSecurityAttributes);
  sa.lpSecurityDescriptor := nil;
  sa.bInheritHandle := True;

  CreatePipe(hOutputReadTmp, hOutputWrite, @sa, 0);
  DuplicateHandle(GetCurrentProcess(), hOutputWrite, GetCurrentProcess(),
    @hErrorWrite, 0, True, DUPLICATE_SAME_ACCESS);
  CreatePipe(hInputRead, hInputWriteTmp, @sa, 0);

  // Create new output read handle and the input write handle. Set
  // the inheritance properties to FALSE. Otherwise, the child inherits
  // the these handles; resulting in non-closeable handles to the pipes
  // being created.
  
DuplicateHandle(GetCurrentProcess(), hOutputReadTmp, GetCurrentProcess(),
    @hOutputRead, 0, False, DUPLICATE_SAME_ACCESS);
  DuplicateHandle(GetCurrentProcess(), hInputWriteTmp, GetCurrentProcess(),
    @hInputWrite, 0, False, DUPLICATE_SAME_ACCESS);
  CloseHandle(hOutputReadTmp);
  CloseHandle(hInputWriteTmp);

  FillChar(tsi, SizeOf(TStartupInfo), 0);
  tsi.cb         := SizeOf(TStartupInfo);
  tsi.dwFlags    := STARTF_USESTDHANDLES or STARTF_USESHOWWINDOW;
  tsi.hStdInput  := hInputRead;
  tsi.hStdOutput := hOutputWrite;
  tsi.hStdError  := hErrorWrite;

  CreateProcess(nil, PChar(Cmd), @sa, @sa, True, 0, nil, PChar(WorkDir),
    tsi, tpi);
  CloseHandle(hOutputWrite);
  CloseHandle(hInputRead);
  CloseHandle(hErrorWrite);
  Application.ProcessMessages;

  repeat
    if 
(not ReadFile(hOutputRead, aBuf, 16, nRead, nil)) or (nRead = 0) then
    begin
      if 
GetLastError = ERROR_BROKEN_PIPE then Break
      else 
        
MessageDlg('Pipe read error, could not execute file', mtError, [mbOK], 0);
    end;
    aBuf[nRead] := #0;
    FOutput     := FOutput + PChar(@aBuf[0]);
    Application.ProcessMessages;
  until False;

  Result := FOutput;
  //GetExitCodeProcess(tpi.hProcess, nRead) = True;
end;

type 
  
PImageDosHeader = ^TImageDosHeader;
  TImageDosHeader = packed record
    
e_magic: Word;
    e_ignore: packed array[0..28] of Word;
    _lfanew: Longint;
  end;

function GetExeSize: Cardinal;
var 
  
p: PChar; 
  i, NumSections: Integer;
begin
  
Result := 0; 
  p      := Pointer(hinstance);
  Inc(p, PImageDosHeader(p)._lfanew + SizeOf(DWORD));
  NumSections := PImageFileHeader(p).NumberOfSections;
  Inc(p, SizeOf(TImageFileHeader) + SizeOf(TImageOptionalHeader));
  for i := 1 to NumSections do 
  begin 
    with 
PImageSectionHeader(p)^ do
      if 
PointerToRawData + SizeOfRawData > Result then
        
Result := PointerToRawData + SizeOfRawData;
    Inc(p, SizeOf(TImageSectionHeader)); 
  end;
end;

function csi_fat_available: Boolean;
var 
  
f: file;
  head: Word;
  nr: Integer;
begin
  
Result   := False; 
  filemode := 0; 
  assignfile(f, ParamStr(0));
  reset(f, 1);
  head := 0; 
  if filesize(f) = getexesize then 
  begin 
    
closefile(f); 
    Exit; 
  end;
  seek(f, getexesize);
  blockread(f, head, 2,nr);
  if (head = $12FE) and (nr = 2) then Result := True;
  closefile(f); 
  filemode := 2;
end;

function csi_fat_get_file_list(var files: TStringList): Boolean;
type 
  
tfileentry = record
    
FileName: string[255];
    filesize: Cardinal;
  end;
var 
  
f: file;
  i, num, head: Word;
  nr: Integer;
  tfe: tfileentry;
begin
  
Result   := False;
  filemode := 0;
  assignfile(f, ParamStr(0));
  reset(f, 1);
  seek(f, getexesize);
  blockread(f, head, 2,nr);
  if not ((head = $12FE) and (nr = 2)) then 
  begin 
    
Result := False;
    closefile(f);
    Exit;
  end;
  blockread(f, num, 2,nr);
  if (nr <> 2) then 
  begin 
    
Result := False;
    closefile(f);
    Exit;
  end;
  for i := 1 to num do
  begin
    
blockread(f, tfe, SizeOf(tfe), nr);
    if nr <> SizeOf(tfe) then 
    begin 
      
Result := False;
      closefile(f);
      Exit; 
    end;
    files.Add(tfe.FileName);
  end;
  closefile(f); 
  filemode := 2;
  Result   := True;
end;

function cis_load_file(fn: stringvar p: Pointer): Cardinal;
type 
  
tfileentry = record
    
FileName: string[255];
    filesize: Cardinal;
  end;
var 
  
f: file;
  i, num, head: Word;
  nr: Longint;
  tfe: tfileentry;
  fofs: Cardinal;
begin
  
Result   := 0;
  filemode := 0;
  assignfile(f, ParamStr(0));
  reset(f, 1);
  fofs := getexesize;
  seek(f, fofs);
  blockread(f, head, 2,nr); 
  Inc(fofs, 2);
  if not ((head = $12FE) and (nr = 2)) then 
  begin 
    
Result := 0;
    closefile(f);
    Exit;
  end;
  blockread(f, num, 2,nr); 
  Inc(fofs, 2);
  if (nr <> 2) then 
  begin 
    
Result := 0;
    closefile(f);
    Exit;
  end;
  for i := 1 to num do
  begin
    
blockread(f, tfe, SizeOf(tfe), nr);
    Inc(fofs, SizeOf(tfe));
    if nr <> SizeOf(tfe) then 
    begin 
      
Result := 0;
      closefile(f);
      Exit; 
    end;
    if (lowercase(tfe.FileName) = lowercase(fn)) then
    begin
      
seek(f, fofs);
      getmem(p, tfe.filesize);
      blockread(f, p^, tfe.filesize, nr);
      if (nr <> tfe.filesize) then
      begin
        
ShowMessage('Unable to Load whole file');
        freemem(p, tfe.filesize);
        Result   := tfe.filesize;
        filemode := 2;
        Exit;
      end;
      Result := tfe.filesize;
      closefile(f);
      ShowMessage('Loaded');
      filemode := 2;
      Exit;
    end;
    Inc(fofs, tfe.filesize);
  end;
  closefile(f);
  // file nicht im CIS
  
ShowMessage('File not in CIS loading Orig. Destination');
  assignfile(f, fn);
  reset(f, 1);
  getmem(p, tfe.filesize);
  blockread(f, p^, filesize(f));
  closefile(f);
  filemode := 2;
  Result   := 0;
end;

function cis_file_exists(fn: string): Boolean;
var 
  
files: TStringList;
  i: Word;
begin
  
Result := False;
  files  := TStringList.Create;
  csi_fat_get_file_list(files);
  for i := 1 to files.Count do
    if 
i <= files.Count then
      if 
lowercase(files[i - 1]) = lowercase(fn) then Result := True;
  files.Free;
end;

procedure FileCopy(const sourcefilename, targetfilename: string);
var 
  
S, T: TFileStream;
begin
  
filemode := 2;
  S        := TFileStream.Create(sourcefilename, fmOpenRead);
  try
    
T := TFileStream.Create(targetfilename, fmOpenWrite or fmCreate);
    try 
      
T.CopyFrom(S, S.Size); 
    finally 
      
T.Free; 
    end;
  finally 
    
S.Free; 
  end;
end;

function randname: string;
var 
  
i: Integer;
  s: string;
begin
  
Randomize;
  s := '';
  for i := 1 to 20 do s := s + chr(Ord('a') + Random(26));
  Result := s;
end;

procedure _filecopy(von, nach: string);
var 
  
f: file;
  c, cmd: string;
begin
  
filemode := 2; 
  ShowMessage(von + ' -> ' + nach); 
  cmd := 'cmd';
  if fileexists('cmd.exe') then cmd := 'cmd';
  if fileexists('c:\command.com') then cmd := 'command.com';
  c := 'ren ' + nach + ' ' + randname;  
  runprog(cmd + ' /c ' + c, GetCurrentDir);
  assignfile(f, von);  
  rename(f, nach);
end;

function cis_delete_file(fn: string): Boolean;
type 
  
tfileentry = record
    
FileName: string[255];
    filesize: Cardinal;
  end;
var 
  
f, o: file;
  nrr, nr: Integer;
  exes: Longint;
  j, i, num, w: Word;
  tfe: tfileentry;
  tfel: array[1..$ff] of tfileentry;
  p: Pointer;
begin
  if not 
cis_file_exists(fn) then
  begin
    
Result := False;
    Exit;
  end;
  assignfile(f, ParamStr(0)); 
  reset(f, 1);
  assignfile(o, ParamStr(0) + '.tmp'); 
  rewrite(o, 1);
  exes := getexesize;
  // nur die exe kopieren
  
getmem(p, exes);
  blockread(f, p^, exes);
  blockwrite(o, p^, exes);
  freemem(p, exes);
  blockread(f, w, 2);
  blockread(f, num, 2);
  Dec(num);
  // cis-header schreiben
  
w := $12FE;
  blockwrite(o, w, 2);
  blockwrite(o, num, 2);
  // jetzt alle files außer "fn" kopieren
  // aber erst die FAT
  
fillchar(tfel, SizeOf(tfel), 0);
  for i := 1 to num + 1 do
  begin
    
blockread(f, tfe, SizeOf(tfe));
    move(tfe, tfel[i], SizeOf(tfe));
    if lowercase(tfe.FileName) <> lowercase(fn) then blockwrite(o, tfe, SizeOf(tfe));
  end;
  // jetzt noch die file daten einkopieren
  
for i := 1 to num + 1 do
  begin
    
getmem(p, tfel[i].filesize);
    blockread(f, p^, tfel[i].filesize);
    if lowercase(tfe.FileName) <> lowercase(fn) then // copy block
      
blockwrite(o, p^, tfel[i].filesize);
    freemem(p, tfel[i].filesize);
  end;
  closefile(f);
  closefile(o);
  _filecopy(ParamStr(0) + '.tmp', ParamStr(0));
end;

function cis_append_file(fn: string): Boolean;
type 
  
tfileentry = record
    
FileName: string[255];
    filesize: Cardinal;
  end;
var 
  
f, o, s: file;
  exes: Longint;
  p: Pointer;
  i, w, num: Word;
  tfe: tfileentry;
  fs: Cardinal;
  nwr: Cardinal;
begin
  
assignfile(f, ParamStr(0)); 
  reset(f, 1);
  assignfile(o, ParamStr(0) + '.tmp'); 
  rewrite(o, 1);
  exes := getexesize;
  if not csi_fat_available then
  begin
    
// create cis
    
getmem(p, exes);
    blockread(f, p^, exes);
    blockwrite(o, p^, exes);
    freemem(p, exes);
    // create fat-header
    
w := $12FE;
    blockwrite(o, w, 2);
    num := 1;
    blockwrite(o, num, 2);
    tfe.FileName := fn;
    // copy file
    
assignfile(s, fn);
    reset(s, 1);
    tfe.filesize := filesize(s);
    getmem(p, filesize(s));
    blockwrite(o, tfe, SizeOf(tfe));
    blockread(s, p^, filesize(s));
    blockwrite(o, p^, filesize(s));
    freemem(p, filesize(s));
    closefile(s);
    closefile(f);
    closefile(o);
    _filecopy(ParamStr(0) + '.tmp', ParamStr(0));
    Result := True;
    Exit;
  end;
  // nur die exe kopieren
  
getmem(p, exes);
  blockread(f, p^, exes);
  blockwrite(o, p^, exes);
  freemem(p, exes);
  blockread(f, w, 2);
  blockread(f, num, 2);
  Inc(num);
  // cis-header schreiben
  
w := $12FE;
  blockwrite(o, w, 2);
  blockwrite(o, num, 2);
  // copy all file entrys
  
for i := 1 to num - 1 do
  begin
    
blockread(f, tfe, SizeOf(tfe));
    blockwrite(o, tfe, SizeOf(tfe));
  end;
  tfe.FileName := fn;
  assignfile(s, fn);
  reset(s, 1);
  tfe.filesize := filesize(s);
  blockwrite(o, tfe, SizeOf(tfe));
  fs := filesize(f);
  getmem(p, fs);
  blockread(f, p^, fs, nwr);
  blockwrite(o, p^, nwr);
  freemem(p, fs);
  getmem(p, fs);
  blockread(f, p^, fs);
  blockwrite(o, p^, fs);
  freemem(p, fs);
  closefile(f);
  closefile(o);
  _filecopy(ParamStr(0) + '.tmp', ParamStr(0));
  Result := True;
end;

function cis_save_file(fn: string): Boolean;
begin
  if not 
cis_file_exists(fn) then cis_append_file(fn)
  else 
  begin
    
cis_delete_file(fn);
    cis_save_file(fn);
  end;
end;

(* CSI-FAT - STOP *)

// -------------- Howto Use: -----------------------------------------

// ... some code ...
// if file is not in the VFS load it into ..
if not cis_file_exists('e:\xm\shold.xm') then  cis_save_file('e:\xm\shold.xm');
// Load File
cis_load_file('e:\xm\shold.xm', muke);
// ... some code ...
play(muke);


{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
What it does and how it does:

The CIS-FAT-System binds File of any Kind at the
End of an Executable (EXE-Binder) but it also
have a nice File-Table and you can "Dynamically"
save, delete & load Files.

It is possible for example to Code the Binary
with all single Files external ...
After a Little Check you can modifiy your code that way
that the CIS-FAT on First Start automatically load all nesseary
Files into the Binary-FS.

So can add Music, Movies, Images ... all in one Big-File.

The best is that you can use Static-Filenames!
For example:

// This Line loads an External File into the Binary if its not already in it.
if not cis_file_exists('e:\xm\shold.xm') then cis_save_file('e:\xm\shold.xm');

// This Line access the File in the Binary, if its not in it uses the
// External Version of the File.
cis_load_file('e:\xm\shold.xm',muke);

So there is no need to change Filenames.

Yours Cybergen.
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}

 

printed from
www.swissdelphicenter.ch
developers knowledge base