#include <stddef.h>
#include <exec/types.h>
#include <exec/libraries.h>
#include <exec/resident.h>
#include <dos/dos.h>
#include <proto/exec.h>
#include "rc4.h"
#include "md5.h"

#ifdef __SASC
#   define SAVEDS    //__saveds confuses data=faronly
#   define ASM       __asm
#   define REG(r,a)  register __ ## r a
#elif defined(__GNUC__)
#   define SAVEDS    /*__geta4*/
#   define ASM
#   define REG(r,a)  a __asm(#r)
#endif

struct MyLibrary {
    struct Library lib;
    BPTR seglist;
};

#define LIB_VERSION  1
#define LIB_REVISION 0

int LibReserved(void) {
    return 0;
}

static const APTR LibInitTable[];
static const char LibName[];
static const char LibId[];

struct MyLibrary * ASM LibInit(REG(a0,BPTR seglist),
			       REG(d0,struct MyLibrary *base),
			       REG(a6,struct Library *sysbase));
struct MyLibrary * ASM LibOpen(REG(a6,struct MyLibrary *base));
BPTR ASM LibClose(REG(a6,struct MyLibrary *base));
BPTR ASM LibExpunge(REG(a6,struct MyLibrary *base));

void Librc4ExpandKey(REG(a0,RC4KEY *rc4),
		     REG(a1,unsigned char const *key),
		     REG(d0,int keylen));
void Librc4Crypt(REG(a0,RC4KEY *rc4),
		 REG(a1,unsigned char *data),
		 REG(d0,int len));
void LibMD5_transform(REG(a0,uint1 block[64]),
		      REG(a1,uint4 state[4]));
void LibMD5_encode(REG(a0,uint1 *output),
		   REG(a1,uint4 *input),
		   REG(d0,uint4 len));

static const struct Resident romtag={
    RTC_MATCHWORD,
    (struct Resident *)&romtag,
    (APTR)((&romtag)+1),
    RTF_AUTOINIT,
    LIB_VERSION,
    NT_LIBRARY,
    0,
    (char*)LibName,
    (char*)LibId,
    (APTR)LibInitTable
};

static const APTR LibFuncTable[]={
    LibOpen,
    LibClose,
    LibExpunge,
    LibReserved,
    Librc4ExpandKey,
    Librc4Crypt,
    LibMD5_transform,
    LibMD5_encode,
    (APTR)-1
};

const char LibName[]="ApdfDecrypt_68k.module";
const char LibId[]="ApdfDecrypt_68k.module 1.0 (21.10.99)";

#define INITBYTE_ENTRY(x)   UWORD a##x;UWORD b##x;UBYTE c##x;UBYTE d##x
#define INITWORD_ENTRY(x)   UWORD a##x;UWORD b##x;UWORD c##x
#define INITAPTR_ENTRY(x)   UWORD a##x;UWORD b##x;APTR c##x
#define INITBYTE(S,E,x)     0xe000,offsetof(struct S,E),(x),0
#define INITWORD(S,E,x)     0xd000,offsetof(struct S,E),(x)
#define INITAPTR(S,E,x)     0xc000,offsetof(struct S,E),(x)

static const struct {
    INITBYTE_ENTRY(0);
    INITAPTR_ENTRY(1);
    INITBYTE_ENTRY(2);
    INITWORD_ENTRY(3);
    INITWORD_ENTRY(4);
    INITAPTR_ENTRY(5);
    UWORD end;
} LibInitData={
    INITBYTE(Node,ln_Type,NT_LIBRARY),
    INITAPTR(Node,ln_Name,(APTR)LibName),
    INITBYTE(Library,lib_Flags,LIBF_SUMUSED|LIBF_CHANGED),
    INITWORD(Library,lib_Version,LIB_VERSION),
    INITWORD(Library,lib_Revision,LIB_REVISION),
    INITAPTR(Library,lib_IdString,(APTR)LibId),
    0
};

static const APTR LibInitTable[]={
    (APTR)((sizeof(struct MyLibrary)+3)&~3),
    (APTR)LibFuncTable,
    (APTR)&LibInitData,
    (APTR)LibInit
};

struct MyLibrary * SAVEDS ASM LibInit(REG(a0,BPTR seglist),
				    REG(d0,struct MyLibrary *base),
				    REG(a6,struct Library *sysbase)) {
    base->seglist=seglist;
    return base;
}

struct MyLibrary * SAVEDS ASM LibOpen(REG(a6,struct MyLibrary *base)) {
    ++base->lib.lib_OpenCnt;
    base->lib.lib_Flags&=~LIBF_DELEXP;
    return base;
}

BPTR SAVEDS ASM LibClose(REG(a6,struct MyLibrary *base)) {
    BPTR retval=0;
    if(base->lib.lib_OpenCnt>0)
	--base->lib.lib_OpenCnt;
    if(base->lib.lib_OpenCnt==0 && base->lib.lib_Flags&LIBF_DELEXP)
	retval=LibExpunge(base);
    return retval;
}

BPTR SAVEDS ASM LibExpunge(REG(a6,struct MyLibrary *base)) {
    BPTR ret=0;
    base->lib.lib_Flags|=LIBF_DELEXP;
    if(base->lib.lib_OpenCnt==0) {
	struct Library *SysBase=*(struct Library**)4;
	ret=base->seglist;
	Remove(&base->lib.lib_Node);
	FreeMem((UBYTE *)base-base->lib.lib_NegSize,
		base->lib.lib_NegSize+base->lib.lib_PosSize);
    }
    return ret;
}


void rc4ExpandKey( RC4KEY *rc4, unsigned char const *key, int keylen );
void rc4Crypt( RC4KEY *rc4, unsigned char *data, int len );
void MD5_transform (uint1 block[64], uint4 state[4]);
void MD5_encode (uint1 *output, uint4 *input, uint4 len);

void SAVEDS ASM Librc4ExpandKey(REG(a0,RC4KEY *rc4),
				REG(a1,unsigned char const *key),
				REG(d0,int keylen)) {
    rc4ExpandKey(rc4,key,keylen);
}

void SAVEDS ASM Librc4Crypt(REG(a0,RC4KEY *rc4),
			    REG(a1,unsigned char *data),
			    REG(d0,int len)) {
    rc4Crypt(rc4,data,len);
}

void SAVEDS ASM LibMD5_transform(REG(a0,uint1 block[64]),
				 REG(a1,uint4 state[4])) {
    MD5_transform(block,state);
}

void SAVEDS ASM LibMD5_encode(REG(a0,uint1 *output),
			      REG(a1,uint4 *input),
			      REG(d0,uint4 len)) {
    MD5_encode(output,input,len);
}

