UNIT Unit2_functions; INTERFACE USES Classes, Dialogs, SysUtils, StrUtils, Math, SQLiteTable3, Unit3_data, Unit4_Exporters; TYPE TExportSet=SET OF (DO_dat,DO_raw,DO_convert,DO_toone); FUNCTION GetFilesList(ext:String; pattern:String; NoEmptyFiles:Boolean):TStringList; FUNCTION GetExtensionsList:TStringList; FUNCTION Decode_Int(buffer:Tdata):LongWord; FUNCTION Encode_Int(input:LongWord):Tdata; FUNCTION Decode_Float(buffer:Tdata):Single; FUNCTION Encode_Float(input:Single):Tdata; FUNCTION LoadDatInfos(filename:String):Boolean; PROCEDURE OpenDatabase(FileName:String); FUNCTION LoadDatFile(fileid:LongWord):Tdata; PROCEDURE UpdateDatFile(fileid:LongWord; data:Tdata); FUNCTION LoadDatFilePart(fileid,offset,size:LongWord; target:Pointer):Boolean; FUNCTION UpdateDatFilePart(fileid,offset,size:LongWord; target:Pointer):Boolean; FUNCTION LoadRawFile(fileid,datlinkoffset,size:LongWord; target:Pointer):Boolean; FUNCTION UpdateRawFile(fileid,datlinkoffset,size:LongWord; target:Pointer):Boolean; FUNCTION ExportFile(fileid:LongWord; filename:String; settings:TExportSet; path:String):Integer; FUNCTION FormatNumber(value:LongWord; width:Byte; leadingzeros:Char):String; FUNCTION FormatFileSize(size:LongWord):String; FUNCTION CreateHexString(data:Tdata; HexOnly:Boolean):String; FUNCTION GetWinFileName(name:String):String; FUNCTION GetExtractPath:String; IMPLEMENTATION TYPE TValueSwitcher=Record CASE IsFloat: Boolean OF True: (ValueFloat:Single); False: (ValueInt:LongWord); END; FUNCTION Decode_Int(buffer:Tdata):LongWord; BEGIN Result:=buffer[0]+buffer[1]*256+buffer[2]*256*256+buffer[3]*256*256*256; END; FUNCTION Encode_Int(input:LongWord):Tdata; BEGIN Result[0]:=input MOD 256; input:=input DIV 256; Result[1]:=input MOD 256; input:=input DIV 256; Result[2]:=input MOD 256; input:=input DIV 256; Result[3]:=input MOD 256; END; FUNCTION Decode_Float(buffer:Tdata):Single; VAR _valueswitcher:TValueSwitcher; BEGIN _valueswitcher.ValueInt:=Decode_Int(buffer); Result:=_valueswitcher.ValueFloat; END; FUNCTION Encode_Float(input:Single):Tdata; VAR _valueswitcher:TValueSwitcher; BEGIN _valueswitcher.ValueFloat:=input; Result:=Encode_Int(_valueswitcher.ValueInt); END; FUNCTION GetFilesList(ext:String; pattern:String; NoEmptyFiles:Boolean):TStringList; VAR i:LongWord; where:String; Tbl:TSQLiteTable; BEGIN SetLength(Result,0); IF opened_state=opened_dat THEN BEGIN FOR i:=0 TO dat_header.Files-1 DO BEGIN IF ( (Length(ext)=0) OR (dat_files[i].Extension=ext) ) AND ( (Length(pattern)=0) OR (Pos(pattern,dat_files[i].Name)>0) ) THEN BEGIN IF NoEmptyFiles THEN BEGIN IF (dat_files[i].FileType AND $02)=0 THEN BEGIN SetLength(Result,Length(Result)+1); Result[High(Result)]:=dat_files[i].FileName; END; END ELSE BEGIN SetLength(Result,Length(Result)+1); Result[High(Result)]:=dat_files[i].FileName; END; END; END; END ELSE BEGIN where:=''; IF Length(ext)>0 THEN BEGIN IF Length(where)>0 THEN where:=where+' AND '; where:=where+'(extension="'+ext+'")'; END; IF Length(pattern)>0 THEN BEGIN IF Length(where)>0 THEN where:=where+' AND '; where:=where+'(name LIKE "%'+pattern+'%")'; END; IF NoEmptyFiles THEN BEGIN IF Length(where)>0 THEN where:=where+' AND '; where:=where+'((contenttype & 2)=0)'; END; IF Length(where)>0 THEN where:=' WHERE '+where; Tbl:=DB.GetTable('SELECT id,name,extension FROM datfiles'+where+' ORDER BY id ASC;'); IF Tbl.Count>0 THEN BEGIN SetLength(Result,Tbl.Count); i:=0; REPEAT Result[i]:=FormatNumber(Tbl.FieldAsInteger('id'),5,'0')+'-'+Tbl.FieldAsString('name')+'.'+Tbl.FieldAsString('extension'); Inc(i); Tbl.Next; UNTIL Tbl.EOF; END; END; END; FUNCTION GetExtensionsList:TStringList; VAR i:LongWord; Tbl:TSQLiteTable; BEGIN SetLength(Result,0); IF opened_state=opened_dat THEN BEGIN FOR i:=0 TO dat_header.Extensions-1 DO BEGIN SetLength(Result,Length(Result)+1); WITH dat_extensionsmap[i] DO BEGIN Result[High(Result)]:=Extension[3]+Extension[2]+Extension[1]+Extension[0]+' ('+IntToStr(ExtCount)+')'; END; END; END ELSE BEGIN Tbl:=DB.GetTable('SELECT extension,count(extension) AS x FROM datfiles GROUP BY extension ORDER BY extension ASC;'); IF Tbl.Count>0 THEN BEGIN SetLength(Result,Tbl.Count); i:=0; REPEAT Result[i]:=Tbl.FieldAsString('extension')+' ('+IntToStr(Tbl.FieldAsInteger('x'))+')'; Inc(i); Tbl.Next; UNTIL Tbl.EOF; END; END; END; FUNCTION LoadDatInfos(filename:String):Boolean; VAR i:LongWord; dat_file:TFileStream; BEGIN Result:=True; opened_state:=opened_dat; dat_filename:=filename; raw_filename:=MidStr(filename,1,Length(filename)-3)+'raw'; dat_file:=TFileStream.Create(filename, fmOpenRead); dat_file.Read(dat_header,SizeOf(dat_header)); FOR i:=0 TO High(dat_header.Ident) DO IF dat_header.Ident[i]<>header_ident1[i] THEN BEGIN Result:=False; Exit; END; SetLength(dat_filesmap,dat_header.Files); SetLength(dat_files,dat_header.Files); FOR i:=0 TO dat_header.Files-1 DO dat_file.Read(dat_filesmap[i],SizeOf(dat_filesmap[i])); FOR i:=0 TO dat_header.Files-1 DO BEGIN dat_files[i].Extension:=dat_filesmap[i].Extension; dat_files[i].Extension:=ReverseString(dat_files[i].Extension); dat_files[i].Size:=dat_filesmap[i].FileSize; dat_files[i].FileType:=dat_filesmap[i].FileType; dat_files[i].DatAddr:=dat_filesmap[i].DataAddr-8+dat_header.DataAddr; IF (dat_filesmap[i].FileType AND $01)=0 THEN BEGIN dat_file.Seek(dat_filesmap[i].NameAddr+dat_header.NamesAddr,soFromBeginning); SetLength(dat_files[i].Name,100); dat_file.Read(dat_files[i].Name[1],100); dat_files[i].Name:=MidStr(dat_files[i].Name,1+4,Pos(#0,dat_files[i].Name)-1-4); END ELSE BEGIN dat_files[i].Name:=''; END; dat_files[i].FileName:=FormatNumber(i,5,'0')+'-'+dat_files[i].Name+'.'+dat_files[i].Extension; END; dat_file.Seek($40+dat_header.Files*$14,soFromBeginning); SetLength(dat_namedfilesmap,dat_header.NamedFiles); FOR i:=0 TO dat_header.NamedFiles-1 DO dat_file.Read(dat_namedfilesmap[i],SizeOf(dat_namedfilesmap[i])); dat_file.Seek($40+dat_header.Files*$14+dat_header.NamedFiles*$8,soFromBeginning); SetLength(dat_extensionsmap,dat_header.Extensions); FOR i:=0 TO dat_header.Extensions-1 DO dat_file.Read(dat_extensionsmap[i],SizeOf(dat_extensionsmap[i])); dat_file.Free; END; FUNCTION LoadDatFile(fileid:LongWord):Tdata; VAR dat_file:TFileStream; Tbl:TSQLiteTable; mem:TMemoryStream; BEGIN IF opened_state=opened_dat THEN BEGIN dat_file:=TFileStream.Create(dat_filename, fmOpenRead); dat_file.Seek(dat_files[fileid].DatAddr,soFromBeginning); SetLength(Result,dat_files[fileid].Size); dat_file.Read(Result[0],dat_files[fileid].Size); dat_file.Free; END ELSE BEGIN Tbl:=DB.GetTable('SELECT data FROM datfiles WHERE id='+IntToStr(fileid)+';'); IF Tbl.Count>0 THEN BEGIN mem:=Tbl.FieldAsBlob('data'); SetLength(Result,mem.Size); mem.Seek(0,soFromBeginning); mem.Read(Result[0],mem.Size); mem.Free; END; END; END; PROCEDURE UpdateDatFile(fileid:LongWord; data:Tdata); VAR dat_file:TFileStream; Tbl:TSQLiteTable; BEGIN IF opened_state=opened_dat THEN BEGIN dat_file:=TFileStream.Create(dat_filename, fmOpenReadWrite); dat_file.Seek(dat_files[fileid].DatAddr,soFromBeginning); dat_file.Write(data[0],Length(data)); dat_file.Free; END ELSE BEGIN END; END; FUNCTION LoadDatFilePart(fileid,offset,size:LongWord; target:Pointer):Boolean; VAR dat_file:TFileStream; Tbl:TSQLiteTable; mem:TMemoryStream; BEGIN IF opened_state=opened_dat THEN BEGIN dat_file:=TFileStream.Create(dat_filename, fmOpenRead); Result:=True; dat_file.Seek(dat_files[fileid].DatAddr+offset,soFromBeginning); dat_file.Read(target^,size); dat_file.Free; END ELSE BEGIN Tbl:=DB.GetTable('SELECT data FROM datfiles WHERE id='+IntToStr(fileid)+';'); IF Tbl.Count>0 THEN BEGIN Result:=True; mem:=Tbl.FieldAsBlob('data'); mem.Seek(offset,soFromBeginning); mem.Read(target^,size); mem.Free; END; END; END; FUNCTION UpdateDatFilePart(fileid,offset,size:LongWord; target:Pointer):Boolean; VAR dat_file:TFileStream; Tbl:TSQLiteTable; BEGIN IF opened_state=opened_dat THEN BEGIN dat_file:=TFileStream.Create(dat_filename, fmOpenReadWrite); Result:=True; dat_file.Seek(dat_files[fileid].DatAddr+offset,soFromBeginning); dat_file.Write(target^,size); dat_file.Free; END ELSE BEGIN END; END; FUNCTION LoadRawFile(fileid,datlinkoffset,size:LongWord; target:Pointer):Boolean; VAR filestream:TFileStream; raw_addr:LongWord; Tbl:TSQLiteTable; mem:TMemoryStream; BEGIN IF opened_state=opened_dat THEN BEGIN Result:=True; LoadDatFilePart(fileid,datlinkoffset,4,@raw_addr); filestream:=TFileStream.Create(AnsiReplaceStr(dat_filename,'.dat','.raw'),fmOpenRead); filestream.Seek(raw_addr,soFromBeginning); filestream.Read(target^,size); filestream.Free; END ELSE BEGIN Tbl:=DB.GetTable('SELECT data FROM rawmap WHERE (src_id='+IntToStr(fileid)+') AND (src_link_offset='+IntToStr(datlinkoffset)+');'); IF Tbl.Count>0 THEN BEGIN Result:=True; mem:=Tbl.FieldAsBlob('data'); mem.Seek(0,soFromBeginning); mem.Read(target^,size); mem.Free; END; END; END; FUNCTION UpdateRawFile(fileid,datlinkoffset,size:LongWord; target:Pointer):Boolean; VAR filestream:TFileStream; raw_addr:LongWord; BEGIN IF opened_state=opened_dat THEN BEGIN Result:=True; LoadDatFilePart(fileid,datlinkoffset,4,@raw_addr); filestream:=TFileStream.Create(AnsiReplaceStr(dat_filename,'.dat','.raw'),fmOpenReadWrite); filestream.Seek(raw_addr,soFromBeginning); filestream.Write(target^,size); filestream.Free; END ELSE BEGIN END; END; FUNCTION FormatNumber(value:LongWord; width:Byte; leadingzeros:Char):String; BEGIN Result:=AnsiReplaceStr(Format('%'+IntToStr(width)+'u',[value]),' ',leadingzeros); END; FUNCTION FormatFileSize(size:LongWord):String; BEGIN IF size>=1000*1024*1024 THEN BEGIN Result:=FloatToStrF(size/1024/1024/1024,ffFixed,5,1)+' GB'; END ELSE BEGIN IF size>=1000*1024 THEN BEGIN Result:=FloatToStrF(size/1024/1024,ffFixed,5,1)+' MB'; END ELSE BEGIN IF size>=1000 THEN BEGIN Result:=FloatToStrF(size/1024,ffFixed,5,1)+' KB'; END ELSE BEGIN Result:=IntToStr(size)+' B'; END; END; END; END; FUNCTION CreateHexString(data:Tdata; HexOnly:Boolean):String; VAR string_build,ascii_version:String; i:LongWord; BEGIN string_build:=''; ascii_version:=''; FOR i:=0 TO High(data) DO BEGIN IF NOT HexOnly THEN IF (i MOD 16)=0 THEN string_build:=string_build+'0x'+IntToHex(i,6)+' '; string_build:=string_build+IntToHex(data[i],2); IF NOT HexOnly THEN BEGIN IF data[i]>=32 THEN ascii_version:=ascii_version+Chr(data[i]) ELSE ascii_version:=ascii_version+'.'; IF ((i+1) MOD 2)=0 THEN string_build:=string_build+#32; IF ((i+1) MOD 16)=0 THEN BEGIN string_build:=string_build+#32+ascii_version+CrLf; ascii_version:=''; END; END; END; Result:=string_build; END; FUNCTION ExportFile(fileid:LongWord; filename:String; settings:TExportSet; path:String):Integer; VAR i:Byte; extension:String; BEGIN Result:=export_noerror; extension:=RightStr(filename,4); IF DO_toone IN settings THEN BEGIN ExportDatFile(fileid,path+'\'+GetWinFileName(filename)); END ELSE BEGIN IF DO_dat IN settings THEN ExportDatFile(fileid,path+'\'+GetWinFileName(filename)); IF DO_raw IN settings THEN BEGIN FOR i:=1 TO Length(ExportHandlers)+1 DO BEGIN IF i<=Length(ExportHandlers) THEN BEGIN IF ExportHandlers[i].Ext=extension THEN BEGIN IF ExportHandlers[i].needed THEN BEGIN CASE ExportHandlers[i].Handler(fileid,path+'\'+GetWinFileName(filename),(DO_convert IN settings)) OF 0: Result:=0; ELSE Result:=export_handlererror; END; END; Break; END; END ELSE BEGIN Result:=export_nohandler; END; END; END; END; END; FUNCTION GetWinFileName(name:String):String; BEGIN Result:=name; Result:=AnsiReplaceStr(Result,'\','__'); Result:=AnsiReplaceStr(Result,'/','__'); Result:=AnsiReplaceStr(Result,'>','__'); Result:=AnsiReplaceStr(Result,'<','__'); END; FUNCTION GetExtractPath:String; BEGIN Result:=ExtractFilePath(dat_filename)+'\extracted_'+ExtractFileName(dat_filename); END; PROCEDURE OpenDatabase(FileName:String); VAR i:Byte; data:Tdata; temps:String; Tbl: TSQLiteTable; BEGIN IF NOT FileExists(FileName) THEN BEGIN ShowMessage('File doesn''t exist!!!'); Exit; END; DB:=TSQLiteDatabase.Create(FileName); Tbl:=DB.GetTable('SELECT name,value FROM globals ORDER BY name ASC'); REPEAT IF Tbl.FieldAsString('name')='dbversion' THEN BEGIN IF Tbl.FieldAsString('value')<>DBversion THEN BEGIN ShowMessage('Database-file '+CrLf+'"'+FileName+'"'+CrLf+'has wrong version. (Required: '+DBversion+'; found: '+Tbl.FieldAsString('value')+')'); Exit; END; END; IF Tbl.FieldAsString('name')='lvl' THEN BEGIN database_level:=StrToInt(Tbl.FieldAsString('value')); END; IF Tbl.FieldAsString('name')='ident' THEN BEGIN temps:=Tbl.FieldAsString('value'); FOR i:=0 TO High(database_ident) DO BEGIN CASE temps[(i*2)+1+0] OF '0'..'9': database_ident[i]:=Ord(temps[(i*2)+1+0])-48; 'A'..'F': database_ident[i]:=Ord(temps[(i*2)+1+0])-55; END; database_ident[i]:=database_ident[i]*16; CASE temps[(i*2)+1+1] OF '0'..'9': database_ident[i]:=database_ident[i]+Ord(temps[(i*2)+1+0])-48; 'A'..'F': database_ident[i]:=database_ident[i]+Ord(temps[(i*2)+1+0])-55; END; END; END; Tbl.Next; UNTIL Tbl.EOF; Tbl.Free; opened_state:=opened_db; END; END.