////////////////////////////////////////////////////////////////////////////////
//
//  ****************************************************************************
//  * Project   : FWZip
//  * Unit Name : UseExDataBlob
//  * Purpose   : Демонстрация работы с блоками ExData
//  * Author    : Александр (Rouse_) Багель
//  * Copyright : © Fangorn Wizards Lab 1998 - 2023.
//  * Version   : 2.0.0
//  * Home Page : http://rouse.drkb.ru
//  * Home Blog : http://alexander-bagel.blogspot.ru
//  ****************************************************************************
//  * Stable Release : http://rouse.drkb.ru/components.php#fwzip
//  * Latest Source  : https://github.com/AlexanderBagel/FWZip
//  ****************************************************************************
//
//  Используемые источники:
//  ftp://ftp.info-zip.org/pub/infozip/doc/appnote-iz-latest.zip
//  https://zlib.net/zlib-1.2.13.tar.gz
//  http://www.base2ti.com/
//

// Данный пример показывает работу с блоками ExData.
// Их добавление при создании архива и извлечение.
// Данные блоки могут содержать любые дополнительные параметры связанные
// с элементом архива. Список зарезервированных блоков см. в коментарии
// обработчика OnSaveExData.

program UseExDataBlob;

{$IFDEF FPC}
  {$MODE Delphi}
  {$H+}
{$ELSE}
  {$APPTYPE CONSOLE}
{$ENDIF}

uses
  SysUtils,
  Classes,
  FWZipWriter,
  FWZipReader,
  FWZipConsts;

const
  TestExDataBlob: Cardinal = $DEADBEEF;

var
  Writer: TFWZipWriter;
  Reader: TFWZipReader;
  Method: TMethod;

//
//  Обработчик при помощи которого мы добавляем блок расширенных данных ExData
//  к каждому элементу массива
// =============================================================================
procedure OnSaveExData({%H-}Self, Sender: TObject; {%H-}ItemIndex: Integer;
  UserExDataBlockCount: Integer; var Tag: Word; Data: TStream);
var
  RandomValue: Cardinal;
begin

  // При добавлении блока расширенных данных следует указать его Тэг и
  // записать сами данные в стрим Data, при этом размер Data не может
  // превысить значение MAXWORD а значение Тэг должно быть отлично от нуля.

  // Данный обработчик будет вызываться до тех пор, пока вы не укажете
  // что более блоков данных нет. Для этого нужно не заполнять стрим Data.

  // Количество вызовов обработчика для текущего элемента можно узнать по
  // переменной UserExDataBlockCount

  // Обратите внимание, следующие значения тэгов зарезервированы:

  {
          The current Header ID mappings defined by PKWARE are:

          0x0001        ZIP64 extended information extra field
          0x0007        AV Info
          0x0008        Reserved for future Unicode file name data (PFS)
          0x0009        OS/2 extended attributes      (also Info-ZIP)
          0x000a        NTFS (Win9x/WinNT FileTimes)
          0x000c        OpenVMS                       (also Info-ZIP)
          0x000d        Unix
          0x000e        Reserved for file stream and fork descriptors
          0x000f        Patch Descriptor
          0x0014        PKCS#7 Store for X.509 Certificates
          0x0015        X.509 Certificate ID and Signature for
                        individual file
          0x0016        X.509 Certificate ID for Central Directory
          0x0017        Strong Encryption Header
          0x0018        Record Management Controls
          0x0019        PKCS#7 Encryption Recipient Certificate List
          0x0065        IBM S/390 (Z390), AS/400 (I400) attributes
                        - uncompressed
          0x0066        Reserved for IBM S/390 (Z390), AS/400 (I400)
                        attributes - compressed

          The Header ID mappings defined by Info-ZIP and third parties are:

          0x07c8        Info-ZIP Macintosh (old, J. Lee)
          0x2605        ZipIt Macintosh (first version)
          0x2705        ZipIt Macintosh v 1.3.5 and newer (w/o full filename)
          0x2805        ZipIt Macintosh 1.3.5+
          0x334d        Info-ZIP Macintosh (new, D. Haase's 'Mac3' field)
          0x4154        Tandem NSK
          0x4341        Acorn/SparkFS (David Pilling)
          0x4453        Windows NT security descriptor (binary ACL)
          0x4704        VM/CMS
          0x470f        MVS
          0x4854        Theos, old inofficial port
          0x4b46        FWKCS MD5 (see below)
          0x4c41        OS/2 access control list (text ACL)
          0x4d49        Info-ZIP OpenVMS (obsolete)
          0x4d63        Macintosh SmartZIP, by Macro Bambini
          0x4f4c        Xceed original location extra field
          0x5356        AOS/VS (binary ACL)
          0x5455        extended timestamp
          0x554e        Xceed unicode extra field
          0x5855        Info-ZIP Unix (original; also OS/2, NT, etc.)
          0x6542        BeOS (BeBox, PowerMac, etc.)
          0x6854        Theos
          0x7441        AtheOS (AtheOS/Syllable attributes)
          0x756e        ASi Unix
          0x7855        Info-ZIP Unix (new)
          0xfb4a        SMS/QDOS
  }

  // Для примера мы заполним три блока данных:
  case UserExDataBlockCount of
    0:
    begin
      // Выбираем незарезервированное значение тэга (например $FFFA)
      Tag := $FFFA;
      // и пишем сами данные
      Data.WriteBuffer(TestExDataBlob, 4);
    end;
    1..2:
    begin
      // Выбираем другое незарезервированное значение тэга
      Tag := $FFFB + UserExDataBlockCount;
      // данные рандомные - просто для демонстрации записи двух и более полей
      Randomize;
      RandomValue := Random(MaxInt);
      Data.WriteBuffer(RandomValue, 4);
    end;
  end;
end;

//
//  Обработчик при помощи которого мы получаем блок расширенных данных ExData
//  который не смог обработать TFWZipReader.
//  Обработчик вызывается для каждого нераспознанного элемента ExData,
//  количество которых не ограничено.
//  Данный обработчик вызывается при вызове метода TFWZipReader.LoadFromFile
//  Sender в данном обработчике является TFWZipReaderItem,
//  т.е. элементом при чтении которого не распознался тэг ExData.
// =============================================================================
procedure OnLoadExData({%H-}Self, Sender: TObject; {%H-}ItemIndex: Integer;
  Tag: Word; Data: TStream);
var
  Value: Cardinal;
begin
  // так как мы знаем как нужно обрабатывать только блок данных с тэгом $FFFA
  // то остальные необходимо пропустить
  if Tag = $FFFA then
  begin
    // Для демонстрации просто проверяем совпадает ли значение в блоке с нашим
    if Data.Size <> 4 then
      raise Exception.Create('Неверный размер блока ExData');
    Value := 0;
    Data.ReadBuffer(Value, Data.Size);
    if Value <> TestExDataBlob then
      raise Exception.Create('Неверное значение блока ExData');

    // связать данные с элементом массива можно например используя поле Tag
    TFWZipReaderItem(Sender).Tag := Integer(Value);

    // вот такой вызов делать нельзя, т.к. в данный момент Sender
    // находится в конструкторе и не добавлен в список элементов
    // главного класса
    // Reader[ItemIndex].Tag := Integer(Value); - ошибочный код
  end;
end;

begin
  SetCurrentDir(ExtractFilePath(ParamStr(0)));
  try
    Writer := TFWZipWriter.Create;
    try
      // Для начала добавим в корень архива файлы из корневой директории
      Writer.AddFolder('', '..\..\', '*.*', False);

      // Назначаем обработчик через который мы будем добавлять блок расширенных данных
      Method.Code := @OnSaveExData;
      Method.Data := Writer;
      Writer.OnSaveExData := TZipSaveExDataEvent(Method);

      // Сохраняем результат
      Writer.BuildZip('..\DemoResults\UseExDataBlob.zip');
    finally
      Writer.Free;
    end;

    Reader := TFWZipReader.Create;
    try

      // Теперь наша задача получить расширенные блоки данных.
      // Для этого необходимо назначить обработчик OnLoadExData
      // и открыть сам архив.
      Method.Code := @OnLoadExData;
      Method.Data := Reader;
      Reader.OnLoadExData := TZipLoadExDataEvent(Method);
      Reader.LoadFromFile('..\DemoResults\UseExDataBlob.zip');
    finally
      Reader.Free;
    end;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  Readln;
end.
