//========================================================================
//
// serverppc.cc
//
// Copyright 1999 Emmanuel Lesueur
//
//========================================================================

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define Object ZZObject
#include <exec/nodes.h>
#include <exec/memory.h>
#include <powerup/ppclib/message.h>
#include <powerup/ppclib/tasks.h>
#include <powerup/ppclib/object.h>
#define strlen ZZstrlen
#include <powerup/gcclib/powerup_protos.h>
#undef strlen
#include <powerup/ppcproto/exec.h>
#include <powerup/ppcproto/dos.h>
#undef Object
#undef Allocate
#include "apdf/AComm.h"
#include "xpdf/rc4.h"
#include "xpdf/UnLZW.h"

#define DB(x) //x

static void* port;
static void* reply_port;
static void* decrypt_lib;
static void* unlzw_lib;
static BPTR modulesdir;

extern "C" {
  void (*do_rc4ExpandKey)( RC4KEY *rc4, unsigned char const *key, int keylen );
  void (*do_rc4Crypt)( RC4KEY *rc4, unsigned char *data, int len );
  void (*MD5_transform)(unsigned char *, unsigned int *);
  void (*MD5_encode)(unsigned char *, unsigned int *, unsigned int);

  const char *(*do_unlzw)(Pipe* in, Pipe* out, const char *cmd);
}


#ifndef NO_CUSTOM_MEM
void* myAllocMem(size_t sz) {
    return PPCAllocMem(sz,MEMF_ANY);
}

void myFreeMem(void* p,size_t sz) {
    PPCFreeMem(p,sz);
}
#endif

static comm_info* ppc_create_comm_info(size_t sz) {
    comm_info* ci=(comm_info*)malloc(sizeof(comm_info));
    if(ci) {
	ci->msg=PPCCreateMessage(reply_port,sz);
	ci->cmddata=PPCAllocVec(sz,MEMF_PUBLIC);
	if(!ci->msg || !ci->cmddata) {
	    if(ci->msg)
		PPCDeleteMessage(ci->msg);
	    if(ci->cmddata)
		PPCFreeVec(ci->cmddata);
	    free(ci);
	    ci=NULL;
	}
    }
    return ci;
}

static void ppc_delete_comm_info(comm_info* ci) {
    PPCDeleteMessage(ci->msg);
    PPCFreeVec(ci->cmddata);
    free(ci);
}

static int ppc_exchange_msg(struct comm_info *ci,struct msg_base *msg,size_t len,int id) {
    DB(printf("SV: exchange_msg(%lx,%lx,%ld,%ld)\n",ci,msg,len,id);)
    PPCSendMessage(port,ci->msg,msg,len,id);
    PPCWaitPort(reply_port);
    PPCGetMessage(reply_port);
    DB(printf("success=%d\n",msg?msg->success:1);)
    return msg?msg->success:1;
}

/*static void ppc_send_msg(struct comm_info *ci,void *msg,size_t len,int id) {
    DB(printf("SV: send_msg(%lx,%lx,%ld,%ld)\n",ci,msg,len,id);)
    PPCSendMessage(port,ci->msg,msg,len,id);
}*/

static void *ppc_get_msg(struct comm_info *ci,void **data,size_t *len,int *id) {
    void *msg;
    PPCWaitPort(reply_port);
    msg=PPCGetMessage(reply_port);
    *data=(void*)PPCGetMessageAttr(msg,PPCMSGTAG_DATA);
    *len=PPCGetMessageAttr(msg,PPCMSGTAG_DATALENGTH);
    *id=PPCGetMessageAttr(msg,PPCMSGTAG_MSGID);
    DB(printf("SV: get_msg(%lx)=(%lx,%lx,%ld,%ld)\n",ci,msg,*data,*len,*id);)
    return msg;
}

static void ppc_reply_msg(struct comm_info *ci,void *msg) {
    DB(printf("SV: reply_msg(%lx,%lx)\n",ci,msg);)
    PPCReplyMessage(msg);
}

bool decryptionEnabled() {
    if(!decrypt_lib) {
	struct TagItem tags[]={
	    {PPCELFLOADTAG_LIBVERSION,1},
	    {TAG_END}
	};
	char buf[256];
	NameFromLock(modulesdir,buf,sizeof(buf));
	AddPart(buf,"ApdfDecrypt_ppc.module",sizeof(buf));
	decrypt_lib=PPCOpenLibrary(buf,tags);
	if(decrypt_lib) {
	    do_rc4ExpandKey=(void (*)( RC4KEY *, unsigned char const *, int))
		PPCGetLibSymbol(decrypt_lib,"rc4ExpandKey");
	    do_rc4Crypt=(void (*)( RC4KEY *, unsigned char *, int))
		PPCGetLibSymbol(decrypt_lib,"rc4Crypt");
	    MD5_transform=(void (*)(unsigned char *, unsigned int *))
		PPCGetLibSymbol(decrypt_lib,"MD5_transform");
	    MD5_encode=(void (*)(unsigned char *, unsigned int *, unsigned int))
		PPCGetLibSymbol(decrypt_lib,"MD5_encode");
	}
    }
    return do_rc4ExpandKey && do_rc4Crypt && MD5_transform && MD5_encode;
}

const char* stub_unlzw(Pipe*,Pipe*,const char*) {
    return "No LZW decompression module.";
}

int main() {
    create_comm_info=ppc_create_comm_info;
    delete_comm_info=ppc_delete_comm_info;
    exchange_msg=ppc_exchange_msg;
    //send_msg=ppc_send_msg;
    get_msg=ppc_get_msg;
    reply_msg=ppc_reply_msg;

    StartupData* sd=(StartupData*)PPCGetTaskAttr(PPCTASKTAG_STARTUP_MSGDATA);
    port=sd->port;
    reply_port=(void*)PPCGetTaskAttr(PPCTASKTAG_MSGPORT);

    modulesdir=sd->dir;

    struct TagItem tags[]={
	{PPCELFLOADTAG_LIBVERSION,1},
	{TAG_END}
    };
    {
	char buf[256];
	NameFromLock(modulesdir,buf,sizeof(buf));
	AddPart(buf,"ApdfUnLZW_ppc.module",sizeof(buf));
	unlzw_lib=PPCOpenLibrary(buf,tags);
    }

    if(unlzw_lib)
	do_unlzw=(const char* (*)(Pipe*,Pipe*,const char*))PPCGetLibSymbol(unlzw_lib,"unlzw");
    if(!do_unlzw)
	do_unlzw=&stub_unlzw;

    comm_info ci;
    ci.msg=NULL;
    ci.cmddata=NULL;
    server(&ci);

    if(decrypt_lib)
	PPCCloseLibrary(decrypt_lib);

    if(unlzw_lib)
	PPCCloseLibrary(unlzw_lib);

    return 0;
}

#ifndef NO_CUSTOM_MEM
#ifdef DEBUGMEM
__asm__(".section \".fini\"");
__asm__(".long printunfreed");
__asm__(".long 108");
#endif

__asm__(".section \".init\"");
__asm__(".long meminit");
__asm__(".long 78");

__asm__(".section \".fini\"");
__asm__(".long memcleanup");
__asm__(".long 78");
#endif
