|
#include <swilib.h> |
|
|
|
// Глобальные переменные и структуры для создания окна и графического контекста |
|
|
|
#define GUI_STATE_CLOSED 0 |
|
#define GUI_STATE_BACKGROUND 1 |
|
#define GUI_STATE_ACTIVE 2 |
|
|
|
wchar_t maincsm_name_body[140]; |
|
|
|
typedef struct{ |
|
CSM_RAM csm; |
|
int guiID; |
|
}MAIN_CSM; |
|
|
|
typedef struct{ |
|
CSM_DESC maincsm; |
|
WSHDR name; |
|
}MAIN_CSM_DESC; |
|
|
|
typedef struct{ |
|
GUI gui; |
|
WSHDR *ws; |
|
}MAIN_GUI; |
|
|
|
int minus11=-11; |
|
|
|
MAIN_CSM_DESC mainCsmDesc={ |
|
{ |
|
0, // onmessage |
|
0, // oncreate |
|
#ifdef NEWSGOLD |
|
0, 0, 0, 0, |
|
#endif |
|
0, // onclose |
|
sizeof(MAIN_CSM), |
|
1, |
|
&minus11 // wtf? |
|
}, |
|
{ |
|
maincsm_name_body, |
|
NAMECSM_MAGIC1, |
|
NAMECSM_MAGIC2, |
|
0, 139, 0 |
|
} |
|
}; |
|
|
|
void *guiFunctions[11]; |
|
RECT guiCanvasSize={0}; |
|
MAIN_CSM mainCSM; |
|
|
|
///// |
|
|
|
IMGHDR imgHdr; |
|
RECT imgRect={0}; |
|
DRWOBJ drwobj; |
|
int fileHandle; |
|
int fileErr; |
|
|
|
unsigned int *fileData; |
|
unsigned int *nextFileData; |
|
unsigned int offsetIntoFileData=0; |
|
unsigned int fileDataLength=0; |
|
|
|
unsigned int frameLength; |
|
unsigned int offsetIntoFrame; |
|
unsigned int offsetIntoBitmap; |
|
int playing=0; |
|
MUTEX fileMutex; |
|
int frameCount=0; |
|
|
|
int mainCsmID; |
|
int audioPlayerID; |
|
int avOffset=0; |
|
|
|
int logFile; |
|
|
|
GBSTMR redrawTimer; |
|
|
|
#define RLV_SKIP 0 |
|
#define RLV_COPY 0x01000000 |
|
#define RLV_MEMSET 0x02000000 |
|
#define RLV_ZEROS 0x03000000 |
|
#define RLV_PADDING 0x04000000 |
|
#define RLV_END_OF_FILE 0x05000000 |
|
|
|
#define FILE_PAGE_SIZE 40960 |
|
|
|
void RedrawTimerProc(){ |
|
int timerValue; |
|
// Подстраиваем синхронизацию звука и картинки. |
|
// Таймер принимает интервал в каких-то максимально всратых единицах, и вообще не особенно точен. |
|
if(avOffset<-67){ // Картинка запаздывает |
|
timerValue=10; |
|
}else if(avOffset>67){ // Картинка рисуется слишком быстро |
|
timerValue=15; |
|
}else{ |
|
timerValue=13; // Примерно 67 мс |
|
} |
|
|
|
GBS_StartTimerProc(&redrawTimer, timerValue, RedrawTimerProc); |
|
REDRAW(); |
|
|
|
// Я хз как нормально отключить таймаут подсветки, поэтому просто каждый кадр ставлю максимальную яркость |
|
// ¯\_(ツ)_/¯ |
|
// Один вызов для экрана, другой для клавиатуры, хз что есть что |
|
SetIllumination(0, 1, 100, 0); |
|
SetIllumination(1, 1, 100, 0); |
|
frameCount++; |
|
} |
|
|
|
void DoReadMoreFileData(){ |
|
MutexLock(&fileMutex); |
|
_read(fileHandle, nextFileData, fileDataLength<<2, &fileErr); |
|
MutexUnlock(&fileMutex); |
|
} |
|
|
|
void ReadMoreFileData(){ |
|
MutexLock(&fileMutex); |
|
unsigned int* tmp=nextFileData; |
|
nextFileData=fileData; |
|
fileData=tmp; |
|
offsetIntoFileData=0; |
|
SUBPROC((void*)DoReadMoreFileData); |
|
MutexUnlock(&fileMutex); |
|
} |
|
|
|
static inline void ApplyNextBitmapOperation(){ |
|
unsigned int command=fileData[offsetIntoFileData++]; |
|
offsetIntoFrame++; |
|
unsigned int cmdArg=command & 0x00ffffff; |
|
switch(command & 0xff000000){ |
|
case RLV_SKIP: |
|
offsetIntoBitmap+=cmdArg; |
|
break; |
|
case RLV_COPY: |
|
memcpy(imgHdr.bitmap+(offsetIntoBitmap<<2), fileData+offsetIntoFileData, cmdArg<<2); |
|
offsetIntoFileData+=cmdArg; |
|
offsetIntoFrame+=cmdArg; |
|
offsetIntoBitmap+=cmdArg; |
|
break; |
|
case RLV_ZEROS: |
|
zeromem(imgHdr.bitmap+(offsetIntoBitmap<<2), cmdArg<<2); |
|
offsetIntoBitmap+=cmdArg; |
|
break; |
|
case RLV_MEMSET: |
|
memset(imgHdr.bitmap+(offsetIntoBitmap<<2), fileData[offsetIntoFileData++], cmdArg<<2); |
|
offsetIntoBitmap+=cmdArg; |
|
offsetIntoFrame++; |
|
break; |
|
} |
|
} |
|
|
|
void RenderNextFrameIntoBitmap(){ |
|
int frameLength=fileData[offsetIntoFileData++]; |
|
int lengthCmd=frameLength & 0xff000000; |
|
if(lengthCmd==RLV_PADDING){ |
|
ReadMoreFileData(); |
|
frameLength=fileData[offsetIntoFileData++]; |
|
}else if(lengthCmd==RLV_END_OF_FILE){ |
|
playing=0; |
|
/* CloseCSM(mainCsmID); */ |
|
return; |
|
} |
|
frameLength>>=2; |
|
offsetIntoFrame=0; |
|
offsetIntoBitmap=0; |
|
if(offsetIntoFileData+frameLength<fileDataLength){ |
|
while(offsetIntoFrame<frameLength){ |
|
ApplyNextBitmapOperation(); |
|
} |
|
} |
|
} |
|
|
|
// Колбэки для окна/"GUI" |
|
|
|
void GUI_OnRedraw(MAIN_GUI *gui){ |
|
if(!playing) |
|
return; |
|
|
|
RenderNextFrameIntoBitmap(); |
|
|
|
DrawObject(&drwobj); |
|
|
|
/* wsprintf(gui->ws, "%d ms", avOffset); */ |
|
|
|
/* DrawString(gui->ws, 0, 0, ScreenW(), 30, FONT_SMALL, 0, GetPaletteAdrByColorIndex(1), GetPaletteAdrByColorIndex(0)); */ |
|
} |
|
|
|
void GUI_OnCreate(MAIN_GUI *gui, void *(*malloc_adr)(int)){ |
|
|
|
/* logFile=_open("4:\\BadApple\\log.txt", A_WriteOnly | A_BIN | A_Create, P_WRITE, &fileErr); */ |
|
|
|
gui->ws=AllocWS(128); |
|
imgHdr.w=ScreenW(); |
|
imgHdr.h=ScreenH(); |
|
imgHdr.bpnum=5; |
|
imgHdr.bitmap=(unsigned char*)malloc(132*176); |
|
gui->gui.state=1; |
|
|
|
zeromem(imgHdr.bitmap, 132*176); |
|
|
|
imgRect.x2=imgHdr.w; |
|
imgRect.y2=imgHdr.h; |
|
SetPropTo_Obj5(&drwobj, &imgRect, 0, &imgHdr); |
|
SetColor(&drwobj, 0, 0); |
|
|
|
MutexCreate(&fileMutex); |
|
|
|
fileHandle=_open("4:\\BadApple\\BadApple.rlv", A_ReadOnly | A_BIN, P_READ, &fileErr); |
|
fileData=malloc(fileDataLength=FILE_PAGE_SIZE); |
|
nextFileData=malloc(FILE_PAGE_SIZE); |
|
fileDataLength>>=2; |
|
DoReadMoreFileData(); |
|
ReadMoreFileData(); |
|
|
|
WSHDR* sndPath=AllocWS(256); |
|
WSHDR* sndFName=AllocWS(256); |
|
str_2ws(sndFName, "BadApple.mp3", 256); |
|
str_2ws(sndPath, "4:\\BadApple", 256); |
|
PLAYFILE_OPT opt; |
|
zeromem(&opt, sizeof(PLAYFILE_OPT)); |
|
opt.repeat_num=1; |
|
opt.time_between_play=0; |
|
opt.play_first=0; |
|
opt.volume=1; |
|
opt.unk5=1; |
|
opt.unk4=0x80000000; |
|
audioPlayerID=PlayFile(0xC, sndPath, sndFName, 0, GBS_GetCurCepid(), MSG_PLAYFILE_REPORT, &opt); |
|
|
|
FreeWS(sndPath); |
|
FreeWS(sndFName); |
|
} |
|
|
|
void GUI_OnClose(MAIN_GUI *gui, void (*mfree_adr)(void *)){ |
|
playing=0; |
|
GBS_DelTimer(&redrawTimer); |
|
gui->gui.state=GUI_STATE_CLOSED; |
|
FreeWS(gui->ws); |
|
mfree(imgHdr.bitmap); |
|
mfree(fileData); |
|
mfree(nextFileData); |
|
_close(fileHandle, &fileErr); |
|
/* _close(logFile, &fileErr); */ |
|
MutexDestroy(&fileMutex); |
|
} |
|
|
|
void GUI_OnFocus(MAIN_GUI *gui, void *(*malloc_adr)(int), void (*mfree_adr)(void *)){ |
|
gui->gui.state=GUI_STATE_ACTIVE; |
|
DisableIDLETMR(); |
|
} |
|
|
|
void GUI_OnUnfocus(MAIN_GUI *gui, void (*mfree_adr)(void *)){ |
|
if(gui->gui.state!=GUI_STATE_ACTIVE) |
|
return; |
|
gui->gui.state=GUI_STATE_BACKGROUND; |
|
} |
|
|
|
int GUI_OnKey(MAIN_GUI *gui, GUI_MSG *msg){ |
|
if(msg->gbsmsg->msg==KEY_DOWN && msg->gbsmsg->submess==RIGHT_SOFT){ |
|
return 1; |
|
} |
|
return 0; |
|
} |
|
|
|
void GUI_OnDealloc(MAIN_GUI *gui, void (*mfree_adr)(void *)){ |
|
// no-op |
|
} |
|
|
|
int GUI_UnknownFunc8(){ |
|
return 0; |
|
} |
|
|
|
int GUI_UnknownFunc9(){ |
|
return 0; |
|
} |
|
|
|
// Колбэки для "CSM" (я хз что такое CSM, если честно) |
|
|
|
void MainCSM_OnClose(CSM_RAM *csm){ |
|
// Здесь надо бы выгружать эльф, но я не умею это делать |
|
} |
|
|
|
int MainCSM_OnMessage(CSM_RAM *data, GBS_MSG *msg){ |
|
MAIN_CSM *csm=(MAIN_CSM*)data; |
|
if(msg->msg==MSG_GUI_DESTROYED && (int)msg->data0==csm->guiID){ |
|
csm->csm.state=-3; |
|
}else if(msg->msg==MSG_PLAYFILE_REPORT){ |
|
/* char buf[200]; */ |
|
/* sprintf(buf, "playfile report: %08x %08x %08x\n", msg->submess, msg->data0, msg->data1); */ |
|
/* _write(logFile, buf, strlen(buf), &fileErr); */ |
|
int sub=msg->submess & 0x0000ffff; |
|
if(sub==0x0004){ // playback started |
|
playing=1; |
|
GBS_StartTimerProc(&redrawTimer, 10, RedrawTimerProc); |
|
}else if(sub==0x0020){ // position update |
|
int currentTime=(int)msg->data0; |
|
int targetTime=frameCount/15*1000+frameCount%15*67; |
|
avOffset=targetTime-currentTime; |
|
} |
|
} |
|
return 1; |
|
} |
|
|
|
void MainCSM_OnCreate(CSM_RAM *data){ |
|
guiFunctions[0]=(void*)GUI_OnRedraw; |
|
guiFunctions[1]=(void*)GUI_OnCreate; |
|
guiFunctions[2]=(void*)GUI_OnClose; |
|
guiFunctions[3]=(void*)GUI_OnFocus; |
|
guiFunctions[4]=(void*)GUI_OnUnfocus; |
|
guiFunctions[5]=(void*)GUI_OnKey; |
|
guiFunctions[6]=NULL; |
|
guiFunctions[7]=(void*)GUI_OnDealloc; |
|
guiFunctions[8]=(void*)GUI_UnknownFunc8; |
|
guiFunctions[9]=(void*)GUI_UnknownFunc9; |
|
guiFunctions[10]=NULL; |
|
|
|
guiCanvasSize.x2=ScreenW()-1; |
|
guiCanvasSize.y2=ScreenH()-1; |
|
|
|
MAIN_CSM *csm=(MAIN_CSM*)data; |
|
MAIN_GUI *gui=malloc(sizeof(MAIN_GUI)); |
|
zeromem(gui, sizeof(MAIN_GUI)); |
|
gui->gui.canvas=&guiCanvasSize; |
|
gui->gui.methods=(void*)guiFunctions; |
|
gui->gui.item_ll.data_mfree=(void (*)(void *))mfree_adr(); |
|
csm->csm.state=0; |
|
csm->csm.unk1=0; |
|
csm->guiID=CreateGUI(gui); |
|
} |
|
|
|
size_t wcslen(wchar_t* str){ |
|
size_t len=0; |
|
while(str[len]){ |
|
len++; |
|
} |
|
return len; |
|
} |
|
|
|
void __init_switab(){ |
|
register int ret asm("r0")=0; |
|
asm volatile("swi 0x80FF\n\t" |
|
:"=r"(ret) |
|
: |
|
:"lr", "memory"); |
|
__sys_switab_addres=(int*)ret; |
|
} |
|
|
|
void _start(){ |
|
__init_switab(); |
|
|
|
mainCsmDesc.maincsm.onMessage=MainCSM_OnMessage; |
|
mainCsmDesc.maincsm.onCreate=MainCSM_OnCreate; |
|
mainCsmDesc.maincsm.onClose=MainCSM_OnClose; |
|
mainCsmDesc.name.wsbody=maincsm_name_body; |
|
|
|
wsprintf((WSHDR*)(&mainCsmDesc.name), "Bad Apple"); |
|
mainCsmID=CreateCSM(&mainCsmDesc.maincsm, &mainCSM, 0); |
|
} |
similar SoC ( MT6261 )
https://www.youtube.com/watch?v=62bWJpoJi-I