Display netloader status with a modified msgbox.

* Enabled StrIds in language.c for this with some adjustments.
* Moved sockets init/exit into netloaderInit/netloaderExit, netloaderInit now returns Result.
* Changed the params for netloaderGetState to an output struct.
* In netloaderTask(), check netloader_exitflag at the end before clearing it.
* Moved code calling loadnro from netloader_loop into netloaderTask.
* Call thrd_sleep with the while-loop calling netloader_loop in netloaderTask.
* Various other changes.
This commit is contained in:
yellows8 2018-10-23 20:11:35 -04:00
parent ba4c80d76d
commit 86632292b0
7 changed files with 197 additions and 122 deletions

View File

@ -752,20 +752,20 @@ const char* const g_strings[StrId_Max][16] =
STR_TW("網路執行模組"),
},
/*[StrId_NetLoaderUnavailable] =
[StrId_NetLoaderUnavailable] =
{
STR_EN("The NetLoader is currently unavailable."),
STR_ES("El cargador de programas no está disponible."),
STR_DE("Der Netzwerk-Loader ist zur Zeit nicht verfügbar."),
STR_FR("Le chargeur de programme 3dslink est indisponible."),
STR_IT("Il caricamento programmi 3dslink non è disponibile."),
STR_JP("3dslinkネットローダは現在利用できません。"),
STR_FR("Le chargeur de programme nxlink est indisponible."),
STR_IT("Il caricamento programmi nxlink non è disponibile."),
STR_JP("nxlinkネットローダは現在利用できません。"),
STR_PT("O carregador de programas está de momento indisponível."),
STR_NL("De netwerk lader is niet beschikbaar."),
STR_KO("현재 네트워크 로더는 사용이 불가합니다."),
STR_RU("Загрузчик в настоящее время недоступен."),
STR_ZH("无法启动 3dslink 网络执行模块。"),
STR_TW("無法啓動 3dslink 網路執行模組。"),
STR_ZH("无法启动 nxlink 网络执行模块。"),
STR_TW("無法啓動 nxlink 網路執行模組。"),
},
[StrId_NetLoaderError] =
@ -786,76 +786,64 @@ const char* const g_strings[StrId_Max][16] =
[StrId_NetLoaderOffline] =
{
STR_EN("Offline, waiting for network…\n\n\n \xEE\x80\x81 Cancel"),
STR_FR("Hors-ligne, en attente d'une connection...\n\n\n \xEE\x80\x81 Annuler"),
STR_IT("Disconnesso, in attesa della connessione…\n\n\n \xEE\x80\x81 Annullare"),
STR_JP("オフラインです。ネットワーク接続を待っています…\n\n\n \xEE\x80\x81 キャンセル"),
STR_KO("연결 끊김, 네트워크 기다리는 중…\n\n\n \xEE\x80\x81 취소")
STR_ZH("无法连接网络,等待网络连接…\n\n\n \xEE\x80\x81 取消"),
STR_TW("當前離線,等待網路連線…\n\n\n \xEE\x80\x81 取消"),
STR_EN("Offline, waiting for network…\n\n\n"),
STR_FR("Hors-ligne, en attente d'une connection...\n\n\n"),
STR_IT("Disconnesso, in attesa della connessione…\n\n\n"),
STR_JP("オフラインです。ネットワーク接続を待っています…\n\n\n"),
STR_KO("연결 끊김, 네트워크 기다리는 중…\n\n\n"),
STR_ZH("无法连接网络,等待网络连接…\n\n\n"),
STR_TW("當前離線,等待網路連線…\n\n\n"),
},
[StrId_NetLoaderActive] =
{
STR_EN(
"Waiting for 3dslink to connect…\n"
"Waiting for nxlink to connect…\n"
"IP Addr: %lu.%lu.%lu.%lu, Port: %d\n\n"
" \xEE\x80\x81 Cancel"
),
STR_ES(
"Esperando a que se conecte 3dslink…\n"
"Esperando a que se conecte nxlink…\n"
"Dir.IP: %lu.%lu.%lu.%lu, Puerto: %d\n\n"
" \xEE\x80\x81 Cancelar"
),
STR_DE(
"Warte auf Verbindung von 3dslink…\n"
"IP Addr: %lu.%lu.%lu.%lu, Port: %d\n\n"
" \xEE\x80\x81 Abbrechen"
),
STR_FR(
"En attente de la connexion de 3dslink…\n"
"En attente de la connexion de nxlink…\n"
"Adr. IP : %lu.%lu.%lu.%lu, Port : %d\n\n"
" \xEE\x80\x81 Annuler"
),
STR_IT(
"In attesa della connessione di 3dslink…\n"
"In attesa della connessione di nxlink…\n"
"Ind. IP : %lu.%lu.%lu.%lu, Porta : %d\n\n"
" \xEE\x80\x81 Annullare"
),
STR_JP(
"3dslinkが接続されるのを待っています…\n"
"nxlinkが接続されるのを待っています…\n"
"IPアドレス%lu.%lu.%lu.%lu, ポート番号:%d\n\n"
" \xEE\x80\x81 キャンセル"
),
STR_PT(
"A aguardar pela conexão do 3dslink…\n"
"A aguardar pela conexão do nxlink…\n"
"End. IP: %lu.%lu.%lu.%lu, Porta: %d\n\n"
" \xEE\x80\x81 Cancelar"
),
STR_NL(
"Wachten op 3dslink verbinding…\n"
"Wachten op nxlink verbinding…\n"
"IP Addr: %lu.%lu.%lu.%lu, Poort: %d\n\n"
" \xEE\x80\x81 Annuleren"
),
STR_KO(
"3dslink의 연결을 대기중…\n"
"nxlink의 연결을 대기중…\n"
"IP 주소: %lu.%lu.%lu.%lu, 포트: %d\n\n"
" \xEE\x80\x81 취소"
),
STR_RU(
"Ожидание подключения 3dslink…\n"
"Ожидание подключения nxlink…\n"
"айпи адрес: %lu.%lu.%lu.%lu, Порт: %d\n\n"
" \xEE\x80\x81 Отмена"
),
STR_ZH(
"等待 3dslink 连接…\n"
"等待 nxlink 连接…\n"
"IP 地址:%lu.%lu.%lu.%lu端口%d\n\n"
" \xEE\x80\x81 取消等待"
),
STR_TW(
"等待 3dslink 連接…\n"
"等待 nxlink 連接…\n"
"IP 位址:%lu.%lu.%lu.%lu連接埠%d\n\n"
" \xEE\x80\x81 取消等待"
),
},
@ -909,6 +897,6 @@ const char* const g_strings[StrId_Max][16] =
"正在傳輸…\n"
"已完成 %zu / %zu KiB"
),
},*/
},
};

View File

@ -63,7 +63,7 @@ void launchMenuBackTask() {
void menuHandleAButton(void) {
menu_s* menu = menuGetCurrent();
if (menuIsMsgBoxOpen()) {
if (hbmenu_state != HBMENU_NETLOADER_ACTIVE && menuIsMsgBoxOpen()) {
menuCloseMsgBox();
}
else if (menu->nEntries > 0 && (hbmenu_state == HBMENU_DEFAULT || hbmenu_state == HBMENU_THEME_MENU))
@ -493,15 +493,33 @@ void drawButtons(menu_s* menu, bool emptyDir, int *x_image_out) {
}
}
void menuUpdateNetloader(char* netloader_displaytext, size_t netloader_displaytext_size, netloaderState *netloader_state) {
char textbody[256];
memset(textbody, 0, sizeof(textbody));
u32 ip = gethostid();
if (ip == INADDR_LOOPBACK)
snprintf(textbody, sizeof(textbody)-1, "%s", textGetString(StrId_NetLoaderOffline));
else {
if (!netloader_state->sock_connected)
snprintf(textbody, sizeof(textbody)-1, textGetString(StrId_NetLoaderActive), ip&0xFF, (ip>>8)&0xFF, (ip>>16)&0xFF, (ip>>24)&0xFF, NXLINK_SERVER_PORT);
else
snprintf(textbody, sizeof(textbody)-1, textGetString(StrId_NetLoaderTransferring), netloader_state->filetotal/1024, netloader_state->filelen/1024);
}
snprintf(netloader_displaytext, netloader_displaytext_size-1, "%s\n\n\n%s", textGetString(StrId_NetLoader), textbody);
}
void menuLoop(void) {
menuEntry_s* me;
menuEntry_s* netloader_me = NULL;
menu_s* menu = NULL;
int i;
int x, y;
int menupath_x_endpos = 918 + 40;
bool netloader_activated = 0, netloader_launch_app = 0;
char netloader_errormsg[1024];
netloaderState netloader_state;
char netloader_displaytext[256];
for (y=0; y<450; y++) {
for (x=0; x<1280; x+=4) {// don't draw bottom pixels as they are covered by the waves
@ -533,26 +551,43 @@ void menuLoop(void) {
drawTime();
drawCharge();
netloaderGetState(&netloader_activated, &netloader_launch_app, &netloader_me, netloader_errormsg, sizeof(netloader_errormsg));
memset(&netloader_state, 0, sizeof(netloader_state));
netloaderGetState(&netloader_state);
if(hbmenu_state == HBMENU_DEFAULT && netloader_activated) {
if(hbmenu_state == HBMENU_DEFAULT && netloader_state.activated) {
hbmenu_state = HBMENU_NETLOADER_ACTIVE;
} else if(hbmenu_state == HBMENU_NETLOADER_ACTIVE && !netloader_activated && !netloader_launch_app) {
menuCloseMsgBox();
menuCreateMsgBox(780,300, "");
} else if(hbmenu_state == HBMENU_NETLOADER_ACTIVE && !netloader_state.activated && !netloader_state.launch_app) {
hbmenu_state = HBMENU_DEFAULT;
menuScan(".");//Reload the menu since netloader may have deleted the NRO if the transfer aborted.
menuCloseMsgBox();
menuMsgBoxSetNetloaderState(0, NULL);
if (netloader_state.errormsg[0]) menuCreateMsgBox(780,300, netloader_state.errormsg);
}
if(hbmenu_state == HBMENU_NETLOADER_ACTIVE) {
memset(netloader_displaytext, 0, sizeof(netloader_displaytext));
menuUpdateNetloader(netloader_displaytext, sizeof(netloader_displaytext), &netloader_state);
menuMsgBoxSetNetloaderState(1, netloader_displaytext);
}
menu = menuGetCurrent();
if (netloader_errormsg[0]) menuCreateMsgBox(780,300, netloader_errormsg);
if (menu->nEntries==0 || hbmenu_state == HBMENU_NETLOADER_ACTIVE)
{
if (hbmenu_state == HBMENU_NETLOADER_ACTIVE) {
if (netloader_launch_app) {
if (netloader_state.launch_app) {
hbmenu_state = HBMENU_DEFAULT;
menuCloseMsgBox();
menuMsgBoxSetNetloaderState(0, NULL);
menuCreateMsgBox(240,240, textGetString(StrId_Loading));
launchMenuEntryTask(netloader_me);
launchMenuEntryTask(netloader_state.me);
}
} else {
DrawText(interuiregular14, 64, 128 + 18, themeCurrent.textColor, textGetString(StrId_NoAppsFound_Msg));

View File

@ -3,6 +3,9 @@
MessageBox currMsgBox;
static bool msgboxNetloaderEnabled;
static char msgboxNetloaderText[256];
void drawMsgBoxBgToBuff(color_t *buff, int width, int height) {
int x, y;
int off;
@ -90,6 +93,8 @@ void menuDrawMsgBox() {
color_t shadow_color;
uint8_t shadow_alpha_base = 80;
const char* textptr = currMsgBox.text;
border_color = MakeColor(themeCurrent.highlightColor.r + (255 - themeCurrent.highlightColor.r) * highlight_multiplier, themeCurrent.highlightColor.g + (255 - themeCurrent.highlightColor.g) * highlight_multiplier, themeCurrent.highlightColor.b + (255 - themeCurrent.highlightColor.b) * highlight_multiplier, 255);
// Darken the background
@ -105,22 +110,26 @@ void menuDrawMsgBox() {
off = (y * currMsgBox.width + x);
curr_color = currMsgBox.bg[off];
if (((x<border_thickness || x>=currMsgBox.width-border_thickness) && y>sep_start_y) ||
(y>sep_start_y && y<=sep_start_y+border_thickness) || (y>=currMsgBox.height-border_thickness)) {
curr_color = border_color;
if (!msgboxNetloaderEnabled) {
if (((x<border_thickness || x>=currMsgBox.width-border_thickness) && y>sep_start_y) ||
(y>sep_start_y && y<=sep_start_y+border_thickness) || (y>=currMsgBox.height-border_thickness)) {
curr_color = border_color;
}
}
DrawPixel(start_x+x, start_y+y, curr_color);
}
}
GetTextDimensions(interuimedium20, currMsgBox.text, &text_width, &text_height);
if (msgboxNetloaderEnabled) textptr = msgboxNetloaderText;
GetTextDimensions(interuimedium20, textptr, &text_width, &text_height);
if (text_width < currMsgBox.width && text_height < sep_start_y) {
DrawText(interuiregular18, start_x + (currMsgBox.width - text_width) / 2, start_y + (currMsgBox.height - text_height - 80) / 2, MakeColor(0, 0, 0, 255), currMsgBox.text);
DrawText(interuiregular18, start_x + (currMsgBox.width - text_width) / 2, start_y + (currMsgBox.height - text_height - 80) / 2, MakeColor(0, 0, 0, 255), textptr);
}
DrawText(interuimedium20, start_x + 365, start_y + 245 + 26, MakeColor(0, 0, 0, 255), textGetString(StrId_MsgBox_OK));
if (!msgboxNetloaderEnabled) DrawText(interuimedium20, start_x + 365, start_y + 245 + 26, MakeColor(0, 0, 0, 255), textGetString(StrId_MsgBox_OK));
shadow_start_y = start_y + currMsgBox.height;
@ -174,3 +183,10 @@ void menuCloseMsgBox() {
MessageBox menuGetCurrentMsgBox() {
return currMsgBox;
}
void menuMsgBoxSetNetloaderState(bool enabled, const char *text) {
msgboxNetloaderEnabled = enabled;
memset(msgboxNetloaderText, 0, sizeof(msgboxNetloaderText));
if (text) strncpy(msgboxNetloaderText, text, sizeof(msgboxNetloaderText)-1);
}

View File

@ -13,3 +13,4 @@ void menuCloseMsgBox();
bool menuIsMsgBoxOpen();
void menuDrawMsgBox(void);
MessageBox menuGetCurrentMsgBox();
void menuMsgBoxSetNetloaderState(bool enabled, const char *text);

View File

@ -45,19 +45,20 @@ static unsigned char out[ZLIB_CHUNK];
static mtx_t netloader_mtx;
static bool netloader_initialized = 0;
static bool netloader_exitflag = 0;
static bool netloader_activated = 0, netloader_launchapp = 0;
static menuEntry_s netloader_me;
static char netloader_errortext[1024];
static volatile bool netloader_initialized = 0;
static volatile bool netloader_exitflag = 0;
static volatile bool netloader_activated = 0, netloader_launchapp = 0;
static volatile size_t netloader_filelen, netloader_filetotal;
static volatile char netloader_errortext[1024];
static bool netloaderGetExit(void);
//---------------------------------------------------------------------------------
static void netloader_error(const char *func, int err) {
//---------------------------------------------------------------------------------
memset(netloader_errortext, 0, sizeof(netloader_errortext));
snprintf(netloader_errortext, sizeof(netloader_errortext)-1, "%s: err=%d\n %s\n", func, err, strerror(errno));
memset((char*)netloader_errortext, 0, sizeof(netloader_errortext));
snprintf((char*)netloader_errortext, sizeof(netloader_errortext)-1, "%s: err=%d\n %s\n", func, err, strerror(errno));
}
//---------------------------------------------------------------------------------
@ -283,6 +284,9 @@ static int decompress(int sock, FILE *fh, size_t filesize) {
}
total += have;
mtx_lock(&netloader_mtx);
netloader_filetotal = total;
mtx_unlock(&netloader_mtx);
//printf("%zu (%zd%%)",total, (100 * total) / filesize);
} while (strm.avail_out == 0);
@ -329,6 +333,10 @@ int loadnro(menuEntry_s *me, int sock, struct in_addr remote) {
return -1;
}
mtx_lock(&netloader_mtx);
netloader_filelen = filelen;
mtx_unlock(&netloader_mtx);
int response = 0;
sanitisePath(filename);
@ -411,19 +419,6 @@ int netloader_activate(void) {
//---------------------------------------------------------------------------------
struct sockaddr_in serv_addr;
#ifdef __SWITCH__
socketInitializeDefault();
#endif
#ifdef __WIN32__
WSADATA wsa_data;
if (WSAStartup (MAKEWORD(2,2), &wsa_data)) {
netloader_error("WSAStartup failed\n",1);
return 1;
}
#endif
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
@ -506,40 +501,30 @@ int netloader_deactivate(void) {
}
#endif
#ifdef __SWITCH__
socketExit();
#endif
#ifdef __WIN32__
WSACleanup ();
#endif
return 0;
}
//---------------------------------------------------------------------------------
int netloader_loop(menuEntry_s *me) {
int netloader_loop(struct sockaddr_in *sa_remote) {
//---------------------------------------------------------------------------------
struct sockaddr_in sa_remote;
#if PING_ENABLED
char recvbuf[256];
socklen_t fromlen = sizeof(sa_remote);
socklen_t fromlen = sizeof(struct sockaddr_in);
int len = recvfrom(netloader_udpfd, recvbuf, sizeof(recvbuf), 0, (struct sockaddr*) &sa_remote, &fromlen);
int len = recvfrom(netloader_udpfd, recvbuf, sizeof(recvbuf), 0, (struct sockaddr*) sa_remote, &fromlen);
if (len!=-1) {
if (strncmp(recvbuf,"nxboot",strlen("nxboot")) == 0) {
sa_remote.sin_family=AF_INET;
sa_remote.sin_port=htons(NXLINK_CLIENT_PORT);
sendto(netloader_udpfd, "bootnx", strlen("bootnx"), 0, (struct sockaddr*) &sa_remote,sizeof(sa_remote));
sa_remote->sin_family=AF_INET;
sa_remote->sin_port=htons(NXLINK_CLIENT_PORT);
sendto(netloader_udpfd, "bootnx", strlen("bootnx"), 0, (struct sockaddr*) sa_remote,sizeof(struct sockaddr_in));
}
}
#endif
if(netloader_listenfd >= 0 && netloader_datafd < 0) {
socklen_t addrlen = sizeof(sa_remote);
netloader_datafd = accept(netloader_listenfd, (struct sockaddr*)&sa_remote, &addrlen);
socklen_t addrlen = sizeof(struct sockaddr_in);
netloader_datafd = accept(netloader_listenfd, (struct sockaddr*)sa_remote, &addrlen);
if(netloader_datafd < 0)
{
@ -561,33 +546,30 @@ int netloader_loop(menuEntry_s *me) {
{
close(netloader_listenfd);
netloader_listenfd = -1;
}
}
if(netloader_datafd >= 0)
{
int result = loadnro(me, netloader_datafd,sa_remote.sin_addr);
if (result== 0) {
return 1;
} else {
return -1;
}
}
return 0;
}
void netloaderGetState(bool *activated, bool *launch_app, menuEntry_s **me, char *errormsg, size_t errormsg_size) {
void netloaderGetState(netloaderState *state) {
if(state==NULL)return;
mtx_lock(&netloader_mtx);
*activated = netloader_activated;
*launch_app = netloader_launchapp;
*me = &netloader_me;
state->activated = netloader_activated;
state->launch_app = netloader_launchapp;
state->me = &netloader_me;
memset(errormsg, 0, errormsg_size);
state->transferring = (netloader_datafd >= 0 && netloader_filelen);
state->sock_connected = netloader_datafd >= 0;
state->filelen = netloader_filelen;
state->filetotal = netloader_filetotal;
memset(state->errormsg, 0, sizeof(state->errormsg));
if(netloader_errortext[0]) {
strncpy(errormsg, netloader_errortext, errormsg_size-1);
memset(netloader_errortext, 0, sizeof(netloader_errortext));
strncpy(state->errormsg, (char*)netloader_errortext, sizeof(state->errormsg)-1);
memset((char*)netloader_errortext, 0, sizeof(netloader_errortext));
}
mtx_unlock(&netloader_mtx);
@ -609,13 +591,31 @@ void netloaderSignalExit(void) {
mtx_unlock(&netloader_mtx);
}
bool netloaderInit(void) {
if (netloader_initialized) return 1;
Result netloaderInit(void) {
Result rc=0;
if (netloader_initialized) return 0;
if (mtx_init(&netloader_mtx, mtx_plain) != thrd_success) return 0;
if (mtx_init(&netloader_mtx, mtx_plain) != thrd_success) return 1;
#ifdef __SWITCH__
rc = socketInitializeDefault();
#endif
#ifdef __WIN32__
WSADATA wsa_data;
if (WSAStartup (MAKEWORD(2,2), &wsa_data)) {
//netloader_error("WSAStartup failed\n",1);
rc = 2;
}
#endif
if (rc) {
mtx_destroy(&netloader_mtx);
return rc;
}
netloader_initialized = 1;
return 1;
return 0;
}
void netloaderExit(void) {
@ -623,16 +623,28 @@ void netloaderExit(void) {
netloader_initialized = 0;
mtx_destroy(&netloader_mtx);
#ifdef __SWITCH__
socketExit();
#endif
#ifdef __WIN32__
WSACleanup ();
#endif
}
void netloaderTask(void* arg) {
int ret=0;
struct sockaddr_in sa_remote;
struct timespec duration = {.tv_nsec = 100000000};
menuEntryInit(&netloader_me,ENTRY_TYPE_FILE);
mtx_lock(&netloader_mtx);
netloader_exitflag = 0;
netloader_activated = 0;
netloader_launchapp = 0;
netloader_filelen = 0;
netloader_filetotal = 0;
mtx_unlock(&netloader_mtx);
if(netloader_activate() == 0) {
@ -645,13 +657,24 @@ void netloaderTask(void* arg) {
return;
}
while((ret = netloader_loop(&netloader_me)) == 0 && !netloaderGetExit());
while((ret = netloader_loop(&sa_remote)) == 0 && !netloaderGetExit()) {
thrd_sleep(&duration, NULL);
}
if(ret == 1 && !netloaderGetExit()) {
int result = loadnro(&netloader_me, netloader_datafd,sa_remote.sin_addr);
if (result== 0) {
ret = 1;
} else {
ret = -1;
}
}
netloader_deactivate();
mtx_lock(&netloader_mtx);
if (ret==1 && !netloader_exitflag) netloader_launchapp = 1;//Access netloader_exitflag directly since the mutex is already locked.
netloader_exitflag = 0;
netloader_activated = 0;
if (ret==1 && !netloader_exitflag) netloader_launchapp = 1;//Access netloader_exitflag directly since the mutex is already locked.
mtx_unlock(&netloader_mtx);
}

View File

@ -1,13 +1,23 @@
#pragma once
typedef struct {
bool activated;
bool launch_app;
bool transferring;
bool sock_connected;
menuEntry_s *me;
size_t filelen, filetotal;
char errormsg[1025];
} netloaderState;
int netloader_activate(void);
int netloader_deactivate(void);
int netloader_loop(menuEntry_s *me);
int netloader_loop(struct sockaddr_in *sa_remote);
bool netloaderInit(void);
Result netloaderInit(void);
void netloaderExit(void);
void netloaderTask(void* arg);
void netloaderGetState(bool *activated, bool *launch_app, menuEntry_s **me, char *errormsg, size_t errormsg_size);
void netloaderGetState(netloaderState *state);
void netloaderSignalExit(void);

View File

@ -64,13 +64,15 @@ int main(int argc, char **argv)
if (R_SUCCEEDED(rc)) powerInit();
if (R_SUCCEEDED(rc) && !netloaderInit()) {
rc = 1;
snprintf(errormsg, sizeof(errormsg)-1, "Error: netloaderInit() failed.");
if (R_SUCCEEDED(rc)) {
rc = netloaderInit();
if (R_FAILED(rc)) {
snprintf(errormsg, sizeof(errormsg)-1, "Error: netloaderInit() failed: 0x%x.", rc);
}
}
if (R_SUCCEEDED(rc) && !workerInit()) {
rc = 2;
rc = 1;
snprintf(errormsg, sizeof(errormsg)-1, "Error: workerInit() failed.");
}
@ -78,13 +80,13 @@ int main(int argc, char **argv)
if (R_SUCCEEDED(rc)) {
if (!launchInit()) {
rc = 3;
rc = 2;
snprintf(errormsg, sizeof(errormsg)-1, "Error: launchInit() failed.");
}
}
if (R_SUCCEEDED(rc) && !fontInitialize()) {
rc = 4;
rc = 3;
snprintf(errormsg, sizeof(errormsg)-1, "Error: fontInitialize() failed.");
}