OpenClonk
C4StartupScenSelDlg.cpp
Go to the documentation of this file.
1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 2005-2009, RedWolf Design GmbH, http://www.clonk.de/
5  * Copyright (c) 2009-2016, The OpenClonk Team and contributors
6  *
7  * Distributed under the terms of the ISC license; see accompanying file
8  * "COPYING" for details.
9  *
10  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
11  * See accompanying file "TRADEMARK" for details.
12  *
13  * To redistribute this file separately, substitute the full license texts
14  * for the above references.
15  */
16 // Startup screen for non-parameterized engine start: Scenario selection dialog
17 
18 #include "C4Include.h"
20 
22 #include "c4group/C4Components.h"
23 #include "game/C4Application.h"
24 #include "graphics/C4Draw.h"
26 #include "gui/C4FileSelDlg.h"
27 #include "gui/C4GameDialogs.h"
28 #include "gui/C4GameOptions.h"
29 #include "gui/C4MouseControl.h"
30 #include "gui/C4StartupMainDlg.h"
31 #include "gui/C4StartupNetDlg.h"
33 
34 // singleton
36 
37 
38 // ----------------------------------------------------------------
39 // Map folder data
40 
42 {
43  pComp->Value(mkNamingAdapt( sFilename, "File" , StdStrBuf()));
44  pComp->Value(mkNamingAdapt( sBaseImage, "BaseImage" , StdStrBuf()));
45  pComp->Value(mkNamingAdapt( sOverlayImage, "OverlayImage" , StdStrBuf()));
46  pComp->Value(mkNamingAdapt( rcOverlayPos, "Area", C4Rect()));
47  pComp->Value(mkNamingAdapt( sTitle, "Title" , StdStrBuf()));
48  pComp->Value(mkNamingAdapt( iTitleFontSize, "TitleFontSize", 20));
49  pComp->Value(mkNamingAdapt( dwTitleInactClr, "TitleColorInactive", 0x7fffffffu));
50  pComp->Value(mkNamingAdapt( dwTitleActClr, "TitleColorActive", 0x0fffffffu));
51  pComp->Value(mkNamingAdapt( iTitleOffX, "TitleOffX", 0));
52  pComp->Value(mkNamingAdapt( iTitleOffY, "TitleOffY", 0));
53  pComp->Value(mkNamingAdapt( byTitleAlign, "TitleAlign", ACenter));
54  pComp->Value(mkNamingAdapt( fTitleBookFont, "TitleUseBookFont", true));
55  pComp->Value(mkNamingAdapt( fImgDump, "ImageDump", false)); // developer help
56 }
57 
59 {
60  pComp->Value(mkNamingAdapt( sPassword, "Access", StdStrBuf()));
61  pComp->Value(mkNamingAdapt( sOverlayImage, "OverlayImage" , StdStrBuf()));
62  pComp->Value(mkNamingAdapt( rcOverlayPos, "Area", C4Rect()));
63 }
64 
65 C4MapFolderData::MapPic::MapPic(const FLOAT_RECT &rcfBounds, const C4Facet &rfct) : C4GUI::Picture(C4Rect(rcfBounds), false), rcfBounds(rcfBounds)
66 {
67  // ctor
68  SetFacet(rfct);
69  SetToolTip(LoadResStr("IDS_MSG_MAP_DESC"));
70 }
71 
72 void C4MapFolderData::MapPic::MouseInput(C4GUI::CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam)
73 {
74  typedef C4GUI::Picture Parent;
75  Parent::MouseInput(rMouse, iButton, iX, iY, dwKeyParam);
76  // input: mouse movement or buttons - deselect everything if clicked
78  {
80  }
81 }
82 
83 void C4MapFolderData::MapPic::DrawElement(C4TargetFacet &cgo)
84 {
85  // get drawing bounds
86  float x0 = rcfBounds.left + cgo.TargetX, y0 = rcfBounds.top + cgo.TargetY;
87  // draw the image
88  GetFacet().DrawXFloat(cgo.Surface, x0, y0, rcfBounds.right-rcfBounds.left, rcfBounds.bottom-rcfBounds.top);
89 }
90 
92 {
93  fCoordinatesAdjusted = false;
94  fctBackgroundPicture.Clear();
95  pScenarioFolder = nullptr;
96  pSelectedEntry = nullptr;
97  pSelectionInfoBox = nullptr;
98  rcScenInfoArea.Set(0,0,0,0);
99  MinResX=MinResY=0;
100  fUseFullscreenMap=false;
101  int i;
102  for (i=0; i<iScenCount; ++i) delete ppScenList[i];
103  iScenCount=0;
104  delete [] ppScenList; ppScenList=nullptr;
105  for (i=0; i<iAccessGfxCount; ++i) delete ppAccessGfxList[i];
106  iAccessGfxCount=0;
107  delete [] ppAccessGfxList; ppAccessGfxList=nullptr;
108  pMainDlg = nullptr;
109 }
110 
112 {
113  // clear previous
114  Clear();
115  // load localization info
116  C4LangStringTable LangTable;
117  bool fHasLangTable = C4Language::LoadComponentHost(&LangTable, hGroup, C4CFN_ScriptStringTbl, Config.General.LanguageEx);
118  // load core data
119  StdStrBuf Buf;
120  if (!hGroup.LoadEntryString(C4CFN_MapFolderData, &Buf)) return false;
121  if (fHasLangTable) LangTable.ReplaceStrings(Buf);
122  if (!CompileFromBuf_LogWarn<StdCompilerINIRead>(mkNamingAdapt(*this, "FolderMap"), Buf, C4CFN_MapFolderData)) return false;
123  // check resolution requirement
124  if (MinResX && MinResX>C4GUI::GetScreenWdt()) return false;
125  if (MinResY && MinResY>C4GUI::GetScreenHgt()) return false;
126  // load images
127  if (!fctBackgroundPicture.Load(hGroup, C4CFN_MapFolderBG, C4FCT_Full, C4FCT_Full, false, 0))
128  {
129  DebugLogF(R"(C4MapFolderData::Load(%s): Could not load background graphic "%s")", hGroup.GetName(), C4CFN_MapFolderBG);
130  return false;
131  }
132  int i;
133  for (i=0; i<iScenCount; ++i)
134  {
135  // init scenario entry stuff
136  Scenario *pScen = ppScenList[i];
137  pScen->pScenEntry = pScenLoaderFolder->FindEntryByName(pScen->sFilename.getData());
138  pScen->pBtn = nullptr;
139  pScen->sTitle.Replace("TITLE", pScen->pScenEntry ? pScen->pScenEntry->GetName().getData() : "<c ff0000>ERROR</c>" /* scenario not loaded; title cannot be referenced */);
140  // developer image dump
141  if (pScen->fImgDump)
142  {
143  C4FacetSurface fctDump; bool fSuccess=false;
144  if (fctDump.Create(pScen->rcOverlayPos.Wdt, pScen->rcOverlayPos.Hgt, C4FCT_Full, C4FCT_Full))
145  {
146  pDraw->Blit(fctBackgroundPicture.Surface,
147  (float) pScen->rcOverlayPos.x, (float) pScen->rcOverlayPos.y,
148  (float) pScen->rcOverlayPos.Wdt, (float) pScen->rcOverlayPos.Hgt,
149  fctDump.Surface,
150  0, 0,
151  fctDump.Wdt, fctDump.Hgt);
152  fSuccess = fctDump.Surface->SavePNG(pScen->sBaseImage.getData(), true, false, false);
153  }
154  if (!fSuccess)
155  DebugLogF(R"(C4MapFolderData::Load(%s): Could not dump graphic "%s")", hGroup.GetName(), pScen->sBaseImage.getData());
156  continue;
157  }
158  // load images
159  if (pScen->sBaseImage.getLength()>0) if (!pScen->fctBase.Load(hGroup, pScen->sBaseImage.getData(), C4FCT_Full, C4FCT_Full, false, 0))
160  {
161  DebugLogF(R"(C4MapFolderData::Load(%s): Could not load base graphic "%s")", hGroup.GetName(), pScen->sBaseImage.getData());
162  return false;
163  }
164  if (pScen->sOverlayImage.getLength()>0) if (!pScen->fctOverlay.Load(hGroup, pScen->sOverlayImage.getData(), C4FCT_Full, C4FCT_Full, false, 0))
165  {
166  DebugLogF(R"(C4MapFolderData::Load(%s): Could not load graphic "%s")", hGroup.GetName(), pScen->sOverlayImage.getData());
167  return false;
168  }
169  }
170  for (i=0; i<iAccessGfxCount; ++i)
171  {
172  AccessGfx *pGfx= ppAccessGfxList[i];
173  if (pGfx->sOverlayImage.getLength()>0) if (!pGfx->fctOverlay.Load(hGroup, pGfx->sOverlayImage.getData(), C4FCT_Full, C4FCT_Full, false, 0))
174  {
175  DebugLogF(R"(C4MapFolderData::Load(%s): Could not load graphic "%s")", hGroup.GetName(), pGfx->sOverlayImage.getData());
176  return false;
177  }
178  }
179  // all loaded
180  pScenarioFolder = pScenLoaderFolder;
181  return true;
182 }
183 
185 {
186  // core values
187  pComp->Value(mkNamingAdapt( rcScenInfoArea, "ScenInfoArea", C4Rect(0,0,0,0)));
188  pComp->Value(mkNamingAdapt( MinResX, "MinResX", 0));
189  pComp->Value(mkNamingAdapt( MinResY, "MinResY", 0));
190  pComp->Value(mkNamingAdapt( fUseFullscreenMap,"FullscreenBG", false));
191  // compile scenario list
192  int32_t iOldScenCount = iScenCount;
193  pComp->Value(mkNamingCountAdapt(iScenCount, "Scenario"));
194  if (pComp->isDeserializer())
195  {
196  while (iOldScenCount--) delete ppScenList[iOldScenCount];
197  delete [] ppScenList;
198  if (iScenCount)
199  {
200  ppScenList = new Scenario *[iScenCount];
201  memset(ppScenList, 0, sizeof(Scenario *)*iScenCount);
202  }
203  else
204  ppScenList = nullptr;
205  }
206  if (iScenCount)
207  {
208  mkPtrAdaptNoNull(*ppScenList);
209  pComp->Value(mkNamingAdapt(mkArrayAdaptMap(ppScenList, iScenCount, mkPtrAdaptNoNull<Scenario>), "Scenario"));
210  }
211  // compile access gfx list
212  int32_t iOldAccesGfxCount = iAccessGfxCount;
213  pComp->Value(mkNamingCountAdapt(iAccessGfxCount, "AccessGfx"));
214  if (pComp->isDeserializer())
215  {
216  while (iOldAccesGfxCount--) delete ppAccessGfxList[iOldAccesGfxCount];
217  delete [] ppAccessGfxList;
218  if (iAccessGfxCount)
219  {
220  ppAccessGfxList = new AccessGfx *[iAccessGfxCount];
221  memset(ppAccessGfxList, 0, sizeof(AccessGfx *)*iAccessGfxCount);
222  }
223  else
224  ppAccessGfxList = nullptr;
225  }
226  if (iAccessGfxCount)
227  {
228  mkPtrAdaptNoNull(*ppAccessGfxList);
229  pComp->Value(mkNamingAdapt(mkArrayAdaptMap(ppAccessGfxList, iAccessGfxCount, mkPtrAdaptNoNull<AccessGfx>), "AccessGfx"));
230  }
231 }
232 
233 void C4MapFolderData::ConvertFacet2ScreenCoord(const C4Rect &rc, FLOAT_RECT *pfrc, float fBGZoomX, float fBGZoomY, int iOffX, int iOffY)
234 {
235  pfrc->left = (fBGZoomX * rc.x) + iOffX;
236  pfrc->top = (fBGZoomY * rc.y) + iOffY;
237  pfrc->right = pfrc->left + (fBGZoomX * rc.Wdt);
238  pfrc->bottom = pfrc->top + (fBGZoomY * rc.Hgt);
239 }
240 
241 void C4MapFolderData::ConvertFacet2ScreenCoord(int32_t *piValue, float fBGZoom, int iOff)
242 {
243  *piValue = int32_t(floorf(fBGZoom * *piValue + 0.5f)) + iOff;
244 }
245 
246 void C4MapFolderData::ConvertFacet2ScreenCoord(C4Rect &rcMapArea, bool fAspect)
247 {
248  if (!fctBackgroundPicture.Wdt || !fctBackgroundPicture.Hgt) return; // invalid BG - should not happen
249  // get zoom of background image
250  float fBGZoomX = 1.0f, fBGZoomY = 1.0f; int iOffX=0, iOffY=0;
251  if (fAspect)
252  {
253  if (fctBackgroundPicture.Wdt * rcMapArea.Hgt > rcMapArea.Wdt * fctBackgroundPicture.Hgt)
254  {
255  // background image is limited by width
256  fBGZoomX = fBGZoomY = (float) rcMapArea.Wdt / fctBackgroundPicture.Wdt;
257  iOffY = std::max<int>(0, (int)(rcMapArea.Hgt - (fBGZoomX * fctBackgroundPicture.Hgt)))/2;
258  }
259  else
260  {
261  // background image is limited by height
262  fBGZoomX = fBGZoomY = (float) rcMapArea.Hgt / fctBackgroundPicture.Hgt;
263  iOffX = std::max<int>(0, (int)(rcMapArea.Wdt - (fBGZoomY * fctBackgroundPicture.Wdt)))/2;
264  }
265  }
266  else
267  {
268  // do not keep aspect: Independant X and Y zoom
269  fBGZoomX = (float) rcMapArea.Wdt / fctBackgroundPicture.Wdt;;
270  fBGZoomY = (float) rcMapArea.Hgt / fctBackgroundPicture.Hgt;;
271  }
272  iOffX -= rcMapArea.x; iOffY -= rcMapArea.y;
273  C4Rect rcBG; rcBG.Set(0,0,fctBackgroundPicture.Wdt, fctBackgroundPicture.Hgt);
274  ConvertFacet2ScreenCoord(rcBG, &rcfBG, fBGZoomX, fBGZoomY, iOffX, iOffY);
275  // default for scenario info area: 1/3rd of right area
276  if (!rcScenInfoArea.Wdt)
277  rcScenInfoArea.Set((int32_t)(fctBackgroundPicture.Wdt*2/3), (int32_t)(fctBackgroundPicture.Hgt/16), (int32_t)(fctBackgroundPicture.Wdt/3), (int32_t)(fctBackgroundPicture.Hgt*7/8));
278  // assume all facet coordinates are referring to background image zoom; convert them to screen coordinates by applying zoom and offset
279  FLOAT_RECT rcfScenInfoArea;
280  ConvertFacet2ScreenCoord(rcScenInfoArea, &rcfScenInfoArea, fBGZoomX, fBGZoomY, iOffX, iOffY);
281  rcScenInfoArea.x = (int32_t) rcfScenInfoArea.left; rcScenInfoArea.y = (int32_t) rcfScenInfoArea.top;
282  rcScenInfoArea.Wdt = (int32_t) (rcfScenInfoArea.right - rcfScenInfoArea.left);
283  rcScenInfoArea.Hgt = (int32_t) (rcfScenInfoArea.bottom - rcfScenInfoArea.top);
284  int i;
285  for (i=0; i<iScenCount; ++i)
286  {
287  Scenario *pScen = ppScenList[i];
288  ConvertFacet2ScreenCoord(pScen->rcOverlayPos, &(pScen->rcfOverlayPos), fBGZoomX, fBGZoomY, iOffX, iOffY);
289  // title sizes
290  ConvertFacet2ScreenCoord(&(pScen->iTitleFontSize), fBGZoomY, 0);
291  // title position: Relative to title rect; so do not add offset here
292  ConvertFacet2ScreenCoord(&(pScen->iTitleOffX), fBGZoomX, 0);
293  ConvertFacet2ScreenCoord(&(pScen->iTitleOffY), fBGZoomY, 0);
294  }
295  for (i=0; i<iAccessGfxCount; ++i) ConvertFacet2ScreenCoord(ppAccessGfxList[i]->rcOverlayPos, &(ppAccessGfxList[i]->rcfOverlayPos), fBGZoomX, fBGZoomY, iOffX, iOffY);
296  // done
297  fCoordinatesAdjusted = true;
298 }
299 
301 {
302  this->pMainDlg = pMainDlg;
303  // convert all coordinates to match the container sizes
304  // do this only once; assume container won't change between loads
305  if (!fCoordinatesAdjusted)
306  {
307  if (!fUseFullscreenMap)
308  ConvertFacet2ScreenCoord(rContainer.GetClientRect(), true);
309  else
310  {
311  C4Rect rcMapRect = pMainDlg->GetBounds();
312  rContainer.ClientPos2ScreenPos(rcMapRect.x, rcMapRect.y);
313  ConvertFacet2ScreenCoord(rcMapRect, false);
314  }
315  }
316  // empty any previous stuff in container
317  while (rContainer.GetFirst()) delete rContainer.GetFirst();
318  // create background image
319  if (!fUseFullscreenMap)
320  rContainer.AddElement(new MapPic(rcfBG, fctBackgroundPicture));
321  else
322  {
323  pMainDlg->SetBackground(&fctBackgroundPicture);
324  }
325  // create mission access overlays
326  int i;
327  for (i=0; i<iAccessGfxCount; ++i)
328  {
329  AccessGfx *pGfx = ppAccessGfxList[i];
330  const char *szPassword = pGfx->sPassword.getData();
331  if (!szPassword || !*szPassword || SIsModule(Config.General.MissionAccess, szPassword))
332  {
333  // ACCESS TO GFX GRANTED: draw it
334  rContainer.AddElement(new MapPic(pGfx->rcfOverlayPos, pGfx->fctOverlay));
335  }
336  }
337  // create buttons for scenarios
338  C4GUI::Button *pBtnFirst = nullptr;
339  for (i=0; i<iScenCount; ++i)
340  {
341  Scenario *pScen = ppScenList[i];
342  if (pScen->pScenEntry && !pScen->pScenEntry->HasMissionAccess())
343  {
344  // no access to this scenario: Do not create a button at all; not even base image. The scenario is "invisible".
345  }
346  else
347  {
349  (pScen->fctBase, pScen->fctOverlay, pScen->rcfOverlayPos, 0, pMainDlg, &C4StartupScenSelDlg::OnButtonScenario);
350  ppScenList[i]->pBtn = pBtn;
351  if (pScen->pScenEntry)
352  pBtn->SetToolTip(FormatString(LoadResStr("IDS_MSG_MAP_STARTSCEN"), pScen->pScenEntry->GetName().getData()).getData());
353  if (pScen->sTitle.getLength()>0)
354  {
355  pBtn->SetText(pScen->sTitle.getData());
356  pBtn->SetTextColors(pScen->dwTitleInactClr, pScen->dwTitleActClr);
357  pBtn->SetTextPos(pScen->iTitleOffX, pScen->iTitleOffY, pScen->byTitleAlign);
358  CStdFont *pUseFont; float fFontZoom=1.0f;
359  if (pScen->fTitleBookFont)
360  pUseFont = &(C4Startup::Get()->Graphics.GetBlackFontByHeight(pScen->iTitleFontSize, &fFontZoom));
361  else
362  pUseFont = &(::GraphicsResource.GetFontByHeight(pScen->iTitleFontSize, &fFontZoom));
363  if (Inside<float>(fFontZoom, 0.8f, 1.25f)) fFontZoom = 1.0f; // some tolerance for font zoom
364  pBtn->SetTextFont(pUseFont, fFontZoom);
365  }
366  rContainer.AddElement(pBtn);
367  if (!pBtnFirst) pBtnFirst = pBtn;
368  }
369  }
370  // create scenario info listbox
371  pSelectionInfoBox = new C4GUI::TextWindow(rcScenInfoArea,
373  C4StartupScenSel_TitlePicturePadding, 100, 4096, nullptr, true, &C4Startup::Get()->Graphics.fctScenSelTitleOverlay, C4StartupScenSel_TitleOverlayMargin);
374  pSelectionInfoBox->SetDecoration(false, false, &C4Startup::Get()->Graphics.sfctBookScroll, true);
375  rContainer.AddElement(pSelectionInfoBox);
376 }
377 
379 {
380  // get associated scenario entry
381  int i;
382  for (i=0; i<iScenCount; ++i)
383  if (pEl == ppScenList[i]->pBtn)
384  break;
385  if (i == iScenCount) return;
386  // select the associated entry
387  pSelectedEntry = ppScenList[i]->pScenEntry;
388 }
389 
391 {
392  pSelectedEntry = nullptr;
393 }
394 
395 
396 
397 // ----------------------------------------------------------------
398 // Scenario list loader
399 
400 // ------------------------------------
401 // Entry
402 
403 C4ScenarioListLoader::Entry::Entry(class C4ScenarioListLoader *pLoader, Folder *pParent) : pLoader(pLoader), pNext(nullptr), pParent(pParent), fBaseLoaded(false), fExLoaded(false)
404 {
405  // ctor: Put into parent tree node
406  if (pParent)
407  {
408  pNext = pParent->pFirst;
409  pParent->pFirst = this;
410  }
411  iIconIndex = -1;
412  iDifficulty = 0;
413  iFolderIndex = 0;
414 }
415 
417 {
418  // dtor: unlink from parent list (MUST be in there)
419  if (pParent)
420  {
421  Entry **ppCheck = &(pParent->pFirst);
422  while (*ppCheck != this)
423  {
424  ppCheck = &(*ppCheck)->pNext;
425  }
426  *ppCheck = pNext;
427  }
428 }
429 
430 
431 bool C4ScenarioListLoader::Entry::Load(C4Group *pFromGrp, const StdStrBuf *psFilename, bool fLoadEx)
432 {
433  // nothing to do if already loaded
434  if (fBaseLoaded && (fExLoaded || !fLoadEx)) return true;
435  C4Group Group;
436  // group specified: Load as child
437  if (pFromGrp)
438  {
439  assert(psFilename);
440  if (!Group.OpenAsChild(pFromGrp, psFilename->getData())) return false;
441  // set FN by complete entry name
442  this->sFilename.Take(Group.GetFullName());
443  }
444  else
445  {
446  // set FN by complete entry name
447  if (psFilename) this->sFilename.Copy(*psFilename);
448  // no parent group: Direct load from filename
449  if (!Group.Open(sFilename.getData())) return false;
450  }
451  // okay; load standard stuff from group
452  bool fNameLoaded=false, fIconLoaded=false;
453  if (fBaseLoaded)
454  {
455  fNameLoaded = fIconLoaded = true;
456  }
457  else
458  {
459  // Set default name as filename without extension
460  sName.Copy(GetFilename(sFilename.getData()));
461  char *szBuf = sName.GrabPointer();
462  RemoveExtension(szBuf);
463  sName.Take(szBuf);
464  // load entry specific stuff that's in the front of the group
465  if (!LoadCustomPre(Group))
466  return false;
467  // Load entry name
468  C4ComponentHost DefNames;
470  if (DefNames.GetLanguageString(Config.General.LanguageEx, sName))
471  fNameLoaded = true;
472  // load entry icon
473  if (Group.FindEntry(C4CFN_IconPNG) && fctIcon.Load(Group, C4CFN_IconPNG, C4FCT_Full, C4FCT_Full, false, 0))
474  fIconLoaded = true;
475  else
476  {
477  C4FacetSurface fctTemp;
478  if (Group.FindEntry(C4CFN_ScenarioIcon) && fctTemp.Load(Group, C4CFN_ScenarioIcon, C4FCT_Full, C4FCT_Full, true, 0))
479  {
480  // old style icon: Blit it on a pieace of paper
481  fctTemp.Surface->Lock();
482  for (int y=0; y<fctTemp.Hgt; ++y)
483  for (int x=0; x<fctTemp.Wdt; ++x)
484  {
485  uint32_t dwPix = fctTemp.Surface->GetPixDw(x,y, false);
486  // transparency has some tolerance...
487  if (Inside<uint8_t>(dwPix & 0xff, 0xb8, 0xff))
488  if (Inside<uint8_t>((dwPix>>0x08) & 0xff, 0x00, 0x0f))
489  if (Inside<uint8_t>((dwPix>>0x10) & 0xff, 0xb8, 0xff))
490  fctTemp.Surface->SetPixDw(x,y,0x00ffffff);
491  }
492  fctTemp.Surface->Unlock();
493  int iIconSize = C4Startup::Get()->Graphics.fctScenSelIcons.Hgt;
494  fctIcon.Create(iIconSize, iIconSize, C4FCT_Full, C4FCT_Full);
496  fctTemp.Draw(fctIcon.Surface, (fctIcon.Wdt-fctTemp.Wdt)/2, (fctIcon.Hgt-fctTemp.Hgt)/2);
497  fctTemp.Clear();
498  fIconLoaded = true;
499  }
500  }
501  // load any entryx-type-specific custom data (e.g. fallbacks for scenario title, and icon)
502  if (!LoadCustom(Group, fNameLoaded, fIconLoaded)) return false;
503  fBaseLoaded = true;
504  }
505  // load extended stuff: title picture
506  if (fLoadEx && !fExLoaded)
507  {
508  // load desc
509  C4ComponentHost DefDesc;
511  {
512  sDesc.Copy(DefDesc.GetData());
513  }
514  // load title
515  fctTitle.Load(Group, C4CFN_ScenarioTitle,C4FCT_Full,C4FCT_Full,false,true);
516  fExLoaded = true;
517  // load version
518  Group.LoadEntryString(C4CFN_Version, &sVersion);
519  }
520  // done, success
521  return true;
522 }
523 
524 // helper func: Recursive check whether a directory contains a .ocs or .ocf file
525 bool DirContainsScenarios(const char *szDir)
526 {
527  // Ignore object and group folders to avoid descending e.g. deep into unpacked Objects.ocd
529  {
530  return false;
531  }
532  // create iterator on free store to avoid stack overflow with deeply recursed folders
533  DirectoryIterator *pIter = new DirectoryIterator(szDir);
534  const char *szChildFilename;
535  for (; (szChildFilename = **pIter); ++*pIter)
536  {
537  // Ignore directory navigation entries and CVS folders
538  if (C4Group_TestIgnore(szChildFilename)) continue;
539  if (WildcardMatch(C4CFN_ScenarioFiles, szChildFilename) || WildcardMatch(C4CFN_FolderFiles, szChildFilename)) break;
540  if (DirectoryExists(szChildFilename))
541  if (DirContainsScenarios(szChildFilename))
542  break;
543  }
544  delete pIter;
545  // return true if loop was broken, in which case a matching entry was found
546  return !!szChildFilename;
547 }
548 
550 {
551  // determine entry type by file type
552  const char *szFilename = sFilename.getData();
553  if (!szFilename || !*szFilename) return nullptr;
554  if (WildcardMatch(C4CFN_ScenarioFiles, sFilename.getData())) return new Scenario(pLoader, pParent);
555  if (WildcardMatch(C4CFN_FolderFiles, sFilename.getData())) return new SubFolder(pLoader, pParent);
556  // regular, open folder (C4Group-packed folders without extensions are not regarded, because they could contain anything!)
557  const char *szExt = GetExtension(szFilename);
558  if ((!szExt || !*szExt) && DirectoryExists(sFilename.getData()))
559  {
560  // do not open folders in the mod directory (and the dir itself - thus match minus the separator),
561  // as contained files will be discovered anyway
562  const char * modsDirectoryPrefix = Config.General.ModsDataPath;
563  if (std::strncmp(szFilename, modsDirectoryPrefix, std::strlen(modsDirectoryPrefix) - 1) == 0)
564  return nullptr;
565  // open folders only if they contain a scenario or folder
566  if (DirContainsScenarios(szFilename))
567  return new RegularFolder(pLoader, pParent);
568  }
569  // type not recognized
570  return nullptr;
571 }
572 
573 bool C4ScenarioListLoader::Entry::RenameTo(const char *szNewName)
574 {
575  // change name+filename
576  // some name sanity validation
577  if (!szNewName || !*szNewName) return false;
578  if (SEqual(szNewName, sName.getData())) return true;
579  char fn[_MAX_PATH_LEN];
580  SCopy(szNewName, fn, _MAX_PATH);
581  // generate new file name
583  if (!*fn) return false;
584  const char *szExt = GetDefaultExtension();
585  if (szExt) { SAppend(".", fn, _MAX_PATH); SAppend(szExt, fn, _MAX_PATH); }
586  char fullfn[_MAX_PATH_LEN];
587  SCopy(sFilename.getData(), fullfn, _MAX_PATH);
588  char *fullfn_fn = GetFilename(fullfn);
589  SCopy(fn, fullfn_fn, _MAX_PATH - (fullfn_fn - fullfn));
590  StdCopyStrBuf strErr(LoadResStr("IDS_FAIL_RENAME"));
591  // check if a rename is due
592  if (!ItemIdentical(sFilename.getData(), fullfn))
593  {
594  // check for duplicate filename
595  if (ItemExists(fullfn))
596  {
597  StdStrBuf sMsg; sMsg.Format(LoadResStr("IDS_ERR_FILEEXISTS"), fullfn);
599  return false;
600  }
601  // OK; then rename
602  if (!C4Group_MoveItem(sFilename.getData(), fullfn, true))
603  {
604  StdStrBuf sMsg; sMsg.Format(LoadResStr("IDS_ERR_RENAMEFILE"), sFilename.getData(), fullfn);
606  return false;
607  }
608  sFilename.Copy(fullfn);
609  }
610  // update real name in group, if this is a group
611  if (C4Group_IsGroup(fullfn))
612  {
613  C4Group Grp;
614  if (!Grp.Open(fullfn))
615  {
616  StdStrBuf sMsg; sMsg.Format(LoadResStr("IDS_ERR_OPENFILE"), sFilename.getData(), Grp.GetError());
618  return false;
619  }
620  if (!Grp.Delete(C4CFN_Title))
621  {
622  StdStrBuf sMsg; sMsg.Format(LoadResStr("IDS_ERR_DELOLDTITLE"), sFilename.getData(), Grp.GetError());
624  return false;
625  }
626  if (!SetTitleInGroup(Grp, szNewName)) return false;
627  if (!Grp.Close())
628  {
629  StdStrBuf sMsg; sMsg.Format(LoadResStr("IDS_ERR_WRITENEWTITLE"), sFilename.getData(), Grp.GetError());
631  return false;
632  }
633  }
634  // update title
635  sName.Copy(szNewName);
636  // done
637  return true;
638 }
639 
640 bool C4ScenarioListLoader::Entry::SetTitleInGroup(C4Group &rGrp, const char *szNewTitle)
641 {
642  // default for group files: Create a title text file and set the title in there
643  // no title needed if filename is sufficient - except for scenarios, where a Scenario.txt could overwrite the title
644  if (!IsScenario())
645  {
646  StdStrBuf sNameByFile; sNameByFile.Copy(GetFilename(sFilename.getData()));
647  char *szBuf = sNameByFile.GrabPointer();
648  RemoveExtension(szBuf);
649  sNameByFile.Take(szBuf);
650  if (SEqual(szNewTitle, sNameByFile.getData())) return true;
651  }
652  // okay, make a title
653  StdStrBuf sTitle; sTitle.Format("%s:%s", Config.General.Language, szNewTitle);
654  if (!rGrp.Add(C4CFN_WriteTitle, sTitle, false, true))
655  {
656  StdStrBuf sMsg; sMsg.Format(LoadResStr("IDS_ERR_ERRORADDINGNEWTITLEFORFIL"), sFilename.getData(), rGrp.GetError());
658  return false;
659  }
660  return true;
661 }
662 
663 
664 // ------------------------------------
665 // Scenario
666 
668 {
669  // load scenario core first
670  StdStrBuf sFileContents;
671  if (!rGrp.LoadEntryString(C4CFN_ScenarioCore, &sFileContents)) return false;
672  if (!CompileFromBuf_LogWarn<StdCompilerINIRead>(mkParAdapt(C4S, false), sFileContents, (rGrp.GetFullName() + DirSep C4CFN_ScenarioCore).getData()))
673  return false;
674  // Mission access
675  fNoMissionAccess = (!C4S.Head.MissionAccess.empty() && !SIsModule(Config.General.MissionAccess, C4S.Head.MissionAccess.c_str()));
676  // Localized parameter definitions. needed for achievements and parameter input boxes.
677  // Only show them for "real" scenarios
678  if (!C4S.Head.SaveGame && !C4S.Head.Replay)
679  {
680  // Skipping ahead in regular reading list, so keep other entries in memory
683  C4LangStringTable ScenarioLangStringTable;
685  ParameterDefs.Load(rGrp, &ScenarioLangStringTable);
686  // achievement images: Loaded from this entry and parent folder
687  nAchievements = 0;
688  const C4ScenarioParameterDefs *deflists[] = { pParent ? pParent->GetAchievementDefs() : nullptr, &ParameterDefs };
689  for (auto deflist : deflists)
690  {
691  if (!deflist) continue;
692  const C4ScenarioParameterDef *def; size_t idx=0;
693  while ((def = deflist->GetParameterDefByIndex(idx++)))
694  {
695  if (def->IsAchievement())
696  {
697  int32_t val = pLoader->GetAchievements().GetValueByID(C4ScenarioParameters::AddFilename2ID(rGrp.GetFullName().getData(), def->GetID()).getData(), def->GetDefault());
698  if (val)
699  {
700  // player has this achievement - find graphics for it
701  const char *achievement_gfx = def->GetAchievement();
702  StdStrBuf sAchievementFilename(C4CFN_Achievements);
703  sAchievementFilename.Replace("*", achievement_gfx);
704  // look in scenario
705  if (!fctAchievements[nAchievements].Load(rGrp, sAchievementFilename.getData(), C4FCT_Height, C4FCT_Full, false, true))
706  {
707  // look in parent folder
708  const C4FacetSurface *fct = nullptr;
709  const C4AchievementGraphics *parent_achv_gfx;
710  if (pParent && (parent_achv_gfx = pParent->GetAchievementGfx())) fct = parent_achv_gfx->FindByName(achievement_gfx);
711  // look in main gfx group file
712  if (!fct) fct = ::GraphicsResource.Achievements.FindByName(achievement_gfx);
713  if (!fct) continue; // achievement graphics not found :(
714  fctAchievements[nAchievements].Set((const C4Facet &)*fct);
715  }
716  // section by achievement index (1-based, since zero means no achievement)
717  if (val>1) fctAchievements[nAchievements].X += fctAchievements[nAchievements].Wdt * (val-1);
718  // description for this achievement is taken from option
719  const C4ScenarioParameterDef::Option *opt = def->GetOptionByValue(val);
720  if (opt) sAchievementDescriptions[nAchievements] = opt->Description;
721  // keep track of achievement count
722  ++nAchievements;
723  if (nAchievements == C4StartupScenSel_MaxAchievements) break;
724  }
725  }
726  }
727  }
728  }
729  return true;
730 }
731 
732 bool C4ScenarioListLoader::Scenario::LoadCustom(C4Group &rGrp, bool fNameLoaded, bool fIconLoaded)
733 {
734  // icon fallback: Standard scenario icon
735  if (!fIconLoaded)
736  {
737  iIconIndex = C4S.Head.Icon;
738  fctIcon.Set(C4Startup::Get()->Graphics.fctScenSelIcons.GetSection(C4S.Head.Icon));
739  }
740  // scenario name fallback to core
741  if (!fNameLoaded)
742  sName = C4S.Head.Title;
743  // difficulty: Set only for regular rounds (not savegame or record) to avoid bogus sorting
744  if (!C4S.Head.SaveGame && !C4S.Head.Replay)
745  iDifficulty = C4S.Head.Difficulty;
746  else
747  iDifficulty = 0;
748  // minimum required player count
749  iMinPlrCount = C4S.GetMinPlayer();
750  return true;
751 }
752 
753 bool C4ScenarioListLoader::Scenario::GetAchievement(int32_t idx, C4Facet *out_facet, const char **out_description)
754 {
755  // return true and fill output parameters if player got the indexed achievement
756  if (idx < 0 || idx >= nAchievements) return false;
757  *out_facet = fctAchievements[idx];
758  *out_description = sAchievementDescriptions[idx].getData();
759  return true;
760 }
761 
763 {
764  // gogo!
765  if (!(C4StartupScenSelDlg::pInstance)) return false;
766  return (C4StartupScenSelDlg::pInstance)->StartScenario(this);
767 }
768 
770 {
771  // safety
773  if (!pDlg) return false;
774  // check mission access
775  if (!HasMissionAccess())
776  {
777  sErrOut.Copy(LoadResStr("IDS_PRC_NOMISSIONACCESS"));
778  return false;
779  }
780  // replay
781  if (C4S.Head.Replay)
782  {
783  // replays can currently not be launched in network mode
784  if (pDlg->IsNetworkStart())
785  {
786  sErrOut.Copy(LoadResStr("IDS_PRC_NONETREPLAY"));
787  return false;
788  }
789  }
790  // regular game
791  else
792  {
793  // check player count
794  int32_t iPlrCount = SModuleCount(Config.General.Participants);
795  int32_t iMaxPlrCount = C4S.Head.MaxPlayer;
796  if (C4S.Head.SaveGame)
797  {
798  // Some scenarios have adjusted MaxPlayerCount to 0 after starting to prevent future joins
799  // make sure it's possible to start the savegame anyway
800  iMaxPlrCount = std::max<int32_t>(iMinPlrCount, iMaxPlrCount);
801 
802  // <Sven2> Savegames store a lot of internal stuff. If you updated clonk in the meantime, many things tend to break
803  if (C4S.Head.C4XVer[0] != C4XVER1 || C4S.Head.C4XVer[1] != C4XVER2)
804  {
805  // Only show a warning to let players try it anyways.
806  sErrOut.Format(LoadResStr("IDS_MSG_SAVEGAMEVERSIONMISMATCH"), C4S.Head.C4XVer[0], C4S.Head.C4XVer[1]);
807  CanHide = false;
808  }
809  }
810  // normal scenarios: At least one player except in network mode, where it is possible to wait for the additional players
811  // Melees need at least two
812  if ((iPlrCount < iMinPlrCount))
813  {
814  if (pDlg->IsNetworkStart())
815  {
816  // network game: Players may yet join in lobby
817  // only issue a warning for too few players (by setting the error but not returning false here)
818  sErrOut.Format(LoadResStr("IDS_MSG_TOOFEWPLAYERSNET"), (int) iMinPlrCount);
819  CanHide = true;
820  }
821  else
822  {
823  // for regular games, this is a fatal no-start-cause
824  sErrOut.Format(LoadResStr("IDS_MSG_TOOFEWPLAYERS"), (int) iMinPlrCount);
825  return false;
826  }
827  }
828  // scenarios (both normal and savegame) may also impose a maximum player restriction
829  if (iPlrCount > iMaxPlrCount)
830  {
831  sErrOut.Format(LoadResStr("IDS_MSG_TOOMANYPLAYERS"), (int) C4S.Head.MaxPlayer);
832  return false;
833  }
834  }
835  // Okay, start!
836  return true;
837 }
838 
840 {
841  return StdCopyStrBuf(LoadResStr("IDS_BTN_STARTGAME"));
842 }
843 
845 {
846  return StdCopyStrBuf(LoadResStr("IDS_DLGTIP_SCENSELNEXT"));
847 }
848 
849 
850 // ------------------------------------
851 // Folder
852 
854 {
855  if (pMapData) delete pMapData;
856  ClearChildren();
857 }
858 
860 {
861  // open as subfolder
862  if (!C4StartupScenSelDlg::pInstance) return false;
864 }
865 int
866 #ifdef _MSC_VER
867 __cdecl
868 #endif
869 EntrySortFunc(const void *pEl1, const void *pEl2)
870 {
871  C4ScenarioListLoader::Entry *pEntry1 = *(C4ScenarioListLoader::Entry * const *) pEl1, *pEntry2 = *(C4ScenarioListLoader::Entry * const *) pEl2;
872  // sort folders before scenarios
873  bool fS1 = !pEntry1->GetIsFolder(), fS2 = !pEntry2->GetIsFolder();
874  if (fS1 != fS2) return fS1-fS2;
875  // sort by folder index (undefined index 0 goes to the end)
876  if (!Config.Startup.AlphabeticalSorting) if (pEntry1->GetFolderIndex() || pEntry2->GetFolderIndex())
877  {
878  if (!pEntry1->GetFolderIndex()) return +1;
879  if (!pEntry2->GetFolderIndex()) return -1;
880  int32_t iDiff = pEntry1->GetFolderIndex() - pEntry2->GetFolderIndex();
881  if (iDiff) return iDiff;
882  }
883  // sort by numbered standard scenario icons
884  if (Inside(pEntry1->GetIconIndex(), 2, 11))
885  {
886  int32_t iDiff = pEntry1->GetIconIndex() - pEntry2->GetIconIndex();
887  if (iDiff) return iDiff;
888  }
889  // sort by difficulty (undefined difficulty goes to the end)
890  if (!Config.Startup.AlphabeticalSorting) if (pEntry1->GetDifficulty() || pEntry2->GetDifficulty())
891  {
892  if (!pEntry1->GetDifficulty()) return +1;
893  if (!pEntry2->GetDifficulty()) return -1;
894  int32_t iDiff = pEntry1->GetDifficulty() - pEntry2->GetDifficulty();
895  if (iDiff) return iDiff;
896  }
897  // otherwise, sort by name
898  return stricmp(pEntry1->GetName().getData(), pEntry2->GetName().getData());
899 }
900 
902 {
903  uint32_t iCount = 0;
904  for (Entry *i = pFirst; i; i = i->pNext) ++iCount;
905  return iCount;
906 }
907 
909 {
910  // use C-Library-QSort on a buffer of entry pointers; then re-link list
911  if (!pFirst) return;
912  uint32_t iCount,i;
913  Entry **ppEntries = new Entry *[i = iCount = GetEntryCount()], **ppI, *pI=pFirst, **ppIThis;
914  for (ppI = ppEntries; i--; pI = pI->pNext) *ppI++ = pI;
915  qsort(ppEntries, iCount, sizeof(Entry *), &EntrySortFunc);
916  ppIThis = &pFirst;
917  for (ppI = ppEntries; iCount--; ppIThis = &((*ppIThis)->pNext)) *ppIThis = *ppI++;
918  *ppIThis = nullptr;
919  delete [] ppEntries;
920 }
921 
923 {
924  // folder deletion: del all the tree non-recursively
925  Folder *pDelFolder = this, *pCheckFolder;
926  for (;;)
927  {
928  // delete all children as long as they are not folders
929  Entry *pChild;
930  while ((pChild = pDelFolder->pFirst))
931  if ((pCheckFolder = pChild->GetIsFolder()))
932  // child entry if folder: Continue delete in there
933  pDelFolder = pCheckFolder;
934  else
935  // regular child entry: del it
936  // destructor of child will remove it from list
937  delete pChild;
938  // this emptied: Done!
939  if (pDelFolder == this) break;
940  // deepest child recursion reached: Travel up folders
941  pDelFolder = (pCheckFolder = pDelFolder)->pParent;
942  assert(pDelFolder);
943  delete pCheckFolder;
944  }
945 }
946 
947 bool C4ScenarioListLoader::Folder::LoadContents(C4ScenarioListLoader *pLoader, C4Group *pFromGrp, const StdStrBuf *psFilename, bool fLoadEx, bool fReload)
948 {
949  // contents already loaded?
950  if (fContentsLoaded && !fReload) return true;
951  // clear previous
952  if (pMapData) { delete pMapData; pMapData = nullptr; }
953  // if filename is not given, assume it's been loaded in this entry
954  if (!psFilename) psFilename = &this->sFilename; else this->sFilename = *psFilename;
955  // nothing loaded: Load now
956  if (!DoLoadContents(pLoader, pFromGrp, *psFilename, fLoadEx)) return false;
957  // sort loaded stuff by name
958  Sort();
959  return true;
960 }
961 
963 {
964  // do a case-insensitive filename comparison
965  for (Entry *pEntry = pFirst; pEntry; pEntry = pEntry->GetNext())
966  if (SEqualNoCase(szFilename, GetFilename(pEntry->GetEntryFilename().getData())))
967  return pEntry;
968  // nothing found
969  return nullptr;
970 }
971 
973 {
974  return StdCopyStrBuf(LoadResStr("IDS_BTN_OPEN"));
975 }
976 
978 {
979  return StdCopyStrBuf(LoadResStr("IDS_DLGTIP_SCENSELNEXT"));
980 }
981 
983 {
984  return false;
985 }
986 
988 {
989  // load folder core if available
990  StdStrBuf sFileContents;
991  if (rGrp.LoadEntryString(C4CFN_FolderCore, &sFileContents))
992  if (!CompileFromBuf_LogWarn<StdCompilerINIRead>(C4F, sFileContents, (rGrp.GetFullName() + DirSep C4CFN_FolderCore).getData()))
993  return false;
994  return true;
995 }
996 
997 // ------------------------------------
998 // SubFolder
999 
1000 bool C4ScenarioListLoader::SubFolder::LoadCustom(C4Group &rGrp, bool fNameLoaded, bool fIconLoaded)
1001 {
1002  // default icon fallback
1003  if (!fIconLoaded)
1004  {
1006  else iIconIndex = C4StartupScenSel_DefaultIcon_Folder;
1007  fctIcon.Set(C4Startup::Get()->Graphics.fctScenSelIcons.GetSection(iIconIndex));
1008  }
1009  // folder index
1010  iFolderIndex = C4F.Head.Index;
1011  return true;
1012 }
1013 
1014 bool C4ScenarioListLoader::SubFolder::DoLoadContents(C4ScenarioListLoader *pLoader, C4Group *pFromGrp, const StdStrBuf &sFilename, bool fLoadEx)
1015 {
1016  assert(pLoader);
1017  // clear any previous
1018  ClearChildren();
1019  // group specified: Load as child
1020  C4Group Group;
1021  if (pFromGrp)
1022  {
1023  if (!Group.OpenAsChild(pFromGrp, sFilename.getData())) return false;
1024  }
1025  else
1026  // no parent group: Direct load from filename
1027  if (!Group.Open(sFilename.getData())) return false;
1028  // Load achievement data contained scenarios can fall back to
1029  C4LangStringTable FolderLangStringTable;
1031  AchievementDefs.Load(Group, &FolderLangStringTable);
1032  AchievementGfx.Init(Group);
1033  // get number of entries, to estimate progress
1034  const char *szC4CFN_ScenarioFiles = C4CFN_ScenarioFiles; // assign values for constant comparison
1035  const char *szSearchMask; int32_t iEntryCount=0;
1036  for (szSearchMask = szC4CFN_ScenarioFiles; szSearchMask;)
1037  {
1038  Group.ResetSearch();
1039  while (Group.FindNextEntry(szSearchMask)) ++iEntryCount;
1040  // next search mask
1041  if (szSearchMask == szC4CFN_ScenarioFiles)
1042  szSearchMask = C4CFN_FolderFiles;
1043  else
1044  szSearchMask = nullptr;
1045  }
1046  // initial progress estimate
1047  if (!pLoader->DoProcessCallback(0, iEntryCount, nullptr)) return false;
1048  // iterate through group contents
1049  char ChildFilename[_MAX_FNAME_LEN]; StdStrBuf sChildFilename; int32_t iLoadCount=0;
1050  for (szSearchMask = szC4CFN_ScenarioFiles; szSearchMask;)
1051  {
1052  Group.ResetSearch();
1053  while (Group.FindNextEntry(szSearchMask, ChildFilename))
1054  {
1055  // mark progress
1056  if (!pLoader->DoProcessCallback(iLoadCount, iEntryCount, ChildFilename)) return false;
1057  sChildFilename.Ref(ChildFilename);
1058  // okay; create this item
1059  Entry *pNewEntry = Entry::CreateEntryForFile(sChildFilename, pLoader, this);
1060  if (pNewEntry)
1061  {
1062  // ...and load it
1063  if (!pNewEntry->Load(&Group, &sChildFilename, fLoadEx))
1064  {
1065  DebugLogF(R"(Error loading entry "%s" in SubFolder "%s"!)", sChildFilename.getData(), Group.GetFullName().getData());
1066  delete pNewEntry;
1067  }
1068  }
1069  ++iLoadCount;
1070  }
1071  // next search mask
1072  if (szSearchMask == szC4CFN_ScenarioFiles)
1073  szSearchMask = C4CFN_FolderFiles;
1074  else
1075  szSearchMask = nullptr;
1076  }
1077  // load map folder data
1078  if (Group.FindEntry(C4CFN_MapFolderData))
1079  {
1080  pMapData = new C4MapFolderData();
1081  if (!pMapData->Load(Group, this))
1082  {
1083  // load error :(
1084  delete pMapData;
1085  pMapData = nullptr;
1086  }
1087  }
1088  // done, success
1089  fContentsLoaded = true;
1090  return true;
1091 }
1092 
1093 
1094 // ------------------------------------
1095 // RegularFolder
1096 
1098 
1099 bool C4ScenarioListLoader::RegularFolder::LoadCustom(C4Group &rGrp, bool fNameLoaded, bool fIconLoaded)
1100 {
1101  // default icon fallback
1102  if (!fIconLoaded)
1103  fctIcon.Set(C4Startup::Get()->Graphics.fctScenSelIcons.GetSection(C4StartupScenSel_DefaultIcon_WinFolder));
1104  // folder index
1105  iFolderIndex = C4F.Head.Index;
1106  return true;
1107 }
1108 
1109 bool C4ScenarioListLoader::RegularFolder::DoLoadContents(C4ScenarioListLoader *pLoader, C4Group *pFromGrp, const StdStrBuf &sFilename, bool fLoadEx)
1110 {
1111  // clear any previous
1112  ClearChildren();
1113  // regular folders must exist and not be within group!
1114  assert(!pFromGrp);
1115  if (sFilename.getData() && sFilename[0])
1116  Merge(sFilename.getData());
1117 
1118  // get number of entries, to estimate progress
1119  int32_t iCountLoaded=0, iCountTotal=0;
1120  NameList::iterator it;
1121  for (it = contents.begin(); it != contents.end(); ++it)
1122  {
1123  if (!DirectoryExists(it->c_str())) continue;
1124  DirectoryIterator DirIter(it->c_str());
1125  const char *szChildFilename;
1126  for (; (szChildFilename = *DirIter); ++DirIter)
1127  {
1128  if (!*szChildFilename || *GetFilename(szChildFilename)=='.') continue;
1129  ++iCountTotal;
1130  }
1131  }
1132  // initial progress estimate
1133  if (!pLoader->DoProcessCallback(iCountLoaded, iCountTotal, nullptr)) return false;
1134 
1135  // do actual loading of files
1136  std::set<std::string> names;
1137  const char *szChildFilename;
1138  for (it = contents.begin(); it != contents.end(); ++it)
1139  {
1140  if (!pLoader->DoProcessCallback(iCountLoaded, iCountTotal, GetFilename(it->c_str()))) return false;
1141  for (DirectoryIterator DirIter(it->c_str()); (szChildFilename = *DirIter); ++DirIter)
1142  {
1143  StdStrBuf sChildFilename(szChildFilename);
1144  szChildFilename = GetFilename(szChildFilename);
1145  // progress callback
1146  if (!pLoader->DoProcessCallback(iCountLoaded, iCountTotal, szChildFilename)) return false;
1147  // Ignore directory navigation entries and CVS folders
1148  if (C4Group_TestIgnore(szChildFilename)) continue;
1149  if (names.find(szChildFilename) != names.end()) continue;
1150  names.insert(szChildFilename);
1151  // filename okay; create this item
1152  Entry *pNewEntry = Entry::CreateEntryForFile(sChildFilename, pLoader, this);
1153  if (pNewEntry)
1154  {
1155  // ...and load it
1156  if (!pNewEntry->Load(nullptr, &sChildFilename, fLoadEx))
1157  {
1158  DebugLogF(R"(Error loading entry "%s" in Folder "%s"!)", szChildFilename, it->c_str());
1159  delete pNewEntry;
1160  }
1161  }
1162  ++iCountLoaded;
1163  }
1164  }
1165  // done, success
1166  fContentsLoaded = true;
1167  return true;
1168 }
1169 
1171 {
1172  contents.emplace_back(szPath);
1173 }
1174 
1175 // ------------------------------------
1176 // C4ScenarioListLoader
1177 
1178 C4ScenarioListLoader::C4ScenarioListLoader(const C4ScenarioParameters &Achievements) : Achievements(Achievements), pRootFolder(nullptr), pCurrFolder(nullptr),
1179  iLoading(0), iProgress(0), iMaxProgress(0), fAbortThis(false), fAbortPrevious(false)
1180 {
1181 }
1182 
1184 {
1185  if (pRootFolder) delete pRootFolder;
1186 }
1187 
1188 bool C4ScenarioListLoader::BeginActivity(bool fAbortPrevious)
1189 {
1190  // if previous activities were running, stop them first if desired
1191  if (iLoading && fAbortPrevious)
1192  this->fAbortPrevious = true;
1193  // mark this activity
1194  ++iLoading;
1195  // progress of activity not yet decided
1196  iProgress = iMaxProgress = 0;
1197  current_load_info.Clear();
1198  // okay; start activity
1199  return true;
1200 }
1201 
1202 void C4ScenarioListLoader::EndActivity()
1203 {
1204  assert(iLoading);
1205  if (!--iLoading)
1206  {
1207  // last activity done: Reset any flags
1208  fAbortThis = false;
1209  fAbortPrevious = false;
1210  iProgress = iMaxProgress = 0;
1211  current_load_info.Clear();
1212  }
1213  else
1214  {
1215  // child activity done: Transfer abort flag for next activity
1216  fAbortThis = fAbortPrevious;
1217  }
1218 }
1219 
1220 bool C4ScenarioListLoader::DoProcessCallback(int32_t iProgress, int32_t iMaxProgress, const char *current_load_info)
1221 {
1222  this->iProgress = iProgress;
1223  this->iMaxProgress = iMaxProgress;
1224  this->current_load_info.Copy(current_load_info);
1225  // callback to dialog
1227  // process callback - abort at a few ugly circumstances...
1228  // schedule with 1ms delay to force event processing
1229  // (delay 0 would be nice, but isn't supported properly by our Windows implementation of ScheduleProcs)
1230  if (!Application.ScheduleProcs(1) // WM_QUIT message?
1231  || !C4StartupScenSelDlg::pInstance // host dialog removed?
1232  || !C4StartupScenSelDlg::pInstance->IsShown() // host dialog closed?
1233  ) return false;
1234  // and also abort if flagged
1235  return !fAbortThis;
1236 }
1237 
1238 bool C4ScenarioListLoader::Load(const StdStrBuf &sRootFolder)
1239 {
1240  // (unthreaded) loading of all entries in root folder
1241  if (!BeginActivity(true)) return false;
1242  if (pRootFolder) { delete pRootFolder; pRootFolder = nullptr; }
1243  pCurrFolder = pRootFolder = new RegularFolder(this, nullptr);
1244  // Load regular game data if no explicit path specified
1245  if(!sRootFolder.getData())
1246  for(const auto & iter : Reloc)
1247  pRootFolder->Merge(iter.strBuf.getData());
1248  bool fSuccess = pRootFolder->LoadContents(this, nullptr, &sRootFolder, false, false);
1249  EndActivity();
1250  return fSuccess;
1251 }
1252 
1253 bool C4ScenarioListLoader::Load(Folder *pSpecifiedFolder, bool fReload)
1254 {
1255  // call safety
1256  if (!pRootFolder || !pSpecifiedFolder) return false;
1257  // set new current and load it
1258  if (!BeginActivity(true)) return false;
1259  pCurrFolder = pSpecifiedFolder;
1260  bool fSuccess = pCurrFolder->LoadContents(this, nullptr, nullptr, false, fReload);
1261  EndActivity();
1262  return fSuccess;
1263 }
1264 
1266 {
1267  // call safety
1268  if (!pRootFolder || !pEntry) return false;
1269  // load info of selection
1270  if (!BeginActivity(false)) return false;
1271  bool fSuccess = pEntry->Load(nullptr, nullptr, true);
1272  EndActivity();
1273  return fSuccess;
1274 }
1275 
1277 {
1278  // call safety
1279  if (!pRootFolder || !pCurrFolder) return false;
1280  // already in root: Can't go up
1281  if (pCurrFolder == pRootFolder) return false;
1282  // otherwise, up one level
1283  return Load(pCurrFolder->GetParent(), false);
1284 }
1285 
1287 {
1288  // call safety
1289  if (!pRootFolder || !pCurrFolder) return false;
1290  // reload current
1291  return Load(pCurrFolder, true);
1292 }
1293 
1294 
1295 
1296 
1297 
1298 
1299 // ----------------------------------------------------------------
1300 // Scenario selection GUI
1301 
1302 
1303 // font clrs
1304 const uint32_t ClrScenarioItem = 0xff000000,
1305  ClrScenarioItemXtra = 0x7f000000,
1307 
1308 // ------------------------------------------------
1309 // --- C4StartupScenSelDlg::ScenListItem
1311  : pIcon(nullptr), pNameLabel(nullptr), pScenListEntry(pForEntry)
1312 {
1313  assert(pScenListEntry);
1314  CStdFont &rUseFont = C4Startup::Get()->Graphics.BookFont;
1315  StdStrBuf sIgnore; bool bIgnore;
1316  bool fEnabled = pScenListEntry->CanOpen(sIgnore, bIgnore) && !pScenListEntry->IsGrayed();
1317  // calc height
1318  int32_t iHeight = rUseFont.GetLineHeight() + 2 * IconLabelSpacing;
1319  // create subcomponents
1320  pIcon = new C4GUI::Picture(C4Rect(0, 0, iHeight, iHeight), true);
1321  pIcon->SetFacet(pScenListEntry->GetIconFacet());
1322  pNameLabel = new C4GUI::Label(pScenListEntry->GetName().getData(), iHeight + IconLabelSpacing, IconLabelSpacing, ALeft, fEnabled ? ClrScenarioItem : ClrScenarioItemDisabled, &rUseFont, false, false);
1323  // achievement components
1324  for (int32_t i=0; i<C4StartupScenSel_MaxAchievements; ++i)
1325  {
1326  C4Facet fct; const char *desc;
1327  if (pForEntry->GetAchievement(i, &fct, &desc))
1328  {
1329  ppAchievements[i] = new C4GUI::Picture(C4Rect(iHeight * (i+2), 0, iHeight, iHeight), true); // position will be adjusted later
1330  ppAchievements[i]->SetFacet(fct);
1331  ppAchievements[i]->SetToolTip(desc);
1332  }
1333  else
1334  {
1335  ppAchievements[i] = nullptr;
1336  }
1337  }
1338  // calc own bounds - use icon bounds only, because only the height is used when the item is added
1339  SetBounds(pIcon->GetBounds());
1340  // add components
1341  AddElement(pIcon); AddElement(pNameLabel);
1342  for (auto & ppAchievement : ppAchievements) if (ppAchievement) AddElement(ppAchievement);
1343  // tooltip by name, so long names can be read via tooltip
1344  SetToolTip(pScenListEntry->GetName().getData());
1345  // add to listbox (will get resized horizontally and moved) - zero indent; no tree structure in this dialog
1346  pForListBox->InsertElement(this, pInsertBeforeElement, 0);
1347  // update name label width to reflect new horizontal size
1348  // name label width must be set so rename edit will take its size
1349  pNameLabel->SetAutosize(false);
1350  C4Rect rcNLB = pNameLabel->GetBounds(); rcNLB.Wdt = GetClientRect().Wdt - rcNLB.x - IconLabelSpacing;
1351  pNameLabel->SetBounds(rcNLB);
1352 }
1353 
1355 {
1356  // parent for client rect
1357  typedef C4GUI::Window ParentClass;
1358  ParentClass::UpdateOwnPos();
1359  // reposition achievement items
1360  C4GUI::ComponentAligner caBounds(GetContainedClientRect(), IconLabelSpacing, IconLabelSpacing);
1361  for (auto & ppAchievement : ppAchievements) if (ppAchievement)
1362  {
1363  ppAchievement->SetBounds(caBounds.GetFromRight(caBounds.GetHeight()));
1364  }
1365 }
1366 
1367 void C4StartupScenSelDlg::ScenListItem::MouseInput(C4GUI::CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam)
1368 {
1369  // double-click opens/starts item - currently processed by ListBox already!
1370  // inherited processing
1371  typedef C4GUI::Window BaseClass;
1372  BaseClass::MouseInput(rMouse, iButton, iX, iY, dwKeyParam);
1373 }
1374 
1376 {
1377  // return whether this item can be selected by entering given char:
1378  // first char of name must match
1379  // FIXME: make unicode-ready
1380  if (!pScenListEntry) return false;
1381  const char *szName = pScenListEntry->GetName().getData();
1382  return szName && (toupper(*szName) == toupper(c[0]));
1383 }
1384 
1386 {
1387  // rename this entry
1389  return true;
1390 }
1391 
1393 {
1394  // no renaming
1396 }
1397 
1399 {
1400  // check validity for new name
1401  if (!GetEntry()->RenameTo(szNewName)) return C4GUI::RenameEdit::RR_Invalid;
1402  // rename label
1403  pNameLabel->SetText(GetEntry()->GetName().getData());
1404  // main dlg update
1406  C4StartupScenSelDlg::pInstance->ResortFolder();
1407  C4StartupScenSelDlg::pInstance->UpdateSelection();
1408  C4StartupScenSelDlg::pInstance->FocusScenList();
1409  // done; rename accepted and control deleted by ResortFolder
1411 }
1412 
1413 // ------------------------------------------------
1414 // --- C4StartupScenSelDlg
1415 
1416 C4StartupScenSelDlg::C4StartupScenSelDlg(bool fNetwork) : C4StartupDlg(LoadResStrNoAmp(fNetwork ? "IDS_DLG_NETSTART" : "IDS_DLG_STARTGAME")), pScenLoader(nullptr), pMapData(nullptr), pfctBackground(nullptr), fIsInitialLoading(false), fStartNetworkGame(fNetwork), pRenameEdit(nullptr)
1417 {
1418  // ctor
1419  // assign singleton
1420  pInstance = this;
1421 
1422  // screen calculations
1423  UpdateSize();
1424  int32_t iButtonWidth,iCaptionFontHgt;
1425  int iButtonHeight = C4GUI_ButtonHgt;
1426  int iBookPageWidth;
1427  int iExtraHPadding = rcBounds.Wdt >= 700 ? rcBounds.Wdt/50 : 0;
1428  ::GraphicsResource.CaptionFont.GetTextExtent("<< BACK", iButtonWidth, iCaptionFontHgt, true);
1429  iButtonWidth *= 3;
1430  C4GUI::ComponentAligner caMain(GetClientRect(), 0,0, true);
1431  C4GUI::ComponentAligner caButtonArea(caMain.GetFromBottom(caMain.GetHeight()/8),rcBounds.Wdt/(rcBounds.Wdt >= 700 ? 128 : 256),0);
1432  C4Rect rcMap = caMain.GetCentered(caMain.GetWidth(), caMain.GetHeight());
1433 
1434  // tabular for different scenario selection designs
1435  pScenSelStyleTabular = new C4GUI::Tabular(rcMap, C4GUI::Tabular::tbNone);
1436  pScenSelStyleTabular->SetSheetMargin(0);
1437  pScenSelStyleTabular->SetGfx(&C4Startup::Get()->Graphics.fctDlgPaper, &C4Startup::Get()->Graphics.fctOptionsTabClip, &C4Startup::Get()->Graphics.fctOptionsIcons, &C4Startup::Get()->Graphics.BookSmallFont, false);
1438  AddElement(pScenSelStyleTabular);
1439  C4GUI::Tabular::Sheet *pSheetBook = pScenSelStyleTabular->AddSheet(nullptr);
1440  /* C4GUI::Tabular::Sheet *pSheetMap = */ pScenSelStyleTabular->AddSheet(nullptr);
1441 
1442  // scenario selection list
1443  C4GUI::ComponentAligner caBook(pSheetBook->GetClientRect(), caMain.GetWidth()/20, caMain.GetHeight()/20, true);
1444  C4GUI::ComponentAligner caBookLeft(caBook.GetFromLeft(iBookPageWidth=caBook.GetWidth()*4/9+4-iExtraHPadding*2), 0,5);
1445 
1446  CStdFont &rScenSelCaptionFont = C4Startup::Get()->Graphics.BookFontTitle;
1447  pScenSelCaption = new C4GUI::Label("", caBookLeft.GetFromTop(rScenSelCaptionFont.GetLineHeight()), ACenter, ClrScenarioItem, &rScenSelCaptionFont, false);
1448  pSheetBook->AddElement(pScenSelCaption);
1449  pScenSelCaption->SetToolTip(LoadResStr("IDS_DLGTIP_SELECTSCENARIO"));
1450 
1451  // search bar
1452  const char *labelText = LoadResStr("IDS_DLG_SEARCH");
1453  int32_t width = 100;
1454  int32_t height; // there's no point in specifying a default height - it's set by GetTextExtent, and we can't know how high the text is
1455  ::GraphicsResource.TextFont.GetTextExtent(labelText, width, height, true);
1456  C4GUI::ComponentAligner caSearchBar(caBookLeft.GetFromBottom(height), 0, 0);
1457  auto *searchLabel = new C4GUI::WoodenLabel(labelText, caSearchBar.GetFromLeft(width + 10), C4GUI_Caption2FontClr, &::GraphicsResource.TextFont);
1458  searchLabel->SetToolTip(LoadResStr("IDS_DLGTIP_SEARCHLIST"));
1459  pSheetBook->AddElement(searchLabel);
1460 
1462  searchBar->SetToolTip(LoadResStr("IDS_DLGTIP_SEARCHLIST"));
1463  pSheetBook->AddElement(searchBar);
1464 
1465  // scenario selection list box
1466  pScenSelList = new C4GUI::ListBox(caBookLeft.GetAll());
1467  pScenSelList->SetToolTip(LoadResStr("IDS_DLGTIP_SELECTSCENARIO"));
1468  pScenSelList->SetDecoration(false, &C4Startup::Get()->Graphics.sfctBookScroll, true);
1469  pSheetBook->AddElement(pScenSelList);
1472  // scenario selection list progress labels
1473  pScenSelProgressLabel = new C4GUI::Label("", pScenSelList->GetBounds().GetMiddleX(), pScenSelList->GetBounds().GetMiddleY()-iCaptionFontHgt, ACenter, ClrScenarioItem, &(C4Startup::Get()->Graphics.BookFontCapt), false);
1474  pSheetBook->AddElement(pScenSelProgressLabel);
1475  pScenSelProgressInfoLabel = new C4GUI::Label("", pScenSelList->GetBounds().GetMiddleX(), pScenSelList->GetBounds().GetMiddleY(), ACenter, ClrScenarioItemXtra, &(C4Startup::Get()->Graphics.BookFontCapt), false);
1476  pSheetBook->AddElement(pScenSelProgressInfoLabel);
1477 
1478  // right side of book: Displaying current selection
1479  C4Rect bounds = caBook.GetFromRight(iBookPageWidth);
1480  const int32_t AvailWidth = bounds.Wdt;
1481  const int32_t AvailHeight = 2 * bounds.Hgt / 5;
1482  int32_t PictureWidth, PictureHeight;
1484  {
1487  }
1488  else
1489  {
1492  }
1494  C4StartupScenSel_TitlePicturePadding, 100, 4096, nullptr, true, &C4Startup::Get()->Graphics.fctScenSelTitleOverlay, C4StartupScenSel_TitleOverlayMargin);
1495  pSelectionInfo->SetDecoration(false, false, &C4Startup::Get()->Graphics.sfctBookScroll, true);
1496  pSheetBook->AddElement(pSelectionInfo);
1497 
1498  // bottom of right side of book: Custom options on selection
1499  // Arbitrary height and invisible by default. Height will be adjusted when options are created.
1500  pSelectionOptions = new C4GameOptionsList(C4Rect(bounds.x, bounds.y+bounds.Hgt-10, bounds.Wdt, 10), false, fNetwork ? C4GameOptionsList::GOLS_PreGameNetwork : C4GameOptionsList::GOLS_PreGameSingle);
1501  pSelectionOptions->SetDecoration(false, &C4Startup::Get()->Graphics.sfctBookScroll, true);
1502  pSelectionOptions->SetVisibility(false);
1503  pSheetBook->AddElement(pSelectionOptions);
1504 
1505  // back button
1507  AddElement(btn = new C4GUI::CallbackButton<C4StartupScenSelDlg>(LoadResStr("IDS_BTN_BACK"), caButtonArea.GetFromLeft(iButtonWidth, iButtonHeight), &C4StartupScenSelDlg::OnBackBtn));
1508  btn->SetToolTip(LoadResStr("IDS_DLGTIP_BACKMAIN"));
1509  AddElement(btn);
1510  // next button
1511  pOpenBtn = new C4GUI::CallbackButton<C4StartupScenSelDlg>(LoadResStr("IDS_BTN_OPEN"), caButtonArea.GetFromRight(iButtonWidth, iButtonHeight), &C4StartupScenSelDlg::OnNextBtn);
1512  pOpenBtn->SetToolTip(LoadResStr("IDS_DLGTIP_SCENSELNEXT"));
1513  // game options boxes
1514  pGameOptionButtons = new C4GameOptionButtons(caButtonArea.GetAll(), fNetwork, true, false);
1515  AddElement(pGameOptionButtons);
1516  // next button adding
1517  AddElement(pOpenBtn);
1518 
1519  // dlg starts with focus on listbox
1520  SetFocus(pScenSelList, false);
1521 
1522  // key bindings
1523  pKeyBack = new C4KeyBinding(C4KeyCodeEx(K_LEFT), "StartupScenSelFolderUp", KEYSCOPE_Gui,
1525  pKeyRefresh = new C4KeyBinding(C4KeyCodeEx(K_F5), "StartupScenSelReload", KEYSCOPE_Gui,
1527  pKeyForward = new C4KeyBinding(C4KeyCodeEx(K_RIGHT), "StartupScenSelNext", KEYSCOPE_Gui,
1529  pKeyRename = new C4KeyBinding(C4KeyCodeEx(K_F2), "StartupScenSelRename", KEYSCOPE_Gui,
1531  pKeyDelete = new C4KeyBinding(C4KeyCodeEx(K_DELETE), "StartupScenSelDelete", KEYSCOPE_Gui,
1533  pKeyCheat = new C4KeyBinding(C4KeyCodeEx(K_M, KEYS_Control), "StartupScenSelCheat", KEYSCOPE_Gui,
1535 }
1536 
1538 {
1539  if (pScenLoader) delete pScenLoader;
1540  if (this == pInstance) pInstance = nullptr;
1541  delete pKeyCheat;
1542  delete pKeyDelete;
1543  delete pKeyRename;
1544  delete pKeyForward;
1545  delete pKeyRefresh;
1546  delete pKeyBack;
1547 }
1548 
1550 {
1551  // draw background
1552  if (pfctBackground)
1553  DrawBackground(cgo, *pfctBackground);
1554 }
1555 
1557 {
1559  // Collect achievements of all activated players
1560  UpdateAchievements();
1561  // init file list
1562  fIsInitialLoading = true;
1563  if (!pScenLoader) pScenLoader = new C4ScenarioListLoader(Achievements);
1564  pScenLoader->Load(StdStrBuf());
1565  UpdateList();
1566  UpdateSelection();
1567  fIsInitialLoading = false;
1568  // network activation by dialog type
1569  Game.NetworkActive = fStartNetworkGame;
1570 }
1571 
1573 {
1574  AbortRenaming();
1575  // clear laoded scenarios
1576  if (pScenLoader)
1577  {
1578  delete pScenLoader;
1579  pScenLoader = nullptr;
1580  UpdateList(); // must clear scenario list, because it points to deleted stuff
1581  UpdateSelection(); // must clear picture facet of selection!
1582  }
1583  // dlg abort: return to main screen
1584  if (!fOK)
1585  {
1586  // clear settings: Password
1587  ::Network.SetPassword(nullptr);
1589  }
1590 }
1591 
1592 void C4StartupScenSelDlg::UpdateList()
1593 {
1594  AbortRenaming();
1595  // default: Show book (also for loading screen)
1596  pMapData = nullptr;
1597  pScenSelStyleTabular->SelectSheet(ShowStyle_Book, false);
1598  // and delete any stuff from map selection
1599  C4GUI::Tabular::Sheet *pMapSheet = pScenSelStyleTabular->GetSheet(ShowStyle_Map);
1600  while (pMapSheet->GetFirst()) delete pMapSheet->GetFirst();
1601  pfctBackground = nullptr;
1602  // for now, all the list is loaded at once anyway
1603  // so just clear and add all loaded items
1604  // remember old selection
1605  C4ScenarioListLoader::Entry *pOldSelection = GetSelectedEntry();
1606  C4GUI::Element *pEl;
1607  while ((pEl = pScenSelList->GetFirst())) delete pEl;
1608  pScenSelCaption->SetText("");
1609  // scen loader still busy: Nothing to add
1610  if (!pScenLoader) return;
1611  if (pScenLoader->IsLoading())
1612  {
1613  StdStrBuf sProgressText;
1614  sProgressText.Format(LoadResStr("IDS_MSG_SCENARIODESC_LOADING"), (int32_t) pScenLoader->GetProgressPercent());
1615  pScenSelProgressLabel->SetText(sProgressText.getData());
1616  pScenSelProgressLabel->SetVisibility(true);
1617  pScenSelProgressInfoLabel->SetText(pScenLoader->GetProgressInfo());
1618  pScenSelProgressInfoLabel->SetVisibility(true);
1619  return;
1620  }
1621  pScenSelProgressLabel->SetVisibility(false);
1622  pScenSelProgressInfoLabel->SetVisibility(false);
1623  // is this a map folder? Then show the map instead
1624  C4ScenarioListLoader::Folder *pFolder = static_cast<C4ScenarioListLoader::Folder *>(pScenLoader->GetCurrFolder());
1625  if ((pMapData = pFolder->GetMapData()))
1626  {
1627  pMapData->ResetSelection();
1628  pMapData->CreateGUIElements(this, *pScenSelStyleTabular->GetSheet(ShowStyle_Map));
1629  pScenSelStyleTabular->SelectSheet(ShowStyle_Map, false);
1630  }
1631  else
1632  {
1633  // book style selection
1634  // add what has been loaded
1635  for (C4ScenarioListLoader::Entry *pEnt = pScenLoader->GetFirstEntry(); pEnt; pEnt = pEnt->GetNext())
1636  {
1637  if (pEnt->IsHidden()) continue; // no UI entry at all for hidden items
1638  if (!SLen(searchBar->GetText()) || SSearchNoCase(pEnt->GetName().getData(), searchBar->GetText()))
1639  {
1640  ScenListItem *pEntItem = new ScenListItem(pScenSelList, pEnt);
1641  if (pEnt == pOldSelection) pScenSelList->SelectEntry(pEntItem, false);
1642  }
1643  else if (pEnt == pOldSelection)
1644  {
1645  pOldSelection = nullptr;
1646  }
1647  }
1648  // set title of current folder
1649  // but not root
1650  if (pFolder && pFolder != pScenLoader->GetRootFolder())
1651  pScenSelCaption->SetText(pFolder->GetName().getData());
1652  else
1653  {
1654  // special root title
1655  pScenSelCaption->SetText(LoadResStr("IDS_DLG_SCENARIOS"));
1656  }
1657  // new list has been loaded: Select first entry if nothing else had been selected
1658  if (!pOldSelection) pScenSelList->SelectFirstEntry(false);
1659  }
1660 }
1661 
1662 void C4StartupScenSelDlg::ResortFolder()
1663 {
1664  // if it's still loading, sorting will be done in the end anyway
1665  if (!pScenLoader || pScenLoader->IsLoading()) return;
1666  C4ScenarioListLoader::Folder *pFolder = pScenLoader->GetCurrFolder();
1667  if (!pFolder) return;
1668  pFolder->Resort();
1669  UpdateList();
1670 }
1671 
1672 void C4StartupScenSelDlg::UpdateSelection()
1673 {
1674  AbortRenaming();
1675  if (!pScenLoader)
1676  {
1677  C4Facet fctNoPic;
1678  pSelectionInfo->SetPicture(fctNoPic);
1679  pSelectionInfo->ClearText(false);
1680  SetOpenButtonDefaultText();
1681  return;
1682  }
1683  // determine target text box
1684  C4GUI::TextWindow *pSelectionInfo = pMapData ? pMapData->GetSelectionInfoBox() : this->pSelectionInfo;
1685  // get selected entry
1686  C4ScenarioListLoader::Entry *pSel = GetSelectedEntry();
1687  if (!pSel)
1688  {
1689  // no selection: Display data of current parent folder
1690  pSel = pScenLoader->GetCurrFolder();
1691  // but not root
1692  if (pSel == pScenLoader->GetRootFolder()) pSel = nullptr;
1693  }
1694  // get title image and desc of selected entry
1695  C4Facet fctTitle; StdStrBuf sTitle, sDesc, sVersion, sAuthor;
1696  if (pSel)
1697  {
1698  pScenLoader->LoadExtended(pSel); // 2do: Multithreaded
1699  fctTitle = pSel->GetTitlePicture();
1700  sTitle.Ref(pSel->GetName());
1701  sDesc.Ref(pSel->GetDesc());
1702  sVersion.Ref(pSel->GetVersion());
1703  sAuthor.Ref(pSel->GetAuthor());
1704  // never show a pure title string: There must always be some text or an image
1705  if (!fctTitle.Surface && (!sDesc || !*sDesc.getData()))
1706  sTitle.Clear();
1707  // selection specific open/start button
1708  pOpenBtn->SetText(pSel->GetOpenText().getData());
1709  pOpenBtn->SetToolTip(pSel->GetOpenTooltip().getData());
1710  }
1711  else
1712  SetOpenButtonDefaultText();
1713  // set data in selection component
1714  pSelectionInfo->ClearText(false);
1715  pSelectionInfo->SetPicture(fctTitle);
1716  if (sTitle && (!sDesc || !*sDesc.getData())) pSelectionInfo->AddTextLine(sTitle.getData(), &C4Startup::Get()->Graphics.BookFontCapt, ClrScenarioItem, false, false);
1717  if (sDesc) pSelectionInfo->AddTextLine(sDesc.getData(), &C4Startup::Get()->Graphics.BookFont, ClrScenarioItem, false, false, &C4Startup::Get()->Graphics.BookFontCapt);
1718  if (sAuthor) pSelectionInfo->AddTextLine(FormatString(LoadResStr("IDS_CTL_AUTHOR"), sAuthor.getData()).getData(),
1720  if (sVersion) pSelectionInfo->AddTextLine(FormatString(LoadResStr("IDS_DLG_VERSION"), sVersion.getData()).getData(),
1722  // update custom scenario options panel
1723  if (pSel)
1724  {
1725  pSelectionOptions->SetParameters(pSel->GetParameterDefs(), pSel->GetParameters());
1726  pSelectionOptions->Update();
1727  }
1728  else
1729  pSelectionOptions->SetParameters(nullptr, nullptr);
1730  // update component heights
1731  C4Rect rcSelBounds = pSelectionInfo->GetBounds();
1732  int32_t ymax = pSelectionOptions->GetBounds().GetBottom();
1733  C4GUI::Element *pLastOption = pSelectionOptions->GetLast();
1734  if (pLastOption)
1735  {
1736  // custom options present: Info box reduced; options box at bottom
1737  // set options box max size to 1/3rd of selection info area
1738  int32_t options_hgt = std::min<int32_t>(pLastOption->GetBounds().GetBottom() + pSelectionOptions->GetMarginTop() + pSelectionOptions->GetMarginTop(), rcSelBounds.Hgt/3);
1739  rcSelBounds.Hgt = ymax - options_hgt - rcSelBounds.y;
1740  pSelectionInfo->SetBounds(rcSelBounds);
1741  rcSelBounds.y = ymax - options_hgt;
1742  rcSelBounds.Hgt = options_hgt;
1743  pSelectionOptions->SetBounds(rcSelBounds);
1744  pSelectionOptions->SetVisibility(true);
1745  }
1746  else
1747  {
1748  // custom options absent: Info takes full area
1749  pSelectionOptions->SetVisibility(false);
1750  rcSelBounds.Hgt = ymax - rcSelBounds.y;
1751  pSelectionInfo->SetBounds(rcSelBounds);
1752  }
1753  pSelectionInfo->UpdateHeight();
1754 }
1755 
1756 C4StartupScenSelDlg::ScenListItem *C4StartupScenSelDlg::GetSelectedItem()
1757 {
1758  return static_cast<ScenListItem *>(pScenSelList->GetSelectedItem());
1759 }
1760 
1761 C4ScenarioListLoader::Entry *C4StartupScenSelDlg::GetSelectedEntry()
1762 {
1763  // map layout: Get selection from map
1764  if (pMapData) return pMapData->GetSelectedEntry();
1765  // get selection in listbox
1766  ScenListItem *pSel = static_cast<ScenListItem *>(pScenSelList->GetSelectedItem());
1767  return pSel ? pSel->GetEntry() : nullptr;
1768 }
1769 
1771 {
1772  assert(pStartScen);
1773  if (!pStartScen) return false;
1774  // get required object definitions
1775  if (pStartScen->GetC4S().Definitions.AllowUserChange)
1776  {
1777  // get definitions as user selects them
1778  StdStrBuf sDefinitions;
1779  if (!pStartScen->GetC4S().Definitions.GetModules(&sDefinitions)) sDefinitions.Copy("Objects.ocd");
1780  if (!C4DefinitionSelDlg::SelectDefinitions(GetScreen(), &sDefinitions))
1781  // user aborted during definition selection
1782  return false;
1783  SCopy(sDefinitions.getData(), ::Game.DefinitionFilenames, (sizeof Game.DefinitionFilenames)-1);
1784  }
1785  else
1786  // for no user change, just set default objects. Custom settings will override later anyway
1787  SCopy("Objects.ocd", ::Game.DefinitionFilenames);
1788  // set other default startup parameters
1789  ::Game.fLobby = !!::Game.NetworkActive; // always lobby in network
1790  ::Game.fObserve = false;
1791  C4ScenarioParameters *custom_params = pStartScen->GetParameters();
1792  if (custom_params) ::Game.StartupScenarioParameters = *custom_params; else ::Game.StartupScenarioParameters.Clear();
1793  // start with this set!
1795  return true;
1796 }
1797 
1799 {
1800  // open it through loader
1801  if (!pScenLoader) return false;
1802  searchBar->ClearText();
1803  bool fSuccess = pScenLoader->Load(pNewFolder, false);
1804  UpdateList();
1805  UpdateSelection();
1806  if (!pMapData) SetFocus(pScenSelList, false);
1807  return fSuccess;
1808 }
1809 
1811 {
1812  AbortRenaming();
1813  // get selected entry
1814  C4ScenarioListLoader::Entry *pSel = GetSelectedEntry();
1815  if (!pSel) return false;
1816  // check if open is possible
1817  StdStrBuf sError;
1818  bool CanHide = false;
1819  if (!pSel->CanOpen(sError, CanHide))
1820  {
1821  GetScreen()->ShowMessage(sError.getData(), LoadResStr("IDS_MSG_CANNOTSTARTSCENARIO"), C4GUI::Ico_Error);
1822  return false;
1823  }
1824  // if CanOpen returned true but set an error message, that means it's a warning. Display it!
1825  if (sError.getLength())
1826  {
1827  if (!GetScreen()->ShowMessageModal(sError.getData(), LoadResStrNoAmp("IDS_DLG_STARTGAME"), C4GUI::MessageDialog::btnOKAbort, C4GUI::Ico_Notify, CanHide ? &Config.Startup.HideMsgStartDedicated : nullptr))
1828  // user chose to not start it
1829  return false;
1830  }
1831  // start it!
1832  return pSel->Start();
1833 }
1834 
1835 bool C4StartupScenSelDlg::DoBack(bool fAllowClose)
1836 {
1837  AbortRenaming();
1838  // if in a subfolder, try backtrace folders first
1839  if (pScenLoader && pScenLoader->FolderBack())
1840  {
1841  searchBar->ClearText();
1842  UpdateList();
1843  UpdateSelection();
1844  return true;
1845  }
1846  // while this isn't multithreaded, the dialog must not be aborted while initial load...
1847  if (pScenLoader && pScenLoader->IsLoading()) return false;
1848  // return to main screen
1849  if (fAllowClose)
1850  {
1851  Close(false);
1852  return true;
1853  }
1854  return false;
1855 }
1856 
1858 {
1859  if (pScenLoader && !pScenLoader->IsLoading())
1860  {
1861  pScenSelList->SelectNone(false);
1862  pScenLoader->ReloadCurrent();
1863  UpdateList();
1864  UpdateSelection();
1865  }
1866 }
1867 
1868 void C4StartupScenSelDlg::SetOpenButtonDefaultText()
1869 {
1870  pOpenBtn->SetText(LoadResStr("IDS_BTN_OPEN"));
1871  pOpenBtn->SetToolTip(LoadResStr("IDS_DLGTIP_SCENSELNEXT"));
1872 }
1873 
1875 {
1876  // no rename in map mode
1877  if (pMapData) return false;
1878  // not if renaming already
1879  if (IsRenaming()) return false;
1880  // forward to selected scenario list item
1881  ScenListItem *pSel = GetSelectedItem();
1882  if (!pSel) return false;
1883  return pSel->KeyRename();
1884 }
1885 
1887 {
1888  // do not delete from map folder
1889  if (pMapData) return false;
1890  // cancel renaming
1891  AbortRenaming();
1892  // delete selected item: Confirmation first
1893  ScenListItem *pSel = GetSelectedItem();
1894  if (!pSel) return false;
1895  C4ScenarioListLoader::Entry *pEnt = pSel->GetEntry();
1896  StdStrBuf sWarning;
1897  sWarning.Format(LoadResStr("IDS_MSG_PROMPTDELETE"), FormatString("%s %s", pEnt->GetTypeName().getData(), pEnt->GetName().getData()).getData(), pEnt->GetEntryFilename().getData());
1898  GetScreen()->ShowRemoveDlg(new C4GUI::ConfirmationDialog(sWarning.getData(), LoadResStr("IDS_MNU_DELETE"),
1900  return true;
1901 }
1902 
1904 {
1905  // deletion confirmed. Do it.
1906  C4ScenarioListLoader::Entry *pEnt = pSel->GetEntry();
1907  if (!C4Group_DeleteItem(pEnt->GetEntryFilename().getData(), true))
1908  {
1909  StdStrBuf sMsg; sMsg.Format("%s", LoadResStr("IDS_FAIL_DELETE"));
1911  return;
1912  }
1913  // remove from scenario list
1914  pScenSelList->SelectEntry(pSel->GetNext(), false);
1915  delete pEnt;
1916  delete pSel;
1917 }
1918 
1920 {
1921  return ::pGUI->ShowRemoveDlg(new C4GUI::InputDialog(LoadResStr("IDS_TEXT_ENTERMISSIONPASSWORD"), LoadResStr("IDS_DLG_MISSIONACCESS"), C4GUI::Ico_Options,
1923  false));
1924 }
1925 
1927 {
1928  // Special character "-": remove mission password(s)
1929  if (SEqual2(rsCheatCode.getData(), "-"))
1930  {
1931  const char *szPass = rsCheatCode.getPtr(1);
1932  if (szPass && *szPass)
1933  {
1934  SRemoveModules(Config.General.MissionAccess, szPass, false);
1935  UpdateList();
1936  UpdateSelection();
1937  return;
1938  }
1939  }
1940 
1941  // No special character: add mission password(s)
1942  const char *szPass = rsCheatCode.getPtr(0);
1943  if (szPass && *szPass)
1944  {
1945  SAddModules(Config.General.MissionAccess, szPass, false);
1946  UpdateList();
1947  UpdateSelection();
1948  return;
1949  }
1950 
1951 }
1952 
1953 void C4StartupScenSelDlg::FocusScenList()
1954 {
1955  SetFocus(pScenSelList, false);
1956 }
1957 
1959 {
1960  // map button was clicked: Update selected scenario
1961  if (!pMapData || !pEl) return;
1962  C4ScenarioListLoader::Entry *pSel = GetSelectedEntry(), *pSel2;
1963  pMapData->OnButtonScenario(pEl);
1964  pSel2 = GetSelectedEntry();
1965  if (pSel && pSel==pSel2)
1966  {
1967  // clicking on the selected scenario again starts it
1968  DoOK();
1969  return;
1970  }
1971  // the first click selects it
1972  SetFocus(pEl, false);
1973  UpdateSelection();
1974 }
1975 
1977 {
1978  // Deselect all so current folder info is displayed
1979  if (GetFocus()) C4GUI::GUISound("UI::Tick");
1980  SetFocus(nullptr, true);
1981  if (pMapData) pMapData->ResetSelection();
1982  UpdateSelection();
1983 }
1984 
1986 {
1987  pRenameEdit = pNewRenameEdit;
1988 }
1989 
1991 {
1992  if (pRenameEdit) pRenameEdit->Abort();
1993 }
1994 
1995 void C4StartupScenSelDlg::UpdateAchievements()
1996 {
1997  // Extract all achievements from activated player files and merge them
1998  Achievements.Clear();
1999  char PlayerFilename[_MAX_FNAME_LEN];
2000  C4Group PlayerGrp;
2001  for (int i = 0; SCopySegment(Config.General.Participants, i, PlayerFilename, ';', _MAX_FNAME, true); i++)
2002  {
2003  const char *szPlayerFilename = Config.AtUserDataPath(PlayerFilename);
2004  if (!FileExists(szPlayerFilename)) continue;
2005  if (!PlayerGrp.Open(szPlayerFilename)) continue;
2006  C4PlayerInfoCore nfo;
2007  if (!nfo.Load(PlayerGrp)) continue;
2008  Achievements.Merge(nfo.Achievements);
2009  }
2010 }
2011 
2013 {
2014  if (pSelectionOptions) pSelectionOptions->Update();
2015 }
2016 
2017 // NICHT: 9, 7.2.2, 113-114, 8a
2018 
#define C4CFN_GenericGroupFiles
Definition: C4Components.h:167
#define C4CFN_Savegames
Definition: C4Components.h:35
#define C4CFN_ScenarioParameterDefs
Definition: C4Components.h:43
#define C4CFN_AnyScriptStringTbl
Definition: C4Components.h:69
#define C4CFN_ScenarioTitle
Definition: C4Components.h:84
#define C4CFN_ScriptStringTbl
Definition: C4Components.h:68
#define C4CFN_MapFolderBG
Definition: C4Components.h:136
#define C4CFN_Achievements
Definition: C4Components.h:90
#define C4CFN_FolderCore
Definition: C4Components.h:44
#define C4CFN_ScenarioIcon
Definition: C4Components.h:85
#define C4CFN_ScenarioDesc
Definition: C4Components.h:88
#define C4CFN_Title
Definition: C4Components.h:82
#define C4CFN_ScenarioCore
Definition: C4Components.h:42
#define C4CFN_ScenarioFiles
Definition: C4Components.h:175
#define C4CFN_Version
Definition: C4Components.h:72
#define C4CFN_DefFiles
Definition: C4Components.h:166
#define C4CFN_IconPNG
Definition: C4Components.h:86
#define C4CFN_MapFolderData
Definition: C4Components.h:135
#define C4CFN_WriteTitle
Definition: C4Components.h:83
#define C4CFN_FolderFiles
Definition: C4Components.h:176
C4Config Config
Definition: C4Config.cpp:930
C4Draw * pDraw
Definition: C4Draw.cpp:42
const int C4FCT_Full
Definition: C4FacetEx.h:26
const int C4FCT_Height
Definition: C4FacetEx.h:27
int32_t PictureWidth
C4Game Game
Definition: C4Globals.cpp:52
C4Application Application
Definition: C4Globals.cpp:44
C4Network2 Network
Definition: C4Globals.cpp:53
C4GraphicsResource GraphicsResource
bool C4Group_MoveItem(const char *source, const char *target, bool no_sorting)
Definition: C4Group.cpp:182
bool C4Group_DeleteItem(const char *item_name, bool do_recycle)
Definition: C4Group.cpp:255
bool C4Group_TestIgnore(const char *filename)
Definition: C4Group.cpp:92
bool C4Group_IsGroup(const char *filename)
Definition: C4Group.cpp:104
C4GUIScreen * pGUI
Definition: C4Gui.cpp:1191
#define C4GUI_ButtonHgt
Definition: C4Gui.h:111
#define C4GUI_Caption2FontClr
Definition: C4Gui.h:38
@ KEYSCOPE_Gui
@ KEYS_Control
const char * LoadResStrNoAmp(const char *id)
Definition: StdResStr2.cpp:23
const char * LoadResStr(const char *id)
Definition: C4Language.h:83
bool DebugLogF(const char *strMessage ...)
Definition: C4Log.cpp:290
const int32_t C4MC_Button_LeftDown
void CompileFunc(C4Real &rValue, StdCompiler *pComp)
Definition: C4Real.cpp:9033
float bottom
Definition: C4Rect.h:25
float top
Definition: C4Rect.h:25
float right
Definition: C4Rect.h:25
float left
Definition: C4Rect.h:25
C4Reloc Reloc
Definition: C4Reloc.cpp:21
int EntrySortFunc(const void *pEl1, const void *pEl2)
const uint32_t ClrScenarioItemXtra
bool DirContainsScenarios(const char *szDir)
const uint32_t ClrScenarioItem
const uint32_t ClrScenarioItemDisabled
const int32_t C4StartupScenSel_DefaultIcon_WinFolder
const int32_t C4StartupScenSel_DefaultIcon_OldIconBG
const int32_t C4StartupScenSel_TitlePictureWdt
const int32_t C4StartupScenSel_DefaultIcon_Folder
const int32_t C4StartupScenSel_TitlePicturePadding
const int32_t C4StartupScenSel_TitlePictureHgt
const int32_t C4StartupScenSel_TitleOverlayMargin
const int32_t C4StartupScenSel_DefaultIcon_SavegamesFolder
const int32_t C4StartupScenSel_MaxAchievements
const int ALeft
Definition: C4Surface.h:41
const int ACenter
Definition: C4Surface.h:41
#define _MAX_FNAME
int stricmp(const char *s1, const char *s2)
#define _MAX_PATH
#define _MAX_PATH_LEN
#define DirSep
#define _MAX_FNAME_LEN
uint32_t DWORD
bool SEqual2(const char *szStr1, const char *szStr2)
Definition: Standard.cpp:204
bool SIsModule(const char *szList, const char *szString, int *ipIndex, bool fCaseSensitive)
Definition: Standard.cpp:547
bool SCopySegment(const char *szString, int iSegment, char *sTarget, char cSeparator, int iMaxL, bool fSkipWhitespace)
Definition: Standard.cpp:279
void SCopy(const char *szSource, char *sTarget, size_t iMaxL)
Definition: Standard.cpp:152
bool SEqualNoCase(const char *szStr1, const char *szStr2, int iLen)
Definition: Standard.cpp:213
bool SRemoveModules(char *szList, const char *szModules, bool fCaseSensitive)
Definition: Standard.cpp:605
int SModuleCount(const char *szList)
Definition: Standard.cpp:617
bool SAddModules(char *szList, const char *szModules, bool fCaseSensitive)
Definition: Standard.cpp:576
const char * SSearchNoCase(const char *szString, const char *szIndex)
Definition: Standard.cpp:384
void SAppend(const char *szSource, char *szTarget, int iMaxL)
Definition: Standard.cpp:263
bool SEqual(const char *szStr1, const char *szStr2)
Definition: Standard.h:93
bool Inside(T ival, U lbound, V rbound)
Definition: Standard.h:43
size_t SLen(const char *sptr)
Definition: Standard.h:74
StdPtrAdapt< T > mkPtrAdaptNoNull(T *&rpObj)
Definition: StdAdaptors.h:638
StdArrayAdapt< T, M > mkArrayAdaptMap(T *pArray, int iSize, M &&map)
Definition: StdAdaptors.h:340
StdParameterAdapt< T, P > mkParAdapt(T &&rObj, P &&rPar)
Definition: StdAdaptors.h:490
StdNamingCountAdapt< int_t > mkNamingCountAdapt(int_t &iCount, const char *szName)
Definition: StdAdaptors.h:1008
StdNamingAdapt< T > mkNamingAdapt(T &&rValue, const char *szName)
Definition: StdAdaptors.h:92
StdStrBuf FormatString(const char *szFmt,...)
Definition: StdBuf.cpp:270
bool DirectoryExists(const char *szFilename)
Definition: StdFile.cpp:708
char * GetExtension(char *szFilename)
Definition: StdFile.cpp:118
bool WildcardMatch(const char *szWildcard, const char *szString)
Definition: StdFile.cpp:396
bool ItemIdentical(const char *szFilename1, const char *szFilename2)
Definition: StdFile.cpp:879
void MakeFilenameFromTitle(char *szTitle)
Definition: StdFile.cpp:426
char * GetFilename(char *szPath)
Definition: StdFile.cpp:42
void RemoveExtension(char *szFilename)
Definition: StdFile.cpp:303
bool FileExists(const char *szFileName)
bool ItemExists(const char *szItemName)
Definition: StdFile.h:75
C4FacetSurface * FindByName(const char *name) const
void OpenGame(const char *scenario=nullptr)
bool Load(C4Group &hGroup, const char *szFilename, const char *szLanguage=nullptr)
const char * GetData() const
bool GetLanguageString(const char *szLanguage, StdStrBuf &rTarget)
char Participants[CFG_MaxString+1]
Definition: C4Config.h:39
char LanguageEx[CFG_MaxString+1]
Definition: C4Config.h:38
char Language[CFG_MaxString+1]
Definition: C4Config.h:37
char ModsDataPath[CFG_MaxString+1]
Definition: C4Config.h:57
char MissionAccess[CFG_MaxString+1]
Definition: C4Config.h:47
C4ConfigGeneral General
Definition: C4Config.h:255
C4ConfigStartup Startup
Definition: C4Config.h:264
const char * AtUserDataPath(const char *filename)
Definition: C4Config.cpp:586
int32_t HideMsgStartDedicated
Definition: C4Config.h:188
int32_t AlphabeticalSorting
Definition: C4Config.h:193
static bool SelectDefinitions(C4GUI::Screen *pOnScreen, StdStrBuf *pSelection)
bool Blit(C4Surface *sfcSource, float fx, float fy, float fwdt, float fhgt, C4Surface *sfcTarget, float tx, float ty, float twdt, float thgt, bool fSrcColKey=false, const C4BltTransform *pTransform=nullptr)
Definition: C4Draw.cpp:301
C4Surface * Surface
Definition: C4Facet.h:117
C4Facet GetPhase(int iPhaseX=0, int iPhaseY=0)
Definition: C4Facet.cpp:59
float Hgt
Definition: C4Facet.h:118
float Wdt
Definition: C4Facet.h:118
void Draw(C4Facet &cgo, bool fAspect=true, int32_t iPhaseX=0, int32_t iPhaseY=0, bool fTransparent=true)
Definition: C4Facet.cpp:154
bool Load(C4Group &hGroup, const char *szName, int iWdt, int iHgt, bool fNoErrIfNotFound, int iFlags)
Definition: C4FacetEx.cpp:84
bool Create(int iWdt, int iHgt, int iWdt2=C4FCT_Full, int iHgt2=C4FCT_Full)
Definition: C4FacetEx.cpp:54
void Clear()
Definition: C4FacetEx.h:44
void Set(const C4Facet &cpy)
Definition: C4FacetEx.h:46
void SetText(const char *szToText)
Definition: C4GuiButton.cpp:55
int32_t GetWidth() const
Definition: C4Gui.h:2803
bool GetFromLeft(int32_t iWdt, int32_t iHgt, C4Rect &rcOut)
Definition: C4Gui.cpp:1076
bool GetCentered(int32_t iWdt, int32_t iHgt, C4Rect &rcOut)
Definition: C4Gui.cpp:1133
int32_t GetHeight() const
Definition: C4Gui.h:2804
bool GetFromRight(int32_t iWdt, int32_t iHgt, C4Rect &rcOut)
Definition: C4Gui.cpp:1093
bool GetFromTop(int32_t iHgt, int32_t iWdt, C4Rect &rcOut)
Definition: C4Gui.cpp:1059
void GetAll(C4Rect &rcOut)
Definition: C4Gui.cpp:1125
bool GetFromBottom(int32_t iHgt, int32_t iWdt, C4Rect &rcOut)
Definition: C4Gui.cpp:1109
void AddElement(Element *pChild)
Element * GetFirst()
Definition: C4Gui.h:829
void SetVisibility(bool fToValue) override
void SetFocus(Control *pCtrl, bool fByMouse)
void Close(bool fOK)
bool IsShown()
Definition: C4Gui.h:2148
bool fOK
Definition: C4Gui.h:2083
void UpdateSize() override
virtual void OnShown()
Definition: C4Gui.h:2209
Control * GetFocus()
Definition: C4Gui.h:2116
void ClearText()
Definition: C4GuiEdit.cpp:125
const char * GetText()
Definition: C4Gui.h:1339
virtual void SetVisibility(bool fToValue)
Definition: C4Gui.cpp:207
C4Rect rcBounds
Definition: C4Gui.h:385
virtual Screen * GetScreen()
Definition: C4Gui.cpp:289
C4Rect GetContainedClientRect()
Definition: C4Gui.h:448
void SetBounds(const C4Rect &rcNewBound)
Definition: C4Gui.h:446
void SetToolTip(const char *szNewTooltip, bool is_immediate=false)
Definition: C4Gui.cpp:409
void ClientPos2ScreenPos(int32_t &riX, int32_t &riY)
Definition: C4Gui.cpp:231
C4Rect & GetBounds()
Definition: C4Gui.h:445
void DrawBackground(C4TargetFacet &cgo, C4Facet &rFromFct)
void SetText(const char *szToText, bool fAllowHotkey=true)
Definition: C4GuiLabels.cpp:74
void SetAutosize(bool fToVal)
Definition: C4Gui.h:507
Element * GetSelectedItem()
Definition: C4Gui.h:1581
bool InsertElement(Element *pChild, Element *pInsertBefore, int32_t iIndent=0)
void SetSelectionDblClickFn(BaseCallbackHandler *pToHandler)
Definition: C4Gui.h:1554
void SetSelectionChangeCallbackFn(BaseCallbackHandler *pToHandler)
Definition: C4Gui.h:1549
void SelectFirstEntry(bool fByUser)
Definition: C4Gui.h:1585
Element * GetFirst()
Definition: C4Gui.h:1572
Element * GetLast()
Definition: C4Gui.h:1573
void SetDecoration(bool fDrawBG, ScrollBarFacets *pToGfx, bool fAutoScroll, bool fDrawBorder=false)
Definition: C4Gui.h:1567
void SelectNone(bool fByUser)
Definition: C4Gui.h:1586
int32_t GetMarginTop() override
Definition: C4Gui.h:1576
void SelectEntry(Element *pNewSel, bool fByUser)
void SetFacet(const C4Facet &fct)
Definition: C4Gui.h:612
bool ShowRemoveDlg(Dialog *pDlg)
bool ShowMessageModal(const char *szMessage, const char *szCaption, DWORD dwButtons, Icons icoIcon, int32_t *piConfigDontShowAgainSetting=nullptr)
bool ShowMessage(const char *szMessage, const char *szCaption, Icons icoIcon, int32_t *piConfigDontShowAgainSetting=nullptr)
Sheet * AddSheet(const char *szTitle, int32_t icoTitle=Ico_None)
void SelectSheet(int32_t iIndex, bool fByUser)
Sheet * GetSheet(int32_t iIndex)
Definition: C4Gui.h:1707
void SetSheetMargin(int32_t iMargin)
Definition: C4Gui.h:1714
void SetGfx(C4Facet *pafctBack, C4Facet *pafctClip, C4Facet *pafctIcons, CStdFont *paSheetCaptionFont, bool fResizeByAspect)
void SetDecoration(bool fDrawBG, bool fDrawFrame, ScrollBarFacets *pToGfx, bool fAutoScroll)
Definition: C4Gui.h:1753
void SetPicture(const C4Facet &rNewPic)
void ClearText(bool fDoUpdate)
Definition: C4Gui.h:1745
void AddTextLine(const char *szText, CStdFont *pFont, DWORD dwClr, bool fDoUpdate, bool fMakeReadableOnBlack, CStdFont *pCaptionFont=nullptr)
Definition: C4Gui.h:1741
void UpdateHeight()
Definition: C4Gui.h:1751
C4Rect & GetClientRect() override
Definition: C4Gui.h:864
class C4ScenarioParameters & StartupScenarioParameters
Definition: C4Game.h:68
bool NetworkActive
Definition: C4Game.h:123
bool fObserve
Definition: C4Game.h:121
char DefinitionFilenames[20 *_MAX_PATH_LEN]
Definition: C4Game.h:105
bool fLobby
Definition: C4Game.h:119
void SetParameters(C4ScenarioParameterDefs *param_defs, C4ScenarioParameters *params)
CStdFont & GetFontByHeight(int32_t iHgt, float *pfZoom=nullptr)
C4AchievementGraphics Achievements
bool FindNextEntry(const char *wildcard, StdStrBuf *filename=nullptr, size_t *size=nullptr, bool start_at_filename=false)
Definition: C4Group.cpp:2217
const char * GetName() const
Definition: C4Group.cpp:2309
StdStrBuf GetFullName() const
Definition: C4Group.cpp:2638
int PreCacheEntries(const char *search_pattern, bool cache_previous=false)
Definition: C4Group.cpp:2831
bool Add(const char *filename, const char *entry_name)
Definition: C4Group.cpp:1621
bool OpenAsChild(C4Group *mother, const char *entry_name, bool is_exclusive=false, bool do_create=false)
Definition: C4Group.cpp:1952
bool LoadEntryString(const char *entry_name, StdStrBuf *buffer)
Definition: C4Group.cpp:2430
const char * GetError()
Definition: C4Group.cpp:650
void ResetSearch(bool reload_contents=false)
Definition: C4Group.cpp:1316
bool Close()
Definition: C4Group.cpp:971
bool Delete(const char *files, bool recursive=false)
Definition: C4Group.cpp:1645
bool FindEntry(const char *wildcard, StdStrBuf *filename=nullptr, size_t *size=nullptr)
Definition: C4Group.cpp:2211
bool Open(const char *group_name, bool do_create=false)
Definition: C4Group.cpp:660
void ReplaceStrings(StdStrBuf &rBuf)
static bool LoadComponentHost(C4ComponentHost *host, C4Group &hGroup, const char *szFilename, const char *szLanguage)
Definition: C4Language.cpp:232
bool Load(C4Group &hGroup, C4ScenarioListLoader::Folder *pScenLoaderFolder)
void CompileFunc(StdCompiler *pComp)
C4GUI::TextWindow * GetSelectionInfoBox() const
void OnButtonScenario(C4GUI::Control *pEl)
C4ScenarioListLoader::Entry * GetSelectedEntry() const
void CreateGUIElements(C4StartupScenSelDlg *pMainDlg, C4GUI::Window &rContainer)
void SetPassword(const char *szToPassword)
Definition: C4Network2.cpp:772
bool Load(C4Group &hGroup)
Definition: C4InfoCore.cpp:88
C4ScenarioParameters Achievements
Definition: C4InfoCore.h:108
Definition: C4Rect.h:28
int32_t y
Definition: C4Rect.h:30
int32_t GetMiddleX() const
Definition: C4Rect.h:56
int32_t Hgt
Definition: C4Rect.h:30
int32_t Wdt
Definition: C4Rect.h:30
int32_t GetBottom() const
Definition: C4Rect.h:58
int32_t GetMiddleY() const
Definition: C4Rect.h:57
int32_t x
Definition: C4Rect.h:30
void Set(int32_t iX, int32_t iY, int32_t iWdt, int32_t iHgt)
Definition: C4Rect.cpp:86
bool AllowUserChange
Definition: C4Scenario.h:93
bool GetModules(StdStrBuf *psOutModules) const
Definition: C4Scenario.cpp:422
C4SDefinitions Definitions
Definition: C4Scenario.h:233
Entry(class C4ScenarioListLoader *pLoader, class Folder *pParent)
virtual Folder * GetIsFolder()
virtual bool CanOpen(StdStrBuf &sError, bool &CanHide)
virtual C4ScenarioParameterDefs * GetParameterDefs()
int iIconIndex
class Folder * GetParent() const
const StdStrBuf & GetEntryFilename() const
virtual bool IsGrayed()
int iDifficulty
const C4Facet & GetTitlePicture() const
virtual bool GetAchievement(int32_t idx, C4Facet *out_facet, const char **out_description)
virtual StdStrBuf GetOpenTooltip()=0
int GetFolderIndex()
bool Load(C4Group *pFromGrp, const StdStrBuf *psFilename, bool fLoadEx)
int iFolderIndex
Entry * GetNext() const
virtual StdStrBuf GetOpenText()=0
virtual ~Entry()
bool RenameTo(const char *szNewName)
virtual bool SetTitleInGroup(C4Group &rGrp, const char *szNewTitle)
virtual C4ScenarioParameters * GetParameters()
const StdStrBuf & GetAuthor() const
const C4Facet & GetIconFacet() const
const StdStrBuf & GetDesc() const
static Entry * CreateEntryForFile(const StdStrBuf &sFilename, C4ScenarioListLoader *pLoader, Folder *pParent)
const StdStrBuf & GetName() const
virtual bool Start()=0
Entry * pNext
class Folder * pParent
int GetDifficulty()
int GetIconIndex()
const StdStrBuf & GetVersion() const
virtual StdStrBuf GetTypeName()=0
bool LoadContents(C4ScenarioListLoader *pLoader, C4Group *pFromGrp, const StdStrBuf *psFilename, bool fLoadEx, bool fReload)
Entry * FindEntryByName(const char *szFilename) const
bool LoadCustomPre(C4Group &rGrp) override
C4MapFolderData * GetMapData() const
bool LoadCustom(C4Group &rGrp, bool fNameLoaded, bool fIconLoaded) override
bool DoLoadContents(C4ScenarioListLoader *pLoader, C4Group *pFromGrp, const StdStrBuf &sFilename, bool fLoadEx) override
bool LoadCustom(C4Group &rGrp, bool fNameLoaded, bool fIconLoaded) override
bool CanOpen(StdStrBuf &sError, bool &CanHide) override
bool LoadCustomPre(C4Group &rGrp) override
bool GetAchievement(int32_t idx, C4Facet *out_facet, const char **out_description) override
C4ScenarioParameters * GetParameters() override
const C4Scenario & GetC4S() const
bool DoLoadContents(C4ScenarioListLoader *pLoader, C4Group *pFromGrp, const StdStrBuf &sFilename, bool fLoadEx) override
bool LoadCustom(C4Group &rGrp, bool fNameLoaded, bool fIconLoaded) override
Folder * GetRootFolder() const
Entry * GetFirstEntry() const
C4ScenarioListLoader(const C4ScenarioParameters &Achievements)
bool DoProcessCallback(int32_t iProgress, int32_t iMaxProgress, const char *current_load_info)
bool Load(const StdStrBuf &sRootFolder)
int32_t GetProgressPercent() const
Folder * GetCurrFolder() const
const char * GetProgressInfo() const
bool LoadExtended(Entry *pEntry)
const char * GetID() const
const Option * GetOptionByValue(int32_t val) const
const char * GetAchievement() const
static StdStrBuf AddFilename2ID(const char *filename, const char *id)
void Merge(const C4ScenarioParameters &other)
C4FacetID fctScenSelIcons
Definition: C4Startup.h:73
C4FacetID fctOptionsTabClip
Definition: C4Startup.h:83
C4FacetID fctOptionsIcons
Definition: C4Startup.h:83
CStdFont BookFontTitle
Definition: C4Startup.h:77
CStdFont & GetBlackFontByHeight(int32_t iHgt, float *pfZoom)
Definition: C4Startup.cpp:98
CStdFont BookFontCapt
Definition: C4Startup.h:77
CStdFont BookFont
Definition: C4Startup.h:77
CStdFont BookSmallFont
Definition: C4Startup.h:77
static C4Startup * Get()
Definition: C4Startup.h:147
class C4StartupDlg * SwitchDialog(DialogID eToDlg, bool fFade=true, const char *szSubDialog=nullptr)
Definition: C4Startup.cpp:139
C4StartupGraphics Graphics
Definition: C4Startup.h:112
bool CheckNameHotkey(const char *c) override
C4ScenarioListLoader::Entry * GetEntry() const
C4GUI::RenameEdit::RenameResult DoRenaming(RenameParams par, const char *szNewName)
void MouseInput(C4GUI::CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam) override
ScenListItem(C4GUI::ListBox *pForListBox, C4ScenarioListLoader::Entry *pForEntry, C4GUI::Element *pInsertBeforeElement=nullptr)
void DeleteConfirm(ScenListItem *pSel)
static C4StartupScenSelDlg * pInstance
void StartRenaming(C4GUI::RenameEdit *pNewRenameEdit)
void DrawElement(C4TargetFacet &cgo) override
bool DoBack(bool fAllowClose)
void OnClosed(bool fOK) override
void OnSelChange(class C4GUI::Element *pEl)
void KeyCheat2(const StdStrBuf &rsCheatCode)
C4StartupScenSelDlg(bool fNetwork)
bool StartScenario(C4ScenarioListLoader::Scenario *pStartScen)
void SetBackground(C4Facet *pNewBG)
void OnSelDblClick(class C4GUI::Element *pEl)
C4GUI::Edit::InputResult OnSearchBarEnter(C4GUI::Edit *edt, bool fPasting, bool fPastingMore)
void OnNextBtn(C4GUI::Control *btn)
void OnBackBtn(C4GUI::Control *btn)
void OnLeagueOptionChanged() override
void OnButtonScenario(C4GUI::Control *pEl)
bool OpenFolder(C4ScenarioListLoader::Folder *pNewFolder)
bool SetPixDw(int iX, int iY, DWORD dwCol)
Definition: C4Surface.cpp:576
int Wdt
Definition: C4Surface.h:65
DWORD GetPixDw(int iX, int iY, bool fApplyModulation)
Definition: C4Surface.cpp:491
bool Unlock()
Definition: C4Surface.cpp:464
bool Lock()
Definition: C4Surface.cpp:453
bool SavePNG(C4Group &hGroup, const char *szFilename, bool fSaveAlpha=true, bool fSaveOverlayOnly=false)
float TargetY
Definition: C4Facet.h:165
float TargetX
Definition: C4Facet.h:165
int GetLineHeight() const
Definition: C4FontLoader.h:125
bool GetTextExtent(const char *szText, int32_t &rsx, int32_t &rsy, bool fCheckMarkup=true)
void Value(const T &rStruct)
Definition: StdCompiler.h:161
virtual bool isDeserializer()
Definition: StdCompiler.h:53
bool ScheduleProcs(int iTimeout=1000/36)
int Replace(const char *szOld, const char *szNew, size_t iStartSearch=0)
Definition: StdBuf.cpp:284
void Ref(const char *pnData)
Definition: StdBuf.h:455
const char * getData() const
Definition: StdBuf.h:442
void Copy()
Definition: StdBuf.h:467
const char * getPtr(size_t i) const
Definition: StdBuf.h:448
void Clear()
Definition: StdBuf.h:466
size_t getLength() const
Definition: StdBuf.h:445
void Take(char *pnData)
Definition: StdBuf.h:457
void Format(const char *szFmt,...) GNUC_FORMAT_ATTRIBUTE_O
Definition: StdBuf.cpp:174
char * GrabPointer()
Definition: StdBuf.h:459
int32_t GetScreenHgt()
Definition: C4Gui.h:2825
int32_t GetScreenWdt()
Definition: C4Gui.h:2824
@ Ico_Notify
Definition: C4Gui.h:642
@ Ico_Options
Definition: C4Gui.h:656
@ Ico_Error
Definition: C4Gui.h:652
void GUISound(const char *szSound)
Definition: C4Gui.cpp:1175