/*========================================================================= DPANM.HPP Author: Gary Liddon @ Fareham Created: Project: Purpose: Copyright (c) 1997 Gary Liddon ===========================================================================*/ /*---------------------------------------------------------------------- Includes -------- */ /* Std Lib ------- */ //#include <stdlib.h> #include <minmax.h> #include <fstream> #include <iostream> /* Glib ---- */ /* Local ----- */ #include "dpanim.hpp" //#include "ilbm.hpp" /* Name space ---------- */ using namespace std; /*---------------------------------------------------------------------- Structure defintions -------------------- */ typedef UWORD LP_TABLE_ELEMENT; #define MAX_NUMBER_CYCLES 16 #define BMBODY_RUNSKIPDUMP 1 #define MAX_COLORS 256 #define PALETTE_SIZE (MAX_COLORS * sizeof(long)) #define MAX_RECORDS 0xffffu #define MAX_LARGE_PAGE 256 #define LARGE_PAGE_SIZE 0x10000L #define LPF_HEADER_HEAD_SIZE_IN_FILE 256 #define LPF_HEADER_SIZE_IN_FILE ((sizeof(AnimHdr)) + (8 * 16) + (4 * 256) + (sizeof(LpTable[MAX_LARGE_PAGE]))) #define LP_FILE_OFFSET(nLp) ((nLp) * LARGE_PAGE_SIZE + LPF_HEADER_SIZE_IN_FILE) #define MakeID(d,c,b,a) (((ULONG)(a)<<24l) | ((ULONG)(b)<<16l) | ((ULONG)(c)<<8l) | ((ULONG)(d))) #define LARGE_PAGE_FILE_ID MakeID('L','P','F',' ') #define ANIM_CONTENTS_ID MakeID('A','N','I','M') #define FIRST_FRAME_N 1 #define FIRST_RECORD_N 0 // Records #d from 0. /*---------------------------------------------------------------------- Typedefs -------- */ class LpObj; /*---------------------------------------------------------------------- Class Defintions ---------------- */ /* Compressed Frame ---------------- */ class CompFrame : public GObject { public: CompFrame(void); CompFrame(CompFrame const & C); ~CompFrame(void); void operator=(CompFrame const &); /* Make Vectorable */ bool operator==(CompFrame const &) const; bool operator<(CompFrame const &) const; void WriteByte(u8 Byte); void WriteWord(u16 Word); void WriteData(u8 const * Src,int Amount); void Write(ofstream & Out) const; friend ostream & operator<<(ostream & str,CompFrame & Fr); u8 const * SeeData(void) const {return(BinData);} int GetSize() const {return(Len);} protected: void Dump(); void Init(); void Resize(int NewLen); void Increase(int NewLen) {Resize(ActualLen+NewLen);} u8 * BinData; int Len; int ActualLen; enum { RESIZE_CHUNKS=1000, }; }; typedef std::vector<CompFrame> CompFrameVec; /* Large Frame Object ------------------ */ class LpObj : public GObject { public: LpObj(void); bool AddFrame(CompFrame const & C ); void SetBaseRecord(int f){BaseRecord=f;} int NumOfFrames(void) { return(MyFrames.size());} void Write(std::ofstream & Out) const; void WriteHdr(std::ofstream & Out) const; /* Make Vectorable */ void operator=(LpObj const &); bool operator==(LpObj const &) const; bool operator<(LpObj const &) const; protected: int GetSizeInAll(void) const; int BaseRecord; CompFrameVec MyFrames; }; /*---------------------------------------------------------------------- Function Prototypes ------------------- */ static void GenerateLpTable(LpObjVec & LpTable,CompFrameVec & CompFrames); static int GetMaxRecsPerLp(LpObjVec & LpTable); static void WriteWord(std::ofstream & Out,u16 Word); /*---------------------------------------------------------------------- Vars ---- */ //static FilterDetails MyDetails(DpAnimFilter(),".anm"); /*---------------------------------------------------------------------- Data ---- */ /*---------------------------------------------------------------------- Function: Main ---------------------------------------------------------------------- */ DpAnimFilter::DpAnimFilter() { InitVars(); } DpAnimFilter::DpAnimFilter(char const * FName) : GAnimFilter(FName) { InitVars(); loaded_LP=0xffff; } DpAnimFilter::~DpAnimFilter(void) { DeleteVars(); } bool DpAnimFilter::Load(GAnim & Anm) { int Frames; Palette MyPal; MakeVars(); anm_init2(FileName); GetPal(MyPal); anm_read_first_frame(); Frames=0; do { Frame * NewFr; NewFr=&Anm.GetNewFrame(); NewFr->SetFrame(dst_buffer,320,200,MyPal); Frames++; } while(anm_read_next_frame()); fclose(inANM); DeleteVars(); return(true); } void DpAnimFilter::GetPal(Palette & MyPal) { fseek(inANM,sizeof(AnimHdr)+sizeof(ColCycles[MAX_NUMBER_CYCLES]),SEEK_SET); for (int f=0;f<256;f++) { MyPal[f].SetB(fgetc(inANM)); MyPal[f].SetG(fgetc(inANM)); MyPal[f].SetR(fgetc(inANM)); fgetc(inANM); } } void DpAnimFilter::InitVars(void) { lpfTable=NULL; current_LP=NULL; dst_buffer=NULL; } void DpAnimFilter::MakeVars(void) { DeleteVars(); if (!(lpfTable = new LpTable[MAX_LARGE_PAGE])) Error(ERM_OUTOFMEM); if (!(current_LP = new LpTableMem)) Error(ERM_OUTOFMEM); if (!(dst_buffer=new u8[128960])) Error(ERM_OUTOFMEM); } void DpAnimFilter::DeleteVars(void) { if (lpfTable) delete[] lpfTable; if (current_LP) delete current_LP; if (dst_buffer) delete dst_buffer; InitVars(); } bool DpAnimFilter::Save(GAnim & Anm) { MakeVars(); lpfHdr.InitHdr(); ofstream Out; Out.open(FileName,ios::trunc|ios::out|ios::binary); if (!Out) Error(ERR_FATAL,"Can't open %s for output",(char const *)FileName); else { int NumOfFrames; CompFrameVec CompFrames; LpObjVec MyLpTable; NumOfFrames=Anm.NumOfFrames(); if (NumOfFrames) { CompFrames.reserve(NumOfFrames); CompFrames.resize(NumOfFrames); for (int f=0;f<NumOfFrames;f++) Encode(Anm[f],CompFrames[f]); GenerateLpTable(MyLpTable,CompFrames); AnimHdr MyHdr; MyHdr.nLps=MyLpTable.size(); MyHdr.lRecords=NumOfFrames; MyHdr.maxRecsPerLp=GetMaxRecsPerLp(MyLpTable); MyHdr.nFrames=NumOfFrames; WriteHdr(Out,MyHdr); WriteCycs(Out); WritePal(Out,Anm[0].GetPal()); WriteLpTable(Out,MyLpTable); WriteLps(Out,MyLpTable); DiscardAnim(); } } Out.close(); return(true); } ULONG DpAnimFilter::anm_init2(char const * filename) { FILE * Fp; if(!(Fp=fopen(filename,"rb"))) Error(ERR_FATAL,"can't open %s",filename); return(anm_init(Fp)); } /*---------------------------------------------------------------------- Setup buffer for playback and buffer for LP. ---------------------------------------------------------------------- */ ULONG DpAnimFilter::anm_init(FILE * filename) { inANM = filename; fseek(inANM,0,SEEK_SET); fread(&lpfHdr,sizeof(AnimHdr),1,inANM); if ((lpfHdr.id != LARGE_PAGE_FILE_ID) || (lpfHdr.contentType != ANIM_CONTENTS_ID)) Error(ERR_FATAL,"Input file: %s is not an ANM file.",filename); fseek(inANM,lpfHdr.lpfTableOffset,SEEK_SET); fread(lpfTable,sizeof(LpTable[MAX_LARGE_PAGE]),1,inANM); loaded_LP = 0xffff; return(((lpfHdr.hasLastDelta) && (lpfHdr.lastDeltaValid)) ? (lpfHdr.nFrames - 1) : (lpfHdr.nFrames)); } /*---------------------------------------------------------------------- Read LP off disk into memory for playback. ---------------------------------------------------------------------- */ void DpAnimFilter::read_LP(UWORD LP_to_load) { if (LP_to_load == loaded_LP) return; loaded_LP = LP_to_load; fseek(inANM,LP_FILE_OFFSET(LP_to_load),0); fread(current_LP,(ULONG) (8l + lpfTable[LP_to_load].nRecords * 2l),1,inANM); fread(lp_buffer,(ULONG) lpfTable[LP_to_load].nBytes,1,inANM); } /*---------------------------------------------------------------------- Read next frame from anm file. ---------------------------------------------------------------------- */ int DpAnimFilter::anm_read_next_frame(void) { int loop; UWORD load_lp; UWORD base; ULONG lRecords; /* next frame */ current_Frame++; lRecords = lpfHdr.lRecords; if (lpfHdr.hasLastDelta && lpfHdr.lastDeltaValid) lRecords--; if (current_Frame == lRecords) return(0); load_lp = 0; while ((current_Frame >= (lpfTable[load_lp].baseRecord + lpfTable[load_lp].nRecords)) || (current_Frame < lpfTable[load_lp].baseRecord)) { load_lp++; } read_LP(load_lp); base = 0; /* Set start to beginning of buffer */ /* Find start of Frame by adding number of bytes of previous frames. */ for (loop = 0;loop <= (current_Frame - current_LP->baseRecord);loop++) base += current_LP->size[loop]; if (lp_buffer[base] != 'B') Error(ERR_FATAL,"Corrupt Anim File or Non Bitmapped image. base=%d,lp=%d in %s\n",base,load_lp,GetName()); if ((UWORD) lp_buffer[base + 2] != BMBODY_RUNSKIPDUMP) Error(ERR_FATAL,"Compression scheme for frames is unkown."); PlayRunSkipDump(&lp_buffer[base+4],dst_buffer); return(1); } /*---------------------------------------------------------------------- Read first frame of anm file. ---------------------------------------------------------------------- */ void DpAnimFilter::anm_read_first_frame(void) { lp_buffer = &dst_buffer[320u * 200u]; current_Frame = FIRST_FRAME_N; read_LP(loaded_LP); current_Frame = 0xffff; anm_read_next_frame(); } /*---------------------------------------------------------------------- Encode a frame with the DP anim codec and bung it into a comp frame ---------------------------------------------------------------------- */ void DpAnimFilter::Encode(Frame const & Fr,CompFrame & Cfr) { Frame NewFrame; u8 const * Src; int BytesLeft,Index; int const px=320; int const py=200; int const Size=px*py; NewFrame=Fr; NewFrame.Crop(Rect(0,0,px,py)); Src=NewFrame.SeeData(); BytesLeft=px*py; Cfr.WriteByte('B'); Cfr.WriteByte(0); Cfr.WriteWord(BMBODY_RUNSKIPDUMP); while (BytesLeft) { int Length; Index=(px*py)-BytesLeft; Length=GetRunLength(&Src[Index],BytesLeft); if (Length > 3) WriteRun(Src[Index],Length,Cfr); else { Length=GetDataLength(&Src[Index],BytesLeft); WriteDataRun(&Src[Index],Length,Cfr); } BytesLeft-=Length; } WriteEnd(Cfr); } int DpAnimFilter::GetRunLength(u8 const * Src,int BytesLeft) { int Index=0; int LastIndex; bool Done=false; int Previous=Src[0]; for (Index=0;Index<BytesLeft && !Done;Index++) { if (Src[Index] != Previous) Done=true; else LastIndex=Index; Previous=Src[Index]; } // cout<<"Run of "<<LastIndex+1<<endl; return(LastIndex+1); } int DpAnimFilter::GetDataLength(u8 const * Src,int BytesLeft) { int Index=0; int LastIndex=0; bool Done=false; int Previous=Src[0]+1; int RunLen=0; for (Index=0;Index<BytesLeft && !Done;Index++) { if (Src[Index] == Previous) { RunLen++; } else { LastIndex=Index; RunLen=0; } if (RunLen >= 2) Done=true; else Previous=Src[Index]; } // cout<<"Data Run of "<<LastIndex+1<<endl; return(LastIndex+1); } void DpAnimFilter::WriteDataRun(u8 const * Data,int Length,CompFrame &Cfr) { // cout<<"*** Writing unique of "<<Length<<endl; while (Length) { int ToWrite; ToWrite=min(MAX_RUN_WRITE,Length); if (ToWrite<128) Cfr.WriteByte(ToWrite); else { Cfr.WriteByte(0x80); Cfr.WriteWord(0x8000|ToWrite); } Cfr.WriteData(Data,ToWrite); Length-=ToWrite; } } void DpAnimFilter::WriteRun(u8 Val,int Length,CompFrame &Cfr) { // cout<<"*** Writing run of "<<Length<<endl; while (Length) { int ToWrite; ToWrite=min(MAX_RUN_WRITE,Length); if (ToWrite<128) { Cfr.WriteByte(0); Cfr.WriteByte(ToWrite); } else { Cfr.WriteByte(0x80); Cfr.WriteWord(0xc000|ToWrite); } Cfr.WriteByte(Val); Length-=ToWrite; } } void DpAnimFilter::WriteEnd(CompFrame &Cfr) { Cfr.WriteByte(0x80); Cfr.WriteWord(0); } void DpAnimFilter::PlayRunSkipDump(UBYTE const *src, UBYTE *dst) { BOOL Done; UBYTE * odst; odst=dst; Done=FALSE; while (!Done) { u8 count; count=*src++; if (count == 0) { u8 clearlength,pixel; clearlength=*src++; pixel=*src++; memset(dst,pixel,clearlength); dst+=clearlength; } else { if (!(count&0x80)) { memcpy(dst,src,count); dst+=count; src+=count; } else { count&=~0x80; if (count==0) { uint wordcount; wordcount=(*src++); wordcount|=(*src++)<<8; if (wordcount==0) Done=TRUE; else { if (wordcount&0x8000) { wordcount&=~0x8000; if(wordcount&0x4000) { u8 pixel; wordcount&=~0x4000; pixel=*src++; memset(dst,pixel,wordcount); } else { memcpy(dst,src,wordcount); src+=wordcount; } } dst+=wordcount; } } else dst+=count; } } } } /*---------------------------------------------------------------------- DpAnimFIlter Sub Classes ---------------------------------------------------------------------- */ DpAnimFilter::AnimHdr::AnimHdr(void) { InitHdr(); } void DpAnimFilter::AnimHdr::InitHdr(void) { id=MakeID('L','P','F',' '); maxLps=256; nLps=0; /* updated later */ lRecords=0; /* updated later */ maxRecsPerLp=256; /* updated later */ lpfTableOffset=1280; contentType=MakeID('A','N','I','M'); width=320; height=200; variant=0; version=0; hasLastDelta=0; lastDeltaValid=0; pixelType=0; highestBBComp=1; otherRecordsPerFrame=0; bitmapRecordsPerFrame=1; nFrames=0; /* may be updated later */ framesPerSecond=10; } DpAnimFilter::ColCycles::ColCycles(void) { rate=0; count=0; flags=0; low=0x10; low=0x1f; } int GetMaxRecsPerLp(LpObjVec & LpTable) { int MaxLps=0; int f; for (f=0;f<LpTable.size();f++) { if (LpTable[f].NumOfFrames() > MaxLps) MaxLps=LpTable[f].NumOfFrames(); } return(MaxLps); } void GenerateLpTable(LpObjVec & LpTable,CompFrameVec & CompFrames) { LpObj * ThisObj; int LpIndex; int CompIndex; bool Done; ThisObj=NULL; LpIndex=0; CompIndex=0; Done=false; while (!Done) { if (CompIndex==CompFrames.size()) Done=TRUE; else { if (!ThisObj) { LpTable.resize(LpTable.size()+1); ThisObj=&LpTable[LpTable.size()-1]; ThisObj->SetBaseRecord(CompIndex); } if (ThisObj->AddFrame(CompFrames[CompIndex])) CompIndex++; else ThisObj=NULL; } } } /*---------------------------------------------------------------------- Anim writing stuff ---------------------------------------------------------------------- */ void DpAnimFilter::MakeAnim(void) { } void DpAnimFilter::WriteHdr(ofstream & Out,AnimHdr & A) { Out.write((char const *) &A,sizeof(A)); } void DpAnimFilter::WriteCycs(ofstream & Out) { ColCycles MyCycles[MAX_NUMBER_CYCLES]; Out.write((char const *) MyCycles,sizeof(MyCycles)); } void DpAnimFilter::WritePal(ofstream & Out,Palette const & P) { for (int f=0;f<256;f++) { Out.put((u8)P[f].GetB()); Out.put((u8)P[f].GetG()); Out.put((u8)P[f].GetR()); Out.put((u8)0); } } void DpAnimFilter::WriteLpTable(ofstream & Out,LpObjVec & LpTable) { int WriteFromTable; int WriteDummy; int Val=Out.tellp(); WriteFromTable=min(MAX_LARGE_PAGE,LpTable.size()); for (int f=0;f<WriteFromTable;f++) LpTable[f].WriteHdr(Out); WriteDummy=MAX_LARGE_PAGE-WriteFromTable; if (WriteDummy) { LpObj Dummy; for (int f=0;f<WriteDummy;f++) Dummy.WriteHdr(Out); } int NewVal=Out.tellp(); Val=NewVal-Val; } void DpAnimFilter::WriteLps(ofstream & Out,LpObjVec & LpTable) { for (int f=0;f<LpTable.size();f++) { int Written=Out.tellp(); LpTable[f].Write(Out); int CurrentPos; CurrentPos=Out.tellp(); Written=CurrentPos-Written; int ToWrite=LARGE_PAGE_SIZE-Written; if (ToWrite>0) { for (int f=0;f<ToWrite;f++) Out.put((char)'z'); } } } void DpAnimFilter::DiscardAnim(void) { } /*---------------------------------------------------------------------- A Compressed Frame ---------------------------------------------------------------------- */ CompFrame::CompFrame(void) { Init(); Resize(RESIZE_CHUNKS); } void CompFrame::Resize(int NewLen) { u8 * NewData; if(!(NewData=new u8[NewLen])) Error(ERM_OUTOFMEM); if (BinData) { int ToCopy; ToCopy=min(Len,NewLen); memcpy(NewData,BinData,ToCopy); delete BinData; } BinData=NewData; ActualLen=NewLen; Len=min(Len,ActualLen); } void CompFrame::Init(void) { Len=0; ActualLen=0; BinData=NULL; } void CompFrame::Dump(void) { if (BinData) delete BinData; Init(); } CompFrame::~CompFrame(void) { Dump(); } void CompFrame::WriteByte(u8 Byte) { if ((Len+1) >= ActualLen) Increase(RESIZE_CHUNKS); BinData[Len]=Byte; Len+=1; } void CompFrame::WriteWord(u16 Word) { if ((Len+2) >= ActualLen) Increase(RESIZE_CHUNKS); BinData[Len]=Word&0xff; BinData[Len+1]=Word>>8; Len+=2; } void CompFrame::WriteData(u8 const * Src,int Amount) { if ((Len+Amount) >= ActualLen) Increase(Amount+RESIZE_CHUNKS); memcpy(&BinData[Len],Src,Amount); Len+=Amount; } void CompFrame::Write(ofstream & Out) const { if (Len) { char const * Data; Data=(char const *)BinData; Out.write(Data,Len); } } ostream & operator<<(ostream & str,CompFrame & Fr) { str<<"size: "<<Fr.GetSize(); return(str); } void CompFrame::operator=(CompFrame const & Fr) { Dump(); Resize(Fr.Len); if (Fr.Len) memcpy(BinData,Fr.BinData,Fr.Len); Len=Fr.Len; } CompFrame::CompFrame(CompFrame const & C) { Init(); *this=C; } /*---------------------------------------------------------------------- Large Page Shite ---------------------------------------------------------------------- */ LpObj::LpObj(void) { BaseRecord=0; } void LpObj::WriteHdr(ofstream & Out) const { WriteWord(Out,BaseRecord); WriteWord(Out,MyFrames.size()); WriteWord(Out,GetSizeInAll()+1+1+2); } void LpObj::Write(ofstream & Out) const { int f; WriteWord(Out,BaseRecord); WriteWord(Out,MyFrames.size()); WriteWord(Out,GetSizeInAll()+1+1+2); WriteWord(Out,0); for (f=0;f<MyFrames.size();f++) WriteWord(Out,MyFrames[f].GetSize()); for (f=0;f<MyFrames.size();f++) { MyFrames[f].Write(Out); } } bool LpObj::AddFrame(CompFrame const & C ) { if (((GetSizeInAll()+C.GetSize()) > 64000) && MyFrames.size()) return(false); else { MyFrames.push_back(C); return(true); } } void LpObj::operator=(LpObj const & No) { BaseRecord=No.BaseRecord; MyFrames=No.MyFrames; } int LpObj::GetSizeInAll(void) const { int Total=0; for (int f=0;f<MyFrames.size();f++) Total+=MyFrames[f].GetSize(); return(Total); } /*---------------------------------------------------------------------- Helpy Stuff ---------------------------------------------------------------------- */ void WriteWord(ofstream & Out,u16 Word) { Out.put((u8)(Word&0xff)); Out.put((u8)(Word>>8)); } /*=========================================================================== end */