unit par2lib_header;

(*

  PAR2Lib - Copyright (C) 2006-2007 - Jorrit Jongma

  Library name    PAR2Lib
  Author          Jorrit Jongma
  E-Mail          Jorrit@Jongma.org, use [PAR2LIB] as subject prefix!
  Web             http://www.jongma.org/par2lib

  Date            4 December 2007
  Version         0.7
  License         Free for personal and non-commercial use.
                  Different licenses available on request.
                  Crediting me would be nice, but is not required.

  Source          Closed, homegrown. (Not based on Parchive code)



  LIMITATIONS

  This library can only process PAR2 files. It does not do PAR (v1) files nor
  does it give you the ability to create PAR2 files. Both of those may be in
  future versions.



  TRANSLATION

  Care has been taken to make sure this library can be used from other
  languages, but headers are only supplied for Delphi, which uses
  Object Pascal as language.

  So here's a crash course in the language:

    function      Same as in most other languages
    procedure     Function that doesn't return anything (void function)

    Cardinal      Unsigned 32-bit Integer
    Int64         Signed 64-bit Integer
    PChar         Pointer to memory containing an ASCII null-terminated string
    Pointer       Hey, it's a pointer! :)

    stdcall       Calling convention. HINT: This is the same calling convention
                  used for Windows API functions.

  Function declarations:

    function <name>(<parametername>: <parametertype>; <parameter2name>: <parameter2type>; ...): <returntype>;
    procedure <name>(<parametername>: <parametertype>; <parameter2name>: <parameter2type>; ...);

  "var" parameters:

    Var parameters are parameters that are passed by reference instead of value.
    If a parameter list has a var parameter, this mean what is actually passed
    is a pointer to the variable, instead of the variable itself.

  "function" parameters:

    The callbacks are set using parameters that reference functions. These are
    just pointers to the actual function (again, just like callbacks in the
    Windows API)


  I'm not much of a C(++) person, but it should be easy enough translating this
  header file to a C header with the above information. You only need the part
  between the "interface" and "implementation" keywords.



  CHANGELOG

  0.7    4-Dec-2007   Added sample application
  0.7    4-Dec-2007   Added MD5 checks for repaired blocks
  0.7    4-Dec-2007   Added capability to repair files >2GB
  0.7    4-Dec-2007   Added extra scanning capabilities to
                      find blocks that are good, but misaligned.

  0.6   17-Oct-2006   Added FreeMem Callback
  0.6   17-Oct-2006   Added ReadProgress Callback

  0.5   17-Oct-2006   First release

*)

interface

const
  PAR2_DLL = 'par2lib.dll';

  PAR2_False = 0;
  PAR2_True = 1;

  // Got it!
  PAR2_Available_Yes = 0;
  // Probably have it soon!
  PAR2_Available_NotYet = 1;
  // No chance!
  PAR2_Available_Never = 2;

  // Files are complete, no repair necessary
  PAR2_Go_Success                           = 0;

  // PAR2_Go_Success < PAR2_Go::returnValue < PAR2_Error_Base, ReturnValue is the amount of blocks still needed.
  // Just call PAR2_Go again when you have more data.
  PAR2_Go_Error_Base                        = 1 shl 16;
  // Files are incomplete but can be repaired
  PAR2_Go_Error_Call_Repair                 = PAR2_Go_Error_Base + 0;
  // Files are incomplete and repairs are not possible
  PAR2_Go_Error_Repair_Not_Possible         = PAR2_Go_Error_Base + 1;
  // Callbacks are not set
  PAR2_Go_Error_No_Par_Available_Callback   = PAR2_Go_Error_Base + 2;
  PAR2_Go_Error_No_Par_Needed_Callback      = PAR2_Go_Error_Base + 3;
  PAR2_Go_Error_No_Data_Available_Callback  = PAR2_Go_Error_Base + 4;
  PAR2_Go_Error_No_Data_Needed_Callback     = PAR2_Go_Error_Base + 5;
  PAR2_Go_Error_No_Repaired_Block_Callback  = PAR2_Go_Error_Base + 6;
  PAR2_Go_Error_No_Free_Mem_Callback        = PAR2_Go_Error_Base + 7;
  // Need ANY other PAR file that was not previously asked for. This is a
  // special error value which is NOT necessarily fatal! It just means
  // the PAR2 engine needs data contained in a par2 file to be able to do
  // anything useful. You need to give the PAR2 engine the file in the
  // PARNeeded callback ASAP.
  PAR2_Go_Error_Need_PAR                    = PAR2_Go_Error_Base + 8;

  PAR2_Repair_Progress_Start                = 0;
  PAR2_Repair_Progress_Repair_Position      = 1;
  PAR2_Repair_Progress_Repair_Matrix        = 2;
  PAR2_Repair_Progress_Repair_Blocks        = 3;
  PAR2_Repair_Progress_Done                 = 4;

  PAR2_Repair_Progress_Percent_Start        = 101;
  PAR2_Repair_Progress_Percent_Done         = 102;


type
  // Need more PAR blocks! If BlocksNeeded = 0, we want ANY (not previously given) par2 file.
  // Available is called to see if you got them, Needed is called to retrieve the actual data.
  // You can determine what par2 file you return by looking at BlocksNeeded. If you return
  // a par2 with less blocks than we need, these callbacks will be called again with the
  // amount of blocks we still need.

      // Return PAR2_Available_Yes if you have a file we can use.
      // Return PAR2_Available_NotYet if you will probably have it at a later time, e.g., it is still
      //    queued for download (or you can queue for download as needed now)
      // Return PAR2_Available_Never if you have no file we can use, and will not have it later -
      //    this might ultimately trigger PAR2_Go_Error_Repair_Not_Possible
      //
      // You need to set the ID to a value you can resolve back to the file in question. The ID will be
      // used later, possibly many times. Return the size of that file in FileSize
      TPAR2_PARAvailableCallback = function(UserData: Cardinal; BlocksNeeded: Cardinal; var ID: Cardinal; var FileSize: Int64): Cardinal; stdcall;

      // Return PAR2_True if successfull, PAR2_False if not. Note that it shouldn't
      // actually happen that you return PAR2_False, since that should have been
      // caught in the PARAvailableCallback.
      //
      // If you set FreeBuffer to PAR2_True, the PAR2 engine will take care of deallocating the memory
      // whenever it doesn't need it anymore. If you set it to PAR2_False, it is assumed you take care
      // of that. If you do it yourself, remember to NOT release any of the referenced memory until
      // you have called PAR2_Free
      //
      // We only want the data from StartPos to StartPos + Length - 1 ( = Length bytes).
      TPAR2_PARNeededCallback = function(UserData: Cardinal; ID: Cardinal; StartPos: Int64; Length: Cardinal; var Buffer: Pointer; var BufferSize: Cardinal; var FreeBuffer: Cardinal): Cardinal; stdcall;

  // Need data! Filename is null-terminated.
  // 'Filename' is not used by the PAR2 engine itself to read or write to files, it is merely used to identify
  // data. We only want the data from StartPos to StartPos + Length - 1 ( = Length bytes).

      // Return PAR2_Available_Yes if you have the data
      // Return PAR2_Available_NotYet if you will probably have the data at a later time
      // Return PAR2_Available_Never if you do not and will not have the data
      //
      // This callback will called repeatedly (once for every block, every time you call
      // PAR2_Go), until you return either PAR2_Available_Yes or PAR2_Available_Never for
      // that block of data.
      TPAR2_DataAvailableCallback = function(UserData: Cardinal; Filename: PChar; StartPos: Int64; Length: Cardinal): Cardinal; stdcall;

      // See PARNeededCallback and and DataAvailableCallback. You should be able to figure the parameters and return values out ;)
      //
      // Note that you do not need to be sure the data returned is correct. For example, a part of the data requested may
      // have been missing on the server. You can just return anything in the buffer. If the sizes don't match, or the MD5 or CRC32
      // of the requested data does not match the listed values in the PAR2 files, the requested data is marked as PAR2_Available_Never
      // internally automatically.
      //
      // If you implement a memory cache of files on your side of things, just allocate as much memory as you can/want, and keep
      // the first N files that fit in there (last maybe partial ;)), read other data that is requested from disk when it is requested.
      // Do not rotate that cache - because of the way PAR2 works (not just this implementation), data will be requested in cycles.
      // If you would use a rotating cache (First In First Out (FIFO) or Last In Last Out (LILO)) this will just make you use more
      // memory and gain no performance at all. So your cache should be non-rotating (First In Last Out (FILO) or Last In First Out (LIFO))
      // (Yes, oh observant reader, FIFO == LILO and FILO == LIFO, but different people use different terms)
      TPAR2_DataNeededCallback = function(UserData: Cardinal; Filename: PChar; StartPos: Int64; Length: Cardinal; var Buffer: Pointer; var BufferSize: Cardinal; var FreeBuffer: Cardinal): Cardinal; stdcall;

  // Get some progress! How many (original) data blocks have we read and checked out of how many (original)?
  TPAR2_ReadProgressCallback = procedure(UserData: Cardinal; Current: Cardinal; Maximum: Cardinal; Percent: Cardinal); stdcall;

  // We got a repaired data block! If it's the last block of a file, BufferSize is smaller than the block size.
  TPAR2_RepairedBlockCallback = procedure(UserData: Cardinal; Filename: PChar; StartPos: Int64; Buffer: Pointer; BufferSize: Cardinal); stdcall;

  // Get some progress! Stage is one of the PAR2_Repair_Progress_.... codes. isPercent can be PAR2_True or PAR2_False, and it
  // tells you the Percent value is used to show where we are in that stage. The stages that use percentages are called first
  // with PAR2_Repair_Progress_Percent_Start and last with PAR2_Repair_Progress_Percent_Done.
  TPAR2_RepairProgressCallback = procedure(UserData: Cardinal; Stage: Cardinal; isPercent: Cardinal; Percent: Cardinal); stdcall;

  // Free some memory you have allocated before
  TPAR2_FreeMemCallback = procedure(UserData: Cardinal; P: Pointer); stdcall;

// Call this first. The return value is the handle used by other calls.
// MaxMem is in bytes. This does not actually guarantee that there will not be more memory used then MaxMem. Constructing the repair
// matrix is something that has to be done and will use as much memory as needed. For the actual repair, the least amount of memory
// used will equal twice the blocksize. However, if MaxMem > BlockSize then multiple blocks can be repaired in parallel which can
// give a fairly large speed increase, especially if blocks are read from disk instead of memory. Advised is at least 16mb. Note that
// this MaxMem is only relevant to repair blocks. Say you have a 2000 blocks repair which has 100 blocks missing, actual maximum memory
// used would be 100 * BlockSize. So if BlockSize would be the often-used 384000 bytes, about 38mb would be optimal. You can deduct the
// amount of missing blocks from adding up all the BlocksNeeded values in the PARNeeded calls.
function  PAR2_Init(MaxMem: Cardinal): Cardinal; stdcall; {$IFNDEF DLL} external PAR2_DLL; {$ENDIF}

// Call these after init. Set your callbacks for a better life.
procedure PAR2_SetPARAvailable(Handle: Cardinal; PARAvailableCallback: TPAR2_PARAvailableCallback); stdcall; {$IFNDEF DLL} external PAR2_DLL; {$ENDIF}
procedure PAR2_SetPARNeeded(Handle: Cardinal; PARNeededCallback: TPAR2_PARNeededCallback); stdcall; {$IFNDEF DLL} external PAR2_DLL; {$ENDIF}
procedure PAR2_SetDataAvailable(Handle: Cardinal; DataAvailableCallback: TPAR2_DataAvailableCallback); stdcall; {$IFNDEF DLL} external PAR2_DLL; {$ENDIF}
procedure PAR2_SetDataNeeded(Handle: Cardinal; DataNeededCallback: TPAR2_DataNeededCallback); stdcall; {$IFNDEF DLL} external PAR2_DLL; {$ENDIF}
procedure PAR2_SetReadProgress(Handle: Cardinal; ReadProgressCallback: TPAR2_ReadProgressCallback); stdcall; {$IFNDEF DLL} external PAR2_DLL; {$ENDIF}
procedure PAR2_SetRepairedBlock(Handle: Cardinal; RepairedBlockCallback: TPAR2_RepairedBlockCallback); stdcall; {$IFNDEF DLL} external PAR2_DLL; {$ENDIF}
procedure PAR2_SetRepairProgress(Handle: Cardinal; RepairProgressCallback: TPAR2_RepairProgressCallback); stdcall; {$IFNDEF DLL} external PAR2_DLL; {$ENDIF}
procedure PAR2_SetFreeMem(Handle: Cardinal; FreeMemCallback: TPAR2_FreeMemCallback); stdcall; {$IFNDEF DLL} external PAR2_DLL; {$ENDIF}

// Call this to set UserData. This values will be returned in every callback
procedure PAR2_SetUserData(Handle: Cardinal; UserData: Cardinal); stdcall; {$IFNDEF DLL} external PAR2_DLL; {$ENDIF}

// If you have called PAR2_Init and set up all the callbacks, call this function
// everytime you have received/downloaded/found a file until it returns PAR2_Go_Success, PAR2_Go_Error_Call_Repair
// PAR2_Error_Repair_Not_Possible. If the return value > PAR2_Go_Success
// but < PAR2_Go_Error_Base, the return value is the amount of blocks
// still needed to complete the file. That is only FYI, do not use it to select
// which files you still want to get, you should determine that from the
// PARNeeded and DataNeeded callbacks.
function  PAR2_Go(Handle: Cardinal): Cardinal; stdcall; {$IFNDEF DLL} external PAR2_DLL; {$ENDIF}

// Repair the data. ONLY call this if PAR2_Go has returned PAR2_Go_Error_Call_Repair (!!!!)
// Returns PAR2_True if repair was succesful, PAR2_False if not.
function  PAR2_Repair(Handle: Cardinal): Cardinal; stdcall; {$IFNDEF DLL} external PAR2_DLL; {$ENDIF}

// Call this last. When everything's done, free all used memory and such.
procedure PAR2_Free(Handle: Cardinal); stdcall; {$IFNDEF DLL} external PAR2_DLL; {$ENDIF}

implementation

{$IFDEF DLL}

uses par2;

function  PAR2_Init(MaxMem: Cardinal): Cardinal; stdcall;
begin
  Result := Cardinal(TPAR2.Create(MaxMem));
end;

procedure PAR2_SetPARAvailable(Handle: Cardinal; PARAvailableCallback: TPAR2_PARAvailableCallback); stdcall;
var P: TPAR2;
begin
  Cardinal(P) := Handle;
  P.PARAvailable := PARAvailableCallback;
end;

procedure PAR2_SetPARNeeded(Handle: Cardinal; PARNeededCallback: TPAR2_PARNeededCallback); stdcall;
var P: TPAR2;
begin
  Cardinal(P) := Handle;
  P.PARNeeded := PARNeededCallback;
end;

procedure PAR2_SetDataAvailable(Handle: Cardinal; DataAvailableCallback: TPAR2_DataAvailableCallback); stdcall;
var P: TPAR2;
begin
  Cardinal(P) := Handle;
  P.DataAvailable := DataAvailableCallback;
end;

procedure PAR2_SetDataNeeded(Handle: Cardinal; DataNeededCallback: TPAR2_DataNeededCallback); stdcall;
var P: TPAR2;
begin
  Cardinal(P) := Handle;
  P.DataNeeded := DataNeededCallback;
end;

procedure PAR2_SetReadProgress(Handle: Cardinal; ReadProgressCallback: TPAR2_ReadProgressCallback); stdcall; {$IFNDEF DLL} external PAR2_DLL; {$ENDIF}
var P: TPAR2;
begin
  Cardinal(P) := Handle;
  P.ReadProgress := ReadProgressCallback;
end;

procedure PAR2_SetRepairedBlock(Handle: Cardinal; RepairedBlockCallback: TPAR2_RepairedBlockCallback); stdcall;
var P: TPAR2;
begin
  Cardinal(P) := Handle;
  P.RepairedBlock := RepairedBlockCallback;
end;

procedure PAR2_SetRepairProgress(Handle: Cardinal; RepairProgressCallback: TPAR2_RepairProgressCallback); stdcall;
var P: TPAR2;
begin
  Cardinal(P) := Handle;
  P.RepairProgress := RepairProgressCallback;
end;

procedure PAR2_SetFreeMem(Handle: Cardinal; FreeMemCallback: TPAR2_FreeMemCallback); stdcall;
var P: TPAR2;
begin
  Cardinal(P) := Handle;
  P.ExtFreeMem := FreeMemCallback; 
end;

procedure PAR2_SetUserData(Handle: Cardinal; UserData: Cardinal); stdcall;
var P: TPAR2;
begin
  Cardinal(P) := Handle;
  P.UserData := UserData;
end;

function  PAR2_Go(Handle: Cardinal): Cardinal; stdcall;
var P: TPAR2;
begin
  Cardinal(P) := Handle;
  Result := P.Go;
end;

function  PAR2_Repair(Handle: Cardinal): Cardinal; stdcall;
var P: TPAR2;
begin
  Cardinal(P) := Handle;
  Result := P.Repair;
end;

procedure PAR2_Free(Handle: Cardinal); stdcall;
var P: TPAR2;
begin
  Cardinal(P) := Handle;
  P.Free;
end;

exports
  PAR2_Init,
  PAR2_SetPARAvailable,
  PAR2_SetPARNeeded,
  PAR2_SetDataAvailable,
  PAR2_SetDataNeeded,
  PAR2_SetReadProgress,
  PAR2_SetRepairedBlock,
  PAR2_SetRepairProgress,
  PAR2_SetFreeMem,
  PAR2_SetUserData,
  PAR2_Go,
  PAR2_Repair,
  PAR2_Free;
{$ENDIF}

end.
