1/*
2 * LegacyClonk
3 *
4 * Copyright (c) RedWolf Design
5 * Copyright (c) 2003, Sven2
6 * Copyright (c) 2017-2021, The LegacyClonk Team and contributors
7 *
8 * Distributed under the terms of the ISC license; see accompanying file
9 * "COPYING" for details.
10 *
11 * "Clonk" is a registered trademark of Matthes Bender, used with permission.
12 * See accompanying file "TRADEMARK" for details.
13 *
14 * To redistribute this file separately, substitute the full license texts
15 * for the above references.
16 */
17
18// startup screen
19
20#include "C4GuiResource.h"
21#include <C4Include.h>
22#include <C4LoaderScreen.h>
23
24#include <C4LogBuf.h>
25#include <C4Log.h>
26#include <C4Game.h>
27#include <C4Random.h>
28#include <C4GroupSet.h>
29
30C4LoaderScreen::C4LoaderScreen() : TitleFont(Game.GraphicsResource.FontTitle), LogFont(Game.GraphicsResource.FontTiny)
31{}
32
33bool C4LoaderScreen::Init(const char *szLoaderSpec)
34{
35 // Determine loader specification
36 if (!szLoaderSpec || !szLoaderSpec[0])
37 szLoaderSpec = "Loader*";
38 char szLoaderSpecPng[128 + 1 + 4], szLoaderSpecBmp[128 + 1 + 4];
39 char szLoaderSpecJpg[128 + 1 + 4], szLoaderSpecJpeg[128 + 1 + 5];
40 SCopy(szSource: szLoaderSpec, sTarget: szLoaderSpecPng); DefaultExtension(szFileName: szLoaderSpecPng, szExtension: "png");
41 SCopy(szSource: szLoaderSpec, sTarget: szLoaderSpecBmp); DefaultExtension(szFileName: szLoaderSpecBmp, szExtension: "bmp");
42 SCopy(szSource: szLoaderSpec, sTarget: szLoaderSpecJpg); DefaultExtension(szFileName: szLoaderSpecJpg, szExtension: "jpg");
43 SCopy(szSource: szLoaderSpec, sTarget: szLoaderSpecJpeg); DefaultExtension(szFileName: szLoaderSpecJpeg, szExtension: "jpeg");
44 int iLoaders = 0;
45 C4Group *pGroup = nullptr, *pChosenGrp = nullptr;
46 char ChosenFilename[_MAX_PATH + 1];
47 // query groups of equal priority in set
48 while (pGroup = Game.GroupSet.FindGroup(C4GSCnt_Loaders, pAfter: pGroup, fSamePrio: true))
49 {
50 iLoaders += SeekLoaderScreens(rFromGrp&: *pGroup, szWildcard: szLoaderSpecPng, iLoaderCount: iLoaders, szDstName: ChosenFilename, ppDestGrp: &pChosenGrp);
51 iLoaders += SeekLoaderScreens(rFromGrp&: *pGroup, szWildcard: szLoaderSpecJpeg, iLoaderCount: iLoaders, szDstName: ChosenFilename, ppDestGrp: &pChosenGrp);
52 iLoaders += SeekLoaderScreens(rFromGrp&: *pGroup, szWildcard: szLoaderSpecJpg, iLoaderCount: iLoaders, szDstName: ChosenFilename, ppDestGrp: &pChosenGrp);
53 // lower the chance for any loader other than png
54 iLoaders *= 2;
55 iLoaders += SeekLoaderScreens(rFromGrp&: *pGroup, szWildcard: szLoaderSpecBmp, iLoaderCount: iLoaders, szDstName: ChosenFilename, ppDestGrp: &pChosenGrp);
56 }
57 // nothing found? seek in main gfx grp
58 C4Group GfxGrp;
59 if (!iLoaders)
60 {
61 // open it
62 GfxGrp.Close();
63 if (!GfxGrp.Open(szGroupName: Config.AtExePath(C4CFN_Graphics)))
64 {
65 LogFatal(id: C4ResStrTableKey::IDS_PRC_NOGFXFILE, C4CFN_Graphics, args: GfxGrp.GetError());
66 return false;
67 }
68 // seek for png-loaders
69 iLoaders = SeekLoaderScreens(rFromGrp&: GfxGrp, szWildcard: szLoaderSpecPng, iLoaderCount: iLoaders, szDstName: ChosenFilename, ppDestGrp: &pChosenGrp);
70 iLoaders += SeekLoaderScreens(rFromGrp&: GfxGrp, szWildcard: szLoaderSpecJpg, iLoaderCount: iLoaders, szDstName: ChosenFilename, ppDestGrp: &pChosenGrp);
71 iLoaders += SeekLoaderScreens(rFromGrp&: GfxGrp, szWildcard: szLoaderSpecJpeg, iLoaderCount: iLoaders, szDstName: ChosenFilename, ppDestGrp: &pChosenGrp);
72 iLoaders *= 2;
73 // seek for bmp-loaders
74 iLoaders += SeekLoaderScreens(rFromGrp&: GfxGrp, szWildcard: szLoaderSpecBmp, iLoaderCount: iLoaders, szDstName: ChosenFilename, ppDestGrp: &pChosenGrp);
75 // Still nothing found: fall back to general loader spec in main graphics group
76 if (!iLoaders)
77 {
78 iLoaders = SeekLoaderScreens(rFromGrp&: GfxGrp, szWildcard: "Loader*.png", iLoaderCount: 0, szDstName: ChosenFilename, ppDestGrp: &pChosenGrp);
79 iLoaders += SeekLoaderScreens(rFromGrp&: GfxGrp, szWildcard: "Loader*.jpg", iLoaderCount: iLoaders, szDstName: ChosenFilename, ppDestGrp: &pChosenGrp);
80 iLoaders += SeekLoaderScreens(rFromGrp&: GfxGrp, szWildcard: "Loader*.jpeg", iLoaderCount: iLoaders, szDstName: ChosenFilename, ppDestGrp: &pChosenGrp);
81 }
82 // Not even default loaders available? Fail.
83 if (!iLoaders)
84 {
85 LogFatalNTr(fmt: "No loaders found for loader specification: {}/{}/{}/{}", args: +szLoaderSpecPng, args: +szLoaderSpecBmp, args: +szLoaderSpecJpg, args: +szLoaderSpecJpeg);
86 return false;
87 }
88 }
89
90 // load loader
91 fctBackground.GetFace().SetBackground();
92 if (!fctBackground.Load(hGroup&: *pChosenGrp, szName: ChosenFilename, iWdt: C4FCT_Full, iHgt: C4FCT_Full, fOwnPal: true)) return false;
93
94 // init fonts
95 if (!Game.GraphicsResource.InitFonts())
96 return false;
97
98 // initial draw
99 C4Facet cgo;
100 cgo.Set(nsfc: Application.DDraw->lpPrimary, nx: 0, ny: 0, nwdt: Config.Graphics.ResX, nhgt: Config.Graphics.ResY);
101 Draw(cgo);
102
103 // done, success!
104 return true;
105}
106
107int C4LoaderScreen::SeekLoaderScreens(C4Group &rFromGrp, const char *szWildcard, int iLoaderCount, char *szDstName, C4Group **ppDestGrp)
108{
109 bool fFound;
110 int iLocalLoaders = 0;
111 char Filename[_MAX_PATH + 1];
112 for (fFound = rFromGrp.FindEntry(szWildCard: szWildcard, sFileName: Filename); fFound; fFound = rFromGrp.FindNextEntry(szWildCard: szWildcard, sFileName: Filename))
113 {
114 // loader found; choose it, if Daniel wants it that way
115 ++iLocalLoaders;
116 if (!SafeRandom(range: ++iLoaderCount))
117 {
118 // copy group and path
119 *ppDestGrp = &rFromGrp;
120 SCopy(szSource: Filename, sTarget: szDstName, _MAX_PATH);
121 }
122 }
123 return iLocalLoaders;
124}
125
126void C4LoaderScreen::Draw(C4Facet &cgo, int iProgress, C4LogBuffer *pLog, int Process)
127{
128 // cgo.X/Y is assumed 0 here...
129 // fixed positions for now
130 int iHIndent = 20;
131 int iVIndent = 20;
132 int iLogBoxHgt = 84;
133 int iLogBoxMargin = 2;
134 int iVMargin = 5;
135 int iProgressBarHgt = 15;
136 CStdFont &rLogBoxFont = LogFont, &rProgressBarFont = Game.GraphicsResource.FontRegular;
137 float fLogBoxFontZoom = 1.0f;
138 // Background (loader)
139 fctBackground.DrawFullScreen(cgo);
140 // draw scenario title
141 Application.DDraw->StringOut(szText: Game.Parameters.ScenarioTitle.getData(), rFont&: TitleFont, fZoom: 1.0f, sfcDest: cgo.Surface, iTx: cgo.Wdt - iHIndent, iTy: cgo.Hgt - iVIndent - iLogBoxHgt - iVMargin - iProgressBarHgt - iVMargin - TitleFont.GetLineHeight(), dwFCol: 0xdddddddd, byForm: ARight, fDoMarkup: true);
142 // draw progress bar
143 Application.DDraw->DrawBoxDw(sfcDest: cgo.Surface, iX1: iHIndent, iY1: cgo.Hgt - iVIndent - iLogBoxHgt - iVMargin - iProgressBarHgt, iX2: cgo.Wdt - iHIndent, iY2: cgo.Hgt - iVIndent - iLogBoxHgt - iVMargin, dwClr: 0x4f000000);
144 int iProgressBarWdt = cgo.Wdt - iHIndent * 2 - 2;
145 if (C4GUI::IsGUIValid())
146 {
147 C4GUI::GetRes()->fctProgressBar.DrawX(sfcTarget: cgo.Surface, iX: iHIndent + 1, iY: cgo.Hgt - iVIndent - iLogBoxHgt - iVMargin - iProgressBarHgt + 1, iWdt: iProgressBarWdt * iProgress / 100, iHgt: iProgressBarHgt - 2);
148 }
149 else
150 {
151 Application.DDraw->DrawBoxDw(sfcDest: cgo.Surface, iX1: iHIndent + 1, iY1: cgo.Hgt - iVIndent - iLogBoxHgt - iVMargin - iProgressBarHgt + 1, iX2: iHIndent + 1 + iProgressBarWdt * iProgress / 100, iY2: cgo.Hgt - iVIndent - iLogBoxHgt - iVMargin - 1, dwClr: 0x4fff0000);
152 }
153 Application.DDraw->StringOut(szText: (std::to_string(val: iProgress) + '%').c_str(), rFont&: rProgressBarFont, fZoom: 1.0f, sfcDest: cgo.Surface, iTx: cgo.Wdt / 2, iTy: cgo.Hgt - iVIndent - iLogBoxHgt - iVMargin - rProgressBarFont.GetLineHeight() / 2 - iProgressBarHgt / 2, dwFCol: 0xffffffff, byForm: ACenter, fDoMarkup: true);
154 // draw log box
155 if (pLog)
156 {
157 Application.DDraw->DrawBoxDw(sfcDest: cgo.Surface, iX1: iHIndent, iY1: cgo.Hgt - iVIndent - iLogBoxHgt, iX2: cgo.Wdt - iHIndent, iY2: cgo.Hgt - iVIndent, dwClr: 0x7f000000);
158 int iLineHgt = int(fLogBoxFontZoom * rLogBoxFont.GetLineHeight()); if (!iLineHgt) iLineHgt = 5;
159 int iLinesVisible = (iLogBoxHgt - 2 * iLogBoxMargin) / iLineHgt;
160 int iX = iHIndent + iLogBoxMargin;
161 int iY = cgo.Hgt - iVIndent - iLogBoxHgt + iLogBoxMargin;
162 int32_t w, h;
163 for (int i = -iLinesVisible; i < 0; ++i)
164 {
165 const char *szLine = pLog->GetLine(iLineIndex: i, ppFont: nullptr, pdwClr: nullptr, pNewParagraph: nullptr);
166 if (!szLine || !*szLine) continue;
167 rLogBoxFont.GetTextExtent(szText: szLine, rsx&: w, rsy&: h, fCheckMarkup: true);
168 lpDDraw->TextOut(szText: szLine, rFont&: rLogBoxFont, fZoom: fLogBoxFontZoom, sfcDest: cgo.Surface, iTx: iX, iTy: iY);
169 iY += h;
170 }
171 // append process text
172 if (Process)
173 {
174 iY -= h; iX += w;
175 lpDDraw->TextOut(szText: (std::to_string(val: Process) + "%").c_str(), rFont&: rLogBoxFont, fZoom: fLogBoxFontZoom, sfcDest: cgo.Surface, iTx: iX, iTy: iY);
176 }
177 }
178}
179