/* cdromemu.c:
 *
 * Emulates trackdisk.device commands CMD_READ and TD_CHANGESTATE.
 *
 * Based on the example_device program by SAS.
 */

#define  _USEOLDEXEC_ 1
#include <exec/types.h>
#include <exec/nodes.h>
#include <exec/memory.h>
#include <exec/resident.h>
#include <exec/libraries.h>
#include <exec/execbase.h>
#include <exec/io.h>
#include <exec/errors.h>
#include <libraries/dos.h>
#include <devices/trackdisk.h>
#include <dos/dostags.h>
#include <utility/tagitem.h>
#include <intuition/intuition.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/intuition.h>

#include <string.h>
#include <dos.h>

#define CMD_TERM     0x7ff0
#define CMD_STARTUP  0x7ff1

struct MsgPort *myPort;
extern struct ExecBase *SysBase;

struct IntuitionBase* IntuitionBase;

struct START_MSG {
        struct Message msg;
        long devbase;
};

void cmd_handler(void)
{
    struct IORequest *ior;
    struct IOExtTD *iotd;
    long input;
    struct Process *proc;   
    struct START_MSG *msg;
    char filename[80];
    static struct EasyStruct easy = {
      sizeof (struct EasyStruct),
      0,
      "cdromemu error",
      NULL,
      "Abort"
    };
    short ok = 1;
    long res;

    proc = (struct Process *)FindTask((char *)NULL);

    /* get the startup message */
    while((msg = (struct START_MSG *)GetMsg(&proc->pr_MsgPort)) == NULL) 
        WaitPort(&proc->pr_MsgPort);
    
    /* builtin compiler functions to set A4 to the global */
    /* data area */
    putreg(REG_A6, msg->devbase); 
    geta4();
    myPort = CreatePort(0,0);
    ReplyMsg((struct Message *)msg);

    if (myPort == NULL) return;

    IntuitionBase = (struct IntuitionBase *) OpenLibrary ("intuition.library", 0);

    if (GetVar ("CDROM_FILE", filename, 79, 0) < 0) {
      easy.es_TextFormat = "Environment variable CDROM_FILE not defined.";
      if (IntuitionBase)
        EasyRequest (NULL, &easy, NULL, NULL);
      ok = 0;
    }

    if (ok) {
      input = Open(filename, MODE_OLDFILE);
      if (input == NULL) {
        easy.es_TextFormat = "Cannot open file %s";
        if (IntuitionBase)
          EasyRequest (NULL, &easy, NULL, filename, NULL);
        ok = 0;
      }
    }
    
    while (1) 
    {
        WaitPort(myPort);
        while (ior = (struct IORequest *)GetMsg(myPort)) 
        {
	    switch(ior->io_Command) 
            {
                case CMD_TERM:
		    if (ok)
                      Close(input);
                    Forbid();
                    ReplyMsg(&ior->io_Message);
                    return;
                    
                case CMD_READ:
		    if (ok) {
                      iotd = (struct IOExtTD *)ior;
		      res = Seek (input, iotd->iotd_Req.io_Offset,
		      		  OFFSET_BEGINNING);
		      if (res < 0) {
		        iotd->iotd_Req.io_Error = 1;
		        break;
		      }
                      iotd->iotd_Req.io_Actual = Read(input,
                                             iotd->iotd_Req.io_Data, 
                                             iotd->iotd_Req.io_Length);
		    } else
		      iotd->iotd_Req.io_Actual = 0;
                    break;
            }
            ReplyMsg(&ior->io_Message);
        }
    }
}


int  __saveds __asm __UserDevInit(register __d0 long unit,
                                  register __a0 struct IORequest *ior,
                                  register __a6 struct MyLibrary *libbase)
{
      struct Process *myProc;
      struct START_MSG msg;
      
      if (SysBase->LibNode.lib_Version < 36)
          return 1; /* can only run under 2.0 or greater */ 
      
      myProc = CreateNewProcTags(NP_Entry, cmd_handler,
                                 NP_StackSize, 4096,
                                 NP_Name, "CMD_Handler",
                                 TAG_DONE);
      if (myProc == NULL) 
        return 1;

      /* Send the startup message with the library base pointer */
      msg.msg.mn_Length = sizeof(struct START_MSG) - 
                          sizeof (struct Message);
      msg.msg.mn_ReplyPort = CreatePort(0,0);
      msg.msg.mn_Node.ln_Type = NT_MESSAGE;
      msg.devbase = getreg(REG_A6);
      PutMsg(&myProc->pr_MsgPort, (struct Message *)&msg);
      WaitPort(msg.msg.mn_ReplyPort);

      if (myPort == NULL) /* CMD_Handler allocates this */
        return NULL;
      
      DeletePort(msg.msg.mn_ReplyPort);
      
      return 0;
}


void __saveds __asm __UserDevCleanup(register __a0 struct IORequest *ior,
                                     register __a6 struct MyLibrary *libbase)
{
    struct IORequest newior;    
        
    /* send a message to the child process to shut down. */
    newior.io_Message.mn_ReplyPort = CreateMsgPort();
    newior.io_Command = CMD_TERM;
    newior.io_Unit = ior->io_Unit;

    PutMsg(myPort, &newior.io_Message);
    WaitPort(newior.io_Message.mn_ReplyPort);
    DeleteMsgPort(newior.io_Message.mn_ReplyPort);
    DeletePort(myPort);
}


void __saveds __asm DevBeginIO(register __a1 struct IORequest *ior)
{
    ior->io_Error = 0;

    ior->io_Flags &= ~IOF_QUICK;
    switch(ior->io_Command) 
    {
        case CMD_READ:
          PutMsg(myPort, &ior->io_Message);
          break;
 
        case TD_CHANGESTATE: {
          struct IOExtTD *iotd = (struct IOExtTD*) ior;
          iotd->iotd_Req.io_Actual = 0;
 	  ReplyMsg(&ior->io_Message);
	  break;
        }
        default:
            ior->io_Error = IOERR_NOCMD;
 	    ReplyMsg(&ior->io_Message);
           break;
    }
}

void __saveds __asm DevAbortIO(register __a1 struct IORequest *ior)
{
}
