unit Access_OniArchive; interface uses DataAccess, Classes; type { THeader = packed record Ident: array[0..$13] of Byte; Files: LongWord; NamedFiles: LongWord; Extensions: LongWord; DataAddr: LongWord; DataSize: LongWord; NamesAddr: LongWord; NamesSize: LongWord; Ident2: array[0..$F] of Byte; end; TFilesMap = array of packed record Extension: array[0..$3] of Char; DataAddr: LongWord; NameAddr: LongWord; FileSize: LongWord; FileType: LongWord; end; TFileInfo = packed record ID: Integer; FileName: String; FileNameHex: String; Extension: String[4]; Name: String; Size: LongWord; FileType: LongWord; DatAddr: LongWord; opened: Boolean; end; TFiles = array of TFileInfo; TNamedFilesMap = array of packed record FileNumber: LongWord; blubb: LongWord; end; TExtensionsMap = array of packed record Ident: array[0..$7] of Byte; Extension: array[0..$3] of Char; ExtCount: LongWord; end; } TAccess_OniArchive = class(TDataAccess) private { Fdat_file: TFileStream; Fraw_file: TFileStream; Fsep_file: TFileStream; Fdat_header: THeader; Fdat_filesmap: TFilesMap; Fdat_files: TFiles; Fdat_namedfilesmap: TNamedFilesMap; Fdat_extensionsmap: TExtensionsMap; FUnloadWhenUnused: Boolean; FDatOpened: Boolean; FRawOpened: Boolean; FSepOpened: Boolean; protected public property UnloadWhenUnused: Boolean Read FUnloadWhenUnused Write FUnloadWhenUnused; constructor Create(DatFilename: String; var Result: Boolean); override; procedure Close; override; function GetFileInfo(fileid: Integer): TFileInfo; override; function GetFilesList(ext: String; pattern: String; NoEmptyFiles: Boolean; sort: TSortType): TStringArray; override; function GetFilesCount: LongWord; override; function GetExtensionsList: TStringArray; override; function GetExtendedExtensionsList: TExtensionsMap; override; function LoadDatFile(fileid: LongWord): Tdata; override; procedure UpdateDatFile(fileid: LongWord; Data: Tdata); override; procedure LoadDatFilePart(fileid, offset, size: LongWord; target: Pointer); override; procedure UpdateDatFilePart(fileid, offset, size: LongWord; target: Pointer); override; procedure LoadRawOffset(loc_sep: Boolean; raw_addr, size: LongWord; target: Pointer); function GetRawList(fileid: LongWord): TRawList; override; procedure LoadRawFile(fileid, dat_offset: LongWord; target: Pointer); override; procedure UpdateRawFile(fileid, dat_offset: LongWord; size: LongWord; target: Pointer); override; procedure LoadRawFilePart(fileid, dat_offset: LongWord; offset, size: LongWord; target: Pointer); override; procedure UpdateRawFilePart(fileid, dat_offset: LongWord; offset, size: LongWord; target: Pointer); override; function AppendRawFile(loc_sep: Boolean; size: LongWord; target: Pointer): LongWord; override;//Returns new Address published } end; implementation (* ================================================================================ Implementation of TOniDataDat *) { constructor TOniDataDat.Create(DatFilename: String; var Result: Boolean); const header_ident1_pc: array[0..$13] of Byte = ($1F, $27, $DC, $33, $DF, $BC, $03, $00, $31, $33, $52, $56, $40, $00, $14, $00, $10, $00, $08, $00); header_ident1_mac: array[0..$13] of Byte = ($61, $30, $C1, $23, $DF, $BC, $03, $00, $31, $33, $52, $56, $40, $00, $14, $00, $10, $00, $08, $00); header_ident1_macbeta: array[0..$13] of Byte = ($81, $11, $8D, $23, $DF, $BC, $03, $00, $31, $33, $52, $56, $40, $00, $14, $00, $10, $00, $08, $00); header_ident2: array[0..$F] of Byte = ($99, $CF, $40, $00, $90, $4F, $63, $00, $F4, $55, $5F, $00, $90, $4F, $63, $00); var i: LongWord; header_pc, header_mac: Boolean; begin FUnloadWhenUnused := True; FDatOpened := False; FRawOpened := False; if not FileExists(DatFilename) then begin ShowMessage('File doesn''t exist!!!'); Result := False; Exit; end; FFileName := DatFilename; Fdat_file := TFileStream.Create(FFileName, fmOpenRead); Fdat_file.Read(Fdat_header, SizeOf(Fdat_header)); header_pc := True; header_mac := True; for i := 0 to High(Fdat_header.Ident) do begin FLevelInfo.Ident[i] := Fdat_header.Ident[i]; if Fdat_header.Ident[i] <> header_ident1_pc[i] then header_pc := False; if Fdat_header.Ident[i] <> header_ident1_mac[i] then header_mac := False; end; if not (header_pc xor header_mac) then begin Result := False; Exit; end else begin if (header_pc and not header_mac) then Fos_mac := False else Fos_mac := True; end; SetLength(Fdat_filesmap, Fdat_header.Files); SetLength(Fdat_files, Fdat_header.Files); for i := 0 to Fdat_header.Files - 1 do Fdat_file.Read(Fdat_filesmap[i], SizeOf(Fdat_filesmap[i])); for i := 0 to Fdat_header.Files - 1 do begin Fdat_files[i].ID := i; Fdat_files[i].Extension := Fdat_filesmap[i].Extension; Fdat_files[i].Extension := ReverseString(Fdat_files[i].Extension); Fdat_files[i].Size := Fdat_filesmap[i].FileSize; Fdat_files[i].FileType := Fdat_filesmap[i].FileType; Fdat_files[i].DatAddr := Fdat_filesmap[i].DataAddr - 8 + Fdat_header.DataAddr; if (Fdat_filesmap[i].FileType and $01) = 0 then begin Fdat_file.Seek(Fdat_filesmap[i].NameAddr + Fdat_header.NamesAddr, soFromBeginning); SetLength(Fdat_files[i].Name, 100); Fdat_file.Read(Fdat_files[i].Name[1], 100); Fdat_files[i].Name := MidStr(Fdat_files[i].Name, 1 + 4, Pos( #0, Fdat_files[i].Name) - 1 - 4); end else begin Fdat_files[i].Name := ''; end; Fdat_files[i].FileName := FormatNumber(i, 5, '0') + '-' + Fdat_files[i].Name + '.' + Fdat_files[i].Extension; Fdat_files[i].FileNameHex := IntToHex(i, 4) + '-' + Fdat_files[i].Name + '.' + Fdat_files[i].Extension; end; Fdat_file.Seek($40 + Fdat_header.Files * $14, soFromBeginning); SetLength(Fdat_namedfilesmap, Fdat_header.NamedFiles); for i := 0 to Fdat_header.NamedFiles - 1 do Fdat_file.Read(Fdat_namedfilesmap[i], SizeOf(Fdat_namedfilesmap[i])); Fdat_file.Seek($40 + Fdat_header.Files * $14 + Fdat_header.NamedFiles * $8, soFromBeginning); SetLength(Fdat_extensionsmap, Fdat_header.Extensions); for i := 0 to Fdat_header.Extensions - 1 do Fdat_file.Read(Fdat_extensionsmap[i], SizeOf(Fdat_extensionsmap[i])); Fdat_file.Seek(Fdat_files[0].DatAddr + 7, soFromBeginning); Fdat_file.Read(FLevelInfo.LevelNumber, 1); FLevelInfo.LevelNumber := FLevelInfo.LevelNumber div 2; Fdat_file.Free; Result := True; FBackend := ODB_Dat; end; procedure TOniDataDat.Close; begin if not FUnloadWhenUnused and FDatOpened then Fdat_file.Free; if not FUnloadWhenUnused and FRawOpened then Fraw_file.Free; if not FUnloadWhenUnused and FSepOpened then Fsep_file.Free; Self.Free; end; function TOniDataDat.GetFileInfo(fileid: Integer): TFileInfo; begin if fileid = -1 then begin Result := inherited GetFileInfo(fileid); Exit; end; if fileid < Self.GetFilesCount then Result := Fdat_files[fileid] else Result.ID := -1; end; function TOniDataDat.GetFilesList(ext: String; pattern: String; NoEmptyFiles: Boolean; sort: TSortType): TStringArray; var i: LongWord; list: TStringList; id, name, extension: String; fields: TStrings; procedure getfields; begin fields.CommaText := StringReplace(AnsiQuotedStr(list.Strings[i], '"'), ';', '","', [rfReplaceAll]); if sort in [stIDAsc, stIDDesc] then begin id := fields.Strings[0]; name := fields.Strings[1]; extension := fields.Strings[2]; end; if sort in [stNameAsc, stNameDesc] then begin id := fields.Strings[1]; name := fields.Strings[0]; extension := fields.Strings[2]; end; if sort in [stExtAsc, stExtDesc] then begin id := fields.Strings[1]; name := fields.Strings[2]; extension := fields.Strings[0]; end; end; begin list := TStringList.Create; list.Sorted := True; for i := 0 to Fdat_header.Files - 1 do begin if ((Length(ext) = 0) or (Pos(Fdat_files[i].Extension, ext) > 0)) and ((Length(pattern) = 0) or (Pos(UpperCase(pattern), UpperCase(Fdat_files[i].Name)) > 0)) then begin if (NoEmptyFiles = False) or ((Fdat_files[i].FileType and $02) = 0) then begin if AppSettings.FilenumbersAsHex then id := IntToHex(Fdat_files[i].ID, 4) else id := FormatNumber(Fdat_files[i].ID, 5, '0'); name := Fdat_files[i].Name; extension := Fdat_files[i].Extension; case sort of stIDAsc, stIDDesc: list.Add(id + ';' + name + ';' + extension); stNameAsc, stNameDesc: list.Add(name + ';' + id + ';' + extension); stExtAsc, stExtDesc: list.Add(extension + ';' + id + ';' + name); end; end; end; end; SetLength(Result, list.Count); if Length(Result) > 0 then begin fields := TStringList.Create; if sort in [stIDAsc, stNameAsc, stExtAsc] then for i := 0 to list.Count - 1 do begin getfields; Result[i] := id + '-' + name + '.' + extension; end else for i := list.Count - 1 downto 0 do begin getfields; Result[list.Count - i - 1] := id + '-' + name + '.' + extension; end; fields.Free; end; list.Free; end; function TOniDataDat.GetFilesCount: LongWord; begin Result := Fdat_header.Files; end; function TOniDataDat.GetExtensionsList: TStringArray; var i: LongWord; begin SetLength(Result, Fdat_header.Extensions); for i := 0 to Fdat_header.Extensions - 1 do begin with Fdat_extensionsmap[i] do begin Result[i] := Extension[3] + Extension[2] + Extension[1] + Extension[0] + ' (' + IntToStr(ExtCount) + ')'; end; end; end; function TOniDataDat.GetExtendedExtensionsList: TExtensionsMap; var i: LongWord; begin SetLength(Result, Fdat_header.Extensions); for i := 0 to Fdat_header.Extensions - 1 do begin Result[i] := Fdat_extensionsmap[i]; end; end; function TOniDataDat.LoadDatFile(fileid: LongWord): Tdata; begin if fileid < Self.GetFilesCount then begin if FUnloadWhenUnused or not FDatOpened then Fdat_file := TFileStream.Create(FFileName, fmOpenReadWrite); Fdat_file.Seek(Fdat_files[fileid].DatAddr, soFromBeginning); SetLength(Result, Fdat_files[fileid].Size); Fdat_file.Read(Result[0], Fdat_files[fileid].Size); if UnloadWhenUnused then Fdat_file.Free else FDatOpened := True; end; end; procedure TOniDataDat.UpdateDatFile(fileid: LongWord; Data: Tdata); begin if fileid < Self.GetFilesCount then begin if FUnloadWhenUnused or not FDatOpened then Fdat_file := TFileStream.Create(FFileName, fmOpenReadWrite); Fdat_file.Seek(Fdat_files[fileid].DatAddr, soFromBeginning); Fdat_file.Write(Data[0], Length(Data)); if UnloadWhenUnused then Fdat_file.Free else FDatOpened := True; end; end; procedure TOniDataDat.LoadDatFilePart(fileid, offset, size: LongWord; target: Pointer); begin if fileid < Self.GetFilesCount then begin if FUnloadWhenUnused or not FDatOpened then Fdat_file := TFileStream.Create(FFileName, fmOpenReadWrite); Fdat_file.Seek(Fdat_files[fileid].DatAddr + offset, soFromBeginning); Fdat_file.Read(target^, size); if UnloadWhenUnused then Fdat_file.Free else FDatOpened := True; end; end; procedure TOniDataDat.UpdateDatFilePart(fileid, offset, size: LongWord; target: Pointer); begin if fileid < Self.GetFilesCount then begin if FUnloadWhenUnused or not FDatOpened then Fdat_file := TFileStream.Create(FFileName, fmOpenReadWrite); Fdat_file.Seek(Fdat_files[fileid].DatAddr + offset, soFromBeginning); Fdat_file.Write(target^, size); if UnloadWhenUnused then Fdat_file.Free else FDatOpened := True; end; end; function TOniDataDat.GetRawList(fileid: LongWord): TRawList; var i: LongWord; begin SetLength(Result, 0); for i := 0 to High(RawListHandlers) do if UpperCase(RawListHandlers[i].Ext) = UpperCase(Fdat_files[fileid].extension) then if RawListHandlers[i].needed then begin Result := RawListHandlers[i].Handler(Self, fileid); Break; end else Break; end; procedure TOniDataDat.LoadRawOffset(loc_sep: Boolean; raw_addr, size: LongWord; target: Pointer); begin if not loc_sep then begin if FUnloadWhenUnused or not FRawOpened then Fraw_file := TFileStream.Create(AnsiReplaceStr(FFileName, '.dat', '.raw'), fmOpenReadWrite); if raw_addr <= Fraw_file.Size then begin Fraw_file.Seek(raw_addr, soFromBeginning); Fraw_file.Read(target^, size); end; if UnloadWhenUnused then Fraw_file.Free else FRawOpened := True; end else begin if FUnloadWhenUnused or not FSepOpened then Fsep_file := TFileStream.Create(AnsiReplaceStr(FFileName, '.dat', '.sep'), fmOpenReadWrite); if raw_addr <= Fsep_file.Size then begin Fsep_file.Seek(raw_addr, soFromBeginning); Fsep_file.Read(target^, size); end; if UnloadWhenUnused then Fsep_file.Free else FSepOpened := True; end; end; procedure TOniDataDat.LoadRawFile(fileid, dat_offset: LongWord; target: Pointer); var raw_info: TRawInfo; begin if fileid < Self.GetFilesCount then begin raw_info := Self.GetRawInfo(fileid, dat_offset); if not raw_info.loc_sep then begin if FUnloadWhenUnused or not FRawOpened then Fraw_file := TFileStream.Create(AnsiReplaceStr(FFileName, '.dat', '.raw'), fmOpenReadWrite); Fraw_file.Seek(raw_info.raw_addr, soFromBeginning); Fraw_file.Read(target^, raw_info.raw_size); if UnloadWhenUnused then Fraw_file.Free else FRawOpened := True; end else begin if FUnloadWhenUnused or not FSepOpened then Fsep_file := TFileStream.Create(AnsiReplaceStr(FFileName, '.dat', '.sep'), fmOpenReadWrite); Fsep_file.Seek(raw_info.raw_addr, soFromBeginning); Fsep_file.Read(target^, raw_info.raw_size); if UnloadWhenUnused then Fsep_file.Free else FSepOpened := True; end; end; end; procedure TOniDataDat.UpdateRawFile(fileid, dat_offset: LongWord; size: LongWord; target: Pointer); var raw_info: TRawInfo; begin if fileid < Self.GetFilesCount then begin raw_info := Self.GetRawInfo(fileid, dat_offset); if not raw_info.loc_sep then begin if FUnloadWhenUnused or not FRawOpened then Fraw_file := TFileStream.Create(AnsiReplaceStr(FFileName, '.dat', '.raw'), fmOpenReadWrite); Fraw_file.Seek(raw_info.raw_addr, soFromBeginning); Fraw_file.Write(target^, raw_info.raw_size); if UnloadWhenUnused then Fraw_file.Free else FRawOpened := True; end else begin if FUnloadWhenUnused or not FSepOpened then Fsep_file := TFileStream.Create(AnsiReplaceStr(FFileName, '.dat', '.sep'), fmOpenReadWrite); Fsep_file.Seek(raw_info.raw_addr, soFromBeginning); Fsep_file.Write(target^, raw_info.raw_size); if UnloadWhenUnused then Fsep_file.Free else FSepOpened := True; end; end; end; procedure TOniDataDat.LoadRawFilePart(fileid, dat_offset: LongWord; offset, size: LongWord; target: Pointer); var raw_info: TRawInfo; Data: Tdata; mem: TMemoryStream; begin if fileid < Self.GetFilesCount then begin raw_info := Self.GetRawInfo(fileid, dat_offset); SetLength(Data, raw_info.raw_size); Self.LoadRawFile(fileid, dat_offset, @Data[0]); mem := TMemoryStream.Create; mem.Write(Data[offset], size); mem.Read(target^, size); mem.Free; end; end; procedure TOniDataDat.UpdateRawFilePart(fileid, dat_offset: LongWord; offset, size: LongWord; target: Pointer); var raw_info: TRawInfo; begin if fileid < Self.GetFilesCount then begin raw_info := Self.GetRawInfo(fileid, dat_offset); if not raw_info.loc_sep then begin if FUnloadWhenUnused or not FRawOpened then Fraw_file := TFileStream.Create(AnsiReplaceStr(FFileName, '.dat', '.raw'), fmOpenReadWrite); Fraw_file.Seek(raw_info.raw_addr + offset, soFromBeginning); Fraw_file.Write(target^, raw_info.raw_size); if UnloadWhenUnused then Fraw_file.Free else FRawOpened := True; end else begin if FUnloadWhenUnused or not FSepOpened then Fsep_file := TFileStream.Create(AnsiReplaceStr(FFileName, '.dat', '.sep'), fmOpenReadWrite); Fsep_file.Seek(raw_info.raw_addr + offset, soFromBeginning); Fsep_file.Write(target^, raw_info.raw_size); if UnloadWhenUnused then Fsep_file.Free else FSepOpened := True; end; end; end; function TOniDataDat.AppendRawFile(loc_sep: Boolean; size: LongWord; target: Pointer): LongWord; //Returns new Address begin if not loc_sep then begin if FUnloadWhenUnused or not FRawOpened then Fraw_file := TFileStream.Create(AnsiReplaceStr(FFileName, '.dat', '.raw'), fmOpenReadWrite); Result := Fraw_file.Size; Fraw_file.Seek(0, soFromEnd); Fraw_file.Write(target^, size); if UnloadWhenUnused then Fraw_file.Free else FRawOpened := True; end else begin if FUnloadWhenUnused or not FSepOpened then Fsep_file := TFileStream.Create(AnsiReplaceStr(FFileName, '.dat', '.sep'), fmOpenReadWrite); Result := Fsep_file.Size; Fsep_file.Seek(0, soFromEnd); Fsep_file.Write(target^, size); if UnloadWhenUnused then Fsep_file.Free else FSepOpened := True; end; end; } end.