UIQ3 update, some makefile unification, rm old configs, stuff
[picodrive.git] / platform / uiq3 / engine / main.cpp
index 93ef047..03c3e4f 100644 (file)
 #include "debug.h"\r
 #include "../Engine.h"\r
 \r
-#include "../../../pico/picoInt.h"\r
+#include <Pico/PicoInt.h>\r
+#include "../../common/emu.h"\r
+#include "../emu.h"\r
 #include "vid.h"\r
-#include "polledAS.h"\r
+#include "PolledAS.h"\r
 //#include "audio.h"\r
 #include "audio_mediaserver.h"\r
 \r
-#include <EZlib.h>\r
-#include "../../../zlib/gzio_symb.h"\r
+//#include <ezlib.h>\r
+#include <zlib/zlib.h>\r
 \r
 \r
 //#define BENCHMARK\r
@@ -78,21 +80,20 @@ const char *actionNames[] = {
 // globals are allowed, so why not to (ab)use them?\r
 //TInt machineUid = 0;\r
 int gamestate = PGS_Paused, gamestate_next = PGS_Paused;\r
-TPicoConfig *currentConfig = 0;\r
-static char noticeMsg[64];                                     // notice msg to draw\r
+char *loadrom_fname = NULL;\r
+int   loadrom_result = 0;\r
 static timeval noticeMsgTime = { 0, 0 };       // when started showing\r
 static CGameAudioMS *gameAudio = 0;                    // the audio object itself\r
-static int reset_timing, pico_was_reset;\r
-static int state_slot = 0;\r
-extern const char *RomFileName;\r
+static int reset_timing;\r
+extern int pico_was_reset;\r
 extern RSemaphore initSemaphore;\r
 extern RSemaphore pauseSemaphore;\r
+extern RSemaphore loadWaitSemaphore;\r
 \r
 // some forward declarations\r
 static void MainInit();\r
 static void MainExit();\r
 static void DumpMemInfo();\r
-void MainOldCleanup();\r
 \r
 \r
 class TPicoDirectScreenAccess : public MDirectScreenAccess\r
@@ -128,24 +129,8 @@ public:
 };\r
 \r
 \r
-static int snd_excess_add = 0, snd_excess_cnt = 0; // hack\r
-\r
-static void updateSound(void)\r
+static void updateSound(int len)\r
 {\r
-       int len = PsndLen;\r
-\r
-       snd_excess_cnt += snd_excess_add;\r
-       if (snd_excess_cnt >= 0x10000) {\r
-               snd_excess_cnt -= 0x10000;\r
-               if (PicoOpt&8) {\r
-                       PsndOut[len*2]   = PsndOut[len*2-2];\r
-                       PsndOut[len*2+1] = PsndOut[len*2-1];\r
-               } else {\r
-                       PsndOut[len]   = PsndOut[len-1];\r
-               }\r
-               len++;\r
-       }\r
-\r
        PsndOut = gameAudio->NextFrameL(len);\r
        if(!PsndOut) { // sound output problems?\r
                strcpy(noticeMsg, "SOUND@OUTPUT@ERROR;@SOUND@DISABLED");\r
@@ -190,12 +175,16 @@ static void TargetEpocGameL()
        MainInit();\r
        buff[0] = 0;\r
 \r
+       PicoInit();\r
+\r
        // just to keep the backlight on (works only on UIQ2)\r
        //blevent.Set(TRawEvent::EActive);\r
 \r
        // loop?\r
-       for(;;) {\r
-               if(gamestate == PGS_Running) {\r
+       for(;;)\r
+       {\r
+               if (gamestate == PGS_Running)\r
+               {\r
                        // switch context to other thread\r
                        User::After(50000);\r
                        // prepare window and stuff\r
@@ -217,15 +206,14 @@ static void TargetEpocGameL()
                        if(!noticeMsgTime.tv_sec && pico_was_reset)\r
                                gettimeofday(&noticeMsgTime, 0);\r
 \r
-                       if (PsndOut) {\r
-                               snd_excess_cnt = 0;\r
-                               snd_excess_add = ((PsndRate - PsndLen*target_fps)<<16) / target_fps;\r
-                       }\r
+                       // prepare CD buffer\r
+                       if (PicoAHW & PAHW_MCD) PicoCDBufferInit();\r
 \r
                        pico_was_reset = 0;\r
                        reset_timing = 1;\r
 \r
-                       while(gamestate == PGS_Running) {\r
+                       while (gamestate == PGS_Running)\r
+                       {\r
                                gettimeofday(&tval, 0);\r
                                if(reset_timing) {\r
                                        reset_timing = 0;\r
@@ -242,7 +230,8 @@ static void TargetEpocGameL()
                                }\r
 \r
                                // second changed?\r
-                               if(thissec != tval.tv_sec) {\r
+                               if (thissec != tval.tv_sec)\r
+                               {\r
 #ifdef BENCHMARK\r
                                        static int bench = 0, bench_fps = 0, bench_fps_s = 0, bfp = 0, bf[4];\r
                                        if(++bench == 10) {\r
@@ -254,14 +243,14 @@ static void TargetEpocGameL()
                                        bench_fps += frames_shown;\r
                                        sprintf(buff, "%02i/%02i/%02i", frames_shown, bench_fps_s, (bf[0]+bf[1]+bf[2]+bf[3])>>2);\r
 #else\r
-                                       if(currentConfig->iFlags & 2\r
+                                       if (currentConfig.EmuOpt & EOPT_SHOW_FPS\r
                                                sprintf(buff, "%02i/%02i", frames_shown, frames_done);\r
 #endif\r
 \r
 \r
                                        thissec = tval.tv_sec;\r
 \r
-                                       if(PsndOut == 0 && currentConfig->iFrameskip >= 0) {\r
+                                       if(PsndOut == 0 && currentConfig.Frameskip >= 0) {\r
                                                frames_done = frames_shown = 0;\r
                                        } else {\r
                                                // it is quite common for this implementation to leave 1 fame unfinished\r
@@ -278,8 +267,10 @@ static void TargetEpocGameL()
 \r
 \r
                                lim_time = (frames_done+1) * target_frametime;\r
-                               if(currentConfig->iFrameskip >= 0) { // frameskip enabled\r
-                                       for(i = 0; i < currentConfig->iFrameskip; i++) {\r
+                               if (currentConfig.Frameskip >= 0) // frameskip enabled\r
+                               {\r
+                                       for (i = 0; i < currentConfig.Frameskip && gamestate == PGS_Running; i++)\r
+                                       {\r
                                                CGameWindow::DoKeys();\r
                                                SkipFrame(); frames_done++;\r
                                                if (PsndOut) { // do framelimitting if sound is enabled\r
@@ -291,13 +282,17 @@ static void TargetEpocGameL()
                                                }\r
                                                lim_time += target_frametime;\r
                                        }\r
-                               } else if(tval.tv_usec > lim_time) { // auto frameskip\r
+                               }\r
+                               else if(tval.tv_usec > lim_time) { // auto frameskip\r
                                        // no time left for this frame - skip\r
                                        CGameWindow::DoKeys();\r
                                        SkipFrame(); frames_done++;\r
                                        continue;\r
                                }\r
 \r
+                               // we might have lost focus already\r
+                               if (gamestate != PGS_Running) break;\r
+\r
                                CGameWindow::DoKeys();\r
                                PicoFrame();\r
 \r
@@ -306,7 +301,7 @@ static void TargetEpocGameL()
                                if(thissec != tval.tv_sec) tval.tv_usec+=1000000;\r
 \r
                                // sleep if we are still too fast\r
-                               if(PsndOut != 0 || currentConfig->iFrameskip < 0)\r
+                               if(PsndOut != 0 || currentConfig.Frameskip < 0)\r
                                {\r
                                        // TODO: check if User::After() is accurate\r
                                        gettimeofday(&tval, 0);\r
@@ -324,18 +319,36 @@ static void TargetEpocGameL()
                                        vidDrawFrame(notice, buff, frames_shown);\r
 \r
                                frames_done++; frames_shown++;\r
-                       }\r
+                       } // while\r
+\r
+                       if (PicoAHW & PAHW_MCD) PicoCDBufferFree();\r
 \r
                        // save SRAM\r
-                       if((currentConfig->iFlags & 1) && SRam.changed) {\r
-                               saveLoadGame(0, 1);\r
+                       if ((currentConfig.EmuOpt & EOPT_USE_SRAM) && SRam.changed) {\r
+                               emu_SaveLoadGame(0, 1);\r
                                SRam.changed = 0;\r
                        }\r
+                       CPolledActiveScheduler::Instance()->Schedule();\r
                        CGameWindow::FreeResources();\r
-               } else if(gamestate == PGS_Paused) {\r
+               }\r
+               else if(gamestate == PGS_ReloadRom)\r
+               {\r
+                       loadrom_result = emu_ReloadRom(loadrom_fname);\r
+                       pico_was_reset = 1;\r
+                       if (loadrom_result)\r
+                               gamestate = PGS_Running;\r
+                       else\r
+                               gamestate = PGS_Paused;\r
+                       DEBUGPRINT(_L("done loading ROM, retval=%i"), loadrom_result);\r
+                       loadWaitSemaphore.Signal();\r
+                       User::After(50000);\r
+               }\r
+               else if(gamestate == PGS_Paused) {\r
                        DEBUGPRINT(_L("pausing.."));\r
                        pauseSemaphore.Wait();\r
-               } else if(gamestate == PGS_KeyConfig) {\r
+               }\r
+               else if(gamestate == PGS_KeyConfig)\r
+               {\r
                        // switch context to other thread\r
                        User::After(50000);\r
                        // prepare window and stuff\r
@@ -364,6 +377,10 @@ static void TargetEpocGameL()
                }\r
        }\r
 \r
+       // this thread has to close it's own handles,\r
+       // other one will crash trying to do that\r
+       PicoExit();\r
+\r
        MainExit();\r
 }\r
 \r
@@ -373,9 +390,6 @@ static void MainInit()
 {\r
        DEBUGPRINT(_L("\r\n\r\nstarting.."));\r
 \r
-       // our thread might have been crashed previously, so many other objects may be still floating around\r
-       MainOldCleanup();\r
-\r
        DEBUGPRINT(_L("CPolledActiveScheduler::NewL()"));\r
        CPolledActiveScheduler::NewL(); // create Polled AS for the sound engine\r
 \r
@@ -384,8 +398,7 @@ static void MainInit()
        DumpMemInfo();\r
 \r
        // try to start pico\r
-       DEBUGPRINT(_L("PicoInit();"));\r
-       PicoInit();\r
+       DEBUGPRINT(_L("PicoInit()"));\r
        PicoDrawSetColorFormat(2);\r
        PicoWriteSound = updateSound;\r
 \r
@@ -403,13 +416,6 @@ static void MainExit()
 \r
        DEBUGPRINT(_L("%i: cleaning up.."), (TInt32) thisThread.Id());\r
 \r
-       // save SRAM\r
-       if((currentConfig->iFlags & 1) && SRam.changed) {\r
-               saveLoadGame(0, 1);\r
-               SRam.changed = 0;\r
-       }\r
-\r
-       PicoExit();\r
 //     pauseSemaphore.Close();\r
 \r
        if(gameAudio) delete gameAudio;\r
@@ -418,20 +424,6 @@ static void MainExit()
        delete CPolledActiveScheduler::Instance();\r
 }\r
 \r
-void MainOldCleanup()\r
-{\r
-       DEBUGPRINT(_L("MainOldCleanup.."));\r
-\r
-       // There was previously a handle leak here, so thread stuff was not cleaned\r
-       // and I thought I would have to do it mself.\r
-\r
-       // clean any resources which might be left after a thread crash\r
-       //CGameWindow::FreeResources(ETrue);\r
-\r
-       //if(CPolledActiveScheduler::Instance())\r
-       //      delete CPolledActiveScheduler::Instance();\r
-}\r
-\r
 static void DumpMemInfo()\r
 {\r
        TInt    ramSize, ramSizeFree, romSize;\r
@@ -444,12 +436,20 @@ static void DumpMemInfo()
 }\r
 \r
 \r
+extern "C" TInt my_SetExceptionHandler(TInt, TExceptionHandler, TUint32);\r
+\r
 TInt EmuThreadFunction(TAny*)\r
 {\r
+       TInt ret;\r
        const TUint32 exs = KExceptionAbort|KExceptionKill|KExceptionUserInterrupt|KExceptionFpe|KExceptionFault|KExceptionInteger|KExceptionDebug;\r
        \r
-       DEBUGPRINT(_L("EmuThreadFunction()"));\r
-       User::SetExceptionHandler(ExceptionHandler, exs/*(TUint32) -1*/); // does not work?\r
+       DEBUGPRINT(_L("EmuThreadFunction(), def ExceptionHandler %08x, my %08x"),\r
+               User::ExceptionHandler(), ExceptionHandler);\r
+       User::SetJustInTime(1);\r
+       ret = User::SetExceptionHandler(ExceptionHandler, exs/*(TUint32) -1*/); // does not work :(\r
+       // my_SetExceptionHandler(KCurrentThreadHandle, ExceptionHandler, 0xffffffff);\r
+       DEBUGPRINT(_L("SetExceptionHandler %i, %08x"), ret, User::ExceptionHandler());\r
+       User::ModifyExceptionMask(0, exs);\r
 \r
        //TInt pc, sp;\r
        //asm volatile ("str pc, %0" : "=m" (pc) );\r
@@ -479,7 +479,7 @@ TInt EmuThreadFunction(TAny*)
 \r
        TRAPD(error, TargetEpocGameL());\r
 \r
-       __ASSERT_ALWAYS(!error, User::Panic(_L("Picosmall"), error));\r
+       __ASSERT_ALWAYS(!error, User::Panic(_L("PicoDrive"), error));\r
        delete cleanup;\r
 \r
        DEBUGPRINT(_L("exitting.."));   \r
@@ -586,17 +586,19 @@ void CGameWindow::ConstructResourcesL()
        // try to start the audio engine\r
        static int PsndRate_old = 0, PicoOpt_old = 0, pal_old = 0;\r
 \r
-       if(gamestate == PGS_Running && (currentConfig->iFlags & 4)) {\r
+       if (gamestate == PGS_Running && (currentConfig.EmuOpt & EOPT_EN_SOUND))\r
+       {\r
                TInt err = 0;\r
                if(PsndRate != PsndRate_old || (PicoOpt&11) != (PicoOpt_old&11) || Pico.m.pal != pal_old) {\r
                        // if rate changed, reset all enabled chips, else reset only those chips, which were recently enabled\r
                        //sound_reset(PsndRate != PsndRate_old ? PicoOpt : (PicoOpt&(PicoOpt^PicoOpt_old)));\r
-                       sound_rerate();\r
+                       PsndRerate(1);\r
                }\r
                if(!gameAudio || PsndRate != PsndRate_old || ((PicoOpt&8) ^ (PicoOpt_old&8)) || Pico.m.pal != pal_old) { // rate or stereo or pal/ntsc changed\r
                        if(gameAudio) delete gameAudio; gameAudio = 0;\r
                        DEBUGPRINT(_L("starting audio: %i len: %i stereo: %i, pal: %i"), PsndRate, PsndLen, PicoOpt&8, Pico.m.pal);\r
-                       TRAP(err, gameAudio = CGameAudioMS::NewL(PsndRate, (PicoOpt&8) ? 1 : 0, Pico.m.pal ? 50 : 60));\r
+                       TRAP(err, gameAudio = CGameAudioMS::NewL(PsndRate, (PicoOpt&8) ? 1 : 0,\r
+                                               Pico.m.pal ? 50 : 60, currentConfig.volume));\r
                }\r
                if( gameAudio) {\r
                        TRAP(err, PsndOut = gameAudio->ResumeL());\r
@@ -678,11 +680,6 @@ void CGameWindow::FreeResources()
        }\r
        \r
        vidFree();\r
-\r
-       // emu might change renderer by itself, so we may need to sync config\r
-       if(currentConfig && currentConfig->iPicoOpt != PicoOpt) {\r
-               currentConfig->iFlags |= 0x80;\r
-       }\r
 }\r
 \r
 \r
@@ -708,7 +705,7 @@ void CGameWindow::DoKeys(void)
                                const TPicoAreaConfigEntry *e = areaConfig + 1;\r
                                for(i = 0; !e->rect.IsEmpty(); e++, i++)\r
                                        if(e->rect.Contains(p)) {\r
-                                               areaActions = currentConfig->iAreaBinds[i];\r
+                                               areaActions = currentConfig.KeyBinds[i+256];\r
                                                break;\r
                                        }\r
                                //DEBUGPRINT(_L("pointer event: %i %i"), p.iX, p.iY);\r
@@ -767,7 +764,7 @@ void CGameWindow::DoKeys(void)
                for(i = 9; i >= 0; i--) {\r
                        int scan = pressedKeys[i];\r
                        if(scan) {\r
-                               if(keyFlags[scan] & 1) allActions |= currentConfig->iKeyBinds[scan];\r
+                               if(keyFlags[scan] & 1) allActions |= currentConfig.KeyBinds[scan];\r
                                if((keyFlags[scan]& 3)==3) forceUpdate = 1;\r
                                if(keyFlags[scan] & 2) keyFlags[scan] &= ~1;\r
                        }\r
@@ -807,7 +804,7 @@ void CGameWindow::DoKeysConfig(TUint &which)
                                        const TPicoAreaConfigEntry *e = areaConfig + 1;\r
                                        for(i = 0; e->rect != TRect(0,0,0,0); e++, i++)\r
                                                if(e->rect.Contains(p)) {\r
-                                                       currentConfig->iAreaBinds[i] ^= currentActCode;\r
+                                                       currentConfig.KeyBinds[i+256] ^= currentActCode;\r
                                                        break;\r
                                                }\r
                                }\r
@@ -822,7 +819,7 @@ void CGameWindow::DoKeysConfig(TUint &which)
                                if(which == 31) {\r
                                        gamestate = PGS_Paused;\r
                                } else if (scan < 256) {\r
-                                       if(!(keyFlags[scan]&0x40)) currentConfig->iKeyBinds[scan] ^= currentActCode;\r
+                                       if(!(keyFlags[scan]&0x40)) currentConfig.KeyBinds[scan] ^= currentActCode;\r
                                }\r
                        }\r
 \r
@@ -844,22 +841,22 @@ void CGameWindow::DoKeysConfig(TUint &which)
 \r
 void CGameWindow::RunEvents(TUint32 which)\r
 {\r
-       if(which & 0x4000) currentConfig->iFrameskip = -1;\r
-       if(which & 0x2000) currentConfig->iFrameskip =  8;\r
-       if(which & 0x1800) { // save or load (but not both)\r
+       if (which & 0x4000) currentConfig.Frameskip = -1;\r
+       if (which & 0x2000) currentConfig.Frameskip =  8;\r
+       if (which & 0x1800) { // save or load (but not both)\r
                if(PsndOut) gameAudio->Pause(); // this may take a while, so we pause sound output\r
 \r
                vidDrawNotice((which & 0x1000) ? "LOADING@GAME" : "SAVING@GAME");\r
-               saveLoadGame(which & 0x1000);\r
+               emu_SaveLoadGame(which & 0x1000, 0);\r
 \r
                if(PsndOut) PsndOut = gameAudio->ResumeL();\r
                reset_timing = 1;\r
        }\r
-       if(which & 0x0400) gamestate = PGS_Paused;\r
-       if(which & 0x0200) { // switch renderer\r
-               if(!(currentConfig->iScreenMode == TPicoConfig::PMFit &&\r
-                       (currentConfig->iScreenRotation == TPicoConfig::PRot0 || currentConfig->iScreenRotation == TPicoConfig::PRot180))) {\r
-\r
+       if (which & 0x0400) gamestate = PGS_Paused;\r
+       if (which & 0x0200) { // switch renderer\r
+               if (!(currentConfig.scaling == TPicoConfig::PMFit &&\r
+                       (currentConfig.rotation == TPicoConfig::PRot0 || currentConfig.rotation == TPicoConfig::PRot180)))\r
+               {\r
                        PicoOpt^=0x10;\r
                        vidInit(0, 1);\r
 \r
@@ -878,111 +875,20 @@ void CGameWindow::RunEvents(TUint32 which)
                sprintf(noticeMsg, "SAVE@SLOT@%i@SELECTED", state_slot);\r
                gettimeofday(&noticeMsgTime, 0);\r
        }\r
-       if(which & 0x0020) if(gameAudio) gameAudio->ChangeVolume(0);\r
-       if(which & 0x0010) if(gameAudio) gameAudio->ChangeVolume(1);\r
+       if(which & 0x0020) if(gameAudio) currentConfig.volume = gameAudio->ChangeVolume(0);\r
+       if(which & 0x0010) if(gameAudio) currentConfig.volume = gameAudio->ChangeVolume(1);\r
 }\r
 \r
 \r
-// must use wrappers, or else will run into some weird loader error (see pico/area.c)\r
-static size_t fRead2(void *p, size_t _s, size_t _n, void *file)\r
-{\r
-       return fread(p, _s, _n, (FILE *) file);\r
-}\r
-\r
-static size_t fWrite2(void *p, size_t _s, size_t _n, void *file)\r
-{\r
-       return fwrite(p, _s, _n, (FILE *) file);\r
-}\r
-\r
-static size_t gzRead2(void *p, size_t, size_t _n, void *file)\r
-{\r
-       return gzread(file, p, _n);\r
-}\r
-\r
-static size_t gzWrite2(void *p, size_t, size_t _n, void *file)\r
+extern "C" void emu_noticeMsgUpdated(void)\r
 {\r
-       return gzwrite(file, p, _n);\r
-}\r
-\r
-\r
-// this function is shared between both threads\r
-int saveLoadGame(int load, int sram)\r
-{\r
-       int res = 0;\r
-\r
-       if(!RomFileName) return -1;\r
-\r
-       // make save filename\r
-       char saveFname[KMaxFileName];\r
-       strcpy(saveFname, RomFileName);\r
-       saveFname[KMaxFileName-8] = 0;\r
-       if(saveFname[strlen(saveFname)-4] == '.') saveFname[strlen(saveFname)-4] = 0;\r
-       if(sram) strcat(saveFname, ".srm");\r
-       else {\r
-               if(state_slot > 0 && state_slot < 10) sprintf(saveFname, "%s.%i", saveFname, state_slot);\r
-               strcat(saveFname, ".mds");\r
-       }\r
-\r
-       DEBUGPRINT(_L("saveLoad (%i, %i): %S"), load, sram, DO_CONV(saveFname));\r
-\r
-       if(sram) {\r
-               FILE *sramFile;\r
-               int sram_size = SRam.end-SRam.start+1;\r
-               if(SRam.reg_back & 4) sram_size=0x2000;\r
-               if(!SRam.data) return 0; // SRam forcefully disabled for this game\r
-               if(load) {\r
-                       sramFile = fopen(saveFname, "rb");\r
-                       if(!sramFile) return -1;\r
-                       fread(SRam.data, 1, sram_size, sramFile);\r
-                       fclose(sramFile);\r
-               } else {\r
-                       // sram save needs some special processing\r
-                       // see if we have anything to save\r
-                       for(; sram_size > 0; sram_size--)\r
-                               if(SRam.data[sram_size-1]) break;\r
-                       \r
-                       if(sram_size) {\r
-                               sramFile = fopen(saveFname, "wb");\r
-                               res = fwrite(SRam.data, 1, sram_size, sramFile);\r
-                               res = (res != sram_size) ? -1 : 0;\r
-                               fclose(sramFile);\r
-                       }\r
-               }\r
-               return res;\r
-       } else {\r
-               void *PmovFile = NULL;\r
-               // try gzip first\r
-               if(currentConfig->iFlags & 0x80) {\r
-                       strcat(saveFname, ".gz");\r
-                       if( (PmovFile = gzopen(saveFname, load ? "rb" : "wb")) ) {\r
-                               areaRead  = gzRead2;\r
-                               areaWrite = gzWrite2;\r
-                               if(!load) gzsetparams(PmovFile, 9, Z_DEFAULT_STRATEGY);\r
-                       } else\r
-                               saveFname[strlen(saveFname)-3] = 0;\r
-               }\r
-               if(!PmovFile) { // gzip failed or was disabled\r
-                       if( (PmovFile = fopen(saveFname, load ? "rb" : "wb")) ) {\r
-                               areaRead  = fRead2;\r
-                               areaWrite = fWrite2;\r
-                       }\r
-               }\r
-               if(PmovFile) {\r
-                       PmovState(load ? 6 : 5, PmovFile); // load/save\r
-                       strcpy(noticeMsg, load ? "GAME@LOADED" : "GAME@SAVED");\r
-                       if(areaRead == gzRead2)\r
-                                gzclose(PmovFile);\r
-                       else fclose ((FILE *) PmovFile);\r
-                       PmovFile = 0;\r
-                       if (load) Pico.m.dirtyPal=1;\r
-               } else {\r
-                       strcpy(noticeMsg, load ? "LOAD@FAILED" : "SAVE@FAILED");\r
-                       res = -1;\r
-               }\r
-\r
-               gettimeofday(&noticeMsgTime, 0);\r
-               return res;\r
+       char *p = noticeMsg;\r
+       while (*p) {\r
+               if (*p == ' ') *p = '@';\r
+               if (*p < '0' || *p > 'Z') { *p = 0; break; }\r
+               p++;\r
        }\r
+       gettimeofday(&noticeMsgTime, 0);\r
 }\r
 \r
 // static class members\r