ref: d7ad068d56bdec30805a36a9973f5924ad00641b
dir: /src/OSPLAN9.c/
#include <u.h>
#include <libc.h>
#include <stdio.h>
#include <draw.h>
#include <mouse.h>
#include <cursor.h>
#include <keyboard.h>
#include <thread.h>
#include <plumb.h>
#define Uint8 u8int
#define Uint16 u16int
#define Uint32 u32int
#include "CNFGRAPI.h"
#include "SYSDEPNS.h"
#include "ENDIANAC.h"
#include "MYOSGLUE.h"
#include "STRCONST.h"
GLOBALOSGLUPROC MyMoveBytes(anyp srcPtr, anyp destPtr, si5b byteCount)
{
memmove(destPtr, srcPtr, byteCount);
}
#define NeedCell2PlainAsciiMap 1
#include "INTLCHAR.h"
#if dbglog_HAVE
#define dbglog_ToStdErr 0
#if ! dbglog_ToStdErr
LOCALVAR FILE *dbglog_File = NULL;
#endif
LOCALFUNC blnr dbglog_open0(void)
{
#if dbglog_ToStdErr
return trueblnr;
#else
dbglog_File = fopen("dbglog.txt", "w");
return (NULL != dbglog_File);
#endif
}
LOCALPROC dbglog_write0(char *s, uimr L)
{
#if dbglog_ToStdErr
(void) fwrite(s, 1, L, stderr);
#else
if (dbglog_File != NULL) {
(void) fwrite(s, 1, L, dbglog_File);
}
#endif
}
LOCALPROC dbglog_close0(void)
{
#if ! dbglog_ToStdErr
if (dbglog_File != NULL) {
fclose(dbglog_File);
dbglog_File = NULL;
}
#endif
}
#endif
/* --- information about the environment --- */
#define WantColorTransValid 0
#include "COMOSGLU.h"
#include "PBUFSTDC.h"
#include "CONTROLM.h"
/* --- text translation --- */
LOCALPROC NativeStrFromCStr(char *r, char *s)
{
ui3b ps[ClStrMaxLength];
int i;
int L;
ClStrFromSubstCStr(&L, ps, s);
for (i = 0; i < L; ++i) {
r[i] = Cell2PlainAsciiMap[ps[i]];
}
r[L] = 0;
}
/* --- drives --- */
#define NotAfileRef NULL
LOCALVAR FILE *Drives[NumDrives]; /* open disk image files */
LOCALPROC InitDrives(void)
{
/*
This isn't really needed, Drives[i] and DriveNames[i]
need not have valid values when not vSonyIsInserted[i].
*/
tDrive i;
for (i = 0; i < NumDrives; ++i) {
Drives[i] = NotAfileRef;
}
}
GLOBALOSGLUFUNC tMacErr vSonyTransfer(blnr IsWrite, ui3p Buffer,
tDrive Drive_No, ui5r Sony_Start, ui5r Sony_Count,
ui5r *Sony_ActCount)
{
tMacErr err = mnvm_miscErr;
FILE *refnum = Drives[Drive_No];
ui5r NewSony_Count = 0;
if (0 == fseek(refnum, Sony_Start, SEEK_SET)) {
if (IsWrite) {
NewSony_Count = fwrite(Buffer, 1, Sony_Count, refnum);
} else {
NewSony_Count = fread(Buffer, 1, Sony_Count, refnum);
}
if (NewSony_Count == Sony_Count) {
err = mnvm_noErr;
}
}
if (nullpr != Sony_ActCount) {
*Sony_ActCount = NewSony_Count;
}
return err; /*& figure out what really to return &*/
}
GLOBALOSGLUFUNC tMacErr vSonyGetSize(tDrive Drive_No, ui5r *Sony_Count)
{
tMacErr err = mnvm_miscErr;
FILE *refnum = Drives[Drive_No];
long v;
if (0 == fseek(refnum, 0, SEEK_END)) {
v = ftell(refnum);
if (v >= 0) {
*Sony_Count = v;
err = mnvm_noErr;
}
}
return err; /*& figure out what really to return &*/
}
LOCALFUNC tMacErr vSonyEject0(tDrive Drive_No, blnr deleteit)
{
FILE *refnum = Drives[Drive_No];
DiskEjectedNotify(Drive_No);
fclose(refnum);
Drives[Drive_No] = NotAfileRef; /* not really needed */
return mnvm_noErr;
}
GLOBALOSGLUFUNC tMacErr vSonyEject(tDrive Drive_No)
{
return vSonyEject0(Drive_No, falseblnr);
}
#if IncludeSonyNew
GLOBALOSGLUFUNC tMacErr vSonyEjectDelete(tDrive Drive_No)
{
return vSonyEject0(Drive_No, trueblnr);
}
#endif
#if IncludeSonyGetName
GLOBALOSGLUFUNC tMacErr vSonyGetName(tDrive Drive_No, tPbuf *r)
{
r[0] = 0;
return mnvm_noErr;
}
#endif
LOCALPROC UnInitDrives(void)
{
tDrive i;
for (i = 0; i < NumDrives; ++i) {
if (vSonyIsInserted(i)) {
(void) vSonyEject(i);
}
}
}
LOCALFUNC blnr Sony_Insert0(FILE *refnum, blnr locked,
char *drivepath)
{
tDrive Drive_No;
blnr IsOk = falseblnr;
if (! FirstFreeDisk(&Drive_No)) {
MacMsg(kStrTooManyImagesTitle, kStrTooManyImagesMessage,
falseblnr);
} else {
/* printf("Sony_Insert0 %d\n", (int)Drive_No); */
{
Drives[Drive_No] = refnum;
DiskInsertNotify(Drive_No, locked);
IsOk = trueblnr;
}
}
if (! IsOk) {
fclose(refnum);
}
return IsOk;
}
LOCALFUNC blnr Sony_Insert1(char *drivepath, blnr silentfail)
{
blnr locked = falseblnr;
/* printf("Sony_Insert1 %s\n", drivepath); */
FILE *refnum = fopen(drivepath, "rb+");
if (NULL == refnum) {
locked = trueblnr;
refnum = fopen(drivepath, "rb");
}
if (NULL == refnum) {
if (! silentfail) {
MacMsg(kStrOpenFailTitle, kStrOpenFailMessage, falseblnr);
}
} else {
return Sony_Insert0(refnum, locked, drivepath);
}
return falseblnr;
}
LOCALFUNC tMacErr LoadMacRomFrom(char *path)
{
tMacErr err;
FILE *ROM_File;
int File_Size;
ROM_File = fopen(path, "rb");
if (NULL == ROM_File) {
err = mnvm_fnfErr;
} else {
File_Size = fread(ROM, 1, kROM_Size, ROM_File);
if (File_Size != kROM_Size) {
if (feof(ROM_File)) {
MacMsgOverride(kStrShortROMTitle,
kStrShortROMMessage);
err = mnvm_eofErr;
} else {
MacMsgOverride(kStrNoReadROMTitle,
kStrNoReadROMMessage);
err = mnvm_miscErr;
}
} else {
err = ROM_IsValid();
}
fclose(ROM_File);
}
return err;
}
LOCALFUNC blnr Sony_Insert1a(char *drivepath, blnr silentfail)
{
blnr v;
if (! ROM_loaded) {
v = (mnvm_noErr == LoadMacRomFrom(drivepath));
} else {
v = Sony_Insert1(drivepath, silentfail);
}
return v;
}
static void
plumbproc(void *)
{
int f;
Plumbmsg *m;
char tmp[1024];
threadsetname("plumb");
if ((f = plumbopen("minivmac", OREAD)) >= 0) {
while ((m = plumbrecv(f)) != nil) {
snprint(tmp, sizeof(tmp), "%s/%s", m->wdir, m->data);
Sony_Insert1a(tmp, 0);
plumbfree(m);
}
}
threadexits(nil);
}
LOCALFUNC blnr Sony_Insert2(char *s)
{
return Sony_Insert1(s, trueblnr);
}
LOCALFUNC blnr Sony_InsertIth(int i)
{
blnr v;
if ((i > 9) || ! FirstFreeDisk(nullpr)) {
v = falseblnr;
} else {
char s[] = "disk?.dsk";
s[4] = '0' + i;
v = Sony_Insert2(s);
}
return v;
}
LOCALFUNC blnr LoadInitialImages(void)
{
if (! AnyDiskInserted()) {
int i;
for (i = 1; Sony_InsertIth(i); ++i) {
/* stop on first error (including file not found) */
}
}
proccreate(plumbproc, nil, mainstacksize);
return trueblnr;
}
/* --- ROM --- */
LOCALVAR char *rom_path = NULL;
LOCALFUNC blnr LoadMacRom(void)
{
tMacErr err;
if ((NULL == rom_path)
|| (mnvm_fnfErr == (err = LoadMacRomFrom(rom_path))))
if (mnvm_fnfErr == (err = LoadMacRomFrom(RomFileName)))
{
}
return trueblnr; /* keep launching Mini vMac, regardless */
}
/* --- video out --- */
#if VarFullScreen
LOCALVAR blnr UseFullScreen = (WantInitFullScreen != 0);
#endif
#if EnableMagnify
LOCALVAR blnr UseMagnify = (WantInitMagnify != 0);
#endif
LOCALVAR blnr gBackgroundFlag = falseblnr;
LOCALVAR blnr gTrueBackgroundFlag = falseblnr;
LOCALVAR blnr CurSpeedStopped = falseblnr;
#if EnableMagnify
#define MaxScale MyWindowScale
#else
#define MaxScale 1
#endif
LOCALVAR Image *my_surface = nullpr;
LOCALVAR u8int *pixels;
LOCALVAR ui3p ScalingBuff = nullpr;
LOCALVAR ui3p CLUT_final;
#define CLUT_finalsz (256 * 8 * 4 * MaxScale)
/*
256 possible values of one byte
8 pixels per byte maximum (when black and white)
4 bytes per destination pixel maximum
multiplied by MyWindowScale if EnableMagnify
*/
#define ScrnMapr_DoMap UpdateBWDepth3Copy
#define ScrnMapr_Src GetCurDrawBuff()
#define ScrnMapr_Dst ScalingBuff
#define ScrnMapr_SrcDepth 0
#define ScrnMapr_DstDepth 3
#define ScrnMapr_Map CLUT_final
#include "SCRNMAPR.h"
#define ScrnMapr_DoMap UpdateBWDepth4Copy
#define ScrnMapr_Src GetCurDrawBuff()
#define ScrnMapr_Dst ScalingBuff
#define ScrnMapr_SrcDepth 0
#define ScrnMapr_DstDepth 4
#define ScrnMapr_Map CLUT_final
#include "SCRNMAPR.h"
#define ScrnMapr_DoMap UpdateBWDepth5Copy
#define ScrnMapr_Src GetCurDrawBuff()
#define ScrnMapr_Dst ScalingBuff
#define ScrnMapr_SrcDepth 0
#define ScrnMapr_DstDepth 5
#define ScrnMapr_Map CLUT_final
#include "SCRNMAPR.h"
#define ScrnMapr_DoMap UpdateBWDepth3ScaledCopy
#define ScrnMapr_Src GetCurDrawBuff()
#define ScrnMapr_Dst ScalingBuff
#define ScrnMapr_SrcDepth 0
#define ScrnMapr_DstDepth 3
#define ScrnMapr_Map CLUT_final
#define ScrnMapr_Scale MyWindowScale
#include "SCRNMAPR.h"
#define ScrnMapr_DoMap UpdateBWDepth4ScaledCopy
#define ScrnMapr_Src GetCurDrawBuff()
#define ScrnMapr_Dst ScalingBuff
#define ScrnMapr_SrcDepth 0
#define ScrnMapr_DstDepth 4
#define ScrnMapr_Map CLUT_final
#define ScrnMapr_Scale MyWindowScale
#include "SCRNMAPR.h"
#define ScrnMapr_DoMap UpdateBWDepth5ScaledCopy
#define ScrnMapr_Src GetCurDrawBuff()
#define ScrnMapr_Dst ScalingBuff
#define ScrnMapr_SrcDepth 0
#define ScrnMapr_DstDepth 5
#define ScrnMapr_Map CLUT_final
#define ScrnMapr_Scale MyWindowScale
#include "SCRNMAPR.h"
#if (0 != vMacScreenDepth) && (vMacScreenDepth < 4)
#define ScrnMapr_DoMap UpdateColorDepth3Copy
#define ScrnMapr_Src GetCurDrawBuff()
#define ScrnMapr_Dst ScalingBuff
#define ScrnMapr_SrcDepth vMacScreenDepth
#define ScrnMapr_DstDepth 3
#define ScrnMapr_Map CLUT_final
#include "SCRNMAPR.h"
#define ScrnMapr_DoMap UpdateColorDepth4Copy
#define ScrnMapr_Src GetCurDrawBuff()
#define ScrnMapr_Dst ScalingBuff
#define ScrnMapr_SrcDepth vMacScreenDepth
#define ScrnMapr_DstDepth 4
#define ScrnMapr_Map CLUT_final
#include "SCRNMAPR.h"
#define ScrnMapr_DoMap UpdateColorDepth5Copy
#define ScrnMapr_Src GetCurDrawBuff()
#define ScrnMapr_Dst ScalingBuff
#define ScrnMapr_SrcDepth vMacScreenDepth
#define ScrnMapr_DstDepth 5
#define ScrnMapr_Map CLUT_final
#include "SCRNMAPR.h"
#define ScrnMapr_DoMap UpdateColorDepth3ScaledCopy
#define ScrnMapr_Src GetCurDrawBuff()
#define ScrnMapr_Dst ScalingBuff
#define ScrnMapr_SrcDepth vMacScreenDepth
#define ScrnMapr_DstDepth 3
#define ScrnMapr_Map CLUT_final
#define ScrnMapr_Scale MyWindowScale
#include "SCRNMAPR.h"
#define ScrnMapr_DoMap UpdateColorDepth4ScaledCopy
#define ScrnMapr_Src GetCurDrawBuff()
#define ScrnMapr_Dst ScalingBuff
#define ScrnMapr_SrcDepth vMacScreenDepth
#define ScrnMapr_DstDepth 4
#define ScrnMapr_Map CLUT_final
#define ScrnMapr_Scale MyWindowScale
#include "SCRNMAPR.h"
#define ScrnMapr_DoMap UpdateColorDepth5ScaledCopy
#define ScrnMapr_Src GetCurDrawBuff()
#define ScrnMapr_Dst ScalingBuff
#define ScrnMapr_SrcDepth vMacScreenDepth
#define ScrnMapr_DstDepth 5
#define ScrnMapr_Map CLUT_final
#define ScrnMapr_Scale MyWindowScale
#include "SCRNMAPR.h"
#endif
LOCALPROC HaveChangedScreenBuff(ui4r top, ui4r left,
ui4r bottom, ui4r right)
{
int i;
int j;
ui3b *p;
Uint32 pixel;
#if (0 != vMacScreenDepth) && (vMacScreenDepth < 4)
Uint32 CLUT_pixel[CLUT_size];
#endif
Uint32 BWLUT_pixel[2];
ui5r top2 = top;
ui5r left2 = left;
ui5r bottom2 = bottom;
ui5r right2 = right;
#if EnableMagnify
if (UseMagnify) {
top2 *= MyWindowScale;
left2 *= MyWindowScale;
bottom2 *= MyWindowScale;
right2 *= MyWindowScale;
}
#endif
/* FIXME image locking here? */
{
int bpp = 4; /* FIXME RGB32 for now, make it dynamic though */
ui5r ExpectedPitch = vMacScreenWidth * bpp;
#if EnableMagnify
if (UseMagnify) {
ExpectedPitch *= MyWindowScale;
}
#endif
#if 0 != vMacScreenDepth
if (UseColorMode) {
#if vMacScreenDepth < 4
for (i = 0; i < CLUT_size; ++i) {
CLUT_pixel[i] = (CLUT_blues[i]>>8)<<24 | (CLUT_greens[i]>>8)<<16 | (CLUT_reds[i]>>8)<<8 | 0xff;
}
#endif
} else
#endif
{
BWLUT_pixel[1] = 0x000000ff;
/* black */
BWLUT_pixel[0] = 0xffffffff;
/* white */
}
if ((0 == ((bpp - 1) & bpp)) /* a power of 2 */
#if (vMacScreenDepth > 3)
&& ! UseColorMode
#endif
)
{
int k;
Uint32 v;
#if EnableMagnify
int a;
#endif
int PixPerByte =
#if (0 != vMacScreenDepth) && (vMacScreenDepth < 4)
UseColorMode ? (1 << (3 - vMacScreenDepth)) :
#endif
8;
Uint8 *p4 = (Uint8 *)CLUT_final;
for (i = 0; i < 256; ++i) {
for (k = PixPerByte; --k >= 0; ) {
#if (0 != vMacScreenDepth) && (vMacScreenDepth < 4)
if (UseColorMode) {
v = CLUT_pixel[
#if 3 == vMacScreenDepth
i
#else
(i >> (k << vMacScreenDepth))
& (CLUT_size - 1)
#endif
];
} else
#endif
{
v = BWLUT_pixel[(i >> k) & 1];
}
#if EnableMagnify
for (a = UseMagnify ? MyWindowScale : 1; --a >= 0; )
#endif
{
switch (bpp) {
case 1: /* Assuming 8-bpp */
*p4++ = v;
break;
case 2: /* Probably 15-bpp or 16-bpp */
*(Uint16 *)p4 = v;
p4 += 2;
break;
case 4: /* Probably 32-bpp */
*(Uint32 *)p4 = v;
p4 += 4;
break;
}
}
}
}
ScalingBuff = (ui3p)pixels;
#if (0 != vMacScreenDepth) && (vMacScreenDepth < 4)
if (UseColorMode) {
#if EnableMagnify
if (UseMagnify) {
switch (bpp) {
case 1:
UpdateColorDepth3ScaledCopy(
top, left, bottom, right);
break;
case 2:
UpdateColorDepth4ScaledCopy(
top, left, bottom, right);
break;
case 4:
UpdateColorDepth5ScaledCopy(
top, left, bottom, right);
break;
}
} else
#endif
{
switch (bpp) {
case 1:
UpdateColorDepth3Copy(top, left, bottom, right);
break;
case 2:
UpdateColorDepth4Copy(top, left, bottom, right);
break;
case 4:
UpdateColorDepth5Copy(top, left, bottom, right);
break;
}
}
} else
#endif
{
#if EnableMagnify
if (UseMagnify) {
switch (bpp) {
case 1:
UpdateBWDepth3ScaledCopy(
top, left, bottom, right);
break;
case 2:
UpdateBWDepth4ScaledCopy(
top, left, bottom, right);
break;
case 4:
UpdateBWDepth5ScaledCopy(
top, left, bottom, right);
break;
}
} else
#endif
{
switch (bpp) {
case 1:
UpdateBWDepth3Copy(top, left, bottom, right);
break;
case 2:
UpdateBWDepth4Copy(top, left, bottom, right);
break;
case 4:
UpdateBWDepth5Copy(top, left, bottom, right);
break;
}
}
}
} else {
ui3b *the_data = (ui3b *)GetCurDrawBuff();
/* adapted from putpixel in SDL documentation */
for (i = top2; i < bottom2; ++i) {
for (j = left2; j < right2; ++j) {
int i0 = i;
int j0 = j;
Uint8 *bufp = (Uint8 *)pixels
+ i * Dx(my_surface->r)*bpp + j * bpp;
#if EnableMagnify
if (UseMagnify) {
i0 /= MyWindowScale;
j0 /= MyWindowScale;
}
#endif
#if 0 != vMacScreenDepth
if (UseColorMode) {
#if vMacScreenDepth < 4
p = the_data + ((i0 * vMacScreenWidth + j0)
>> (3 - vMacScreenDepth));
{
ui3r k = (*p >> (((~ j0)
& ((1 << (3 - vMacScreenDepth)) - 1))
<< vMacScreenDepth))
& (CLUT_size - 1);
pixel = CLUT_pixel[k];
}
#elif 4 == vMacScreenDepth
p = the_data + ((i0 * vMacScreenWidth + j0) << 1);
{
ui4r t0 = do_get_mem_word(p);
pixel =
(((t0 & 0x7C00) >> 7)
| ((t0 & 0x7000) >> 12))<<24 |
(((t0 & 0x03E0) >> 2)
| ((t0 & 0x0380) >> 7))<<16 |
(((t0 & 0x001F) << 3)
| ((t0 & 0x001C) >> 2))<<8 |
0xff;
}
#elif 5 == vMacScreenDepth
p = the_data + ((i0 * vMacScreenWidth + j0) << 2);
pixel = p[1]<<24 | p[2]<<16 | p[3]<<8 | 0xff;
#endif
} else
#endif
{
p = the_data + ((i0 * vMacScreenWidth + j0) / 8);
pixel = BWLUT_pixel[(*p >> ((~ j0) & 0x7)) & 1];
}
switch (bpp) {
case 1: /* Assuming 8-bpp */
*bufp = pixel;
break;
case 2: /* Probably 15-bpp or 16-bpp */
*(Uint16 *)bufp = pixel;
break;
case 3:
bufp[0] = (pixel >> 16) & 0xff;
bufp[1] = (pixel >> 8) & 0xff;
bufp[2] = pixel & 0xff;
break;
case 4: /* Probably 32-bpp */
*(Uint32 *)bufp = pixel;
break;
}
}
}
}
}
/* FIXME image unlocking? */
lockdisplay(display);
if(loadimage(my_surface, my_surface->r, pixels, 4*Dx(my_surface->r)*Dy(my_surface->r)) < 0)
sysfatal("%r");
Rectangle r = screen->r;
r.min.x += (Dx(r) - Dx(my_surface->r))/2;
r.min.y += (Dy(r) - Dy(my_surface->r))/2;
draw(screen, r, my_surface, nil, ZP);
flushimage(display, 1);
unlockdisplay(display);
}
LOCALPROC MyDrawChangesAndClear(void)
{
if (ScreenChangedBottom > ScreenChangedTop) {
HaveChangedScreenBuff(ScreenChangedTop, ScreenChangedLeft,
ScreenChangedBottom, ScreenChangedRight);
ScreenClearChanges();
}
}
GLOBALOSGLUPROC DoneWithDrawingForTick(void)
{
#if EnableFSMouseMotion
if (HaveMouseMotion) {
AutoScrollScreen();
}
#endif
MyDrawChangesAndClear();
}
/* --- mouse --- */
/* cursor hiding */
LOCALVAR blnr HaveCursorHidden = falseblnr;
LOCALVAR blnr WantCursorHidden = falseblnr;
LOCALPROC ForceShowCursor(void)
{
if (HaveCursorHidden) {
HaveCursorHidden = falseblnr;
// FIXME pointer toggle
//(void) SDL_ShowCursor(SDL_ENABLE);
}
}
/* cursor moving */
LOCALFUNC blnr MyMoveMouse(si4b h, si4b v)
{
#if EnableMagnify
if (UseMagnify) {
h *= MyWindowScale;
v *= MyWindowScale;
}
#endif
// FIXME pointer warp
//SDL_WarpMouse(h, v);
return trueblnr;
}
/* cursor state */
LOCALPROC MousePositionNotify(int NewMousePosh, int NewMousePosv)
{
blnr ShouldHaveCursorHidden = trueblnr;
#if EnableMagnify
if (UseMagnify) {
NewMousePosh /= MyWindowScale;
NewMousePosv /= MyWindowScale;
}
#endif
#if EnableFSMouseMotion
if (HaveMouseMotion) {
MyMousePositionSetDelta(NewMousePosh - SavedMouseH,
NewMousePosv - SavedMouseV);
SavedMouseH = NewMousePosh;
SavedMouseV = NewMousePosv;
} else
#endif
{
if (NewMousePosh < 0) {
NewMousePosh = 0;
ShouldHaveCursorHidden = falseblnr;
} else if (NewMousePosh >= vMacScreenWidth) {
NewMousePosh = vMacScreenWidth - 1;
ShouldHaveCursorHidden = falseblnr;
}
if (NewMousePosv < 0) {
NewMousePosv = 0;
ShouldHaveCursorHidden = falseblnr;
} else if (NewMousePosv >= vMacScreenHeight) {
NewMousePosv = vMacScreenHeight - 1;
ShouldHaveCursorHidden = falseblnr;
}
#if VarFullScreen
if (UseFullScreen)
#endif
#if MayFullScreen
{
ShouldHaveCursorHidden = trueblnr;
}
#endif
/* if (ShouldHaveCursorHidden || CurMouseButton) */
/*
for a game like arkanoid, would like mouse to still
move even when outside window in one direction
*/
MyMousePositionSet(NewMousePosh, NewMousePosv);
}
WantCursorHidden = ShouldHaveCursorHidden;
}
/* --- keyboard input --- */
LOCALFUNC ui3r Rune2MacKeyCode(Rune i)
{
ui3r v = MKC_None;
switch (i) {
case Kbs: v = MKC_BackSpace; break;
case '\t': v = MKC_Tab; break;
//FIXME case clear: v = MKC_Clear; break;
case '\n': v = MKC_Return; break;
//FIXME case pause: v = MKC_Pause; break;
case Kesc: v = MKC_formac_Escape; break;
case ' ': v = MKC_Space; break;
case '\'': v = MKC_SingleQuote; break;
case ',': v = MKC_Comma; break;
case '-': v = MKC_Minus; break;
case '.': v = MKC_Period; break;
case '/': v = MKC_formac_Slash; break;
case '0': v = MKC_0; break;
case '1': v = MKC_1; break;
case '2': v = MKC_2; break;
case '3': v = MKC_3; break;
case '4': v = MKC_4; break;
case '5': v = MKC_5; break;
case '6': v = MKC_6; break;
case '7': v = MKC_7; break;
case '8': v = MKC_8; break;
case '9': v = MKC_9; break;
case ';': v = MKC_SemiColon; break;
case '=': v = MKC_Equal; break;
case '[': v = MKC_LeftBracket; break;
case '\\': v = MKC_formac_BackSlash; break;
case ']': v = MKC_RightBracket; break;
case '`': v = MKC_formac_Grave; break;
case 'a': v = MKC_A; break;
case 'b': v = MKC_B; break;
case 'c': v = MKC_C; break;
case 'd': v = MKC_D; break;
case 'e': v = MKC_E; break;
case 'f': v = MKC_F; break;
case 'g': v = MKC_G; break;
case 'h': v = MKC_H; break;
case 'i': v = MKC_I; break;
case 'j': v = MKC_J; break;
case 'k': v = MKC_K; break;
case 'l': v = MKC_L; break;
case 'm': v = MKC_M; break;
case 'n': v = MKC_N; break;
case 'o': v = MKC_O; break;
case 'p': v = MKC_P; break;
case 'q': v = MKC_Q; break;
case 'r': v = MKC_R; break;
case 's': v = MKC_S; break;
case 't': v = MKC_T; break;
case 'u': v = MKC_U; break;
case 'v': v = MKC_V; break;
case 'w': v = MKC_W; break;
case 'x': v = MKC_X; break;
case 'y': v = MKC_Y; break;
case 'z': v = MKC_Z; break;
case Kup: v = MKC_Up; break;
case Kdown: v = MKC_Down; break;
case Kright: v = MKC_Right; break;
case Kleft: v = MKC_Left; break;
case Kins: v = MKC_formac_Help; break;
case Khome: v = MKC_formac_Home; break;
case Kend: v = MKC_formac_End; break;
case Kpgup: v = MKC_formac_PageUp; break;
case Kpgdown: v = MKC_formac_PageDown; break;
//case KF|1: v = MKC_formac_F1; break;
//case KF|2: v = MKC_formac_F2; break;
//case KF|3: v = MKC_formac_F3; break;
case KF|4: v = MKC_formac_F4; break;
case KF|5: v = MKC_formac_F5; break;
case KF|6: v = MKC_F6; break;
case KF|7: v = MKC_F7; break;
case KF|8: v = MKC_F8; break;
case KF|9: v = MKC_F9; break;
case KF|10: v = MKC_F10; break;
case KF|11: v = MKC_F11; break;
case KF|12: v = MKC_F12; break;
case Kshift: v = MKC_formac_Shift; break;
case Kctl: v = MKC_formac_Control; break;
case Kalt: case KF|1: v = MKC_formac_Command; break;
case KF|2: v = MKC_formac_Command; break;
case KF|3: v = MKC_formac_Option; break;
//FIXME case SDLK_HELP: v = MKC_formac_Help; break;
//FIXME case SDLK_PRINT: v = MKC_Print; break;
default:
break;
}
return v;
}
LOCALPROC DoKeyCode(Rune r, blnr down)
{
ui3r v = Rune2MacKeyCode(r);
if (MKC_None != v) {
Keyboard_UpdateKeyMap2(v, down);
}
}
LOCALPROC DisableKeyRepeat(void)
{
}
LOCALPROC RestoreKeyRepeat(void)
{
}
LOCALPROC ReconnectKeyCodes3(void)
{
}
LOCALPROC DisconnectKeyCodes3(void)
{
DisconnectKeyCodes2();
MyMouseButtonSet(falseblnr);
}
/* --- time, date, location --- */
#define dbglog_TimeStuff (0 && dbglog_HAVE)
LOCALVAR ui5b TrueEmulatedTime = 0;
#define MyInvTimeDivPow 16
#define MyInvTimeDiv (1 << MyInvTimeDivPow)
#define MyInvTimeDivMask (MyInvTimeDiv - 1)
#define MyInvTimeStep 1089590 /* 1000 / 60.14742 * MyInvTimeDiv */
LOCALVAR Uint32 LastTime;
LOCALVAR Uint32 NextIntTime;
LOCALVAR ui5b NextFracTime;
LOCALPROC IncrNextTime(void)
{
NextFracTime += MyInvTimeStep;
NextIntTime += (NextFracTime >> MyInvTimeDivPow);
NextFracTime &= MyInvTimeDivMask;
}
LOCALPROC InitNextTime(void)
{
NextIntTime = LastTime;
NextFracTime = 0;
IncrNextTime();
}
LOCALVAR ui5b NewMacDateInSeconds;
static int usensec = 0;
/*
* nsec() is wallclock and can be adjusted by timesync
* so need to use cycles() instead, but fall back to
* nsec() in case we can't
*
* "fasthz" is how many ticks there are in a second
* can be read from /dev/time
*
* perhaps using RDTSCP is even better
*/
static uvlong
nanosec(void)
{
static uvlong fasthz, xstart;
uvlong x, div;
int f, n, i;
char tmp[128], *e;
if (fasthz == ~0ULL)
return nsec() - xstart;
if (fasthz == 0) {
fasthz = ~0ULL;
xstart = nsec();
if (usensec)
return 0;
if ((f = open("/dev/time", OREAD)) >= 0 && (n = read(f, tmp, sizeof(tmp)-1)) > 2) {
tmp[n] = 0;
e = tmp;
for (i = 0; i < 3; i++)
strtoll(e, &e, 10);
if ((fasthz = strtoll(e, nil, 10)) < 1)
fasthz = ~0ULL;
else
cycles(&xstart);
}
close(f);
if (fasthz == ~0ULL) {
fprint(2, "couldn't get fasthz, falling back to nsec()\n");
fprint(2, "you might want to disable aux/timesync\n");
return 0;
}
}
cycles(&x);
x -= xstart;
for (div = 1000000000ULL; x < 0x1999999999999999ULL && div > 1 ; div /= 10ULL, x *= 10ULL);
return x / (fasthz / div);
}
LOCALFUNC blnr UpdateTrueEmulatedTime(void)
{
Uint32 LatestTime;
si5b TimeDiff;
LatestTime = nanosec() / 1000000ULL;
if (LatestTime != LastTime) {
NewMacDateInSeconds = LatestTime / 1000;
/* no date and time api in SDL */
LastTime = LatestTime;
TimeDiff = (LatestTime - NextIntTime);
/* this should work even when time wraps */
if (TimeDiff >= 0) {
if (TimeDiff > 256) {
/* emulation interrupted, forget it */
++TrueEmulatedTime;
InitNextTime();
#if dbglog_TimeStuff
dbglog_writelnNum("emulation interrupted",
TrueEmulatedTime);
#endif
} else {
do {
++TrueEmulatedTime;
IncrNextTime();
TimeDiff = (LatestTime - NextIntTime);
} while (TimeDiff >= 0);
}
return trueblnr;
} else {
if (TimeDiff < -256) {
#if dbglog_TimeStuff
dbglog_writeln("clock set back");
#endif
/* clock goofed if ever get here, reset */
InitNextTime();
}
}
}
return falseblnr;
}
LOCALFUNC blnr CheckDateTime(void)
{
if (CurMacDateInSeconds != NewMacDateInSeconds) {
CurMacDateInSeconds = NewMacDateInSeconds;
return trueblnr;
} else {
return falseblnr;
}
}
LOCALPROC StartUpTimeAdjust(void)
{
LastTime = nanosec() / 1000000ULL;
InitNextTime();
}
LOCALFUNC blnr InitLocationDat(void)
{
LastTime = nanosec() / 1000000ULL;
InitNextTime();
NewMacDateInSeconds = LastTime / 1000;
CurMacDateInSeconds = NewMacDateInSeconds;
return trueblnr;
}
/* --- sound --- */
static int audiofd;
static u8int *audiobuf;
static int audiooff;
GLOBALOSGLUFUNC tpSoundSamp MySound_BeginWrite(ui4r n, ui4r *actL)
{
*actL = n;
audiooff += n;
return audiobuf + audiooff - n;
}
LOCALVAR blnr HaveSoundOut = falseblnr;
static char audiofmt[32];
static void
runpcmconv(void *x)
{
int *p;
p = x;
dup(p[0], 0); close(p[0]); close(p[1]);
dup(open("/dev/audio", OWRITE), 1);
procexecl(nil, "/bin/audio/pcmconv", "pcmconv", "-i", audiofmt, nil);
threadexits("exec: %r");
}
#define SOUND_SAMPLERATE 22255
LOCALFUNC blnr MySound_Init(void)
{
int p[2];
sprint(
audiofmt, "%c%dc1r%d",
3 == kLn2SoundSampSz ? 'u' : 's',
3 == kLn2SoundSampSz ? 8 : 16,
SOUND_SAMPLERATE
);
pipe(p);
procrfork(runpcmconv, p, 4096, RFFDG);
close(p[0]);
audiofd = p[1];
HaveSoundOut = trueblnr;
audiobuf = malloc(65536);
return trueblnr;
}
GLOBALOSGLUPROC MySound_EndWrite(ui4r actL)
{
write(audiofd, audiobuf, audiooff);
audiooff = 0;
}
/* --- basic dialogs --- */
LOCALPROC CheckSavedMacMsg(void)
{
/* called only on quit, if error saved but not yet reported */
if (nullpr != SavedBriefMsg) {
char briefMsg0[ClStrMaxLength + 1];
char longMsg0[ClStrMaxLength + 1];
NativeStrFromCStr(briefMsg0, SavedBriefMsg);
NativeStrFromCStr(longMsg0, SavedLongMsg);
fprint(2, "%s\n", briefMsg0);
fprint(2, "%s\n", longMsg0);
SavedBriefMsg = nullpr;
}
}
/* --- main window creation and disposal --- */
LOCALVAR int my_argc;
LOCALVAR char **my_argv;
enum {
Cmouse,
Cresize,
Numchan,
};
static Mousectl *mctl;
static Keyboardctl kctl;
static Rune rune;
static Mouse mouse;
static int altdown, ctldown, shiftdown;
static Alt a[Numchan+1] = {
[Cmouse] = { nil, &mouse, CHANRCV },
[Cresize] = { nil, nil, CHANRCV },
{ nil, nil, CHANNOBLK },
};
static void
kbdproc(void *)
{
char buf[128], buf2[128], *s;
int kfd, n, kbin;
Rune r;
threadsetname("kbdproc");
if ((kfd = open("/dev/kbd", OREAD)) < 0)
sysfatal("/dev/kbd: %r");
kbin = open("/dev/kbin", OWRITE);
buf2[0] = 0;
buf2[1] = 0;
buf[0] = 0;
for (;;) {
if (buf[0] != 0) {
n = strlen(buf)+1;
memmove(buf, buf+n, sizeof(buf)-n);
}
if (buf[0] == 0) {
n = read(kfd, buf, sizeof(buf)-1);
if (n <= 0)
break;
buf[n-1] = 0;
buf[n] = 0;
}
switch (buf[0]) {
default:
continue;
case 'k':
s = buf+1;
while (*s) {
s += chartorune(&r, s);
if (utfrune(buf2+1, r) == nil) {
DoKeyCode(r, 1);
if (r == Kalt) {
/* magic trick: write Alt scancode to disable the "compose" mode */
if (kbin >= 0)
write(kbin, "\x46", 1);
}
}
}
break;
case 'K':
s = buf2+1;
while (*s) {
s += chartorune(&r, s);
if (utfrune(buf+1, r) == nil)
DoKeyCode(r, 0);
}
break;
}
strcpy(buf2, buf);
}
threadexits(nil);
}
static Cursor emptycursor = { {0, 0}, { 0 }, { 0 } };
LOCALFUNC blnr Screen_Init(void)
{
InitKeyCodes();
threadsetname(kStrAppName);
if(initdraw(nil, nil, kStrAppName) < 0)
sysfatal("initdraw: %r");
if ((mctl = initmouse(nil, screen)) == nil)
sysfatal("initmouse: %r");
setcursor(mctl, &emptycursor);
display->locking = 1;
unlockdisplay(display);
a[Cmouse].c = mctl->c;
a[Cresize].c = mctl->resizec;
proccreate(kbdproc, nil, mainstacksize);
return trueblnr;
}
#if MayFullScreen
LOCALVAR blnr GrabMachine = falseblnr;
#endif
#if MayFullScreen
LOCALPROC GrabTheMachine(void)
{
#if GrabKeysFullScreen
#endif
#if EnableFSMouseMotion
/*
if magnification changes, need to reset,
even if HaveMouseMotion already true
*/
if (MyMoveMouse(ViewHStart + (ViewHSize / 2),
ViewVStart + (ViewVSize / 2)))
{
SavedMouseH = ViewHStart + (ViewHSize / 2);
SavedMouseV = ViewVStart + (ViewVSize / 2);
HaveMouseMotion = trueblnr;
}
#endif
}
#endif
#if MayFullScreen
LOCALPROC UngrabMachine(void)
{
#if EnableFSMouseMotion
if (HaveMouseMotion) {
(void) MyMoveMouse(CurMouseH, CurMouseV);
HaveMouseMotion = falseblnr;
}
#endif
#if GrabKeysFullScreen
#endif
}
#endif
#if EnableFSMouseMotion
LOCALPROC MyMouseConstrain(void)
{
si4b shiftdh;
si4b shiftdv;
if (SavedMouseH < ViewHStart + (ViewHSize / 4)) {
shiftdh = ViewHSize / 2;
} else if (SavedMouseH > ViewHStart + ViewHSize - (ViewHSize / 4)) {
shiftdh = - ViewHSize / 2;
} else {
shiftdh = 0;
}
if (SavedMouseV < ViewVStart + (ViewVSize / 4)) {
shiftdv = ViewVSize / 2;
} else if (SavedMouseV > ViewVStart + ViewVSize - (ViewVSize / 4)) {
shiftdv = - ViewVSize / 2;
} else {
shiftdv = 0;
}
if ((shiftdh != 0) || (shiftdv != 0)) {
SavedMouseH += shiftdh;
SavedMouseV += shiftdv;
if (! MyMoveMouse(SavedMouseH, SavedMouseV)) {
HaveMouseMotion = falseblnr;
}
}
}
#endif
LOCALFUNC blnr CreateMainWindow(void)
{
int NewWindowHeight = vMacScreenHeight;
int NewWindowWidth = vMacScreenWidth;
blnr v = falseblnr;
#if EnableMagnify && 1
if (UseMagnify) {
NewWindowHeight *= MyWindowScale;
NewWindowWidth *= MyWindowScale;
}
#endif
#if VarFullScreen
if (UseFullScreen)
#endif
#if MayFullScreen
{
}
#endif
ViewHStart = 0;
ViewVStart = 0;
ViewHSize = vMacScreenWidth;
ViewVSize = vMacScreenHeight;
freeimage(my_surface);
my_surface = allocimage(
display,
Rect(0, 0, NewWindowWidth, NewWindowHeight),
CHAN4(CBlue, 8, CGreen, 8, CRed, 8, CIgnore, 8),
0,
DWhite);
if (NULL == my_surface) {
fprint(2, "allocimage: %r\n");
} else {
#if 0 != vMacScreenDepth
ColorModeWorks = trueblnr;
#endif
v = trueblnr;
pixels = realloc(pixels, 4*NewWindowWidth*NewWindowHeight);
}
return v;
}
#if EnableRecreateW
LOCALFUNC blnr ReCreateMainWindow(void)
{
ForceShowCursor(); /* hide/show cursor api is per window */
#if MayFullScreen
if (GrabMachine) {
GrabMachine = falseblnr;
UngrabMachine();
}
#endif
#if EnableMagnify
UseMagnify = WantMagnify;
#endif
#if VarFullScreen
UseFullScreen = WantFullScreen;
#endif
(void) CreateMainWindow();
if (HaveCursorHidden) {
(void) MyMoveMouse(CurMouseH, CurMouseV);
}
return trueblnr;
}
#endif
LOCALPROC ZapWinStateVars(void)
{
}
#if VarFullScreen
LOCALPROC ToggleWantFullScreen(void)
{
WantFullScreen = ! WantFullScreen;
}
#endif
/* --- SavedTasks --- */
LOCALPROC LeaveBackground(void)
{
ReconnectKeyCodes3();
DisableKeyRepeat();
}
LOCALPROC EnterBackground(void)
{
RestoreKeyRepeat();
DisconnectKeyCodes3();
ForceShowCursor();
}
LOCALPROC LeaveSpeedStopped(void)
{
StartUpTimeAdjust();
}
LOCALPROC EnterSpeedStopped(void)
{
}
LOCALPROC CheckForSavedTasks(void)
{
if (MyEvtQNeedRecover) {
MyEvtQNeedRecover = falseblnr;
/* attempt cleanup, MyEvtQNeedRecover may get set again */
MyEvtQTryRecoverFromFull();
}
#if EnableFSMouseMotion
if (HaveMouseMotion) {
MyMouseConstrain();
}
#endif
if (RequestMacOff) {
RequestMacOff = falseblnr;
if (AnyDiskInserted()) {
MacMsgOverride(kStrQuitWarningTitle,
kStrQuitWarningMessage);
} else {
ForceMacOff = trueblnr;
}
}
if (ForceMacOff) {
return;
}
if (gTrueBackgroundFlag != gBackgroundFlag) {
gBackgroundFlag = gTrueBackgroundFlag;
if (gTrueBackgroundFlag) {
EnterBackground();
} else {
LeaveBackground();
}
}
if (CurSpeedStopped != (SpeedStopped ||
(gBackgroundFlag && ! RunInBackground
#if EnableAutoSlow && 0
&& (QuietSubTicks >= 4092)
#endif
)))
{
CurSpeedStopped = ! CurSpeedStopped;
if (CurSpeedStopped) {
EnterSpeedStopped();
} else {
LeaveSpeedStopped();
}
}
if ((nullpr != SavedBriefMsg) & ! MacMsgDisplayed) {
MacMsgDisplayOn();
}
#if EnableRecreateW
if (0
#if EnableMagnify
|| (UseMagnify != WantMagnify)
#endif
#if VarFullScreen
|| (UseFullScreen != WantFullScreen)
#endif
)
{
(void) ReCreateMainWindow();
}
#endif
#if MayFullScreen
if (GrabMachine != (
#if VarFullScreen
UseFullScreen &&
#endif
! (gTrueBackgroundFlag || CurSpeedStopped)))
{
GrabMachine = ! GrabMachine;
if (GrabMachine) {
GrabTheMachine();
} else {
UngrabMachine();
}
}
#endif
if (NeedWholeScreenDraw) {
NeedWholeScreenDraw = falseblnr;
ScreenChangedAll();
}
#if NeedRequestIthDisk
if (0 != RequestIthDisk) {
Sony_InsertIth(RequestIthDisk);
RequestIthDisk = 0;
}
#endif
if (HaveCursorHidden != (WantCursorHidden
&& ! (gTrueBackgroundFlag || CurSpeedStopped)))
{
HaveCursorHidden = ! HaveCursorHidden;
//FIXME setcursor(mctl, HaveCursorHidden ? &emptycursor : nil);
}
}
/* --- command line parsing --- */
LOCALFUNC blnr ScanCommandLine(void)
{
char *pa;
int i = 1;
label_retry:
if (i < my_argc) {
pa = my_argv[i++];
if ('-' == pa[0]) {
if ((0 == strcmp(pa, "--rom"))
|| (0 == strcmp(pa, "-r")))
{
if (i < my_argc) {
rom_path = my_argv[i++];
goto label_retry;
}
} else
{
MacMsg(kStrBadArgTitle, kStrBadArgMessage, falseblnr);
}
} else {
(void) Sony_Insert1(pa, falseblnr);
goto label_retry;
}
}
return trueblnr;
}
/* --- main program flow --- */
GLOBALOSGLUFUNC blnr ExtraTimeNotOver(void)
{
UpdateTrueEmulatedTime();
return TrueEmulatedTime == OnTrueTime;
}
LOCALPROC WaitForTheNextEvent(void)
{
switch (alt(a)) {
case -1:
//threadexitsall(nil);
case Cmouse:
MousePositionNotify(mouse.xy.x-screen->r.min.x-(Dx(screen->r)-Dx(my_surface->r))/2, mouse.xy.y-screen->r.min.y-(Dx(screen->r)-Dx(my_surface->r))/2);
MyMouseButtonSet(mouse.buttons & 1);
break;
case Cresize:
if(getwindow(display, Refnone) < 0)
sysfatal("getwindow: %r");
break;
}
}
GLOBALOSGLUPROC WaitForNextTick(void)
{
label_retry:
CheckForSavedTasks();
if (ForceMacOff) {
return;
}
if (1) {//CurSpeedStopped) {
DoneWithDrawingForTick();
WaitForTheNextEvent();
if (CurSpeedStopped)
goto label_retry;
}
if (ExtraTimeNotOver()) {
sleep(NextIntTime - LastTime); /* FIXME do a better job like in orca */
goto label_retry;
}
if (CheckDateTime()) {
#if EnableDemoMsg
DemoModeSecondNotify();
#endif
}
OnTrueTime = TrueEmulatedTime;
#if dbglog_TimeStuff
dbglog_writelnNum("WaitForNextTick, OnTrueTime", OnTrueTime);
#endif
}
/* --- platform independent code can be thought of as going here --- */
#include "PROGMAIN.h"
LOCALPROC ZapOSGLUVars(void)
{
InitDrives();
ZapWinStateVars();
}
LOCALPROC ReserveAllocAll(void)
{
#if dbglog_HAVE
dbglog_ReserveAlloc();
#endif
ReserveAllocOneBlock(&ROM, kROM_Size, 5, falseblnr);
ReserveAllocOneBlock(&screencomparebuff,
vMacScreenNumBytes, 5, trueblnr);
#if UseControlKeys
ReserveAllocOneBlock(&CntrlDisplayBuff,
vMacScreenNumBytes, 5, falseblnr);
#endif
ReserveAllocOneBlock(&CLUT_final, CLUT_finalsz, 5, falseblnr);
EmulationReserveAlloc();
}
LOCALFUNC blnr AllocMyMemory(void)
{
uimr n;
blnr IsOk = falseblnr;
ReserveAllocOffset = 0;
ReserveAllocBigBlock = nullpr;
ReserveAllocAll();
n = ReserveAllocOffset;
ReserveAllocBigBlock = (ui3p)calloc(1, n);
if (NULL == ReserveAllocBigBlock) {
MacMsg(kStrOutOfMemTitle, kStrOutOfMemMessage, trueblnr);
} else {
ReserveAllocOffset = 0;
ReserveAllocAll();
if (n != ReserveAllocOffset) {
/* oops, program error */
} else {
IsOk = trueblnr;
}
}
return IsOk;
}
LOCALPROC UnallocMyMemory(void)
{
if (nullpr != ReserveAllocBigBlock) {
free((char *)ReserveAllocBigBlock);
}
}
LOCALFUNC blnr InitOSGLU(void)
{
if (AllocMyMemory())
#if dbglog_HAVE
if (dbglog_open())
#endif
if (ScanCommandLine())
if (LoadMacRom())
if (LoadInitialImages())
if (InitLocationDat())
if (MySound_Init())
if (Screen_Init())
if (CreateMainWindow())
if (WaitForRom())
{
return trueblnr;
}
return falseblnr;
}
LOCALPROC UnInitOSGLU(void)
{
if (MacMsgDisplayed) {
MacMsgDisplayOff();
}
RestoreKeyRepeat();
#if MayFullScreen
UngrabMachine();
#endif
#if IncludePbufs
UnInitPbufs();
#endif
UnInitDrives();
ForceShowCursor();
#if dbglog_HAVE
dbglog_close();
#endif
UnallocMyMemory();
CheckSavedMacMsg();
}
#if IncludeHostTextClipExchange
GLOBALOSGLUFUNC tMacErr HTCEexport(tPbuf i)
{
return mnvm_miscErr;
}
#endif
#if IncludeHostTextClipExchange
GLOBALOSGLUFUNC tMacErr HTCEimport(tPbuf *r)
{
return mnvm_miscErr;
}
#endif
void
threadmain(int argc, char **argv)
{
my_argc = argc;
my_argv = argv;
ZapOSGLUVars();
if (InitOSGLU()) {
ProgramMain();
}
UnInitOSGLU();
threadexitsall(nil);
}