#include "LScreens.h" #ifndef CC_BUILD_WEB #include "String.h" #include "LWidgets.h" #include "LWeb.h" #include "Launcher.h" #include "Gui.h" #include "Drawer2D.h" #include "ExtMath.h" #include "Platform.h" #include "Stream.h" #include "Funcs.h" #include "Resources.h" #include "Logger.h" #include "Window.h" #include "Input.h" #include "Options.h" #include "Utils.h" #include "LBackend.h" #include "Http.h" #include "Game.h" #define LAYOUTS static const struct LLayout #define IsEnterButton(btn) (btn == CCKEY_ENTER || btn == CCPAD_START || btn == CCPAD_A || btn == CCKEY_KP_ENTER) #define IsBackButton(btn) (btn == CCKEY_ESCAPE || btn == CCPAD_SELECT || btn == CCPAD_B) /*########################################################################################################################* *---------------------------------------------------------Screen base-----------------------------------------------------* *#########################################################################################################################*/ static void LScreen_NullFunc(struct LScreen* s) { } CC_NOINLINE static int LScreen_IndexOf(struct LScreen* s, void* w) { int i; for (i = 0; i < s->numWidgets; i++) { if (s->widgets[i] == w) return i; } return -1; } static void LScreen_DoLayout(struct LScreen* s) { int i; for (i = 0; i < s->numWidgets; i++) { LBackend_LayoutWidget(s->widgets[i]); } } static void LScreen_Tick(struct LScreen* s) { struct LWidget* w = s->selectedWidget; if (w && w->VTABLE->Tick) w->VTABLE->Tick(w); } void LScreen_SelectWidget(struct LScreen* s, int idx, struct LWidget* w, cc_bool was) { if (!w) return; w->selected = true; s->selectedWidget = w; if (w->VTABLE->OnSelect) w->VTABLE->OnSelect(w, idx, was); } void LScreen_UnselectWidget(struct LScreen* s, int idx, struct LWidget* w) { if (!w) return; w->selected = false; s->selectedWidget = NULL; if (w->VTABLE->OnUnselect) w->VTABLE->OnUnselect(w, idx); } static void LScreen_CycleSelected(struct LScreen* s, int dir) { struct LWidget* w; int index = 0, i, j; if (s->selectedWidget) { index = LScreen_IndexOf(s, s->selectedWidget) + dir; } for (j = 0; j < s->numWidgets; j++) { i = (index + j * dir) % s->numWidgets; if (i < 0) i += s->numWidgets; w = s->widgets[i]; if (!w->autoSelectable) continue; LScreen_UnselectWidget(s, 0, s->selectedWidget); LScreen_SelectWidget(s, 0, w, false); return; } } static void LScreen_KeyDown(struct LScreen* s, int key, cc_bool was) { if (IsEnterButton(key)) { /* Shouldn't multi click when holding down Enter */ if (was) return; if (s->selectedWidget && s->selectedWidget->OnClick) { s->selectedWidget->OnClick(s->selectedWidget); } else if (s->hoveredWidget && s->hoveredWidget->OnClick) { s->hoveredWidget->OnClick(s->hoveredWidget); } else if (s->onEnterWidget) { s->onEnterWidget->OnClick(s->onEnterWidget); } return; } /* Active widget takes input priority over default behaviour */ if (s->selectedWidget && s->selectedWidget->VTABLE->KeyDown) { if (s->selectedWidget->VTABLE->KeyDown(s->selectedWidget, key, was)) return; } if (key == CCKEY_TAB || key == CCPAD_X) { LScreen_CycleSelected(s, Input_IsShiftPressed() ? -1 : 1); } else if (Input_IsUpButton(key)) { LScreen_CycleSelected(s, -1); } else if (Input_IsDownButton(key)) { LScreen_CycleSelected(s, 1); } else if (IsBackButton(key) && s->onEscapeWidget) { s->onEscapeWidget->OnClick(s->onEscapeWidget); } } static void LScreen_MouseUp(struct LScreen* s, int idx) { } static void LScreen_MouseWheel(struct LScreen* s, float delta) { } static void LScreen_DrawBackground(struct LScreen* s, struct Context2D* ctx) { if (!s->title) { Launcher_DrawBackground(ctx, 0, 0, ctx->width, ctx->height); return; } Launcher_DrawBackgroundAll(ctx); LBackend_DrawTitle(ctx, s->title); } CC_NOINLINE static void LScreen_Reset(struct LScreen* s) { int i; s->Activated = LScreen_NullFunc; s->LoadState = LScreen_NullFunc; s->Deactivated = LScreen_NullFunc; s->Layout = LScreen_DoLayout; s->Tick = LScreen_Tick; s->KeyDown = LScreen_KeyDown; s->MouseUp = LScreen_MouseUp; s->MouseWheel = LScreen_MouseWheel; s->DrawBackground = LScreen_DrawBackground; s->ResetArea = Launcher_DrawBackground; /* reset all widgets mouse state */ for (i = 0; i < s->numWidgets; i++) { s->widgets[i]->hovered = false; s->widgets[i]->selected = false; } s->numWidgets = 0; s->hoveredWidget = NULL; s->selectedWidget = NULL; } void LScreen_AddWidget(void* screen, void* widget) { struct LScreen* s = (struct LScreen*)screen; struct LWidget* w = (struct LWidget*)widget; if (s->numWidgets >= s->maxWidgets) Logger_Abort("Can't add anymore widgets to this LScreen"); s->widgets[s->numWidgets++] = w; } static void SwitchToChooseMode(void* w) { ChooseModeScreen_SetActive(false); } static void SwitchToColours(void* w) { ColoursScreen_SetActive(); } static void SwitchToDirectConnect(void* w) { DirectConnectScreen_SetActive(); } static void SwitchToMain(void* w) { MainScreen_SetActive(); } static void SwitchToSettings(void* w) { SettingsScreen_SetActive(); } static void SwitchToThemes(void* w) { ThemesScreen_SetActive(); } static void SwitchToUpdates(void* w) { UpdatesScreen_SetActive(); } /*########################################################################################################################* *-------------------------------------------------------ChooseModeScreen--------------------------------------------------* *#########################################################################################################################*/ static struct ChooseModeScreen { LScreen_Layout struct LLine seps[2]; struct LButton btnEnhanced, btnClassicHax, btnClassic, btnBack; struct LLabel lblHelp, lblEnhanced[2], lblClassicHax[2], lblClassic[2]; cc_bool firstTime; } ChooseModeScreen; #define CHOOSEMODE_SCREEN_MAX_WIDGETS 12 static struct LWidget* chooseMode_widgets[CHOOSEMODE_SCREEN_MAX_WIDGETS]; LAYOUTS mode_seps0[] = { { ANCHOR_CENTRE, -5 }, { ANCHOR_CENTRE, -85 } }; LAYOUTS mode_seps1[] = { { ANCHOR_CENTRE, -5 }, { ANCHOR_CENTRE, -15 } }; LAYOUTS mode_btnEnhanced[] = { { ANCHOR_CENTRE_MIN, -250 }, { ANCHOR_CENTRE, -120 } }; LAYOUTS mode_lblEnhanced0[] = { { ANCHOR_CENTRE_MIN, -85 }, { ANCHOR_CENTRE, -120 - 12 } }; LAYOUTS mode_lblEnhanced1[] = { { ANCHOR_CENTRE_MIN, -85 }, { ANCHOR_CENTRE, -120 + 12 } }; LAYOUTS mode_btnClassicHax[] = { { ANCHOR_CENTRE_MIN, -250 }, { ANCHOR_CENTRE, -50 } }; LAYOUTS mode_lblClassicHax0[] = { { ANCHOR_CENTRE_MIN, -85 }, { ANCHOR_CENTRE, -50 - 12 } }; LAYOUTS mode_lblClassicHax1[] = { { ANCHOR_CENTRE_MIN, -85 }, { ANCHOR_CENTRE, -50 + 12 } }; LAYOUTS mode_btnClassic[] = { { ANCHOR_CENTRE_MIN, -250 }, { ANCHOR_CENTRE, 20 } }; LAYOUTS mode_lblClassic0[] = { { ANCHOR_CENTRE_MIN, -85 }, { ANCHOR_CENTRE, 20 - 12 } }; LAYOUTS mode_lblClassic1[] = { { ANCHOR_CENTRE_MIN, -85 }, { ANCHOR_CENTRE, 20 + 12 } }; LAYOUTS mode_lblHelp[] = { { ANCHOR_CENTRE, 0 }, { ANCHOR_CENTRE, 160 } }; LAYOUTS mode_btnBack[] = { { ANCHOR_CENTRE, 0 }, { ANCHOR_CENTRE, 170 } }; CC_NOINLINE static void ChooseMode_Click(cc_bool classic, cc_bool classicHacks) { Options_SetBool(OPT_CLASSIC_MODE, classic); if (classic) Options_SetBool(OPT_CLASSIC_HACKS, classicHacks); Options_SetBool(OPT_CUSTOM_BLOCKS, !classic); Options_SetBool(OPT_CPE, !classic); Options_SetBool(OPT_SERVER_TEXTURES, !classic); Options_SetBool(OPT_CLASSIC_TABLIST, classic); Options_SetBool(OPT_CLASSIC_OPTIONS, classic); Options_SaveIfChanged(); Launcher_LoadTheme(); LBackend_UpdateTitleFont(); MainScreen_SetActive(); } static void UseModeEnhanced(void* w) { ChooseMode_Click(false, false); } static void UseModeClassicHax(void* w) { ChooseMode_Click(true, true); } static void UseModeClassic(void* w) { ChooseMode_Click(true, false); } static void ChooseModeScreen_Activated(struct LScreen* s_) { struct ChooseModeScreen* s = (struct ChooseModeScreen*)s_; LLine_Add(s, &s->seps[0], 490, mode_seps0); LLine_Add(s, &s->seps[1], 490, mode_seps1); LButton_Add(s, &s->btnEnhanced, 145, 35, "Enhanced", UseModeEnhanced, mode_btnEnhanced); LLabel_Add(s, &s->lblEnhanced[0], "&eEnables custom blocks, changing env", mode_lblEnhanced0); LLabel_Add(s, &s->lblEnhanced[1], "&esettings, longer messages, and more", mode_lblEnhanced1); LButton_Add(s, &s->btnClassicHax, 145, 35, "Classic +hax", UseModeClassicHax, mode_btnClassicHax); LLabel_Add(s, &s->lblClassicHax[0], "&eSame as Classic mode, except that", mode_lblClassicHax0); LLabel_Add(s, &s->lblClassicHax[1], "&ehacks (noclip/fly/speed) are enabled", mode_lblClassicHax1); LButton_Add(s, &s->btnClassic, 145, 35, "Classic", UseModeClassic, mode_btnClassic); LLabel_Add(s, &s->lblClassic[0], "&eOnly uses blocks and features from", mode_lblClassic0); LLabel_Add(s, &s->lblClassic[1], "ðe original minecraft classic", mode_lblClassic1); if (s->firstTime) { LLabel_Add(s, &s->lblHelp, "&eClick &fEnhanced &eif you're not sure which mode to choose.", mode_lblHelp); } else { LButton_Add(s, &s->btnBack, 80, 35, "Back", SwitchToSettings, mode_btnBack); } } void ChooseModeScreen_SetActive(cc_bool firstTime) { struct ChooseModeScreen* s = &ChooseModeScreen; LScreen_Reset((struct LScreen*)s); s->widgets = chooseMode_widgets; s->maxWidgets = Array_Elems(chooseMode_widgets); s->firstTime = firstTime; s->Activated = ChooseModeScreen_Activated; s->title = "Choose mode"; s->onEnterWidget = (struct LWidget*)&s->btnEnhanced; s->onEscapeWidget = firstTime ? NULL : (struct LWidget*)&s->btnBack; Launcher_SetScreen((struct LScreen*)s); } /*########################################################################################################################* *---------------------------------------------------------ColoursScreen---------------------------------------------------* *#########################################################################################################################*/ #define COLOURS_NUM_ROWS 5 /* Background, border, etc */ #define COLOURS_NUM_COLS 3 /* R, G, B widgets */ #define COLOURS_NUM_ENTRIES (COLOURS_NUM_ROWS * COLOURS_NUM_COLS) static struct ColoursScreen { LScreen_Layout struct LButton btnBack; struct LLabel lblNames[COLOURS_NUM_ROWS]; struct LLabel lblRGB[COLOURS_NUM_COLS]; struct LInput iptColours[COLOURS_NUM_ENTRIES]; struct LCheckbox cbClassic; } ColoursScreen; #define COLOURSSCREEN_MAX_WIDGETS 25 static struct LWidget* colours_widgets[COLOURSSCREEN_MAX_WIDGETS]; #define IptColor_Layout(xx, yy) { { ANCHOR_CENTRE, xx }, { ANCHOR_CENTRE, yy } } LAYOUTS clr_iptColours[COLOURS_NUM_ENTRIES][2] = { IptColor_Layout(30, -100), IptColor_Layout(95, -100), IptColor_Layout(160, -100), IptColor_Layout(30, -60), IptColor_Layout(95, -60), IptColor_Layout(160, -60), IptColor_Layout(30, -20), IptColor_Layout(95, -20), IptColor_Layout(160, -20), IptColor_Layout(30, 20), IptColor_Layout(95, 20), IptColor_Layout(160, 20), IptColor_Layout(30, 60), IptColor_Layout(95, 60), IptColor_Layout(160, 60), }; LAYOUTS clr_lblNames0[] = { { ANCHOR_CENTRE_MAX, 10 }, { ANCHOR_CENTRE, -100 } }; LAYOUTS clr_lblNames1[] = { { ANCHOR_CENTRE_MAX, 10 }, { ANCHOR_CENTRE, -60 } }; LAYOUTS clr_lblNames2[] = { { ANCHOR_CENTRE_MAX, 10 }, { ANCHOR_CENTRE, -20 } }; LAYOUTS clr_lblNames3[] = { { ANCHOR_CENTRE_MAX, 10 }, { ANCHOR_CENTRE, 20 } }; LAYOUTS clr_lblNames4[] = { { ANCHOR_CENTRE_MAX, 10 }, { ANCHOR_CENTRE, 60 } }; LAYOUTS clr_lblRGB0[] = { { ANCHOR_CENTRE, 30 }, { ANCHOR_CENTRE, -130 } }; LAYOUTS clr_lblRGB1[] = { { ANCHOR_CENTRE, 95 }, { ANCHOR_CENTRE, -130 } }; LAYOUTS clr_lblRGB2[] = { { ANCHOR_CENTRE, 160 }, { ANCHOR_CENTRE, -130 } }; LAYOUTS clr_cbClassic[] = { { ANCHOR_CENTRE, -16 }, { ANCHOR_CENTRE, 130 } }; LAYOUTS clr_btnBack[] = { { ANCHOR_CENTRE, 0 }, { ANCHOR_CENTRE, 170 } }; CC_NOINLINE static void ColoursScreen_Set(struct LInput* w, cc_uint8 value) { cc_string tmp; char tmpBuffer[STRING_SIZE]; String_InitArray(tmp, tmpBuffer); String_AppendInt(&tmp, value); LInput_SetText(w, &tmp); } CC_NOINLINE static void ColoursScreen_Update(struct ColoursScreen* s, int i, BitmapCol color) { ColoursScreen_Set(&s->iptColours[i + 0], BitmapCol_R(color)); ColoursScreen_Set(&s->iptColours[i + 1], BitmapCol_G(color)); ColoursScreen_Set(&s->iptColours[i + 2], BitmapCol_B(color)); } CC_NOINLINE static void ColoursScreen_UpdateAll(struct ColoursScreen* s) { ColoursScreen_Update(s, 0, Launcher_Theme.BackgroundColor); ColoursScreen_Update(s, 3, Launcher_Theme.ButtonBorderColor); ColoursScreen_Update(s, 6, Launcher_Theme.ButtonHighlightColor); ColoursScreen_Update(s, 9, Launcher_Theme.ButtonForeColor); ColoursScreen_Update(s, 12, Launcher_Theme.ButtonForeActiveColor); } static void ColoursScreen_TextChanged(struct LInput* w) { struct ColoursScreen* s = &ColoursScreen; int index = LScreen_IndexOf((struct LScreen*)s, w); BitmapCol* color; cc_uint8 r, g, b; if (index < 3) color = &Launcher_Theme.BackgroundColor; else if (index < 6) color = &Launcher_Theme.ButtonBorderColor; else if (index < 9) color = &Launcher_Theme.ButtonHighlightColor; else if (index < 12) color = &Launcher_Theme.ButtonForeColor; else color = &Launcher_Theme.ButtonForeActiveColor; /* if index of G input, changes to index of R input */ index = (index / 3) * 3; if (!Convert_ParseUInt8(&s->iptColours[index + 0].text, &r)) return; if (!Convert_ParseUInt8(&s->iptColours[index + 1].text, &g)) return; if (!Convert_ParseUInt8(&s->iptColours[index + 2].text, &b)) return; *color = BitmapColor_RGB(r, g, b); Launcher_SaveTheme(); LBackend_ThemeChanged(); } static void ColoursScreen_AdjustSelected(struct LScreen* s, int delta) { struct LInput* w; int index, newVal; cc_uint8 value; if (!s->selectedWidget) return; index = LScreen_IndexOf(s, s->selectedWidget); if (index >= 15) return; w = (struct LInput*)s->selectedWidget; if (!Convert_ParseUInt8(&w->text, &value)) return; newVal = value + delta; Math_Clamp(newVal, 0, 255); ColoursScreen_Set(w, newVal); ColoursScreen_TextChanged(w); } static void ColoursScreen_KeyDown(struct LScreen* s, int key, cc_bool was) { int delta = Input_CalcDelta(key, 1, 10); if (key == CCWHEEL_UP) delta = +1; if (key == CCWHEEL_DOWN) delta = -1; if (delta) { ColoursScreen_AdjustSelected(s, delta); } else { LScreen_KeyDown(s, key, was); } } static void ColoursScreen_ToggleBG(struct LCheckbox* w) { Launcher_Theme.ClassicBackground = w->value; Launcher_SaveTheme(); LBackend_ThemeChanged(); } static void ColoursScreen_AddWidgets(struct ColoursScreen* s) { int i; for (i = 0; i < COLOURS_NUM_ENTRIES; i++) { s->iptColours[i].inputType = KEYBOARD_TYPE_INTEGER; s->iptColours[i].TextChanged = ColoursScreen_TextChanged; LInput_Add(s, &s->iptColours[i], 55, NULL, clr_iptColours[i]); } LLabel_Add(s, &s->lblNames[0], "Background", clr_lblNames0); LLabel_Add(s, &s->lblNames[1], "Button border", clr_lblNames1); LLabel_Add(s, &s->lblNames[2], "Button highlight", clr_lblNames2); LLabel_Add(s, &s->lblNames[3], "Button", clr_lblNames3); LLabel_Add(s, &s->lblNames[4], "Active button", clr_lblNames4); LLabel_Add(s, &s->lblRGB[0], "Red", clr_lblRGB0); LLabel_Add(s, &s->lblRGB[1], "Green", clr_lblRGB1); LLabel_Add(s, &s->lblRGB[2], "Blue", clr_lblRGB2); LButton_Add(s, &s->btnBack, 80, 35, "Back", SwitchToThemes, clr_btnBack); LCheckbox_Add(s, &s->cbClassic, "Classic style", ColoursScreen_ToggleBG, clr_cbClassic); } static void ColoursScreen_Activated(struct LScreen* s_) { struct ColoursScreen* s = (struct ColoursScreen*)s_; ColoursScreen_AddWidgets(s); LCheckbox_Set(&s->cbClassic, Launcher_Theme.ClassicBackground); ColoursScreen_UpdateAll(s); } void ColoursScreen_SetActive(void) { struct ColoursScreen* s = &ColoursScreen; LScreen_Reset((struct LScreen*)s); s->widgets = colours_widgets; s->maxWidgets = Array_Elems(colours_widgets); s->Activated = ColoursScreen_Activated; s->KeyDown = ColoursScreen_KeyDown; s->title = "Custom theme"; s->onEscapeWidget = (struct LWidget*)&s->btnBack; Launcher_SetScreen((struct LScreen*)s); } /*########################################################################################################################* *------------------------------------------------------DirectConnectScreen------------------------------------------------* *#########################################################################################################################*/ static struct DirectConnectScreen { LScreen_Layout struct LButton btnConnect, btnBack; struct LInput iptUsername, iptAddress, iptMppass; struct LLabel lblStatus; } DirectConnectScreen; #define DIRECTCONNECT_SCREEN_MAXWIDGETS 6 static struct LWidget* directConnect_widgets[DIRECTCONNECT_SCREEN_MAXWIDGETS]; LAYOUTS dc_iptUsername[] = { { ANCHOR_CENTRE_MIN, -165 }, { ANCHOR_CENTRE, -120 } }; LAYOUTS dc_iptAddress[] = { { ANCHOR_CENTRE_MIN, -165 }, { ANCHOR_CENTRE, -75 } }; LAYOUTS dc_iptMppass[] = { { ANCHOR_CENTRE_MIN, -165 }, { ANCHOR_CENTRE, -30 } }; LAYOUTS dc_btnConnect[] = { { ANCHOR_CENTRE, -110 }, { ANCHOR_CENTRE, 20 } }; LAYOUTS dc_btnBack[] = { { ANCHOR_CENTRE, 125 }, { ANCHOR_CENTRE, 20 } }; LAYOUTS dc_lblStatus[] = { { ANCHOR_CENTRE, 0 }, { ANCHOR_CENTRE, 70 } }; static void DirectConnectScreen_UrlFilter(cc_string* str) { static const cc_string prefix = String_FromConst("mc://"); cc_string parts[6]; if (!String_CaselessStarts(str, &prefix)) return; /* mc://[ip:port]/[username]/[mppass] */ if (String_UNSAFE_Split(str, '/', parts, 6) != 5) return; LInput_SetString(&DirectConnectScreen.iptAddress, &parts[2]); LInput_SetString(&DirectConnectScreen.iptUsername, &parts[3]); LInput_SetString(&DirectConnectScreen.iptMppass, &parts[4]); str->length = 0; } static void DirectConnectScreen_StartClient(void* w) { static const cc_string defMppass = String_FromConst("(none)"); static const cc_string defPort = String_FromConst("25565"); const cc_string* user = &DirectConnectScreen.iptUsername.text; const cc_string* addr = &DirectConnectScreen.iptAddress.text; const cc_string* mppass = &DirectConnectScreen.iptMppass.text; struct LLabel* status = &DirectConnectScreen.lblStatus; cc_string ip, port; cc_uint16 raw_port; cc_sockaddr addrs[SOCKET_MAX_ADDRS]; int numAddrs; int index = String_LastIndexOf(addr, ':'); if (index == 0 || index == addr->length - 1) { LLabel_SetConst(status, "&cInvalid address"); return; } /* support either "[IP]" or "[IP]:[PORT]" */ if (index == -1) { ip = *addr; port = defPort; } else { ip = String_UNSAFE_Substring(addr, 0, index); port = String_UNSAFE_SubstringAt(addr, index + 1); } if (!user->length) { LLabel_SetConst(status, "&cUsername required"); return; } if (Socket_ParseAddress(&ip, 0, addrs, &numAddrs)) { LLabel_SetConst(status, "&cInvalid ip"); return; } if (!Convert_ParseUInt16(&port, &raw_port)) { LLabel_SetConst(status, "&cInvalid port"); return; } if (!mppass->length) mppass = &defMppass; Options_PauseSaving(); Options_Set("launcher-dc-username", user); Options_Set("launcher-dc-ip", &ip); Options_Set("launcher-dc-port", &port); Options_SetSecure("launcher-dc-mppass", mppass); Options_ResumeSaving(); LLabel_SetConst(status, ""); Launcher_StartGame(user, mppass, &ip, &port, &String_Empty); } static void DirectConnectScreen_Activated(struct LScreen* s_) { struct DirectConnectScreen* s = (struct DirectConnectScreen*)s_; LInput_Add(s, &s->iptUsername, 330, "Username..", dc_iptUsername); LInput_Add(s, &s->iptAddress, 330, "IP address:Port number..", dc_iptAddress); LInput_Add(s, &s->iptMppass, 330, "Mppass..", dc_iptMppass); LButton_Add(s, &s->btnConnect, 110, 35, "Connect", DirectConnectScreen_StartClient, dc_btnConnect); LButton_Add(s, &s->btnBack, 80, 35, "Back", SwitchToMain, dc_btnBack); LLabel_Add(s, &s->lblStatus, "", dc_lblStatus); s->iptUsername.ClipboardFilter = DirectConnectScreen_UrlFilter; s->iptAddress.ClipboardFilter = DirectConnectScreen_UrlFilter; s->iptMppass.ClipboardFilter = DirectConnectScreen_UrlFilter; } static void DirectConnectScreen_Load(struct LScreen* s_) { struct DirectConnectScreen* s = (struct DirectConnectScreen*)s_; cc_string addr; char addrBuffer[STRING_SIZE]; cc_string mppass; char mppassBuffer[STRING_SIZE]; cc_string user, ip, port; Options_Reload(); Options_UNSAFE_Get("launcher-dc-username", &user); Options_UNSAFE_Get("launcher-dc-ip", &ip); Options_UNSAFE_Get("launcher-dc-port", &port); String_InitArray(mppass, mppassBuffer); Options_GetSecure("launcher-dc-mppass", &mppass); String_InitArray(addr, addrBuffer); String_Format2(&addr, "%s:%s", &ip, &port); /* don't want just ':' for address */ if (addr.length == 1) addr.length = 0; LInput_SetText(&s->iptUsername, &user); LInput_SetText(&s->iptAddress, &addr); LInput_SetText(&s->iptMppass, &mppass); } void DirectConnectScreen_SetActive(void) { struct DirectConnectScreen* s = &DirectConnectScreen; LScreen_Reset((struct LScreen*)s); s->widgets = directConnect_widgets; s->maxWidgets = Array_Elems(directConnect_widgets); s->Activated = DirectConnectScreen_Activated; s->LoadState = DirectConnectScreen_Load; s->title = "Direct connect"; s->onEnterWidget = (struct LWidget*)&s->btnConnect; s->onEscapeWidget = (struct LWidget*)&s->btnBack; Launcher_SetScreen((struct LScreen*)s); } /*########################################################################################################################* *----------------------------------------------------------MFAScreen------------------------------------------------------* *#########################################################################################################################*/ static struct MFAScreen { LScreen_Layout struct LInput iptCode; struct LButton btnSignIn, btnCancel; struct LLabel lblTitle; } MFAScreen; #define MFA_SCREEN_MAX_WIDGETS 4 static struct LWidget* mfa_widgets[MFA_SCREEN_MAX_WIDGETS]; LAYOUTS mfa_lblTitle[] = { { ANCHOR_CENTRE, 0 }, { ANCHOR_CENTRE, -115 } }; LAYOUTS mfa_iptCode[] = { { ANCHOR_CENTRE, 0 }, { ANCHOR_CENTRE, -75 } }; LAYOUTS mfa_btnSignIn[] = { { ANCHOR_CENTRE, -90 }, { ANCHOR_CENTRE, -25 } }; LAYOUTS mfa_btnCancel[] = { { ANCHOR_CENTRE, 90 }, { ANCHOR_CENTRE, -25 } }; static void MainScreen_DoLogin(void); static void MFAScreen_SignIn(void* w) { MainScreen_SetActive(); MainScreen_DoLogin(); } static void MFAScreen_Cancel(void* w) { LInput_ClearText(&MFAScreen.iptCode); MainScreen_SetActive(); } static void MFAScreen_Activated(struct LScreen* s_) { struct MFAScreen* s = (struct MFAScreen*)s_; s->iptCode.inputType = KEYBOARD_TYPE_INTEGER; LLabel_Add(s, &s->lblTitle, "", mfa_lblTitle); LInput_Add(s, &s->iptCode, 280, "Login code..", mfa_iptCode); LButton_Add(s, &s->btnSignIn, 100, 35, "Sign in", MFAScreen_SignIn, mfa_btnSignIn); LButton_Add(s, &s->btnCancel, 100, 35, "Cancel", MFAScreen_Cancel, mfa_btnCancel); LLabel_SetConst(&s->lblTitle, s->iptCode.text.length ? "&cWrong code entered (Check emails)" : "&cLogin code required (Check emails)"); } void MFAScreen_SetActive(void) { struct MFAScreen* s = &MFAScreen; LScreen_Reset((struct LScreen*)s); s->widgets = mfa_widgets; s->maxWidgets = Array_Elems(mfa_widgets); s->Activated = MFAScreen_Activated; s->title = "Enter login code"; s->onEnterWidget = (struct LWidget*)&s->btnSignIn; Launcher_SetScreen((struct LScreen*)s); } /*########################################################################################################################* *----------------------------------------------------------SplitScreen----------------------------------------------------* *#########################################################################################################################*/ #ifdef CC_BUILD_SPLITSCREEN static struct SplitScreen { LScreen_Layout struct LButton btnPlayers[3], btnBack; cc_bool signingIn; } SplitScreen; #define SPLITSCREEN_MAX_WIDGETS 4 static struct LWidget* split_widgets[SPLITSCREEN_MAX_WIDGETS]; LAYOUTS sps_btnPlayers2[] = { { ANCHOR_CENTRE, 0 }, { ANCHOR_CENTRE, -120 } }; LAYOUTS sps_btnPlayers3[] = { { ANCHOR_CENTRE, 0 }, { ANCHOR_CENTRE, -70 } }; LAYOUTS sps_btnPlayers4[] = { { ANCHOR_CENTRE, 0 }, { ANCHOR_CENTRE, -20 } }; LAYOUTS sps_btnBack[] = { { ANCHOR_CENTRE, 0 }, { ANCHOR_CENTRE, 170 } }; static void SplitScreen_Start(int players) { static const cc_string user = String_FromConst(DEFAULT_USERNAME); Game_NumLocalPlayers = players; Launcher_StartGame(&user, &String_Empty, &String_Empty, &String_Empty, &String_Empty); } static void SplitScreen_Players2(void* w) { SplitScreen_Start(2); } static void SplitScreen_Players3(void* w) { SplitScreen_Start(3); } static void SplitScreen_Players4(void* w) { SplitScreen_Start(4); } static void SplitScreen_Activated(struct LScreen* s_) { struct SplitScreen* s = (struct SplitScreen*)s_; LButton_Add(s, &s->btnPlayers[0], 300, 35, "2 player splitscreen", SplitScreen_Players2, sps_btnPlayers2); LButton_Add(s, &s->btnPlayers[1], 300, 35, "3 player splitscreen", SplitScreen_Players3, sps_btnPlayers3); LButton_Add(s, &s->btnPlayers[2], 300, 35, "4 player splitscreen", SplitScreen_Players4, sps_btnPlayers4); LButton_Add(s, &s->btnBack, 100, 35, "Back", SwitchToMain, sps_btnBack); } void SplitScreen_SetActive(void) { struct SplitScreen* s = &SplitScreen; LScreen_Reset((struct LScreen*)s); s->widgets = split_widgets; s->maxWidgets = Array_Elems(split_widgets); s->Activated = SplitScreen_Activated; s->title = "Splitscreen mode"; Launcher_SetScreen((struct LScreen*)s); } static void SwitchToSplitScreen(void* w) { SplitScreen_SetActive(); } #endif /*########################################################################################################################* *----------------------------------------------------------MainScreen-----------------------------------------------------* *#########################################################################################################################*/ static struct MainScreen { LScreen_Layout struct LButton btnLogin, btnResume, btnDirect, btnSPlayer, btnSplit; struct LButton btnRegister, btnOptions, btnUpdates; struct LInput iptUsername, iptPassword; struct LLabel lblStatus, lblUpdate; cc_bool signingIn; } MainScreen; #define MAINSCREEN_MAX_WIDGETS 12 static struct LWidget* main_widgets[MAINSCREEN_MAX_WIDGETS]; LAYOUTS main_iptUsername[] = { { ANCHOR_CENTRE_MIN, -140 }, { ANCHOR_CENTRE, -120 } }; LAYOUTS main_iptPassword[] = { { ANCHOR_CENTRE_MIN, -140 }, { ANCHOR_CENTRE, -75 } }; LAYOUTS main_btnLogin[] = { { ANCHOR_CENTRE, -90 }, { ANCHOR_CENTRE, -25 } }; LAYOUTS main_lblStatus[] = { { ANCHOR_CENTRE, 0 }, { ANCHOR_CENTRE, 20 } }; LAYOUTS main_btnResume[] = { { ANCHOR_CENTRE, 90 }, { ANCHOR_CENTRE, -25 } }; LAYOUTS main_btnDirect[] = { { ANCHOR_CENTRE, 0 }, { ANCHOR_CENTRE, 60 } }; LAYOUTS main_btnSPlayer[] = { { ANCHOR_CENTRE, 0 }, { ANCHOR_CENTRE, 110 } }; LAYOUTS main_btnSplit[] = { { ANCHOR_CENTRE, 0 }, { ANCHOR_CENTRE, 160 } }; LAYOUTS main_btnRegister[] = { { ANCHOR_MIN, 6 }, { ANCHOR_MAX, 6 } }; LAYOUTS main_btnOptions[] = { { ANCHOR_CENTRE, 0 }, { ANCHOR_MAX, 6 } }; LAYOUTS main_btnUpdates[] = { { ANCHOR_MAX, 6 }, { ANCHOR_MAX, 6 } }; LAYOUTS main_lblUpdate_N[] = { { ANCHOR_MAX, 10 }, { ANCHOR_MAX, 45 } }; LAYOUTS main_lblUpdate_H[] = { { ANCHOR_MAX, 10 }, { ANCHOR_MAX, 6 } }; struct ResumeInfo { cc_string user, ip, port, server, mppass; char _userBuffer[STRING_SIZE], _serverBuffer[STRING_SIZE]; char _ipBuffer[16], _portBuffer[16], _mppassBuffer[STRING_SIZE]; cc_bool valid; }; CC_NOINLINE static void MainScreen_GetResume(struct ResumeInfo* info, cc_bool full) { String_InitArray(info->server, info->_serverBuffer); Options_Get(ROPT_SERVER, &info->server, ""); String_InitArray(info->user, info->_userBuffer); Options_Get(ROPT_USER, &info->user, ""); String_InitArray(info->ip, info->_ipBuffer); Options_Get(ROPT_IP, &info->ip, ""); String_InitArray(info->port, info->_portBuffer); Options_Get(ROPT_PORT, &info->port, ""); if (!full) return; String_InitArray(info->mppass, info->_mppassBuffer); Options_GetSecure(ROPT_MPPASS, &info->mppass); info->valid = info->user.length && info->mppass.length && info->ip.length && info->port.length; } CC_NOINLINE static void MainScreen_Error(struct HttpRequest* req, const char* action) { cc_string str; char strBuffer[STRING_SIZE]; struct MainScreen* s = &MainScreen; String_InitArray(str, strBuffer); Launcher_DisplayHttpError(req, action, &str); LLabel_SetText(&s->lblStatus, &str); s->signingIn = false; } static void MainScreen_DoLogin(void) { struct MainScreen* s = &MainScreen; cc_string* user = &s->iptUsername.text; cc_string* pass = &s->iptPassword.text; if (!user->length) { LLabel_SetConst(&s->lblStatus, "&cUsername required"); return; } if (!pass->length) { LLabel_SetConst(&s->lblStatus, "&cPassword required"); return; } if (GetTokenTask.Base.working) return; Options_Set(LOPT_USERNAME, user); Options_SetSecure(LOPT_PASSWORD, pass); GetTokenTask_Run(); LLabel_SetConst(&s->lblStatus, "&eSigning in.."); s->signingIn = true; } static void MainScreen_Login(void* w) { MainScreen_DoLogin(); } static void MainScreen_Register(void* w) { static const cc_string regUrl = String_FromConst(REGISTERNEW_URL); cc_result res = Process_StartOpen(®Url); if (res) Logger_SimpleWarn(res, "opening register webpage in browser"); } static void MainScreen_Resume(void* w) { struct ResumeInfo info; MainScreen_GetResume(&info, true); if (!info.valid) return; Launcher_StartGame(&info.user, &info.mppass, &info.ip, &info.port, &info.server); } static void MainScreen_Singleplayer(void* w) { static const cc_string defUser = String_FromConst(DEFAULT_USERNAME); const cc_string* user = &MainScreen.iptUsername.text; if (!user->length) user = &defUser; Launcher_StartGame(user, &String_Empty, &String_Empty, &String_Empty, &String_Empty); } static void MainScreen_ResumeHover(void* w) { cc_string str; char strBuffer[STRING_SIZE]; struct MainScreen* s = &MainScreen; struct ResumeInfo info; if (s->signingIn) return; MainScreen_GetResume(&info, false); if (!info.user.length) return; String_InitArray(str, strBuffer); if (info.server.length && String_Equals(&info.user, &s->iptUsername.text)) { String_Format1(&str, "&eResume to %s", &info.server); } else if (info.server.length) { String_Format2(&str, "&eResume as %s to %s", &info.user, &info.server); } else { String_Format3(&str, "&eResume as %s to %s:%s", &info.user, &info.ip, &info.port); } LLabel_SetText(&s->lblStatus, &str); } static void MainScreen_ResumeUnhover(void* w) { struct MainScreen* s = &MainScreen; if (s->signingIn) return; LLabel_SetConst(&s->lblStatus, ""); } CC_NOINLINE static cc_uint32 MainScreen_GetVersion(const cc_string* version) { cc_uint8 raw[4] = { 0, 0, 0, 0 }; cc_string parts[4]; int i, count; /* 1.0.1 -> { 1, 0, 1, 0 } */ count = String_UNSAFE_Split(version, '.', parts, 4); for (i = 0; i < count; i++) { Convert_ParseUInt8(&parts[i], &raw[i]); } return Stream_GetU32_BE(raw); } static void MainScreen_ApplyUpdateLabel(struct MainScreen* s) { static const cc_string currentStr = String_FromConst(GAME_APP_VER); cc_uint32 latest, current; if (CheckUpdateTask.Base.success) { latest = MainScreen_GetVersion(&CheckUpdateTask.latestRelease); current = MainScreen_GetVersion(¤tStr); #ifdef CC_BUILD_FLATPAK LLabel_SetConst(&s->lblUpdate, false ? "&aUpdate available" : "&eUp to date"); #else LLabel_SetConst(&s->lblUpdate, false ? "&aNew release" : "&eUp to date"); #endif } else { LLabel_SetConst(&s->lblUpdate, "&cCheck failed"); } } #ifdef CC_BUILD_CONSOLE static void MainScreen_ExitApp(void* w) { Window_Main.Exists = false; } #endif static void MainScreen_Activated(struct LScreen* s_) { struct MainScreen* s = (struct MainScreen*)s_; s->iptPassword.inputType = KEYBOARD_TYPE_PASSWORD; s->lblUpdate.small = true; #ifdef CC_BUILD_NETWORKING LInput_Add(s, &s->iptUsername, 280, "Username..", main_iptUsername); LInput_Add(s, &s->iptPassword, 280, "Password..", main_iptPassword); LButton_Add(s, &s->btnLogin, 100, 35, "Sign in", MainScreen_Login, main_btnLogin); LButton_Add(s, &s->btnResume, 100, 35, "Resume", MainScreen_Resume, main_btnResume); #endif LLabel_Add(s, &s->lblStatus, "", main_lblStatus); #ifdef CC_BUILD_NETWORKING LButton_Add(s, &s->btnDirect, 200, 35, "Direct connect", SwitchToDirectConnect, main_btnDirect); #endif LButton_Add(s, &s->btnSPlayer, 200, 35, "Singleplayer", MainScreen_Singleplayer, main_btnSPlayer); #ifdef CC_BUILD_SPLITSCREEN LButton_Add(s, &s->btnSplit, 200, 35, "Splitscreen (WIP)", SwitchToSplitScreen, main_btnSplit); #endif if (Process_OpenSupported) { LButton_Add(s, &s->btnRegister, 100, 35, "Register", MainScreen_Register, main_btnRegister); } LButton_Add(s, &s->btnOptions, 100, 35, "Options", SwitchToSettings, main_btnOptions); #ifdef CC_BUILD_CONSOLE LLabel_Add(s, &s->lblUpdate, "&eChecking..", main_lblUpdate_N); LButton_Add(s, &s->btnUpdates, 100, 35, "Exit", MainScreen_ExitApp, main_btnUpdates); #else LLabel_Add(s, &s->lblUpdate, "&eChecking..", Updater_Supported ? main_lblUpdate_N : main_lblUpdate_H); if (Updater_Supported) { LButton_Add(s, &s->btnUpdates, 100, 35, "Updates", SwitchToUpdates, main_btnUpdates); } #endif s->btnResume.OnHover = MainScreen_ResumeHover; s->btnResume.OnUnhover = MainScreen_ResumeUnhover; if (CheckUpdateTask.Base.completed) MainScreen_ApplyUpdateLabel(s); } static void MainScreen_Load(struct LScreen* s_) { cc_string user, pass; char passBuffer[STRING_SIZE]; struct MainScreen* s = (struct MainScreen*)s_; String_InitArray(pass, passBuffer); Options_UNSAFE_Get(LOPT_USERNAME, &user); Options_GetSecure(LOPT_PASSWORD, &pass); LInput_SetText(&s->iptUsername, &user); LInput_SetText(&s->iptPassword, &pass); /* Auto sign in when automatically joining a server */ if (!Launcher_AutoHash.length) return; if (!user.length || !pass.length) return; MainScreen_DoLogin(); } static void MainScreen_TickCheckUpdates(struct MainScreen* s) { if (!CheckUpdateTask.Base.working) return; LWebTask_Tick(&CheckUpdateTask.Base, NULL); if (!CheckUpdateTask.Base.completed) return; MainScreen_ApplyUpdateLabel(s); } static void MainScreen_LoginPhase2(struct MainScreen* s, const cc_string* user) { /* website returns case correct username */ if (!String_Equals(&s->iptUsername.text, user)) { LInput_SetText(&s->iptUsername, user); } String_Copy(&Launcher_Username, user); FetchServersTask_Run(); LLabel_SetConst(&s->lblStatus, "&eRetrieving servers list.."); } static void MainScreen_SignInError(struct HttpRequest* req) { MainScreen_Error(req, "signing in"); } static void MainScreen_TickGetToken(struct MainScreen* s) { if (!GetTokenTask.Base.working) return; LWebTask_Tick(&GetTokenTask.Base, MainScreen_SignInError); if (!GetTokenTask.Base.completed) return; if (!GetTokenTask.Base.success) return; if (!GetTokenTask.error && String_CaselessEquals(&GetTokenTask.username, &s->iptUsername.text)) { /* Already logged in, go straight to fetching servers */ MainScreen_LoginPhase2(s, &GetTokenTask.username); } else { SignInTask_Run(&s->iptUsername.text, &s->iptPassword.text, &MFAScreen.iptCode.text); } } static void MainScreen_TickSignIn(struct MainScreen* s) { if (!SignInTask.Base.working) return; LWebTask_Tick(&SignInTask.Base, MainScreen_SignInError); if (!SignInTask.Base.completed) return; if (SignInTask.needMFA) { MFAScreen_SetActive(); return; } if (SignInTask.error) { LLabel_SetConst(&s->lblStatus, SignInTask.error); } else if (SignInTask.Base.success) { MainScreen_LoginPhase2(s, &SignInTask.username); } } static void MainScreen_ServersError(struct HttpRequest* req) { MainScreen_Error(req, "retrieving servers list"); } static void MainScreen_TickFetchServers(struct MainScreen* s) { if (!FetchServersTask.Base.working) return; LWebTask_Tick(&FetchServersTask.Base, MainScreen_ServersError); if (!FetchServersTask.Base.completed) return; if (!FetchServersTask.Base.success) return; s->signingIn = false; if (Launcher_AutoHash.length) { Launcher_ConnectToServer(&Launcher_AutoHash); Launcher_AutoHash.length = 0; } else { ServersScreen_SetActive(); } } static void MainScreen_Tick(struct LScreen* s_) { struct MainScreen* s = (struct MainScreen*)s_; LScreen_Tick(s_); MainScreen_TickCheckUpdates(s); MainScreen_TickGetToken(s); MainScreen_TickSignIn(s); MainScreen_TickFetchServers(s); } void MainScreen_SetActive(void) { struct MainScreen* s = &MainScreen; LScreen_Reset((struct LScreen*)s); s->widgets = main_widgets; s->maxWidgets = Array_Elems(main_widgets); s->Activated = MainScreen_Activated; s->LoadState = MainScreen_Load; s->Tick = MainScreen_Tick; s->title = "ClassiHax"; #ifdef CC_BUILD_NETWORKING s->onEnterWidget = (struct LWidget*)&s->btnLogin; #else s->onEnterWidget = (struct LWidget*)&s->btnSPlayer; #endif Launcher_SetScreen((struct LScreen*)s); } #ifdef CC_BUILD_RESOURCES /*########################################################################################################################* *----------------------------------------------------CheckResourcesScreen-------------------------------------------------* *#########################################################################################################################*/ static struct CheckResourcesScreen { LScreen_Layout struct LLabel lblLine1, lblLine2, lblStatus; struct LButton btnYes, btnNo; } CheckResourcesScreen; #define CHECKRESOURCES_SCREEN_MAX_WIDGET 5 static struct LWidget* checkResources_widgets[CHECKRESOURCES_SCREEN_MAX_WIDGET]; LAYOUTS cres_lblLine1[] = { { ANCHOR_CENTRE, 0 }, { ANCHOR_CENTRE, -50 } }; LAYOUTS cres_lblLine2[] = { { ANCHOR_CENTRE, 0 }, { ANCHOR_CENTRE, -30 } }; LAYOUTS cres_lblStatus[] = { { ANCHOR_CENTRE, 0 }, { ANCHOR_CENTRE, 10 } }; LAYOUTS cres_btnYes[] = { { ANCHOR_CENTRE, -70 }, { ANCHOR_CENTRE, 45 } }; LAYOUTS cres_btnNo[] = { { ANCHOR_CENTRE, 70 }, { ANCHOR_CENTRE, 45 } }; static void CheckResourcesScreen_Yes(void* w) { FetchResourcesScreen_SetActive(); } static void CheckResourcesScreen_Next(void* w) { Http_ClearPending(); if (Options_LoadResult != ReturnCode_FileNotFound) { MainScreen_SetActive(); } else { ChooseModeScreen_SetActive(true); } } static void CheckResourcesScreen_AddWidgets(struct CheckResourcesScreen* s) { const char* line1_msg = Resources_MissingRequired ? "Some required resources weren't found" : "Some optional resources weren't found"; s->lblStatus.small = true; LLabel_Add(s, &s->lblLine1, line1_msg, cres_lblLine1); LLabel_Add(s, &s->lblLine2, "Okay to download?", cres_lblLine2); LLabel_Add(s, &s->lblStatus, "", cres_lblStatus); LButton_Add(s, &s->btnYes, 70, 35, "Yes", CheckResourcesScreen_Yes, cres_btnYes); LButton_Add(s, &s->btnNo, 70, 35, "No", CheckResourcesScreen_Next, cres_btnNo); } static void CheckResourcesScreen_Activated(struct LScreen* s_) { struct CheckResourcesScreen* s = (struct CheckResourcesScreen*)s_; cc_string str; char buffer[STRING_SIZE]; float size; CheckResourcesScreen_AddWidgets(s); size = Resources_MissingSize / 1024.0f; String_InitArray(str, buffer); String_Format1(&str, "&eDownload size: %f2 megabytes", &size); LLabel_SetText(&s->lblStatus, &str); } static void CheckResourcesScreen_ResetArea(struct Context2D* ctx, int x, int y, int width, int height) { int R = BitmapCol_R(Launcher_Theme.BackgroundColor) * 0.78f; /* 153 -> 120 */ int G = BitmapCol_G(Launcher_Theme.BackgroundColor) * 0.70f; /* 127 -> 89 */ int B = BitmapCol_B(Launcher_Theme.BackgroundColor) * 0.88f; /* 172 -> 151 */ Gradient_Noise(ctx, BitmapColor_RGB(R, G, B), 4, x, y, width, height); } static void CheckResourcesScreen_DrawBackground(struct LScreen* s, struct Context2D* ctx) { int x, y, width, height; BitmapCol color = BitmapColor_Scale(Launcher_Theme.BackgroundColor, 0.2f); Context2D_Clear(ctx, color, 0, 0, ctx->width, ctx->height); width = Display_ScaleX(380); height = Display_ScaleY(140); x = Gui_CalcPos(ANCHOR_CENTRE, 0, width, ctx->width); y = Gui_CalcPos(ANCHOR_CENTRE, 0, height, ctx->height); CheckResourcesScreen_ResetArea(ctx, x, y, width, height); } void CheckResourcesScreen_SetActive(void) { struct CheckResourcesScreen* s = &CheckResourcesScreen; LScreen_Reset((struct LScreen*)s); s->widgets = checkResources_widgets; s->maxWidgets = Array_Elems(checkResources_widgets); s->Activated = CheckResourcesScreen_Activated; s->DrawBackground = CheckResourcesScreen_DrawBackground; s->ResetArea = CheckResourcesScreen_ResetArea; s->onEnterWidget = (struct LWidget*)&s->btnYes; Launcher_SetScreen((struct LScreen*)s); } /*########################################################################################################################* *----------------------------------------------------FetchResourcesScreen-------------------------------------------------* *#########################################################################################################################*/ static struct FetchResourcesScreen { LScreen_Layout struct LLabel lblStatus; struct LButton btnCancel; struct LSlider sdrProgress; } FetchResourcesScreen; #define FETCHRESOURCES_SCREEN_MAX_WIDGETS 3 static struct LWidget* fetchResources_widgets[FETCHRESOURCES_SCREEN_MAX_WIDGETS]; LAYOUTS fres_lblStatus[] = { { ANCHOR_CENTRE, 0 }, { ANCHOR_CENTRE, -10 } }; LAYOUTS fres_btnCancel[] = { { ANCHOR_CENTRE, 0 }, { ANCHOR_CENTRE, 45 } }; LAYOUTS fres_sdrProgress[] = { { ANCHOR_CENTRE, 0 }, { ANCHOR_CENTRE, 15 } }; static void FetchResourcesScreen_Error(struct HttpRequest* req) { cc_string str; char buffer[STRING_SIZE]; String_InitArray(str, buffer); Launcher_DisplayHttpError(req, "downloading resources", &str); LLabel_SetText(&FetchResourcesScreen.lblStatus, &str); } static void FetchResourcesScreen_Activated(struct LScreen* s_) { struct FetchResourcesScreen* s = (struct FetchResourcesScreen*)s_; s->lblStatus.small = true; LLabel_Add(s, &s->lblStatus, "", fres_lblStatus); LButton_Add(s, &s->btnCancel, 120, 35, "Cancel", CheckResourcesScreen_Next, fres_btnCancel); LSlider_Add(s, &s->sdrProgress, 200, 12, BitmapColor_RGB(0, 220, 0), fres_sdrProgress); Fetcher_ErrorCallback = FetchResourcesScreen_Error; Fetcher_Run(); } static void FetchResourcesScreen_UpdateStatus(struct FetchResourcesScreen* s, int reqID) { cc_string str; char strBuffer[STRING_SIZE]; const char* name; int count; name = Fetcher_RequestName(reqID); if (!name) return; String_InitArray(str, strBuffer); count = Fetcher_Downloaded + 1; String_Format3(&str, "&eFetching %c.. (%i/%i)", name, &count, &Resources_MissingCount); if (String_Equals(&str, &s->lblStatus.text)) return; LLabel_SetText(&s->lblStatus, &str); } static void FetchResourcesScreen_UpdateProgress(struct FetchResourcesScreen* s) { int reqID, progress; if (!Http_GetCurrent(&reqID, &progress)) return; FetchResourcesScreen_UpdateStatus(s, reqID); /* making request still, haven't started download yet */ if (progress < 0 || progress > 100) return; LSlider_SetProgress(&s->sdrProgress, progress); } static void FetchResourcesScreen_Tick(struct LScreen* s_) { struct FetchResourcesScreen* s = (struct FetchResourcesScreen*)s_; if (!Fetcher_Working) return; FetchResourcesScreen_UpdateProgress(s); Fetcher_Update(); if (!Fetcher_Completed) return; if (Fetcher_Failed) return; Launcher_TryLoadTexturePack(); CheckResourcesScreen_Next(NULL); } void FetchResourcesScreen_SetActive(void) { struct FetchResourcesScreen* s = &FetchResourcesScreen; LScreen_Reset((struct LScreen*)s); s->widgets = fetchResources_widgets; s->maxWidgets = Array_Elems(fetchResources_widgets); s->Activated = FetchResourcesScreen_Activated; s->Tick = FetchResourcesScreen_Tick; s->DrawBackground = CheckResourcesScreen_DrawBackground; s->ResetArea = CheckResourcesScreen_ResetArea; Launcher_SetScreen((struct LScreen*)s); } #endif /*########################################################################################################################* *--------------------------------------------------------ServersScreen----------------------------------------------------* *#########################################################################################################################*/ static struct ServersScreen { LScreen_Layout struct LInput iptSearch, iptHash; struct LButton btnBack, btnConnect, btnRefresh; struct LTable table; struct FontDesc rowFont; float tableAcc; } ServersScreen; static struct LWidget* servers_widgets[6]; LAYOUTS srv_iptSearch[] = { { ANCHOR_MIN, 10 }, { ANCHOR_MIN, 10 } }; LAYOUTS srv_iptHash[] = { { ANCHOR_MIN, 10 }, { ANCHOR_MAX, 10 } }; LAYOUTS srv_table[5] = { { ANCHOR_MIN, 10 }, { ANCHOR_MIN | LLAYOUT_EXTRA, 50 }, { LLAYOUT_WIDTH, 0 }, { LLAYOUT_HEIGHT, 50 } }; LAYOUTS srv_btnBack[] = { { ANCHOR_MAX, 10 }, { ANCHOR_MIN, 10 } }; LAYOUTS srv_btnConnect[] = { { ANCHOR_MAX, 10 }, { ANCHOR_MAX, 10 } }; LAYOUTS srv_btnRefresh[] = { { ANCHOR_MAX, 135 }, { ANCHOR_MIN, 10 } }; static void ServersScreen_Connect(void* w) { struct LTable* table = &ServersScreen.table; cc_string* hash = &ServersScreen.iptHash.text; if (!hash->length && table->rowsCount) { hash = <able_Get(table->topRow)->hash; } Launcher_ConnectToServer(hash); } static void ServersScreen_Refresh(void* w) { struct LButton* btn; if (FetchServersTask.Base.working) return; FetchServersTask_Run(); btn = &ServersScreen.btnRefresh; LButton_SetConst(btn, "&eWorking.."); } static void ServersScreen_HashFilter(cc_string* str) { int lastIndex; if (!str->length) return; /* Server url look like http://www.classicube.net/server/play/aaaaa/ */ /* Trim it to only be the aaaaa */ if (str->buffer[str->length - 1] == '/') str->length--; /* Trim the URL parts before the hash */ lastIndex = String_LastIndexOf(str, '/'); if (lastIndex == -1) return; *str = String_UNSAFE_SubstringAt(str, lastIndex + 1); } static void ServersScreen_SearchChanged(struct LInput* w) { struct ServersScreen* s = &ServersScreen; LTable_ApplyFilter(&s->table); LBackend_MarkDirty(&s->table); } static void ServersScreen_HashChanged(struct LInput* w) { struct ServersScreen* s = &ServersScreen; LTable_ShowSelected(&s->table); LBackend_MarkDirty(&s->table); } static void ServersScreen_OnSelectedChanged(void) { struct ServersScreen* s = &ServersScreen; LBackend_MarkDirty(&s->iptHash); LBackend_MarkDirty(&s->table); } static void ServersScreen_ReloadServers(struct ServersScreen* s) { int i; LTable_Sort(&s->table); for (i = 0; i < FetchServersTask.numServers; i++) { FetchFlagsTask_Add(&FetchServersTask.servers[i]); } } static void ServersScreen_AddWidgets(struct ServersScreen* s) { LInput_Add(s, &s->iptSearch, 370, "Search servers..", srv_iptSearch); LInput_Add(s, &s->iptHash, 475, "classicube.net/server/play/...", srv_iptHash); LButton_Add(s, &s->btnBack, 110, 30, "Back", SwitchToMain, srv_btnBack); LButton_Add(s, &s->btnConnect, 130, 30, "Connect", ServersScreen_Connect, srv_btnConnect); LButton_Add(s, &s->btnRefresh, 110, 30, "Refresh", ServersScreen_Refresh, srv_btnRefresh); s->iptSearch.skipsEnter = true; s->iptSearch.TextChanged = ServersScreen_SearchChanged; s->iptHash.TextChanged = ServersScreen_HashChanged; s->iptHash.ClipboardFilter = ServersScreen_HashFilter; s->table.filter = &s->iptSearch.text; s->table.selectedHash = &s->iptHash.text; s->table.OnSelectedChanged = ServersScreen_OnSelectedChanged; if (s->table.VTABLE) { LScreen_AddWidget(s, &s->table); } else { LTable_Add(s, &s->table, srv_table); } LTable_Reset(&s->table); } static void ServersScreen_Activated(struct LScreen* s_) { struct ServersScreen* s = (struct ServersScreen*)s_; ServersScreen_AddWidgets(s); s->tableAcc = 0.0f; LInput_ClearText(&s->iptHash); LInput_ClearText(&s->iptSearch); ServersScreen_ReloadServers(s); /* This is so typing on keyboard by default searchs server list */ /* But don't do that when it would cause on-screen keyboard to show */ if (Window_Main.SoftKeyboard) return; LScreen_SelectWidget(s_, 0, (struct LWidget*)&s->iptSearch, false); } static void ServersScreen_Tick(struct LScreen* s_) { struct ServersScreen* s = (struct ServersScreen*)s_; LScreen_Tick(s_); LWebTask_Tick(&FetchFlagsTask.Base, NULL); if (!FetchServersTask.Base.working) return; LWebTask_Tick(&FetchServersTask.Base, NULL); if (!FetchServersTask.Base.completed) return; if (FetchServersTask.Base.success) { ServersScreen_ReloadServers(s); LBackend_MarkDirty(&s->table); } LButton_SetConst(&s->btnRefresh, FetchServersTask.Base.success ? "Refresh" : "&cFailed"); } static void ServersScreen_MouseWheel(struct LScreen* s_, float delta) { struct ServersScreen* s = (struct ServersScreen*)s_; s->table.VTABLE->MouseWheel(&s->table, delta); } static void ServersScreen_KeyDown(struct LScreen* s_, int key, cc_bool was) { struct ServersScreen* s = (struct ServersScreen*)s_; if (!LTable_HandlesKey(key)) { LScreen_KeyDown(s_, key, was); } else { s->table.VTABLE->KeyDown(&s->table, key, was); } } static void ServersScreen_MouseUp(struct LScreen* s_, int idx) { struct ServersScreen* s = (struct ServersScreen*)s_; s->table.VTABLE->OnUnselect(&s->table, idx); } void ServersScreen_SetActive(void) { struct ServersScreen* s = &ServersScreen; LScreen_Reset((struct LScreen*)s); s->widgets = servers_widgets; s->maxWidgets = Array_Elems(servers_widgets); s->Activated = ServersScreen_Activated; s->Tick = ServersScreen_Tick; s->MouseWheel = ServersScreen_MouseWheel; s->KeyDown = ServersScreen_KeyDown; s->MouseUp = ServersScreen_MouseUp; s->onEnterWidget = (struct LWidget*)&s->btnConnect; s->onEscapeWidget = (struct LWidget*)&s->btnBack; Launcher_SetScreen((struct LScreen*)s); } /*########################################################################################################################* *--------------------------------------------------------SettingsScreen---------------------------------------------------* *#########################################################################################################################*/ static struct SettingsScreen { LScreen_Layout struct LButton btnMode, btnColours, btnBack; struct LLabel lblMode, lblColours; struct LCheckbox cbExtra, cbEmpty, cbScale; struct LLine sep; } SettingsScreen; #define SETTINGS_SCREEN_MAX_WIDGETS 9 static struct LWidget* settings_widgets[SETTINGS_SCREEN_MAX_WIDGETS]; LAYOUTS set_btnMode[] = { { ANCHOR_CENTRE, -135 }, { ANCHOR_CENTRE, -70 } }; LAYOUTS set_lblMode[] = { { ANCHOR_CENTRE_MIN, -70 }, { ANCHOR_CENTRE, -70 } }; LAYOUTS set_btnColours[] = { { ANCHOR_CENTRE, -135 }, { ANCHOR_CENTRE, -20 } }; LAYOUTS set_lblColours[] = { { ANCHOR_CENTRE_MIN, -70 }, { ANCHOR_CENTRE, -20 } }; LAYOUTS set_sep[] = { { ANCHOR_CENTRE, 0 }, { ANCHOR_CENTRE, 15 } }; LAYOUTS set_cbExtra[] = { { ANCHOR_CENTRE_MIN, -190 }, { ANCHOR_CENTRE, 44 } }; LAYOUTS set_cbEmpty[] = { { ANCHOR_CENTRE_MIN, -190 }, { ANCHOR_CENTRE, 84 } }; LAYOUTS set_cbScale[] = { { ANCHOR_CENTRE_MIN, -190 }, { ANCHOR_CENTRE, 124 } }; LAYOUTS set_btnBack[] = { { ANCHOR_CENTRE, 0 }, { ANCHOR_CENTRE, 170 } }; #if defined CC_BUILD_MOBILE static void SettingsScreen_LockOrientation(struct LCheckbox* w) { Options_SetBool(OPT_LANDSCAPE_MODE, w->value); Window_LockLandscapeOrientation(w->value); LBackend_Redraw(); } #else static void SettingsScreen_AutoClose(struct LCheckbox* w) { Options_SetBool(LOPT_AUTO_CLOSE, w->value); } #endif static void SettingsScreen_ShowEmpty(struct LCheckbox* w) { Launcher_ShowEmptyServers = w->value; Options_SetBool(LOPT_SHOW_EMPTY, w->value); } static void SettingsScreen_DPIScaling(struct LCheckbox* w) { #if defined CC_BUILD_WIN DisplayInfo.DPIScaling = w->value; Options_SetBool(OPT_DPI_SCALING, w->value); Window_ShowDialog("Restart required", "You must restart ClassiCube before display scaling takes effect"); #else Window_ShowDialog("Restart required", "Display scaling is currently only supported on Windows"); #endif } static void SettingsScreen_AddWidgets(struct SettingsScreen* s) { LLine_Add(s, &s->sep, 380, set_sep); LButton_Add(s, &s->btnMode, 110, 35, "Mode", SwitchToChooseMode, set_btnMode); LLabel_Add(s, &s->lblMode, "&eChange the enabled features", set_lblMode); if (!Options_GetBool(OPT_CLASSIC_MODE, false)) { LButton_Add(s, &s->btnColours, 110, 35, "Theme", SwitchToThemes, set_btnColours); LLabel_Add(s, &s->lblColours, "&eChange how the launcher looks", set_lblColours); } #if defined CC_BUILD_MOBILE LCheckbox_Add(s, &s->cbExtra, "Force landscape", SettingsScreen_LockOrientation, set_cbExtra); #else LCheckbox_Add(s, &s->cbExtra, "Close this after game starts", SettingsScreen_AutoClose, set_cbExtra); #endif LCheckbox_Add(s, &s->cbEmpty, "Show empty servers in list", SettingsScreen_ShowEmpty, set_cbEmpty); LCheckbox_Add(s, &s->cbScale, "Use display scaling", SettingsScreen_DPIScaling, set_cbScale); LButton_Add(s, &s->btnBack, 80, 35, "Back", SwitchToMain, set_btnBack); } static void SettingsScreen_Activated(struct LScreen* s_) { struct SettingsScreen* s = (struct SettingsScreen*)s_; SettingsScreen_AddWidgets(s); #if defined CC_BUILD_MOBILE LCheckbox_Set(&s->cbExtra, Options_GetBool(OPT_LANDSCAPE_MODE, false)); #else LCheckbox_Set(&s->cbExtra, Options_GetBool(LOPT_AUTO_CLOSE, false)); #endif LCheckbox_Set(&s->cbEmpty, Launcher_ShowEmptyServers); LCheckbox_Set(&s->cbScale, DisplayInfo.DPIScaling); } void SettingsScreen_SetActive(void) { struct SettingsScreen* s = &SettingsScreen; LScreen_Reset((struct LScreen*)s); s->widgets = settings_widgets; s->maxWidgets = Array_Elems(settings_widgets); s->Activated = SettingsScreen_Activated; s->title = "Options"; s->onEscapeWidget = (struct LWidget*)&s->btnBack; Launcher_SetScreen((struct LScreen*)s); } /*########################################################################################################################* *----------------------------------------------------------ThemesScreen----------------------------------------------------* *#########################################################################################################################*/ static struct ThemesScreen { LScreen_Layout struct LButton btnModern, btnClassic, btnNordic; struct LButton btnCustom, btnBack; } ThemesScreen; #define THEME_SCREEN_MAX_WIDGETS 5 static struct LWidget* themes_widgets[THEME_SCREEN_MAX_WIDGETS]; LAYOUTS the_btnModern[] = { { ANCHOR_CENTRE, 0 }, { ANCHOR_CENTRE, -120 } }; LAYOUTS the_btnClassic[] = { { ANCHOR_CENTRE, 0 }, { ANCHOR_CENTRE, -70 } }; LAYOUTS the_btnNordic[] = { { ANCHOR_CENTRE, 0 }, { ANCHOR_CENTRE, -20 } }; LAYOUTS the_btnCustom[] = { { ANCHOR_CENTRE, 0 }, { ANCHOR_CENTRE, 120 } }; LAYOUTS the_btnBack[] = { { ANCHOR_CENTRE, 0 }, { ANCHOR_CENTRE, 170 } }; static void ThemesScreen_Set(const struct LauncherTheme* theme) { Launcher_Theme = *theme; Launcher_SaveTheme(); LBackend_ThemeChanged(); } static void ThemesScreen_Modern(void* w) { ThemesScreen_Set(&Launcher_ModernTheme); } static void ThemesScreen_Classic(void* w) { ThemesScreen_Set(&Launcher_ClassicTheme); } static void ThemesScreen_Nordic(void* w) { ThemesScreen_Set(&Launcher_NordicTheme); } static void ThemesScreen_Activated(struct LScreen* s_) { struct ThemesScreen* s = (struct ThemesScreen*)s_; LButton_Add(s, &s->btnModern, 200, 35, "Modern", ThemesScreen_Modern, the_btnModern); LButton_Add(s, &s->btnClassic, 200, 35, "Classic", ThemesScreen_Classic, the_btnClassic); LButton_Add(s, &s->btnNordic, 200, 35, "Nordic", ThemesScreen_Nordic, the_btnNordic); LButton_Add(s, &s->btnCustom, 200, 35, "Custom", SwitchToColours, the_btnCustom); LButton_Add(s, &s->btnBack, 80, 35, "Back", SwitchToSettings, the_btnBack); } void ThemesScreen_SetActive(void) { struct ThemesScreen* s = &ThemesScreen; LScreen_Reset((struct LScreen*)s); s->widgets = themes_widgets; s->maxWidgets = Array_Elems(themes_widgets); s->Activated = ThemesScreen_Activated; s->title = "Select theme"; s->onEscapeWidget = (struct LWidget*)&s->btnBack; Launcher_SetScreen((struct LScreen*)s); } /*########################################################################################################################* *--------------------------------------------------------UpdatesScreen----------------------------------------------------* *#########################################################################################################################*/ static struct UpdatesScreen { LScreen_Layout struct LLine seps[2]; struct LButton btnRel[2], btnDev[2], btnBack; struct LLabel lblYour, lblRel, lblDev, lblInfo, lblStatus; int buildProgress, buildIndex; cc_bool pendingFetch, release; } UpdatesScreen; #define UPDATESSCREEN_MAX_WIDGETS 12 static struct LWidget* updates_widgets[UPDATESSCREEN_MAX_WIDGETS]; LAYOUTS upd_lblYour[] = { { ANCHOR_CENTRE, -5 }, { ANCHOR_CENTRE, -120 } }; LAYOUTS upd_seps0[] = { { ANCHOR_CENTRE, 0 }, { ANCHOR_CENTRE, -100 } }; LAYOUTS upd_seps1[] = { { ANCHOR_CENTRE, 0 }, { ANCHOR_CENTRE, -5 } }; LAYOUTS upd_lblRel[] = { { ANCHOR_CENTRE, -20 }, { ANCHOR_CENTRE, -75 } }; LAYOUTS upd_lblDev[] = { { ANCHOR_CENTRE, -30 }, { ANCHOR_CENTRE, 20 } }; LAYOUTS upd_lblInfo[] = { { ANCHOR_CENTRE, 0 }, { ANCHOR_CENTRE, 105 } }; LAYOUTS upd_lblStatus[] = { { ANCHOR_CENTRE, 0 }, { ANCHOR_CENTRE, 130 } }; LAYOUTS upd_btnBack[] = { { ANCHOR_CENTRE, 0 }, { ANCHOR_CENTRE, 170 } }; /* Update button layouts when 1 build */ LAYOUTS upd_btnRel0_1[] = { { ANCHOR_CENTRE, 0 }, { ANCHOR_CENTRE, -40 } }; LAYOUTS upd_btnDev0_1[] = { { ANCHOR_CENTRE, 0 }, { ANCHOR_CENTRE, 55 } }; /* Update button layouts when 2 builds */ LAYOUTS upd_btnRel0_2[] = { { ANCHOR_CENTRE, -80 }, { ANCHOR_CENTRE, -40 } }; LAYOUTS upd_btnRel1_2[] = { { ANCHOR_CENTRE, 80 }, { ANCHOR_CENTRE, -40 } }; LAYOUTS upd_btnDev0_2[] = { { ANCHOR_CENTRE, -80 }, { ANCHOR_CENTRE, 55 } }; LAYOUTS upd_btnDev1_2[] = { { ANCHOR_CENTRE, 80 }, { ANCHOR_CENTRE, 55 } }; CC_NOINLINE static void UpdatesScreen_FormatTime(cc_string* str, int delta) { const char* span; int unit, value = Math_AbsI(delta); if (value < SECS_PER_MIN) { span = "second"; unit = 1; } else if (value < SECS_PER_HOUR) { span = "minute"; unit = SECS_PER_MIN; } else if (value < SECS_PER_DAY) { span = "hour"; unit = SECS_PER_HOUR; } else { span = "day"; unit = SECS_PER_DAY; } value /= unit; String_AppendInt(str, value); String_Append(str, ' '); String_AppendConst(str, span); if (value > 1) String_Append(str, 's'); String_AppendConst(str, delta >= 0 ? " ago" : " in the future"); } static void UpdatesScreen_Format(struct LLabel* lbl, const char* prefix, cc_uint64 timestamp) { cc_string str; char buffer[STRING_SIZE]; TimeMS now; int delta; String_InitArray(str, buffer); String_AppendConst(&str, prefix); if (!timestamp) { String_AppendConst(&str, "&cCheck failed"); } else { now = DateTime_CurrentUTC() - UNIX_EPOCH_SECONDS; delta = (int)(now - timestamp); UpdatesScreen_FormatTime(&str, delta); } LLabel_SetText(lbl, &str); } static void UpdatesScreen_FormatBoth(struct UpdatesScreen* s) { UpdatesScreen_Format(&s->lblRel, "Latest release: ", CheckUpdateTask.relTimestamp); UpdatesScreen_Format(&s->lblDev, "Latest dev build: ", CheckUpdateTask.devTimestamp); } static void UpdatesScreen_UpdateHeader(struct UpdatesScreen* s, cc_string* str) { const char* message; if ( s->release) message = "&eFetching latest release "; if (!s->release) message = "&eFetching latest dev build "; String_Format2(str, "%c%c", message, Updater_Info.builds[s->buildIndex].name); } static void UpdatesScreen_DoFetch(struct UpdatesScreen* s) { cc_string str; char strBuffer[STRING_SIZE]; cc_uint64 time; time = s->release ? CheckUpdateTask.relTimestamp : CheckUpdateTask.devTimestamp; if (!time || FetchUpdateTask.Base.working) return; FetchUpdateTask_Run(s->release, s->buildIndex); s->pendingFetch = false; s->buildProgress = -1; String_InitArray(str, strBuffer); UpdatesScreen_UpdateHeader(s, &str); String_AppendConst(&str, ".."); LLabel_SetText(&s->lblStatus, &str); } static void UpdatesScreen_Get(cc_bool release, int buildIndex) { struct UpdatesScreen* s = &UpdatesScreen; /* This code is deliberately split up to handle this particular case: */ /* The user clicked this button before CheckUpdateTask completed, */ /* therefore update fetching would not actually occur. (as timestamp is 0) */ /* By storing requested build, it can be fetched later upon completion. */ s->pendingFetch = true; s->release = release; s->buildIndex = buildIndex; UpdatesScreen_DoFetch(s); } static void UpdatesScreen_CheckTick(struct UpdatesScreen* s) { if (!CheckUpdateTask.Base.working) return; LWebTask_Tick(&CheckUpdateTask.Base, NULL); if (!CheckUpdateTask.Base.completed) return; UpdatesScreen_FormatBoth(s); if (s->pendingFetch) UpdatesScreen_DoFetch(s); } static void UpdatesScreen_UpdateProgress(struct UpdatesScreen* s, struct LWebTask* task) { cc_string str; char strBuffer[STRING_SIZE]; int progress = Http_CheckProgress(task->reqID); if (progress == s->buildProgress) return; s->buildProgress = progress; if (progress < 0 || progress > 100) return; String_InitArray(str, strBuffer); UpdatesScreen_UpdateHeader(s, &str); String_Format1(&str, " &a%i%%", &s->buildProgress); LLabel_SetText(&s->lblStatus, &str); } static void FetchUpdatesError(struct HttpRequest* req) { cc_string str; char strBuffer[STRING_SIZE]; String_InitArray(str, strBuffer); Launcher_DisplayHttpError(req, "fetching update", &str); LLabel_SetText(&UpdatesScreen.lblStatus, &str); } static void UpdatesScreen_FetchTick(struct UpdatesScreen* s) { if (!FetchUpdateTask.Base.working) return; LWebTask_Tick(&FetchUpdateTask.Base, FetchUpdatesError); UpdatesScreen_UpdateProgress(s, &FetchUpdateTask.Base); if (!FetchUpdateTask.Base.completed) return; if (!FetchUpdateTask.Base.success) return; /* FetchUpdateTask handles saving the updated file for us */ Launcher_ShouldExit = true; Launcher_ShouldUpdate = true; } static void UpdatesScreen_Rel_0(void* w) { UpdatesScreen_Get(true, 0); } static void UpdatesScreen_Rel_1(void* w) { UpdatesScreen_Get(true, 1); } static void UpdatesScreen_Dev_0(void* w) { UpdatesScreen_Get(false, 0); } static void UpdatesScreen_Dev_1(void* w) { UpdatesScreen_Get(false, 1); } static void UpdatesScreen_AddWidgets(struct UpdatesScreen* s) { int builds = Updater_Info.numBuilds; LLine_Add(s, &s->seps[0], 320, upd_seps0); LLine_Add(s, &s->seps[1], 320, upd_seps1); LLabel_Add(s, &s->lblYour, "Your build: (unknown)", upd_lblYour); LLabel_Add(s, &s->lblRel, "Latest release: Checking..", upd_lblRel); LLabel_Add(s, &s->lblDev, "Latest dev build: Checking..", upd_lblDev); LLabel_Add(s, &s->lblStatus, "", upd_lblStatus); LLabel_Add(s, &s->lblInfo, Updater_Info.info, upd_lblInfo); LButton_Add(s, &s->btnBack, 80, 35, "Back", SwitchToMain, upd_btnBack); if (builds >= 1) { LButton_Add(s, &s->btnRel[0], 130, 35, Updater_Info.builds[0].name, UpdatesScreen_Rel_0, builds == 1 ? upd_btnRel0_1 : upd_btnRel0_2); LButton_Add(s, &s->btnDev[0], 130, 35, Updater_Info.builds[0].name, UpdatesScreen_Dev_0, builds == 1 ? upd_btnDev0_1 : upd_btnDev0_2); } if (builds >= 2) { LButton_Add(s, &s->btnRel[1], 130, 35, Updater_Info.builds[1].name, UpdatesScreen_Rel_1, upd_btnRel1_2); LButton_Add(s, &s->btnDev[1], 130, 35, Updater_Info.builds[1].name, UpdatesScreen_Dev_1, upd_btnDev1_2); } } static void UpdatesScreen_Activated(struct LScreen* s_) { struct UpdatesScreen* s = (struct UpdatesScreen*)s_; cc_uint64 buildTime; cc_result res; UpdatesScreen_AddWidgets(s); /* Initially fill out with update check result from main menu */ if (CheckUpdateTask.Base.completed && CheckUpdateTask.Base.success) { UpdatesScreen_FormatBoth(s); } CheckUpdateTask_Run(); s->pendingFetch = false; res = Updater_GetBuildTime(&buildTime); if (res) { Logger_SysWarn(res, "getting build time"); return; } UpdatesScreen_Format(&s->lblYour, "Your build: ", buildTime); } static void UpdatesScreen_Tick(struct LScreen* s_) { struct UpdatesScreen* s = (struct UpdatesScreen*)s_; LScreen_Tick(s_); UpdatesScreen_FetchTick(s); UpdatesScreen_CheckTick(s); } /* Aborts fetch if it is in progress */ static void UpdatesScreen_Deactivated(struct LScreen* s_) { struct UpdatesScreen* s = (struct UpdatesScreen*)s_; s->buildProgress = -1; FetchUpdateTask.Base.working = false; s->lblStatus.text.length = 0; } void UpdatesScreen_SetActive(void) { struct UpdatesScreen* s = &UpdatesScreen; LScreen_Reset((struct LScreen*)s); s->widgets = updates_widgets; s->maxWidgets = Array_Elems(updates_widgets); s->Activated = UpdatesScreen_Activated; s->Tick = UpdatesScreen_Tick; s->Deactivated = UpdatesScreen_Deactivated; s->title = "Update game"; s->onEscapeWidget = (struct LWidget*)&s->btnBack; Launcher_SetScreen((struct LScreen*)s); } #endif