/* LPF Large Page Format
   
   The LPF format is used to store .ANM animations created with:
   'DeluxePaint Animation (v1.0)' for IBM Personal System/2 and IBM-compatible computers 
   (c) 1985-1990 Electronic Arts
   Software Design and Development by Steve Shaw and the Multimedia Group of Electronic Arts 
   (Based on Amiga program by Dan Silva)
   Producer: Tom Casey
   
   
   Versions:
   v0.1: 30.03.2011: Definition of the LPF file format structs
   
   
   History:
   This work is based on a Borland-C source code with no name, written by Jari Komppa.
   
   Andy Hodgetts completely rewrote the code and deciphered the accompanied 'optimized' assembler file. 
   The result was a script ('ANM.C') written in C who only decoded the first frame of an LPF file.
   
   This is an other attempt by Werner and Walter Randelshofer to decrypt the structure of the LPF format.
   
   
   Copyrights: 
   - copyright (c) 1998 by Jari Komppa aka Sol/Trauma
   - copyright (c) by Andy Hodgetts
   - copyright (c) 2011 by Walter Randelshofer
   
   
   The LPF file format:
   - File suffix: .ANM
   - Screen mode: VGA 320 x 200 px (always)
   - Color mode: 8 bit, 256 colors (always)
   - Color palette: fixed color palette for the entire animation.
   - Color cycling: CRNG color cycling (max. 16 color cycling ranges).
   - Frame rate: fixed frame rate for the entire animation.
   - Compression: "Run, Copy & Skip", a loss less delta compression algorithm, very similar to RLE.
   - The animation is described in a LPF header and a LPF body.
   - The LPF header has a fixed size of 2'816 bytes.
   - Frames are stored in so called 'Large Pages' with a maximal size of 65'536 bytes.
   - The format can store a maximum of 256 large pages.
   - A .ANM file can have a max. size of 16'780'032 bytes.
   - Large pages are filled with frames until the next frame doesn't fit anymore. Then the remaining space of 
     the large page is padded until it reaches the size of 65'536 bytes. The next frame will be moved to a new 
     large page. The last large page is never padded.
   - Large pages start at position 0xB00, 0x10B00, 0x20B00, 0x30B00, ...
   - The maximal number of frames depends on compression ratio! At minimum it's 256, max is (theoretically) 65'534. 
     If a frame compresses down to 50%, it will still take as much as a frame that doesn't compress at all!
   - Maximal amount of frames: max. 256 frames * 256 large pages = 65'536 including wrap-up frame.
   - Since a large page can't fit another one of frames of that size, and the 'frame split between large pages' 
     feature doesn't seem to be in use although it's in the specs ...
   
   
   Disclaimer:
   Since the LPF file format was reverse engineered the script my not fully support all features of the format or 
   deliver unexpected results.
   
*/





typedef uint8    CHAR;   //  8 bits unsigned
typedef shortLE  SHORT;  // 16 bits signed
typedef ushortLE   USHORT; // 16 bits unsigned
typedef intLE    INT;    // 32 bits signed
typedef longLE   LONG;   // 64 bits signed
typedef magic    MAGIC;  //  4 bytes unsigned


/* ============================================
 *
 * LPF header
 *
 * ============================================
 */
magic lpfHeader "Header";
description lpfHeader "Header", "Large Page Format (LPF) header.";


/* LPF 'Large Page Format' header */
enum {
  isANIM = 0 
} lpfhVariant;

enum {
  _timeBase18fps = 0, 
  _timeBase70fps = 1 
} lpfhVersion;

enum {
  hasWrapUp = 1 
} lpfhHasWrapUp;

enum {
  ignore = 0 
} lpfhWrapUp;

enum {
  ignoreWrapUp = 0 
} lpfhLastDeltaValid;

enum {
  _256colors = 0 
} lpfhColorMode;

enum {
  RSD = 1
} lpfhComprType;

enum {
  isOtherRecs = 0
} lpfhOtherRPF;

enum {
  _VGA = 1
} lpfhScrMode;

typedef struct {
  MAGIC id;                            /* LPF chunk: 4 character ID: "LPF ". */
  SHORT maxLps;                        /* Allowed max. number of 'Large Pages': 256 FOR NOW. */
  SHORT nLps;                          /* Number of 'Large Pages' in this file. */
  INT nRecords;                        /* Number of records in this file: 65'534 is current limit + wrap-up frame. */
  SHORT maxRecsPerLp;                  /* Permitted records per 'Large Page': 256 FOR NOW. */
  SHORT lpfTableOffset;                /* Absolute Seek position of first 'Large Page': 1280 FOR NOW. */
  
  MAGIC contentType;                   /* ANIM chunk: 4 character ID: "ANIM". */
  SHORT width;                         /* Screen width in pixels. */
  SHORT height;                        /* Screen height in pixels. */
  CHAR enum lpfhVariant   variant;     /* Variant: 0 == ANIM. */
  CHAR enum lpfhVersion   version;     /* Version: 0 == frame rate in 18/sec; 1 == frame rate in 70/sec. */
  CHAR enum lpfhHasWrapUp hasWrapUp;   /* Has wrap-up frame: 1 == yes. */
  CHAR enum lpfhWrapUp    wrapUp;      /* Wrap-up frame: 0 == ignore. */
  CHAR enum lpfhColorMode colorMode;   /* Color mode: 0 == 256 colors. */
  CHAR enum lpfhComprType comprType;   /* Compression type: 1 == RCS (Run, Copy & Skip). Only one used FOR NOW. */
  CHAR enum lpfhOtherRPF  otherRecsPF; /* Other records per frame: 0 FOR NOW. */
  CHAR enum lpfhScrMode   screenMode;  /* Screen mode: 1 == VGA mode (320x200, 256 colors). Only one implemented so far. */
  
  CHAR[32] recordTypes;                /* Not yet implemented. */
  
  INT nFrames;                         /* Total number of frames including wrap-up frame. */
  SHORT framesPerSecond;               /* Frames per second. */
  
  SHORT[29] padding;                    /* Padding == 0. Padding of 58 bytes to round up to 128 bytes total. */
} lpfHeader;



/* ============================================
 *
 * LPF CRNG Color Range Cycling (fixed amount of 16 CRNGs) 
 *
 * ============================================
 */
magic lpfColorCycling "ColorCycling";
description lpfColorCycling "Color Range Cycling", "Defines up to 16 color range cycles.";

//#define RNG_NORATE 36                /* DPaint uses this rate to mean non-active */
set {
  active        = 1,
  reverse       = 2,
  reverseDPaint = 0x20
} crngActive;

/* A color range cycling is stored in a CRNG chunk. */
typedef struct {
  SHORT pad1;                          /* reserved for future use; store 0 here */
  SHORT rate;                          /* 60/sec=16384, 30/sec=8192, 1/sec=16384/60=273 */
  SHORT set crngActive flags;          /* bit0 set = active, bit 1 set = reverse */
  CHAR low;                            /* lower color registers selected */
  CHAR high;                           /* upper color registers selected */
} lpfColorRangeChunk;

typedef struct {
  lpfColorRangeChunk crng[16];
} lpfColorCycling;


/* ============================================
 *
 * LPF Color Table (fixed amount of 256 colors) 
 *
 * ============================================
 */
magic lpfColorTable "ColorTable";
description lpfColorTable "Color Table", "Defines the 256 colors used by the animation.\nEach entry consists of a BGR value and one byte padding.";

/* Packed array of 256 color registers: 3 bytes (BGR) plus padding each */
typedef struct {
  CHAR[4] bgrp;                 /* Blue value ranging from 0 up to 255. */
                                /* Green value ranging from 0 up to 255. */
                                /* Red value ranging from 0 up to 255. */
                                /* Padding == 0 */
} lpfColorRegister;



/* Color table */
typedef struct {
  lpfColorRegister[256] color;
} lpfColorTable;



/* ============================================
 *
 * LPF Large Page Descriptor 
 *
 * ============================================
 */
magic lpfLargePageDescs "PageDescriptorTable";
description lpfLargePageDescs "Page Descriptor Table", "Descriptors for up to 256 large pages.";

/* Large page descriptor */
typedef struct {
  USHORT baseRecord;                   /* Number of first record in this large page. */
  USHORT nRecords;                     /* Number of records in lp. */
                                       /* bit 15 of "nRecords" == "has continuation from previous large page". */
                                       /* bit 14 of "nRecords" == "final record continues on next large page". */
                                       /* (these are not cared about and thankfully they don't seem to occur) */                                  
  USHORT nBytes;                       /* Total number of bytes of contents, excluding header. */
} lpDescriptor;



/* Large page descriptor table. */
typedef struct {
  lpDescriptor[256] largePage;
} lpfLargePageDescs;


/** Large Page. */
magic lpfLargePage "Page";
description lpfLargePage "Page", "16 KB large page.";

magic lpHeader "PageHeader";
description lpHeader "PageHeader", "Page Header.";

/* Large page descriptor */
typedef struct {
  USHORT baseRecord;                   /* Number of first record in this large page. */
  USHORT nRecords;                     /* Number of records in lp. */
                                       /* bit 15 of "nRecords" == "has continuation from previous large page". */
                                       /* bit 14 of "nRecords" == "final record continues on next large page". */
                                       /* (these are not cared about and thankfully they don't seem to occur) */                                  
  USHORT nBytes;                       /* Total number of bytes of contents, excluding header. */
  USHORT padding;
  USHORT recordSize[nRecords]; /* Record descriptor. */
} lpHeader;


magic lpRecord "Record";
description lpRecord "Record", "Data Record.";
magic lpPadding "Free";
description lpPadding "Free", "Free space in page.";


/*
   File structure of 'CRY.ANM'
   
   --- LPF file header ---
   000000: 4C 50 46 20  = 'LPF ' Chunk
   000004: 00 01        = Allowed max. of large pages: 256
   000006: 01 00        = Large pages: 1
   000008: 17 00 00 00  = Records: 23
   00000C: 00 01        = Permitted records per large page: 256
   00000E: 00 05        = Absolute seek position of first large page: 1'280
   
   000010: 41 4E 49 4D  = 'ANIM' Chunk
   000014: 40 01        = Width: 320 px
   000016: C8 00        = Height: 200 px
   000018: 00           = Variant: 0 = ANIM
   000019: 00           = Version: 0 = frame rate in 18/sec
   00001A: 01           = Has wrap-up frame: 1 = yes
   00001B: 01           = Last delta valid: 1 = yes ???
   00001C: 00           = Color mode: 0 = 256 colors
   00001D: 01           = Compression type: 1 = "Run, Copy & Skip"
   00001E: 00           = Other records per frame: 0 = ???
   00001F: 01           = Bitmap type: 1 = 320 x 200 px, 256 colors
   
   000020: 00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  = Record types: ???
           00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00
   
   000040: 17 00 00 00  = Frames including wrap-up frame: 23
   000044: 0A 00        = Frames per second: 10
   
   000046:                    00 00  00 00 00 00  00 00 00 00  = Padding: 58 bytes filler
           00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00
           00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00
           00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00
   
   --- CRNG Color Cycling (16 color cycling ranges) ---
   000080: 00 00 00 00  00 00 10 1F  00 00 00 00  00 00 20 2F  = Color Cycling???
           00 00 00 00  00 00 60 6F  00 00 00 00  00 00 90 9F
           00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00
           00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00
           00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00
           00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00
           00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00
           00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00
   
   
   --- Color palette (256 colors) ---
   000100: 00 00 00 00  00 00 00 00  23 23 F3 00  43 63 F3 00 = Palette: 1'024 bytes (4 * 256)
   ...
   0004F0: 83 2B C7 00  57 2B C7 00  2B 2B C7 00  FF FF FF 00
   
   
   --- Large page descriptor (256 large pages) ---
   000500: 00 00        = Number of first record in this large page: 0
   000502: 17 00        = Records in large page: 23
   000504: FF 82        = Total number of byte of contents, excluding header and stop markers: 33'535
   ...
   
   
   --- LPF table (Begin of first large page) --
   --- LP header (8 bytes+2 * number of Records) ---
   000B00: 00 00        = Number of first record in this large page: 0
   000B02: 17 00        = Records in large page: 23
   000B04: FF 82        = Total number of byte of contents, excluding header and stop markers: 33'535
   000B06: 00 00        = Padding: 2 bytes
   000B08: 01 16        = Size of Record 0
   000B0a: 01 16        = Size of Record 1
   000B0c: 01 16        = Size of Record 2
   000B0e: ...          = Size of Record ...

   --- Begin of first record ---
   000B48: 01 16 4F 00 ...
   ...
   
   --- Stop markers (2 bytes) denote the end of a frame ---
   XXXXXX: 00 80        = Stop marker: 0x0080
   
   --- Begin of second frame ---
   ...
   
   
   ...
   
   
   --- End of last large table and LPF file ---
   XXXXXX: 00           = The LPF file always ends with 0x00. A LPF file can have an uneven amount of total bytes.
   
   
*/




/* "Run, Copy & Skip" compression algorithm * /
static void decodeframe(char * srcP, char * dstP) {
 /*
  * Decodes one frame with "run, copy and skip" algorithm.
  * we have short and long versions of RLE, copy and skip,
  * plus 'stop' signal. This was fun to reconstruct from assembler.. :)
  *
  * Author: Andy Hodgetts, Jari Komppa
  *
  * Example:
  * decodeframe (ppointer, temp->framebuffer);
  * /
  int count, color;
  while (1) { // We're finished when we get terminate signal :) 
    count=*srcP;
    srcP++;
    if (count==0) { // Short RLE 
      count=*srcP;
      srcP++;
      color=*srcP;
      srcP++;
      memset(dstP,color,count);
      dstP+=count;
    } else if (!(count&0x80)) { // Short copy 
      memcpy(dstP,srcP,count);
      dstP+=count;
      srcP+=count;
    } else { // long op or short skip 
      count-=0x80;
      if (count>0) { // short skip 
        dstP+=count;
      } else { // long op 
        count=*srcP;
        srcP++;
        count+=(*srcP)<<8;
        srcP++;
        if (count==0) // stop sign 
          return;
        if (!(count&0x8000)) { // long skip 
          dstP+=count;
        } else {
          count-=0x8000;
          if (!(count&0x4000)) { // long copy
            memcpy(dstP,srcP,count);
            dstP+=count;
            srcP+=count;
          } else { // and finally, long RLE. 
            count-=0x4000;
            color=*srcP;
            srcP++;
            memset(dstP,color,count);
            dstP+=count;
          }
        }
      }
    }
  }
}
*/