
/****************************************************************************
 *
 * skeleton.c
 *
 * This file is a product of Criterion Software Ltd.
 *
 * This file is provided as is with no warranties of any kind and is
 * provided without any obligation on Criterion Software Ltd.
 * or Canon Inc. to assist in its use or modification.
 *
 * Criterion Software Ltd. and Canon Inc. will not, under any
 * circumstances, be liable for any lost revenue or other damages
 * arising from the use of this file.
 *
 * Copyright (c) 1999, 2000 Criterion Software Ltd.
 * All Rights Reserved.
 *
 */

#include <stdio.h>
#include <math.h>
#include <ctype.h>
#include <string.h>

#include "rwcore.h"
#include "rtpng.h"
#include "rtbmp.h"
#include "rtcharse.h"

#include "skeleton.h"
#include "platform.h"

#ifdef RWMOUSE
#include "mouse.h"
#endif

#ifdef RWTERMINAL
#include "terminal.h"
#endif /* RWTERMINAL */

/* 
 * TODO: we need a pikeyboa.c equivalent to take care of 
 * scancode to rs conversion... 
 */

#define NUMVIEWFILECOLUMNS (7)
#define DEFAULTVIEWPREFIX (RWSTRING("View"))

/* *INDENT-OFF* */
static RwUInt8 
KeysNormal[]=
{
    rsNULL,rsNULL,rsNULL,rsNULL,rsNULL,rsNULL,rsNULL,rsNULL,    /* 0-7   */
    rsNULL,rsNULL,0xd   ,rsNULL,rsNULL,rsNULL,rsNULL,rsNULL,    /* 8-15  */
    rsNULL,rsNULL,rsNULL,rsNULL,rsNULL,rsNULL,rsNULL,rsNULL,    /* 16-23 */
    rsNULL,rsNULL,rsNULL,rsNULL,rsNULL,rsNULL,rsNULL,rsNULL,    /* 24-31 */
    ' ','!','"','#', '$','%','&','\'',                          /* 32-39 */
    '(',')','*','+',',','-','.','/',                            /* 40-47 */
    '0','1','2','3','4','5','6','7',                            /* 48-55 */
    '8','9',':',';','<','=','>','?',                            /*   -3f */
    '@','A','B','C','D','E','F','G',                            /* 40-47 */
    'H','I','J','K','L','M','N','O',                            /* 48-4f */
    'P','Q','R','S','T','U','V','W',                            /* 50-57 */
    'X','Y','Z','[','\\',']','^','_',                           /* 58-5f */
    '`','a','b','c','d','e','f','g',                            /* 60-67 */
    'h','i','j','k','l','m','n','o',                            /* 68-6f */
    'p','q','r','s','t','u','v','w',                            /* 70-77 */
    'x','y','z','{','|','}','~',0x7f,                           /* 78-7f */

    27,rsF1,rsF2,rsF3,rsF4,rsF5,rsF6,rsF7,                      /* 80-87 */
    rsF8,rsF9,rsF10,rsF11, rsF12,rsINS,0x7f,rsHOME,             /* 88-8f */
    rsEND,rsPGUP,rsPGDN,rsUP,rsDOWN,rsLEFT,rsRIGHT,rsINS,       /* 90-97 */
    0x7f,rsHOME,rsEND,rsPGUP,rsPGDN,rsUP,rsDOWN,rsLEFT,         /* 98-9f */
    rsRIGHT,rsNUMLOCK,'/','*', '-','+',0xd,' ',                 /* a0-a7 */
    0x8,0x9,rsCAPSLK,0xd, rsNULL,rsNULL,rsNULL,rsNULL,          /* a8-af */
    rsNULL,rsNULL,rsNULL,rsNULL, rsNULL,rsNULL,rsNULL,rsNULL,   /* b0-b7 */
    rsNULL,rsNULL,rsNULL,rsNULL, rsNULL,rsNULL,rsNULL,rsNULL,   /* b8-bf */
    rsNULL,rsNULL,rsNULL,rsNULL, rsNULL,rsNULL,rsNULL,rsNULL,   /* c0-c7 */
    rsNULL,rsNULL,rsNULL,rsNULL, rsNULL,rsNULL,rsNULL,rsNULL,   /* c8-cf */
    rsNULL,rsNULL,rsNULL,rsNULL, rsNULL,rsNULL,rsNULL,rsNULL,   /* d0-d7 */
    rsNULL,rsNULL,rsNULL,rsNULL, rsNULL,rsNULL,rsNULL,rsNULL,   /* d8-df */
    rsNULL,rsNULL,rsNULL,rsNULL, rsNULL,rsNULL,rsNULL,rsNULL,   /* e0-e7 */
    rsNULL,rsNULL,rsNULL,rsNULL, rsNULL,rsNULL,rsNULL,rsNULL,   /* e8-ef */
    rsNULL,rsNULL,rsNULL,rsNULL, rsNULL,rsNULL,rsNULL,rsNULL,   /* f0-f7 */
    rsNULL,rsNULL,rsNULL,rsNULL, rsNULL,rsNULL,rsNULL,rsNULL    /* f8-ff */
};

static RwUInt8 
KeysShifted[]=
{
    rsNULL,rsNULL,rsNULL,rsNULL,rsNULL,rsNULL,rsNULL,rsNULL,    /* 0-7   */
    rsNULL,rsNULL,0xd,   rsNULL,rsNULL,rsNULL,rsNULL,rsNULL,    /* 8-15  */
    rsNULL,rsNULL,rsNULL,rsNULL,rsNULL,rsNULL,rsNULL,rsNULL,    /* 16-23 */
    rsNULL,rsNULL,rsNULL,rsNULL,rsNULL,rsNULL,rsNULL,rsNULL,    /* 24-31 */
    ' ','!','"','#', '$','%','&','\"',                          /* 32-39 */
    '(',')','*','+','<','_','>','?',                            /* 40-47 */
    ')','!','@','#','$','%','^','&',                            /* 48-55 */
    '*','(',':',':','<','+','>','?',                            /*   -3f */
    '@','A','B','C','D','E','F','G',                            /* 40-47 */
    'H','I','J','K','L','M','N','O',                            /* 48-4f */
    'P','Q','R','S','T','U','V','W',                            /* 50-57 */
    'X','Y','Z','{','|','}','^','_',                            /* 58-5f */
    '~','A','B','C','D','E','F','G',                            /* 60-67 */
    'H','I','J','K','L','M','N','O',                            /* 68-6f */
    'P','Q','R','S','T','U','V','W',                            /* 70-77 */
    'X','Y','Z','{','|','}','~',0x7f,                           /* 78-7f */

    27,rsF1,rsF2,rsF3,rsF4,rsF5,rsF6,rsF7,                      /* 80-87 */
    rsF8,rsF9,rsF10,rsF11, rsF12,rsINS,0x7f,rsHOME,             /* 88-8f */
    rsEND,rsPGUP,rsPGDN,rsUP,rsDOWN,rsLEFT,rsRIGHT,rsINS,       /* 90-97 */
    0x7f,rsHOME,rsEND,rsPGUP,rsPGDN,rsUP,rsDOWN,rsLEFT,         /* 98-9f */
    rsRIGHT,rsNUMLOCK,'/','*', '-','+',0xd,' ',                 /* a0-a7 */
    0x8,0x9,rsCAPSLK,0xd, rsNULL,rsNULL,rsNULL,rsNULL,          /* a8-af */
    rsNULL,rsNULL,rsNULL,rsNULL, rsNULL,rsNULL,rsNULL,rsNULL,   /* b0-b7 */
    rsNULL,rsNULL,rsNULL,rsNULL, rsNULL,rsNULL,rsNULL,rsNULL,   /* b8-bf */
    rsNULL,rsNULL,rsNULL,rsNULL, rsNULL,rsNULL,rsNULL,rsNULL,   /* c0-c7 */
    rsNULL,rsNULL,rsNULL,rsNULL, rsNULL,rsNULL,rsNULL,rsNULL,   /* c8-cf */
    rsNULL,rsNULL,rsNULL,rsNULL, rsNULL,rsNULL,rsNULL,rsNULL,   /* d0-d7 */
    rsNULL,rsNULL,rsNULL,rsNULL, rsNULL,rsNULL,rsNULL,rsNULL,   /* d8-df */
    rsNULL,rsNULL,rsNULL,rsNULL, rsNULL,rsNULL,rsNULL,rsNULL,   /* e0-e7 */
    rsNULL,rsNULL,rsNULL,rsNULL, rsNULL,rsNULL,rsNULL,rsNULL,   /* e8-ef */
    rsNULL,rsNULL,rsNULL,rsNULL, rsNULL,rsNULL,rsNULL,rsNULL,   /* f0-f7 */
    rsNULL,rsNULL,rsNULL,rsNULL, rsNULL,rsNULL,rsNULL,rsNULL    /* f8-ff */
};
/* *INDENT-ON* */

typedef struct PresetView PresetView;
struct PresetView
{
    RwV3d               pos;
    RwReal              el;
    RwReal              az;
    RwReal              nearClip;
    RwReal              farClip;
    RwChar             *description;
    PresetView         *next;
};

static RwV3d        Xaxis = { 1.0f, 0.0f, 0.0f };
static RwV3d        Yaxis = { 0.0f, 1.0f, 0.0f };
static RwV3d        Zaxis = { 0.0f, 0.0f, 1.0f };

static RwChar       ViewsFileName[] = RWSTRING("./views.txt");

static PresetView  *PresetViews = 0;
static RwInt32      NumPresetViews = 0;
static RwInt32      CurrentPresetView = -1;
static RwBool       DefaultVideoMode = TRUE;
//static RwBool       DefaultVideoMode = FALSE;

RsGlobalType        RsGlobal;

/*
 *****************************************************************************
 */
RwUInt8
RsKeyFromScanCode(RwUInt8 scan, RwBool shiftKeyDown)
{
    return shiftKeyDown ? KeysShifted[scan] : KeysNormal[scan];
}

/*
 *****************************************************************************
 */
#ifdef RWSPLASH
RwBool
RsDisplaySplashScreen(RwBool state)
{
    /* state == FALSE: videomode not available
     * state == TRUE: RenderWare video mode selected
     */
    return psDisplaySplashScreen(state);
}
#endif

/*
 *****************************************************************************
 */
RwUInt32
RsTimer(void)
{
    return psTimer();
}

/*
 *****************************************************************************
 */
void
RsWindowSetText(const RwChar * text)
{
    psWindowSetText(text);

    return;
}

/*
 *****************************************************************************
 */
void
RsErrorMessage(const RwChar * text)
{
    /*  KEYUP message lost on dialogue box -
     *  send NULL message to prevent multiple dbox instances
     */
    RsKeyStatus         ks;

    ks.keyScanCode = rsNULL;
    ks.keyCharCode = rsNULL;

    RsKeyboardEventHandler(rsKEYUP, &ks);

    psErrorMessage(text);

    return;
}

/*
 *****************************************************************************
 */
void
RsWarningMessage(const RwChar * text)
{
    /*  KEYUP message lost on dialogue box -
     *  send NULL message to prevent multiple dbox instances
     */
    RsKeyStatus         ks;

    ks.keyScanCode = rsNULL;
    ks.keyCharCode = rsNULL;

    RsKeyboardEventHandler(rsKEYUP, &ks);

    psWarningMessage(text);

    return;
}

/*
 *****************************************************************************
 */
RwChar
RsPathGetSeparator(void)
{
    return psPathGetSeparator();
}

/*
 *****************************************************************************
 */
void
RsCameraShowRaster(RwCamera * camera)
{
#ifdef RWMOUSE
    rsMouseRender(camera);
#endif
    psCameraShowRaster(camera);

    return;
}

/*
 *****************************************************************************
 */
RwBool
RsAlwaysOnTop(RwBool alwaysOnTop)
{
    return psAlwaysOnTop(alwaysOnTop);
}

/*
 *****************************************************************************
 */
RwBool
RsRegisterImageLoader(void)
{
    if (!RwImageRegisterImageFormat(RWSTRING("bmp"), RtBMPImageRead, 0))
    {
        return FALSE;
    }

    if (!RwImageRegisterImageFormat(RWSTRING("png"), RtPNGImageRead, 0))
    {
        return FALSE;
    }

    return TRUE;
}

/*
 *****************************************************************************
 */
static              RwBool
RsSetDebug(void)
{
    RwDebugSetHandler(psDebugMessageHandler);

    RwDebugSendMessage(rwDEBUGMESSAGE, RsGlobal.appName,
                       RWSTRING("Debugging Initialized"));

    return TRUE;
}

/*
 *****************************************************************************
 */
void
RsMouseSetVisibility(RwBool visible)
{
#ifdef RWMOUSE
    rsMouseVisible(visible);
#else
    psMouseSetVisibility(visible);
#endif

    return;
}

/*
 *****************************************************************************
 */
void
RsMouseSetPos(RwV2d * pos)
{
#ifdef RWMOUSE
    rsMouseSetPos(pos);
#endif
    psMouseSetPos(pos);

    return;
}

/*
 *****************************************************************************
 */
RwBool
RsSelectDevice(void)
{
    return psSelectDevice(DefaultVideoMode);
}

/*
 *****************************************************************************
 */
RwBool
RsInputDeviceAttach(RsInputDeviceType inputDevice,
                    RsInputEventHandler inputEventHandler)
{
    switch (inputDevice)
    {
        case rsKEYBOARD:
            {
                RsGlobal.keyboard.inputEventHandler = inputEventHandler;
                RsGlobal.keyboard.used = TRUE;
                break;
            }
        case rsMOUSE:
            {
                RsGlobal.mouse.inputEventHandler = inputEventHandler;
                RsGlobal.mouse.used = TRUE;
                break;
            }
        case rsPAD:
            {
                RsGlobal.pad.inputEventHandler = inputEventHandler;
                RsGlobal.pad.used = TRUE;
                break;
            }
        default:
            {
                return FALSE;
            }
    }

    return TRUE;
}

/*
 *****************************************************************************
 */
static              RwBool
rsCommandLine(RwChar * cmdLine)
{
    RwChar             *s1;
    RwChar             *s2;
    RwChar              fileName[256];

    if (cmdLine)
    {
        s1 = &cmdLine[0];
        s2 = &fileName[0];
        while (*s1)
        {
            /* Extract one file name from the list. */
            while (*s1 && (*s1 != ' '))
            {
                *s2++ = *s1++;
            }
            *s2 = '\0';

            /* only increment s1 if 
             * we have not reached the end of the command line */
            if (*s1)
            {
                s1++;
            }

            if (fileName[0])
            {
                RsEventHandler(rsFILELOAD, fileName);
            }
            s2 = &fileName[0];
        }
    }

    return TRUE;
}

/*
 *****************************************************************************
 */
static              RwBool
rsPreInitCommandLine(RwChar * cmdLine)
{
    if (!strcmp(cmdLine, "-vms"))
    {
        DefaultVideoMode = FALSE;
        return TRUE;
    }
    return FALSE;
}

/*
 *****************************************************************************
 */
RsEventStatus
RsKeyboardEventHandler(RsEvent event, void *param)
{
    if (RsGlobal.keyboard.used)
    {
        return RsGlobal.keyboard.inputEventHandler(event, param);
    }

    return rsEVENTNOTPROCESSED;
}

/*
 *****************************************************************************
 */
RsEventStatus
RsMouseEventHandler(RsEvent event, void *param)
{
#ifdef RWMOUSE
    /* snoop the mouse move event to update the mouse position for
     * a skeleton drawn cursor
     */
    if (event == rsMOUSEMOVE)
    {
        rsMouseAddDelta(&((RsMouseStatus *) param)->delta);
        rsMouseGetPos(&((RsMouseStatus *) param)->pos);
    }

    if ((event == rsLEFTBUTTONDOWN) || (event == rsRIGHTBUTTONDOWN))
    {
        rsMouseGetPos(&((RsMouseStatus *) param)->pos);
    }
#endif

    if (RsGlobal.mouse.used)
    {
        return RsGlobal.mouse.inputEventHandler(event, param);
    }

    return rsEVENTNOTPROCESSED;
}

/*
 *****************************************************************************
 */
RsEventStatus
RsPadEventHandler(RsEvent event, void *param)
{
    if (RsGlobal.pad.used)
    {
        return RsGlobal.pad.inputEventHandler(event, param);
    }

    return rsEVENTNOTPROCESSED;
}

/*
 *****************************************************************************
 */

/*  Message intercept used for test purposes */
#ifdef RWTEST
extern RwBool       _rsTestMessageIntercept(RsEvent *, void **);
#endif /* RWTEST */

RsEventStatus
RsEventHandler(RsEvent event, void *param)
{
    RsEventStatus       result;
    RsEventStatus       es;

#ifdef RWTEST
    /* 
     * If we are in test mode then all messages and parameters are passed
     * to the test harness message intercept handler for processing. 
     * The handler may modify messages and params and/or prevent them from 
     * being passed to the application.
     */
    if (_rsTestMessageIntercept(&event, &param) == FALSE)
        return rsEVENTPROCESSED;
#endif /* RWTEST */

#ifdef RWSPLASH
    /* 
     * Before rendering the first frame, play the splash screen. 
     * Note - there are 2 styles of splash screen - those that change the 
     * video mode and those that use the existing one. The choice of which 
     * to use os driver specific so the handler gets a look in for both 
     * cases. If it doesn't change the video mode then it needs to wait 
     * until RenderWare has started - this is a good place to be sure this 
     * has happened.
     */
    static RwBool       splashPlayed = FALSE;

    if (!splashPlayed && (event == rsIDLE))
    {
        RsEventHandler(rsDISPLAYSPLASH, (void *) TRUE);
        splashPlayed = TRUE;
    }
#endif

#ifdef RWMOUSE
    /* 
     * Snoop the mouse move event to update the mouse position for
     * a skeleton drawn cursor
     */
    if (event == rsMOUSEMOVE)
    {
        rsMouseAddDelta(&((RsMouseStatus *) param)->delta);
        rsMouseGetPos(&((RsMouseStatus *) param)->pos);
    }

    if ((event == rsLEFTBUTTONDOWN) || (event == rsRIGHTBUTTONDOWN))
    {
        rsMouseGetPos(&((RsMouseStatus *) param)->pos);
    }
#endif

    /* 
     * Give the application an opportunity to override any events...
     */
    es = AppEventHandler(event, param);

    /* 
     * We never allow the app to replace the quit behaviour,
     * only to intercept...
     */
    if (event == rsQUITAPP)
    {
        /* 
         * Set the flag which causes the event loop to exit...
         */
        RsGlobal.quit = TRUE;
    }

    if (es == rsEVENTNOTPROCESSED)
    {
        switch (event)
        {
            case rsSELECTDEVICE:
                result =
                    (RsSelectDevice()? rsEVENTPROCESSED : rsEVENTERROR);
                break;

#ifdef RWSPLASH
            case rsDISPLAYSPLASH:
                result = (RsDisplaySplashScreen((RwBool) param) ?
                          rsEVENTPROCESSED : rsEVENTERROR);
                break;
#endif
            case rsCOMMANDLINE:
                result = (rsCommandLine((RwChar *) param) ?
                          rsEVENTPROCESSED : rsEVENTERROR);
                break;
            case rsPREINITCOMMANDLINE:
                result = (rsPreInitCommandLine((RwChar *) param) ?
                          rsEVENTPROCESSED : rsEVENTERROR);
                break;
            case rsINITDEBUG:
                result =
                    (RsSetDebug()? rsEVENTPROCESSED : rsEVENTERROR);
                break;

            case rsREGISTERIMAGELOADER:
                result = (RsRegisterImageLoader()?
                          rsEVENTPROCESSED : rsEVENTERROR);
                break;

            case rsRWTERMINATE:
                RsRwTerminate();
                result = (rsEVENTPROCESSED);
                break;

            case rsRWINITIALIZE:
                result = (RsRwInitialize(param) ?
                          rsEVENTPROCESSED : rsEVENTERROR);
                break;

            case rsTERMINATE:
                RsTerminate();
                result = (rsEVENTPROCESSED);
                break;

            case rsINITIALIZE:
                result =
                    (RsInitialize()? rsEVENTPROCESSED : rsEVENTERROR);
                break;

            default:
                result = (es);
                break;

        }
    }
    else
    {
        result = (es);
    }

    return result;
}

/*
 *****************************************************************************
 */
void
RsPathnameDestroy(RwChar * buffer)
{
    psPathnameDestroy(buffer);

    return;
}

/*
 *****************************************************************************
 */
RwChar             *
RsPathnameCreate(const RwChar * srcBuffer)
{
    return psPathnameCreate(srcBuffer);
}

/*
 *****************************************************************************
 */
#ifdef RWMOUSE
#define RSMOUSETERM()  rsMouseTerm()
#define RSMOUSEINIT()  rsMouseInit()
#endif

#if (!defined(RSMOUSETERM))
#define RSMOUSETERM()          /* No op */
#endif /* (!defined(RSMOUSETERM)) */

#if (!defined(RSMOUSEINIT))
#define RSMOUSEINIT()          /* No op */
#endif /* (!defined(RSMOUSEINIT)) */

void
RsRwTerminate(void)
{
#ifdef RWTERMINAL
    if (NULL != RsGlobal.terminal)
    {
        RsTerminalDestroy(RsGlobal.terminal);
        RsGlobal.terminal = (RsTerminal *)NULL;
    }
#endif /* RWTERMINAL */

    RtCharsetClose();

    RSMOUSETERM();

    /* Close RenderWare */

    RwEngineStop();
    RwEngineClose();
    RwEngineTerm();

    return;
}

/*
 *****************************************************************************
 */
RwBool
RsRwInitialize(void *displayID)
{
    RwEngineOpenParams  openParams;

    /* 
     * Start RenderWare...
     */
    if (!RwEngineInit(psGetMemoryFunctions(), 0))
    {
        return (FALSE);
    }

    /* 
     * Install any platform specific file systems...
     */
    psInstallFileSystem();

    /* 
     * Initialize debug message handling...
     */
    RsEventHandler(rsINITDEBUG, NULL);

    /* 
     * Attach all plugins...
     */
    if (RsEventHandler(rsPLUGINATTACH, NULL) == rsEVENTERROR)
    {
        return (FALSE);
    }

    /* 
     * Attach input devices...
     */
    if (RsEventHandler(rsINPUTDEVICEATTACH, NULL) == rsEVENTERROR)
    {
        return (FALSE);
    }

    openParams.displayID = displayID;

    if (!RwEngineOpen(&openParams))
    {
        RwEngineTerm();
        return (FALSE);
    }

#ifdef RWSPLASH
    /* 
     * Before rendering the first frame, play the splash screen. 
     * Note - there are 2 styles of splash screen - those that change the 
     * video mode and those that use the existing one. The choice of which 
     * to use os driver specific so the handler gets a look in for both 
     * cases. If it doesn't change the video mode then it needs to wait 
     * until RenderWare has started - this is a good place to be sure this 
     * has happened.
     */
    if (RsEventHandler(rsDISPLAYSPLASH, FALSE) == rsEVENTERROR)
    {
        RwEngineClose();
        RwEngineTerm();
        return (FALSE);
    }
#endif

    if (RsEventHandler(rsSELECTDEVICE, displayID) == rsEVENTERROR)
    {
        RwEngineClose();
        RwEngineTerm();
        return (FALSE);
    }

	RwResourcesSetArenaSize(1024 * 1024 * 4); /* 4 Meg */

	RpSkySelectDeepZBuffer ( TRUE );
	RpSkySelectTrueTLClipper (  TRUE );
    RpSkySelectTrueTSClipper (  TRUE );
    
    if (!RwEngineStart())
    {
        RwEngineClose();
        RwEngineTerm();
        return (FALSE);
    }

    /* 
     * Register loaders for an image with a particular file extension...
     */
    RsEventHandler(rsREGISTERIMAGELOADER, NULL);

    psNativeTextureSupport();

    RSMOUSEINIT();

    /* 
     * Set up the charset toolkit for buffered rendering
     * used by both the menu and terminal...
     */
    if (FALSE == RtCharsetOpen())
    {
        RwEngineStop();
        RwEngineClose();
        RwEngineTerm();
        return (FALSE);
    }

#ifdef RWTERMINAL
    /* Start up the terminal */
    {
        static RwRGBA       fore = { 0, 0xff, 0xff, 0xff };
        static RwRGBA       back = { 0, 0, 0, 0 };
        RwSubSystemInfo     si;

        RsGlobal.terminal = RsTerminalCreate(80, 16, &fore, &back, 
                                             (RsTerminalDisplayCallBack)NULL, 
                                             NULL);

        if (!RsGlobal.terminal)
        {
            RwEngineStop();
            RwEngineClose();
            RwEngineTerm();
            return (FALSE);
        }
#ifdef RWDEBUG
        RsTerminalPrintf(RsGlobal.terminal,
                         RWSTRING
                         ("RenderWare V%d.%d%d DEBUG started.\n"),
                         (RwEngineGetVersion() & 0xff00) >> 8,
                         (RwEngineGetVersion() & 0xf0) >> 4,
                         (RwEngineGetVersion() & 0xf));
#else
        RsTerminalPrintf(RsGlobal.terminal,
                         RWSTRING("RenderWare V%d.%d%d started.\n"),
                         (RwEngineGetVersion() & 0xff00) >> 8,
                         (RwEngineGetVersion() & 0xf0) >> 4,
                         (RwEngineGetVersion() & 0xf));
#endif
        RwEngineGetSubSystemInfo(&si, RwEngineGetCurrentSubSystem());
        RsTerminalPrintf(RsGlobal.terminal,
                         RWSTRING("Subsystem: %s.\n"), si.name);
    }
#endif /* RWTERMINAL */

    return TRUE;
}

/*
 *****************************************************************************
 */
void
RsTerminate(void)
{
    psTerminate();

    return;
}

/*
 *****************************************************************************
 */
RwBool
RsInitialize(void)
{
    /* 
     * Initialize Platform independent data...
     */
    RwBool              result;

    RsGlobal.appName = RWSTRING("RenderWare Application");
    RsGlobal.maximumWidth = 0;
    RsGlobal.maximumHeight = 0;
    RsGlobal.maxFPS = 0;
#ifdef RWTERMINAL
    RsGlobal.terminal = (RsTerminal *)NULL;
#endif /* RWTERMINAL */
    RsGlobal.quit = FALSE;

    /* setup the keyboard */
    RsGlobal.keyboard.inputDeviceType = rsKEYBOARD;
    RsGlobal.keyboard.inputEventHandler = 0;
    RsGlobal.keyboard.used = FALSE;

    /* setup the mouse */
    RsGlobal.mouse.inputDeviceType = rsMOUSE;
    RsGlobal.mouse.inputEventHandler = 0;
    RsGlobal.mouse.used = FALSE;

    /* setup the pad */
    RsGlobal.pad.inputDeviceType = rsPAD;
    RsGlobal.pad.inputEventHandler = 0;
    RsGlobal.pad.used = FALSE;

    result = psInitialize();

    return result;
}

/*
 *****************************************************************************
 */
RwBool
RsSetModelTexturePath(const RwChar *objectPathName)
{
    RwChar imagePath[1024], separator;
    RwChar texturesPath[1024];
    RwChar temp[16];
    RwInt32 i;

    separator = RsPathGetSeparator();

    /*
     * Create an image path with the same name as the file object...
     */
    rwstrcpy(imagePath, objectPathName);

    i = rwstrlen(imagePath)-1;

    while( i >= 0 )
    {
        if( imagePath[i] == '.' )
        {
            imagePath[i] = separator;
            imagePath[i+1] = ';';
            imagePath[i+2] = '\0';

            break;
        }

        i--;
    }

    /*
     * Create an image path called 'textures'...
     */
    rwstrcpy(texturesPath, objectPathName);

    i = rwstrlen(texturesPath)-1;

    while( i >= 0 )
    {
        if( texturesPath[i] == separator )
        {
            texturesPath[i+1] = '\0';

            break;
        }

        i--;
    }

    rwsprintf(temp, RWSTRING("textures%c"), separator);

    /*
     * Combine both paths...
     */
    rwstrcat(imagePath, texturesPath);
    rwstrcat(imagePath, temp);

    RwImageSetPath(imagePath);

    return TRUE;
}


/*
 *****************************************************************************
 */
RwBool
RsSetPresetView(RwCamera * camera, RwInt32 viewNum)
{
    RwFrame            *frame;
    RwMatrix           *matrix;
    RwV3d              *pos;
    PresetView         *view;
    RwInt32             count;

    if (!camera || !NumPresetViews || viewNum >= NumPresetViews
        || viewNum < 0)
    {
        return (FALSE);
    }

    CurrentPresetView = viewNum;

    frame = RwCameraGetFrame(camera);
    matrix = RwFrameGetMatrix(frame);
    pos = RwMatrixGetPos(matrix);

    view = PresetViews;
    count = NumPresetViews - viewNum - 1;
    while (count > 0 && view)
    {
        view = view->next;
        count--;
    }

    RwFrameSetIdentity(frame);
    RwFrameRotate(frame, &Xaxis, -view->el, rwCOMBINEREPLACE);
    RwFrameRotate(frame, &Yaxis, view->az, rwCOMBINEPOSTCONCAT);
    RwFrameTranslate(frame, &view->pos, rwCOMBINEPOSTCONCAT);
    RwFrameUpdateObjects(frame);

    RwCameraSetNearClipPlane(camera, view->nearClip);
    RwCameraSetFarClipPlane(camera, view->farClip);

    return TRUE;
}

/*
 *****************************************************************************
 */
RwBool
RsSetNextPresetView(RwCamera * camera)
{
    if (!camera || !NumPresetViews)
    {
        return FALSE;
    }

    if (++CurrentPresetView >= NumPresetViews)
    {
        CurrentPresetView = 0;
    }

    if (!RsSetPresetView(camera, CurrentPresetView))
    {
        return FALSE;
    }

    return TRUE;
}

/*
 *****************************************************************************
 */
RwBool
RsSetPreviousPresetView(RwCamera * camera)
{
    if (!camera || !NumPresetViews)
    {
        return FALSE;
    }

    if (--CurrentPresetView < 0)
    {
        CurrentPresetView = NumPresetViews - 1;
    }

    if (!RsSetPresetView(camera, CurrentPresetView))
    {
        return FALSE;
    }

    return TRUE;
}

/*
 *****************************************************************************
 */
RwBool
RsDestroyPresetViews(void)
{
    PresetView         *view, *ptr;

    /*
     * Delete all preset views
     */
    if (PresetViews)
    {
        ptr = PresetViews;
        while (ptr)
        {
            view = ptr->next;
            if (ptr->description)
            {
                RwFree(ptr->description);
            }
            RwFree(ptr);
            ptr = view;
        }
    }

    PresetViews = 0;
    NumPresetViews = 0;

    return TRUE;
}

/*
 *****************************************************************************
 */
RwBool
RsLoadPresetViews(void)
{
    RwChar             *path;
    RwFileFunctions    *fileFuncs;
    RwChar              line[1024];
    void               *fp;
    RwReal              el, az;
    RwV3d               pos;
    RwReal              farClip;
    RwReal              nearClip;
    PresetView         *view;
    RwChar              description[64];
    RwInt32             result;
    RwInt32             length;

    /* blow away any existing views */
    RsDestroyPresetViews();
    CurrentPresetView = -1;

    fileFuncs = RwOsGetFileInterface();
    if (fileFuncs)
    {
        path = RsPathnameCreate(ViewsFileName);
        fp = fileFuncs->rwfopen(path, RWSTRING("r"));
        RsPathnameDestroy(path);

        if (fp)
        {
            /*
             * Load preset views from the file, one per line 
             */
            while (fileFuncs->rwfgets(line, 1023, fp))
            {
                RwChar             *src, *dst;

                /* delete any control characters */
                length = rwstrlen(line);
                src = dst = line;
                while (*src != '\0')
                {
                    /* is displayable */
                    if (isprint(*src))
                    {
                        *dst++ = *src;
                    }
                    src++;
                }

                /* zero terminated.... */
                *dst = '\0';

                result =
                    rwsscanf(line,
                             RWSTRING("%f%f%f%f%f%f%f %[^\0]"),
                             &pos.x, &pos.y, &pos.z,
                             &el, &az, &nearClip, &farClip,
                             description);

                /* ignore any invalid lines */
                if (result >= NUMVIEWFILECOLUMNS)
                {
                    if (result == NUMVIEWFILECOLUMNS)
                    {
                        /* 
                         * The user has deleted the description 
                         * in the file; use the default one
                         */
                        rwsprintf(description,
                                  "%s%d",
                                  DEFAULTVIEWPREFIX, NumPresetViews);
                        result++;
                    }

                    view = (PresetView *) RwMalloc(sizeof(PresetView));
                    if (!view)
                    {
                        RsErrorMessage(RWSTRING
                                       ("Out of memory - not all preset views loaded"));

                        return FALSE;
                    }
                    view->pos = pos;
                    view->el = el;
                    view->az = az;
                    view->nearClip = nearClip;
                    view->farClip = farClip;
                    view->description =
                        (RwChar *) RwMalloc(rwstrlen(description) + 1);
                    if (!view->description)
                    {
                        RsErrorMessage(RWSTRING
                                       ("Out of memory - not all preset views loaded"));
                        RwFree(view);

                        return FALSE;
                    }
                    rwstrcpy(view->description, description);

                    /* add to head of list */
                    view->next = PresetViews;
                    PresetViews = view;
                    NumPresetViews++;
                }
            }
            fileFuncs->rwfclose(fp);
        }
    }

    return NumPresetViews ? TRUE : FALSE;
}

/*
 *****************************************************************************
 */
static void
CameraElAzPosFromLTM(RwCamera * camera,
                     RwReal * elevation, RwReal * azimuth, RwV3d * pos)
{
    RwMatrix           *matrix;
    RwV3d              *at, projAt;
    RwReal              dot;
    RwFrame            *frame;

    /*
     * Use the camera frame LTM, 
     * as we want the absolute elevation and azimuth parameters, 
     * not relative to the base frame that may be following a spline path...
     */
    frame = RwCameraGetFrame(camera);
    matrix = RwFrameGetLTM(frame);
    at = RwMatrixGetAt(matrix);

    *pos = *RwMatrixGetPos(matrix);

    dot = RwV3dDotProduct(at, &Yaxis);

    /*
     * Make sure the dot product is not out of range (due to numerical
     * inccuracies) so the arc-cos doesn't crash out on us. Usually, if
     * it's outside the useable range, then it is just marginal...
     */
    if (dot > 1.0f)
    {
        dot = 1.0f;
    }
    else if (dot < -1.0f)
    {
        dot = -1.0f;
    }

    *elevation = 90.0f - 180.0f * (RwReal) RwACos(dot) / rwPI;

    RwV3dScale(&projAt, &Yaxis, dot);
    RwV3dSub(&projAt, at, &projAt);
    RwV3dNormalize(&projAt, &projAt);

    dot = RwV3dDotProduct(&projAt, &Zaxis);

    /*
     * Numerical inaccuracies here are more likely due the 
     * approximate square root use by RwV3dNormalize...
     */
    if (dot > 1.0f)
    {
        dot = 1.0f;
    }
    else if (dot < -1.0f)
    {
        dot = -1.0f;
    }

    *azimuth = 180.0f * (RwReal) RwACos(dot) / rwPI;

    if (RwV3dDotProduct(&projAt, &Xaxis) < 0.0f)
    {
        *azimuth = -*azimuth;
    }

    return;
}

/*
 *****************************************************************************
 */
RwChar             *
RsGetPresetViewDescription(void)
{
    PresetView         *view;
    RwInt32             count;

    if (!NumPresetViews || CurrentPresetView == -1)
    {
        return 0;
    }

    view = PresetViews;
    count = NumPresetViews - CurrentPresetView - 1;

    while (count > 0 && view)
    {
        view = view->next;
        count--;
    }

    return view->description;
}

/*
 *****************************************************************************
 */
RwBool
RsSavePresetView(RwCamera * camera)
{
    RwChar             *path;
    RwFileFunctions    *fileFuncs;
    RwChar              line[1024];
    void               *fp;
    RwReal              el, az;
    RwV3d               pos;
    RwUInt32            length;
    RwBool              result;

#if (defined(KAMUI2))
    RsErrorMessage(RWSTRING("Cannot save preset view"));
    return (FALSE);
#endif /* (defined(KAMUI2)) */

    result = FALSE;
    fileFuncs = RwOsGetFileInterface();
    if (fileFuncs)
    {
        path = RsPathnameCreate(ViewsFileName);
        fp = fileFuncs->rwfopen(path, RWSTRING("a"));
        RsPathnameDestroy(path);

        if (fp)
        {
            CameraElAzPosFromLTM(camera, &el, &az, &pos);

            rwsprintf(line,
                      RWSTRING
                      ("%0.6f %0.6f %0.6f  %0.6f %0.6f  %0.6f %0.6f %s%d\n"),
                      pos.x, pos.y, pos.z, el, az,
                      RwCameraGetNearClipPlane(camera),
                      RwCameraGetFarClipPlane(camera),
                      DEFAULTVIEWPREFIX, NumPresetViews);

            length = rwstrlen(line);
            if (fileFuncs->rwfputs(line, fp) > 0)
            {
                result = RsLoadPresetViews();
                if (result)
                {
                    RsSetPresetView(camera, NumPresetViews - 1);
                }
            }
            else
            {
                RsErrorMessage(RWSTRING
                               ("Cannot write to preset view file"));
            }

            fileFuncs->rwfclose(fp);

        }
        else
        {
            RsErrorMessage(RWSTRING("Cannot open preset view file"));
        }
    }

    return result;
}


/*
 *****************************************************************************
 */
RwImage *
RsGrabScreen(RwCamera *camera)
{
    return psGrabScreen(camera);
}


/*
 *****************************************************************************
 */

