Index: oup/current/DataAccess/Access_OniSplitArchive.pas
===================================================================
--- oup/current/DataAccess/Access_OniSplitArchive.pas	(revision 254)
+++ oup/current/DataAccess/Access_OniSplitArchive.pas	(revision 254)
@@ -0,0 +1,532 @@
+unit Access_OniSplitArchive;
+interface
+
+uses DataAccess, Classes, TypeDefs;
+
+type
+  TAccess_OniSplitArchive = class(TDataAccess)
+  private
+    Fdat_file:           TFileStream;
+    Fdat_files:          TFiles;
+    Fdat_extensionsmap:  TExtensionsMap;
+    FRawStart:           Integer;
+  protected
+  public
+    constructor Create(DatFilename: String; ConnectionID: Integer; var Msg: TStatusMessages); override;
+    procedure Close; override;
+
+    function GetFileInfo(FileID: Integer): TFileInfo; override;
+    function GetFilesList(Ext: String; Pattern: String;
+      NoEmptyFiles: Boolean; SortType: TSortType): TStrings; override;
+    function GetFileCount: Integer; override;
+    function GetExtensionsList(ExtListFormat: TExtensionFormat): TStrings; override;
+
+    procedure LoadDatFile(FileID: Integer; var Target: TStream); overload; override;
+    procedure UpdateDatFile(FileID: Integer; Src: TStream); overload; override;
+    procedure LoadDatFilePart(FileID, Offset, Size: Integer; var Target: TStream); overload; override;
+    procedure UpdateDatFilePart(FileID, Offset, Size: Integer; Src: TStream); overload; override;
+
+    function GetDatLinks(FileID: Integer): TDatLinkList; override;
+    function GetDatLink(FileID, DatOffset: Integer): TDatLink; override;
+    function GetRawList(FileID: Integer): TRawDataList; override;
+    function GetRawInfo(FileID, DatOffset: Integer): TRawDataInfo; override;
+    function GetRawsForType(RawType: String): TRawDataList; override;
+
+    procedure LoadRawOffset(LocSep: Boolean; RawAddr, Size: Integer; var target: TStream); overload;
+    procedure LoadRawOffset(LocSep: Boolean; RawAddr, Size: Integer; target: Pointer); overload;
+    procedure LoadRawFile(FileID, DatOffset: Integer; var Target: TStream); overload; override;
+    procedure UpdateRawFile(FileID, DatOffset: Integer; Src: TStream); overload; override;
+    procedure LoadRawFilePart(FileID, DatOffset, Offset, Size: Integer; var Target: TStream); overload; override;
+    procedure UpdateRawFilePart(FileID, DatOffset, Offset, Size: Integer; Src: TStream); overload; override;
+
+    function AppendRawFile(LocSep: Boolean; Src: TStream): Integer; overload; override;
+  published
+  end;
+
+implementation
+
+uses
+  SysUtils, StrUtils, Data, Functions, RawList, DatLinks, Math;
+
+
+(*
+================================================================================
+                      Implementation of  TOniDataDat
+*)
+
+
+constructor TAccess_OniSplitArchive.Create(DatFilename: String; ConnectionID: Integer; var Msg: TStatusMessages);
+var
+  i: Integer;
+  header_pc, header_mac, header_macbeta: Boolean;
+  Fdat_header:   THeader;
+  Fdat_filesmap: TFilesMap;
+  Fdat_namedfilesmap: TNamedFilesMap;
+const
+  HeaderGlobalIdent: TDatGlobalIdent = ($DF, $BC, $03, $00, $32, $33, $52, $56,
+                                        $40, $00, $14, $00, $10, $00, $08, $00);
+begin
+  Msg := SM_UnknownError;
+  if not FileExists(DatFilename) then
+  begin
+    Msg := SM_FileNotFound;
+    Exit;
+  end;
+  FFileName := DatFilename;
+  Fdat_file := TFileStream.Create(FFileName, fmOpenReadWrite);
+  Fdat_file.Read(Fdat_header, SizeOf(Fdat_header));
+  header_pc  := True;
+  header_mac := True;
+  header_macbeta := True;
+  for i := 0 to High(Fdat_header.GlobalIdent) do
+    if Fdat_header.GlobalIdent[i] <> HeaderGlobalIdent[i] then
+    begin
+      Msg := SM_IncompatibleFile;
+      Exit;
+    end;
+
+  for i := 0 to High(Fdat_header.OSIdent) do
+  begin
+    if Fdat_header.OSIdent[i] <> HeaderOSIdentWin[i] then
+      header_pc := False;
+    if Fdat_header.OSIdent[i] <> HeaderOSIdentMac[i] then
+      header_mac := False;
+    if Fdat_header.OSIdent[i] <> HeaderOSIdentMacBeta[i] then
+      header_macbeta := False;
+  end;
+  if not (header_pc xor header_mac xor header_macbeta) then
+  begin
+    Msg := SM_IncompatibleFile;
+    Exit;
+  end
+  else
+  begin
+    if (header_pc and not header_mac and not header_macbeta) then
+      FDataOS := DOS_WIN
+    else if (not header_pc and header_mac and not header_macbeta) then
+      FDataOS := DOS_MAC
+    else if (not header_pc and not header_mac and header_macbeta) then
+      FDataOS := DOS_MACBETA;
+  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;
+  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(FLevelNumber, 1);
+  FLevelNumber := FLevelNumber div 2;
+
+  with Fdat_header do
+    FRawStart := Ident2[0] + Ident2[1]*256 + Ident2[2]*256*256 + Ident2[3]*256*256*256;
+
+  Msg := SM_OK;
+  FBackend := DB_ONISPLIT;
+  FConnectionID := ConnectionID;
+  FChangeRights := [CR_EditDat, CR_EditRaw, CR_AppendRaw];
+
+  inherited;
+end;
+
+
+
+
+procedure TAccess_OniSplitArchive.Close;
+begin
+  if Assigned(Fdat_file) then
+    Fdat_file.Free;
+  Self.Free;
+end;
+
+
+
+
+function TAccess_OniSplitArchive.GetFileInfo(fileid: Integer): TFileInfo;
+begin
+  if fileid = -1 then
+  begin
+    Result := inherited GetFileInfo(fileid);
+    Exit;
+  end;
+  if fileid < Self.GetFileCount then
+    Result    := Fdat_files[fileid]
+  else
+    Result.ID := -1;
+end;
+
+
+
+
+function TAccess_OniSplitArchive.GetFilesList(ext: String; pattern: String;
+  NoEmptyFiles: Boolean; SortType: TSortType): TStrings;
+var
+  i:      Integer;
+  list:   TStringList;
+  id, name, extension: String;
+  fields: TStrings;
+
+  procedure getfields;
+  begin
+    fields.CommaText := StringReplace(AnsiQuotedStr(list.Strings[i], '"'), ';', '","', [rfReplaceAll]);
+    if SortType in [ST_IDAsc, ST_IDDesc] then
+    begin
+      id := fields.Strings[0];
+      name := fields.Strings[1];
+      extension := fields.Strings[2];
+    end;
+    if SortType in [ST_NameAsc, ST_NameDesc] then
+    begin
+      id := fields.Strings[1];
+      name := fields.Strings[0];
+      extension := fields.Strings[2];
+    end;
+    if SortType in [ST_ExtAsc, ST_ExtDesc] then
+    begin
+      id := fields.Strings[1];
+      name := fields.Strings[2];
+      extension := fields.Strings[0];
+    end;
+    if SortType in [ST_ExtNameAsc, ST_ExtNameDesc] then
+    begin
+      id := fields.Strings[2];
+      name := fields.Strings[1];
+      extension := fields.Strings[0];
+    end;
+  end;
+
+begin
+  list := TStringList.Create;
+  list.Sorted := True;
+  if ext = '*' then
+    ext := '';
+  for i := 0 to GetFileCount - 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
+        id := FormatNumber(Fdat_files[i].ID, 5, '0');
+        name := Fdat_files[i].Name;
+        extension := Fdat_files[i].Extension;
+
+        case SortType of
+          ST_IDAsc, ST_IDDesc:     list.Add(id + ';' + name + ';' + extension);
+          ST_NameAsc, ST_NameDesc: list.Add(name + ';' + id + ';' + extension);
+          ST_ExtAsc, ST_ExtDesc:   list.Add(extension + ';' + id + ';' + name);
+          ST_ExtNameAsc, ST_ExtNameDesc: list.Add(name + ';' + extension + ';' + id);
+        end;
+      end;
+    end;
+  end;
+  if not Assigned(Result) then
+    Result := TStringList.Create;
+  if list.Count > 0 then
+  begin
+    fields := TStringList.Create;
+    if SortType in [ST_IDAsc, ST_NameAsc, ST_ExtAsc, ST_ExtNameAsc] then
+      for i := 0 to list.Count - 1 do
+      begin
+        getfields;
+        Result.Add(id + '-' + name + '.' + extension);
+      end
+    else
+      for i := list.Count - 1 downto 0 do
+      begin
+        getfields;
+        Result.Add(id + '-' + name + '.' + extension);
+      end;
+    fields.Free;
+  end;
+  list.Free;
+end;
+
+
+
+
+function TAccess_OniSplitArchive.GetFileCount: Integer;
+begin
+  Result := Length(Fdat_files);
+end;
+
+
+
+
+function TAccess_OniSplitArchive.GetExtensionsList(ExtListFormat: TExtensionFormat): TStrings;
+var
+  i: Integer;
+begin
+  if not Assigned(Result) then
+    Result := TStringList.Create;
+  if Result is TStringList then
+    TStringList(Result).Sorted := True;
+  for i := 0 to Length(Fdat_extensionsmap) - 1 do
+  begin
+    with Fdat_extensionsmap[i] do
+    begin
+      case ExtListFormat of
+        EF_ExtOnly:
+          Result.Add(Extension[3] + Extension[2] + Extension[1] + Extension[0]);
+        EF_ExtCount:
+          Result.Add(Extension[3] + Extension[2] + Extension[1] + Extension[0] +
+                ' (' + IntToStr(ExtCount) + ')');
+      end;
+    end;
+  end;
+end;
+
+
+
+procedure TAccess_OniSplitArchive.LoadDatFile(FileID: Integer; var Target: TStream);
+var
+  streampos: Integer;
+begin
+  if fileid < GetFileCount then
+  begin
+    if not Assigned(Target) then
+      Target := TMemoryStream.Create;
+    if GetFileInfo(FileID).Size > 0 then
+    begin
+      Fdat_file.Seek(Fdat_files[fileid].DatAddr, soFromBeginning);
+      streampos := Target.Position;
+      Target.CopyFrom(Fdat_file, Fdat_files[fileid].Size);
+      Target.Seek(streampos, soFromBeginning);
+    end;
+  end;
+end;
+
+procedure TAccess_OniSplitArchive.UpdateDatFile(FileID: Integer; Src: TStream);
+begin
+  if fileid < GetFileCount then
+  begin
+    Fdat_file.Seek(Fdat_files[fileid].DatAddr, soFromBeginning);
+    Fdat_file.CopyFrom(Src, Fdat_files[fileid].Size);
+  end;
+end;
+
+procedure TAccess_OniSplitArchive.LoadDatFilePart(FileID, Offset, Size: Integer; var Target: TStream);
+var
+  streampos: Integer;
+begin
+  if fileid < GetFileCount then
+  begin
+    if not Assigned(Target) then
+      Target := TMemoryStream.Create;
+    Fdat_file.Seek(Fdat_files[fileid].DatAddr + offset, soFromBeginning);
+    streampos := Target.Position;
+    Target.CopyFrom(Fdat_file, size);
+    Target.Seek(streampos, soFromBeginning);
+  end;
+end;
+
+procedure TAccess_OniSplitArchive.UpdateDatFilePart(FileID, Offset, Size: Integer; Src: TStream);
+begin
+  if fileid < GetFileCount then
+  begin
+    Fdat_file.Seek(Fdat_files[fileid].DatAddr + offset, soFromBeginning);
+    Fdat_file.CopyFrom(Src, Size);
+  end;
+end;
+
+
+
+function TAccess_OniSplitArchive.GetDatLink(FileID, DatOffset: Integer): TDatLink;
+var
+  link: Integer;
+begin
+  Result := DatLinksManager.GetDatLink(FConnectionID, FileID, DatOffset);
+  LoadDatFilePart(fileid, Result.SrcOffset, 4, @link);
+  if link > 0 then
+    Result.DestID := link div 256
+  else
+    Result.DestID := -1;
+end;
+
+
+function TAccess_OniSplitArchive.GetDatLinks(FileID: Integer): TDatLinkList;
+var
+  i: Integer;
+  link: Integer;
+begin
+  Result := DatLinksManager.GetDatLinks(FConnectionID, FileID);
+  if Length(Result) > 0 then
+  begin
+    for i := 0 to High(Result) do
+    begin
+      LoadDatFilePart(fileid, Result[i].SrcOffset, 4, @link);
+      if link > 0 then
+        Result[i].DestID := link div 256
+      else
+        Result[i].DestID := -1;
+    end;
+  end;
+end;
+
+
+function TAccess_OniSplitArchive.GetRawList(FileID: Integer): TRawDataList;
+begin
+  Result := RawLists.GetRawList(FConnectionID, FileID);
+end;
+
+
+function TAccess_OniSplitArchive.GetRawsForType(RawType: String): TRawDataList;
+var
+  i, j: Integer;
+  dats: TStrings;
+  list: TRawDataList;
+begin
+  dats := nil;
+  dats := GetFilesList(MidStr(RawType, 1, 4), '', True, ST_IDAsc);
+  for i := 0 to dats.Count - 1 do
+  begin
+    list := GetRawList(StrToInt(MidStr(dats.Strings[i], 1, 5)));
+    for j := 0 to Length(list) - 1 do
+    begin
+      if (list[j].RawType = RawType) and (list[j].RawSize > 0) then
+      begin
+        SetLength(Result, Length(Result)+1);
+        Result[High(Result)] := list[j];
+      end;
+    end;
+  end;
+end;
+
+
+function TAccess_OniSplitArchive.GetRawInfo(FileID, DatOffset: Integer): TRawDataInfo;
+begin
+  Result := RawLists.GetRawInfo(FConnectionID, FileID, DatOffset);
+end;
+
+
+
+procedure TAccess_OniSplitArchive.LoadRawOffset(LocSep: Boolean; RawAddr, Size: Integer; var target: TStream);
+begin
+  if not Assigned(Target) then
+    Target := TMemoryStream.Create;
+  if FRawStart + RawAddr <= Fdat_file.Size then
+  begin
+    Fdat_file.Seek(FRawStart + RawAddr, soFromBeginning);
+    Target.CopyFrom(Fdat_file, size);
+    Target.Seek(0, soFromBeginning);
+  end;
+end;
+
+procedure TAccess_OniSplitArchive.LoadRawOffset(LocSep: Boolean; RawAddr, Size: Integer; target: Pointer);
+var
+  data: TStream;
+begin
+  data := nil;
+  LoadRawOffset(LocSep, RawAddr, Size, data);
+  data.Read(Target^, Size);
+  data.Free;
+end;
+
+procedure TAccess_OniSplitArchive.LoadRawFile(FileID, DatOffset: Integer; var Target: TStream);
+var
+  raw_info: TRawDataInfo;
+  streampos: Integer;
+begin
+  if not Assigned(Target) then
+    Target := TMemoryStream.Create;
+  if fileid < GetFileCount then
+  begin
+    raw_info := Self.GetRawInfo(FileID, DatOffset);
+
+    Fdat_file.Seek(FRawStart + raw_info.RawAddr, soFromBeginning);
+    streampos := Target.Position;
+    Target.CopyFrom(Fdat_file, raw_info.RawSize);
+    Target.Seek(streampos, soFromBeginning);
+  end;
+end;
+
+procedure TAccess_OniSplitArchive.UpdateRawFile(FileID, DatOffset: Integer; Src: TStream);
+var
+  raw_info: TRawDataInfo;
+begin
+  if fileid < GetFileCount then
+  begin
+    raw_info := GetRawInfo(FileID, DatOffset);
+
+    Fdat_file.Seek(FRawStart + raw_info.RawAddr, soFromBeginning);
+    Fdat_file.CopyFrom(Src, Min(raw_info.RawSize, Src.Size));
+  end;
+end;
+
+procedure TAccess_OniSplitArchive.LoadRawFilePart(FileID, DatOffset, Offset, Size: Integer; var Target: TStream);
+var
+  Data: TStream;
+  streampos: Integer;
+begin
+  if not Assigned(Target) then
+    Target := TMemoryStream.Create;
+  if fileid < Self.GetFileCount then
+  begin
+    data := nil;
+    LoadRawFile(FileID, DatOffset, Data);
+    Data.Seek(Offset, soFromBeginning);
+    streampos := Target.Position;
+    Target.CopyFrom(Data, Size);
+    Target.Seek(streampos, soFromBeginning);
+  end;
+end;
+
+
+procedure TAccess_OniSplitArchive.UpdateRawFilePart(FileID, DatOffset, Offset, Size: Integer; Src: TStream);
+var
+  raw_info: TRawDataInfo;
+begin
+  if fileid < GetFileCount then
+  begin
+    raw_info := GetRawInfo(FileID, DatOffset);
+
+    Fdat_file.Seek(FRawStart + raw_info.RawAddr + Offset, soFromBeginning);
+    Fdat_file.CopyFrom(Src, Size);
+  end;
+end;
+
+function TAccess_OniSplitArchive.AppendRawFile(LocSep: Boolean; Src: TStream): Integer;
+const
+  EmptyBytes: Array[0..31] of Byte = (
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 );
+begin
+  if (Fdat_file.Size mod 32) > 0 then
+    Fdat_file.Write(EmptyBytes[0], 32 - (Fdat_file.Size mod 32));
+  Result := Fdat_file.Size - FRawStart;
+  Fdat_file.Seek(0, soFromEnd);
+  Fdat_file.CopyFrom(Src, Src.Size);
+  if (Fdat_file.Size mod 32) > 0 then
+    Fdat_file.Write(EmptyBytes[0], 32 - (Fdat_file.Size mod 32));
+end;
+
+end.
