UNIT Unit2_functions; INTERFACE USES Classes, Dialogs, SysUtils, StrUtils, Math, Unit3_data, ABSDecUtil, ABSMain, DB; 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 HexToLong(hex:String):LongWord; 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 DataToBin(data:Tdata):String; FUNCTION BinToInt(bin:String):Byte; FUNCTION GetFileInfo(fileid:LongWord):TFileInfo; FUNCTION LoadDatInfos(filename:String):Boolean; PROCEDURE OpenDatabase(FileName:String); PROCEDURE CloseDatabase; 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,dat_offset,raw_addr,size:LongWord; loc_sep:Boolean; target:Pointer):Boolean; FUNCTION LoadRawFileByIDOffset(fileid,dat_offset:LongWord; target:Pointer):Boolean; FUNCTION UpdateRawFile(rawinfo:TRawInfo; 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; FUNCTION Explode(_string:String; delimiter:Char):TStringList; IMPLEMENTATION USES Unit4_Exporters, Unit9_data_structures; VAR Database:TABSDatabase; Query:TABSQuery; TYPE TValueSwitcher=Record CASE IsFloat: Boolean OF True: (ValueFloat:Single); False: (ValueInt:LongWord); END; FUNCTION NormalizeHexString(VAR hex:String):Boolean; VAR i:Byte; BEGIN IF hex[1]='$' THEN BEGIN FOR i:=1 TO Length(hex)-1 DO BEGIN hex[i]:=hex[i+1]; END; SetLength(hex, Length(hex)-1); END; IF (hex[1]='0') AND (UpCase(hex[2])='X') THEN BEGIN FOR i:=1 TO Length(hex)-2 DO BEGIN hex[i]:=hex[i+2]; END; SetLength(hex, Length(hex)-2); END; IF Length(hex)=0 THEN Result:=False ELSE Result:=True; END; FUNCTION HexToLong(hex:String):LongWord; VAR i:Byte; BEGIN IF NormalizeHexString(hex) THEN BEGIN hex:=UpperCase(hex); Result:=0; FOR i:=1 TO Length(hex) DO BEGIN Result:=Result SHL 4; CASE hex[i] OF '0'..'9': Result:=Result+Ord(hex[i])-48; 'A'..'F': Result:=Result+Ord(hex[i])-55; ELSE Result:=0; Exit; END; END; END ELSE BEGIN Result:=0; END; 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 SetLength(Result,4); 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; IF IsNAN(Result) THEN Result:=0.0; END; FUNCTION Encode_Float(input:Single):Tdata; VAR _valueswitcher:TValueSwitcher; BEGIN _valueswitcher.ValueFloat:=input; Result:=Encode_Int(_valueswitcher.ValueInt); END; FUNCTION DataToBin(data:Tdata):String; VAR i,j:Byte; singlebyte:Byte; bytepart:String; BEGIN SetLength(bytepart,8); Result:=''; FOR i:=0 TO High(data) DO BEGIN singlebyte:=data[i]; FOR j:=7 DOWNTO 0 DO BEGIN bytepart[j+1]:=Char((singlebyte AND $01)+48); singlebyte:=singlebyte SHR 1; END; Result:=Result+bytepart+' '; END; END; FUNCTION BinToInt(bin:String):Byte; VAR Add: Integer; i: Byte; BEGIN Result:=0; IF Length(bin)<>8 THEN Exit; Add:=1; FOR i:=8 DOWNTO 1 DO BEGIN IF NOT (bin[i] IN ['0','1']) THEN Exit; IF bin[i] = '1' THEN Inc(Result,Add); Add:=Add SHL 1; END; END; FUNCTION GetFileInfo(fileid:LongWord):TFileInfo; BEGIN IF opened_state=opened_dat THEN BEGIN Result:=dat_files[fileid]; END ELSE BEGIN Query.SQL.Text:='SELECT * FROM datfiles WHERE id='+IntToStr(fileid)+' ORDER BY id ASC;'; Query.Open; IF Query.RecordCount=1 THEN BEGIN Query.First; Result.ID:=Query.FieldByName('id').AsInteger; Result.Name:=Query.FieldByName('name').AsString; Result.Extension:=Query.FieldByName('extension').AsString; Result.FileName:=FormatNumber(Result.ID,5,'0')+'-'+Result.Name+'.'+Result.Extension; Result.Size:=Query.FieldByName('size').AsInteger; Result.FileType:=Query.FieldByName('contenttype').AsInteger; Result.DatAddr:=0; Result.opened:=False; END; Query.Close; END; END; FUNCTION GetFilesList(ext:String; pattern:String; NoEmptyFiles:Boolean):TStringList; VAR i:LongWord; where:String; where_ext:String; 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 (Pos(dat_files[i].Extension,ext)>0) ) AND ( (Length(pattern)=0) OR (Pos(UpperCase(pattern),UpperCase(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 '; IF Pos(',',ext)>0 THEN BEGIN i:=1; where_ext:=''; WHILE i0 THEN where_ext:=where_ext+' OR '; where_ext:=where_ext+'(extension="'+MidStr(ext,i,4)+'")'; i:=i+5; END; where:=where+'('+where_ext+')'; END ELSE BEGIN where:=where+'(extension="'+ext+'")'; END; 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)'; END; IF Length(where)>0 THEN where:=' WHERE '+where; Query.SQL.Text:='SELECT id,name,extension FROM datfiles'+where+' ORDER BY id ASC;'; Query.Open; IF Query.RecordCount>0 THEN BEGIN Query.First; SetLength(Result,Query.RecordCount); i:=0; REPEAT Result[i]:=FormatNumber(Query.FieldByName('id').AsInteger,5,'0')+'-'+Query.FieldByName('name').AsString+'.'+Query.FieldByName('extension').AsString; Inc(i); Query.Next; UNTIL Query.EOF; END; Query.Close; END; END; FUNCTION GetExtensionsList:TStringList; VAR i:LongWord; 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 Query.SQL.Text:='SELECT extension,count(extension) AS x FROM datfiles GROUP BY extension ORDER BY extension ASC;'; Query.Open; IF Query.RecordCount>0 THEN BEGIN SetLength(Result,Query.RecordCount); i:=0; REPEAT Result[i]:=Query.FieldByName('extension').AsString+' ('+IntToStr(Query.FieldByName('x').AsInteger)+')'; Inc(i); Query.Next; UNTIL Query.EOF; END; Query.Close; END; END; FUNCTION LoadDatInfos(filename:String):Boolean; VAR i:LongWord; dat_file:TFileStream; header_pc,header_mac:Boolean; 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)); header_pc:=True; header_mac:=True; FOR i:=0 TO High(dat_header.Ident) DO BEGIN IF dat_header.Ident[i]<>header_ident1_pc[i] THEN BEGIN header_pc:=False; END; IF dat_header.Ident[i]<>header_ident1_mac[i] THEN BEGIN header_mac:=False; END; END; IF NOT (header_pc OR header_mac) THEN BEGIN Result:=False; Exit; END ELSE BEGIN IF (header_pc AND NOT header_mac) THEN dat_os_mac:=False ELSE IF (NOT header_pc AND header_mac) THEN dat_os_mac:=True ELSE BEGIN Result:=False; Exit; END; 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; mem:TStream; 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 Query.SQL.Text:='SELECT data FROM datfiles WHERE id='+IntToStr(fileid)+';'; Query.Open; IF Query.RecordCount>0 THEN BEGIN mem:=Query.CreateBlobStream(Query.FieldByName('data'),bmRead); SetLength(Result,mem.Size); mem.Seek(0,soFromBeginning); mem.Read(Result[0],mem.Size); mem.Free; END; Query.Close; END; END; PROCEDURE UpdateDatFile(fileid:LongWord; data:Tdata); VAR dat_file:TFileStream; 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; mem:TStream; BEGIN Result:=False; 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 Query.SQL.Text:='SELECT data FROM datfiles WHERE id='+IntToStr(fileid)+';'; Query.Open; IF Query.RecordCount>0 THEN BEGIN mem:=Query.CreateBlobStream(Query.FieldByName('data'),bmRead); mem.Seek(offset,soFromBeginning); mem.Read(target^,size); mem.Free; END; Query.Close; END; END; FUNCTION UpdateDatFilePart(fileid,offset,size:LongWord; target:Pointer):Boolean; VAR dat_file:TFileStream; BEGIN Result:=False; 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,dat_offset,raw_addr,size:LongWord; loc_sep:Boolean; target:Pointer):Boolean; VAR filestream:TFileStream; mem:TStream; BEGIN Result:=False; IF opened_state=opened_dat THEN BEGIN Result:=True; IF NOT loc_sep THEN filestream:=TFileStream.Create(AnsiReplaceStr(dat_filename,'.dat','.raw'),fmOpenRead) ELSE filestream:=TFileStream.Create(AnsiReplaceStr(dat_filename,'.dat','.sep'),fmOpenRead); filestream.Seek(raw_addr,soFromBeginning); filestream.Read(target^,size); filestream.Free; END ELSE BEGIN Query.SQL.Text:='SELECT data FROM rawmap WHERE (src_id='+IntToStr(fileid)+') AND (src_link_offset='+IntToStr(dat_offset)+');'; Query.Open; IF Query.RecordCount>0 THEN BEGIN Result:=True; mem:=Query.CreateBlobStream(Query.FieldByName('data'),bmRead); mem.Seek(0,soFromBeginning); mem.Read(target^,size); mem.Free; END; Query.Close; END; END; FUNCTION LoadRawFileByIDOffset(fileid,dat_offset:LongWord; target:Pointer):Boolean; VAR i:Byte; raw_info:TRawInfo; mem:TStream; BEGIN Result:=False; IF opened_state=opened_dat THEN BEGIN Result:=True; raw_info:=GetRawInfo(fileid,dat_offset); LoadRawFile(fileid,dat_offset,raw_info.raw_addr,raw_info.raw_size,raw_info.loc_sep,target); END ELSE BEGIN Query.SQL.Text:='SELECT data FROM rawmap WHERE (src_id='+IntToStr(fileid)+') AND (src_link_offset='+IntToStr(dat_offset)+');'; Query.Open; IF Query.RecordCount>0 THEN BEGIN Result:=True; mem:=Query.CreateBlobStream(Query.FieldByName('data'),bmRead); mem.Seek(0,soFromBeginning); mem.Read(target^,mem.size); mem.Free; END; Query.Close; END; END; FUNCTION UpdateRawFile(rawinfo:TRawInfo; target:Pointer):Boolean; VAR filestream:TFileStream; BEGIN Result:=False; IF opened_state=opened_dat THEN BEGIN Result:=True; IF NOT rawinfo.loc_sep THEN filestream:=TFileStream.Create(AnsiReplaceStr(dat_filename,'.dat','.raw'),fmOpenReadWrite) ELSE filestream:=TFileStream.Create(AnsiReplaceStr(dat_filename,'.dat','.sep'),fmOpenReadWrite); filestream.Seek(rawinfo.raw_addr,soFromBeginning); filestream.Write(target^,rawinfo.raw_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; rawlist:TRawList; 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 rawlist:=GetRawList(fileid); IF Length(rawlist)>0 THEN BEGIN FOR i:=0 TO High(rawlist) DO BEGIN ExportRawFile(fileid,rawlist[i].src_offset,path+'\'+GetWinFileName(filename)); END; END; END; END; END; FUNCTION Explode(_string:String; delimiter:Char):TStringList; VAR start,len:Word; BEGIN SetLength(Result, 0); start:=1; WHILE PosEx(delimiter,_string,start)>0 DO BEGIN len:=PosEx(delimiter,_string,start)-start; SetLength(Result, Length(Result)+1); Result[High(Result)]:=MidStr(_string,start,len); start:=start+len+1; END; SetLength(Result, Length(Result)+1); Result[High(Result)]:=MidStr(_string,start,Length(_string)-start+1); 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; temps:String; BEGIN IF NOT FileExists(FileName) THEN BEGIN ShowMessage('File doesn''t exist!!!'); Exit; END; Database:=TABSDatabase.Create(NIL); Database.DatabaseName:='OLDBcon'; Database.DatabaseFileName:=FileName; Database.Open; Query:=TABSQuery.Create(Database); Query.DatabaseName:='OLDBcon'; Query.SQL.Text:='SELECT [name],[value] FROM globals ORDER BY [name] ASC'; Query.Open; Query.First; REPEAT IF Query.FieldByName('name').AsString='dbversion' THEN BEGIN IF Query.FieldByName('value').AsString<>DBversion THEN BEGIN ShowMessage('Database-file '+CrLf+'"'+FileName+'"'+CrLf+'has wrong version. (Required: '+DBversion+'; found: '+Query.FieldByName('value').AsString+')'); Query.Close; CloseDatabase; Exit; END; END; IF Query.FieldByName('name').AsString='lvl' THEN BEGIN database_level:=StrToInt(Query.FieldByName('value').AsString); END; IF Query.FieldByName('name').AsString='ident' THEN BEGIN temps:=Query.FieldByName('value').AsString; 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; Query.Next; UNTIL Query.Eof; Query.Close; opened_state:=opened_db; END; PROCEDURE CloseDatabase; BEGIN Database.Close; Database.Free; opened_state:=opened_nothing; END; END.