OpenClonk
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros
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
77  if (iButton == C4MC_Button_LeftDown && C4StartupScenSelDlg::pInstance)
78  {
79  C4StartupScenSelDlg::pInstance->DeselectAll();
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  // open folders only if they contain a scenario or folder
561  if (DirContainsScenarios(szFilename))
562  return new RegularFolder(pLoader, pParent);
563  }
564  // type not recognized
565  return nullptr;
566 }
567 
568 bool C4ScenarioListLoader::Entry::RenameTo(const char *szNewName)
569 {
570  // change name+filename
571  // some name sanity validation
572  if (!szNewName || !*szNewName) return false;
573  if (SEqual(szNewName, sName.getData())) return true;
574  char fn[_MAX_PATH+1];
575  SCopy(szNewName, fn, _MAX_PATH);
576  // generate new file name
578  if (!*fn) return false;
579  const char *szExt = GetDefaultExtension();
580  if (szExt) { SAppend(".", fn, _MAX_PATH); SAppend(szExt, fn, _MAX_PATH); }
581  char fullfn[_MAX_PATH+1];
582  SCopy(sFilename.getData(), fullfn, _MAX_PATH);
583  char *fullfn_fn = GetFilename(fullfn);
584  SCopy(fn, fullfn_fn, _MAX_PATH - (fullfn_fn - fullfn));
585  StdCopyStrBuf strErr(LoadResStr("IDS_FAIL_RENAME"));
586  // check if a rename is due
587  if (!ItemIdentical(sFilename.getData(), fullfn))
588  {
589  // check for duplicate filename
590  if (ItemExists(fullfn))
591  {
592  StdStrBuf sMsg; sMsg.Format(LoadResStr("IDS_ERR_FILEEXISTS"), fullfn);
594  return false;
595  }
596  // OK; then rename
597  if (!C4Group_MoveItem(sFilename.getData(), fullfn, true))
598  {
599  StdStrBuf sMsg; sMsg.Format(LoadResStr("IDS_ERR_RENAMEFILE"), sFilename.getData(), fullfn);
601  return false;
602  }
603  sFilename.Copy(fullfn);
604  }
605  // update real name in group, if this is a group
606  if (C4Group_IsGroup(fullfn))
607  {
608  C4Group Grp;
609  if (!Grp.Open(fullfn))
610  {
611  StdStrBuf sMsg; sMsg.Format(LoadResStr("IDS_ERR_OPENFILE"), sFilename.getData(), Grp.GetError());
613  return false;
614  }
615  if (!Grp.Delete(C4CFN_Title))
616  {
617  StdStrBuf sMsg; sMsg.Format(LoadResStr("IDS_ERR_DELOLDTITLE"), sFilename.getData(), Grp.GetError());
619  return false;
620  }
621  if (!SetTitleInGroup(Grp, szNewName)) return false;
622  if (!Grp.Close())
623  {
624  StdStrBuf sMsg; sMsg.Format(LoadResStr("IDS_ERR_WRITENEWTITLE"), sFilename.getData(), Grp.GetError());
626  return false;
627  }
628  }
629  // update title
630  sName.Copy(szNewName);
631  // done
632  return true;
633 }
634 
635 bool C4ScenarioListLoader::Entry::SetTitleInGroup(C4Group &rGrp, const char *szNewTitle)
636 {
637  // default for group files: Create a title text file and set the title in there
638  // no title needed if filename is sufficient - except for scenarios, where a Scenario.txt could overwrite the title
639  if (!IsScenario())
640  {
641  StdStrBuf sNameByFile; sNameByFile.Copy(GetFilename(sFilename.getData()));
642  char *szBuf = sNameByFile.GrabPointer();
643  RemoveExtension(szBuf);
644  sNameByFile.Take(szBuf);
645  if (SEqual(szNewTitle, sNameByFile.getData())) return true;
646  }
647  // okay, make a title
648  StdStrBuf sTitle; sTitle.Format("%s:%s", Config.General.Language, szNewTitle);
649  if (!rGrp.Add(C4CFN_WriteTitle, sTitle, false, true))
650  {
651  StdStrBuf sMsg; sMsg.Format(LoadResStr("IDS_ERR_ERRORADDINGNEWTITLEFORFIL"), sFilename.getData(), rGrp.GetError());
653  return false;
654  }
655  return true;
656 }
657 
658 
659 // ------------------------------------
660 // Scenario
661 
663 {
664  // load scenario core first
665  StdStrBuf sFileContents;
666  if (!rGrp.LoadEntryString(C4CFN_ScenarioCore, &sFileContents)) return false;
667  if (!CompileFromBuf_LogWarn<StdCompilerINIRead>(mkParAdapt(C4S, false), sFileContents, (rGrp.GetFullName() + DirSep C4CFN_ScenarioCore).getData()))
668  return false;
669  // Mission access
670  fNoMissionAccess = (!C4S.Head.MissionAccess.empty() && !SIsModule(Config.General.MissionAccess, C4S.Head.MissionAccess.c_str()));
671  // Localized parameter definitions. needed for achievements and parameter input boxes.
672  // Only show them for "real" scenarios
673  if (!C4S.Head.SaveGame && !C4S.Head.Replay)
674  {
675  // Skipping ahead in regular reading list, so keep other entries in memory
678  C4LangStringTable ScenarioLangStringTable;
680  ParameterDefs.Load(rGrp, &ScenarioLangStringTable);
681  // achievement images: Loaded from this entry and parent folder
682  nAchievements = 0;
683  const C4ScenarioParameterDefs *deflists[] = { pParent ? pParent->GetAchievementDefs() : nullptr, &ParameterDefs };
684  for (auto deflist : deflists)
685  {
686  if (!deflist) continue;
687  const C4ScenarioParameterDef *def; size_t idx=0;
688  while ((def = deflist->GetParameterDefByIndex(idx++)))
689  {
690  if (def->IsAchievement())
691  {
692  int32_t val = pLoader->GetAchievements().GetValueByID(C4ScenarioParameters::AddFilename2ID(rGrp.GetFullName().getData(), def->GetID()).getData(), def->GetDefault());
693  if (val)
694  {
695  // player has this achievement - find graphics for it
696  const char *achievement_gfx = def->GetAchievement();
697  StdStrBuf sAchievementFilename(C4CFN_Achievements);
698  sAchievementFilename.Replace("*", achievement_gfx);
699  // look in scenario
700  if (!fctAchievements[nAchievements].Load(rGrp, sAchievementFilename.getData(), C4FCT_Height, C4FCT_Full, false, true))
701  {
702  // look in parent folder
703  const C4FacetSurface *fct = nullptr;
704  const C4AchievementGraphics *parent_achv_gfx;
705  if (pParent && (parent_achv_gfx = pParent->GetAchievementGfx())) fct = parent_achv_gfx->FindByName(achievement_gfx);
706  // look in main gfx group file
707  if (!fct) fct = ::GraphicsResource.Achievements.FindByName(achievement_gfx);
708  if (!fct) continue; // achievement graphics not found :(
709  fctAchievements[nAchievements].Set((const C4Facet &)*fct);
710  }
711  // section by achievement index (1-based, since zero means no achievement)
712  if (val>1) fctAchievements[nAchievements].X += fctAchievements[nAchievements].Wdt * (val-1);
713  // description for this achievement is taken from option
714  const C4ScenarioParameterDef::Option *opt = def->GetOptionByValue(val);
715  if (opt) sAchievementDescriptions[nAchievements] = opt->Description;
716  // keep track of achievement count
717  ++nAchievements;
718  if (nAchievements == C4StartupScenSel_MaxAchievements) break;
719  }
720  }
721  }
722  }
723  }
724  return true;
725 }
726 
727 bool C4ScenarioListLoader::Scenario::LoadCustom(C4Group &rGrp, bool fNameLoaded, bool fIconLoaded)
728 {
729  // icon fallback: Standard scenario icon
730  if (!fIconLoaded)
731  {
732  iIconIndex = C4S.Head.Icon;
733  fctIcon.Set(C4Startup::Get()->Graphics.fctScenSelIcons.GetSection(C4S.Head.Icon));
734  }
735  // scenario name fallback to core
736  if (!fNameLoaded)
737  sName = C4S.Head.Title;
738  // difficulty: Set only for regular rounds (not savegame or record) to avoid bogus sorting
739  if (!C4S.Head.SaveGame && !C4S.Head.Replay)
740  iDifficulty = C4S.Head.Difficulty;
741  else
742  iDifficulty = 0;
743  // minimum required player count
744  iMinPlrCount = C4S.GetMinPlayer();
745  return true;
746 }
747 
748 bool C4ScenarioListLoader::Scenario::GetAchievement(int32_t idx, C4Facet *out_facet, const char **out_description)
749 {
750  // return true and fill output parameters if player got the indexed achievement
751  if (idx < 0 || idx >= nAchievements) return false;
752  *out_facet = fctAchievements[idx];
753  *out_description = sAchievementDescriptions[idx].getData();
754  return true;
755 }
756 
758 {
759  // gogo!
760  if (!(C4StartupScenSelDlg::pInstance)) return false;
761  return (C4StartupScenSelDlg::pInstance)->StartScenario(this);
762 }
763 
765 {
766  // safety
768  if (!pDlg) return false;
769  // check mission access
770  if (!HasMissionAccess())
771  {
772  sErrOut.Copy(LoadResStr("IDS_PRC_NOMISSIONACCESS"));
773  return false;
774  }
775  // replay
776  if (C4S.Head.Replay)
777  {
778  // replays can currently not be launched in network mode
779  if (pDlg->IsNetworkStart())
780  {
781  sErrOut.Copy(LoadResStr("IDS_PRC_NONETREPLAY"));
782  return false;
783  }
784  }
785  // regular game
786  else
787  {
788  // check player count
789  int32_t iPlrCount = SModuleCount(Config.General.Participants);
790  int32_t iMaxPlrCount = C4S.Head.MaxPlayer;
791  if (C4S.Head.SaveGame)
792  {
793  // Some scenarios have adjusted MaxPlayerCount to 0 after starting to prevent future joins
794  // make sure it's possible to start the savegame anyway
795  iMaxPlrCount = std::max<int32_t>(iMinPlrCount, iMaxPlrCount);
796  }
797  // normal scenarios: At least one player except in network mode, where it is possible to wait for the additional players
798  // Melees need at least two
799  if ((iPlrCount < iMinPlrCount))
800  {
801  if (pDlg->IsNetworkStart())
802  {
803  // network game: Players may yet join in lobby
804  // only issue a warning for too few players (by setting the error but not returning false here)
805  sErrOut.Format(LoadResStr("IDS_MSG_TOOFEWPLAYERSNET"), (int) iMinPlrCount);
806  }
807  else
808  {
809  // for regular games, this is a fatal no-start-cause
810  sErrOut.Format(LoadResStr("IDS_MSG_TOOFEWPLAYERS"), (int) iMinPlrCount);
811  return false;
812  }
813  }
814  // scenarios (both normal and savegame) may also impose a maximum player restriction
815  if (iPlrCount > iMaxPlrCount)
816  {
817  sErrOut.Format(LoadResStr("IDS_MSG_TOOMANYPLAYERS"), (int) C4S.Head.MaxPlayer);
818  return false;
819  }
820  }
821  // Okay, start!
822  return true;
823 }
824 
826 {
827  return StdCopyStrBuf(LoadResStr("IDS_BTN_STARTGAME"));
828 }
829 
831 {
832  return StdCopyStrBuf(LoadResStr("IDS_DLGTIP_SCENSELNEXT"));
833 }
834 
835 
836 // ------------------------------------
837 // Folder
838 
840 {
841  if (pMapData) delete pMapData;
842  ClearChildren();
843 }
844 
846 {
847  // open as subfolder
848  if (!C4StartupScenSelDlg::pInstance) return false;
849  return C4StartupScenSelDlg::pInstance->OpenFolder(this);
850 }
851 int
852 #ifdef _MSC_VER
853 __cdecl
854 #endif
855 EntrySortFunc(const void *pEl1, const void *pEl2)
856 {
857  C4ScenarioListLoader::Entry *pEntry1 = *(C4ScenarioListLoader::Entry * const *) pEl1, *pEntry2 = *(C4ScenarioListLoader::Entry * const *) pEl2;
858  // sort folders before scenarios
859  bool fS1 = !pEntry1->GetIsFolder(), fS2 = !pEntry2->GetIsFolder();
860  if (fS1 != fS2) return fS1-fS2;
861  // sort by folder index (undefined index 0 goes to the end)
862  if (!Config.Startup.AlphabeticalSorting) if (pEntry1->GetFolderIndex() || pEntry2->GetFolderIndex())
863  {
864  if (!pEntry1->GetFolderIndex()) return +1;
865  if (!pEntry2->GetFolderIndex()) return -1;
866  int32_t iDiff = pEntry1->GetFolderIndex() - pEntry2->GetFolderIndex();
867  if (iDiff) return iDiff;
868  }
869  // sort by numbered standard scenario icons
870  if (Inside(pEntry1->GetIconIndex(), 2, 11))
871  {
872  int32_t iDiff = pEntry1->GetIconIndex() - pEntry2->GetIconIndex();
873  if (iDiff) return iDiff;
874  }
875  // sort by difficulty (undefined difficulty goes to the end)
876  if (!Config.Startup.AlphabeticalSorting) if (pEntry1->GetDifficulty() || pEntry2->GetDifficulty())
877  {
878  if (!pEntry1->GetDifficulty()) return +1;
879  if (!pEntry2->GetDifficulty()) return -1;
880  int32_t iDiff = pEntry1->GetDifficulty() - pEntry2->GetDifficulty();
881  if (iDiff) return iDiff;
882  }
883  // otherwise, sort by name
884  return stricmp(pEntry1->GetName().getData(), pEntry2->GetName().getData());
885 }
886 
888 {
889  uint32_t iCount = 0;
890  for (Entry *i = pFirst; i; i = i->pNext) ++iCount;
891  return iCount;
892 }
893 
895 {
896  // use C-Library-QSort on a buffer of entry pointers; then re-link list
897  if (!pFirst) return;
898  uint32_t iCount,i;
899  Entry **ppEntries = new Entry *[i = iCount = GetEntryCount()], **ppI, *pI=pFirst, **ppIThis;
900  for (ppI = ppEntries; i--; pI = pI->pNext) *ppI++ = pI;
901  qsort(ppEntries, iCount, sizeof(Entry *), &EntrySortFunc);
902  ppIThis = &pFirst;
903  for (ppI = ppEntries; iCount--; ppIThis = &((*ppIThis)->pNext)) *ppIThis = *ppI++;
904  *ppIThis = nullptr;
905  delete [] ppEntries;
906 }
907 
909 {
910  // folder deletion: del all the tree non-recursively
911  Folder *pDelFolder = this, *pCheckFolder;
912  for (;;)
913  {
914  // delete all children as long as they are not folders
915  Entry *pChild;
916  while ((pChild = pDelFolder->pFirst))
917  if ((pCheckFolder = pChild->GetIsFolder()))
918  // child entry if folder: Continue delete in there
919  pDelFolder = pCheckFolder;
920  else
921  // regular child entry: del it
922  // destructor of child will remove it from list
923  delete pChild;
924  // this emptied: Done!
925  if (pDelFolder == this) break;
926  // deepest child recursion reached: Travel up folders
927  pDelFolder = (pCheckFolder = pDelFolder)->pParent;
928  assert(pDelFolder);
929  delete pCheckFolder;
930  }
931 }
932 
933 bool C4ScenarioListLoader::Folder::LoadContents(C4ScenarioListLoader *pLoader, C4Group *pFromGrp, const StdStrBuf *psFilename, bool fLoadEx, bool fReload)
934 {
935  // contents already loaded?
936  if (fContentsLoaded && !fReload) return true;
937  // clear previous
938  if (pMapData) { delete pMapData; pMapData = nullptr; }
939  // if filename is not given, assume it's been loaded in this entry
940  if (!psFilename) psFilename = &this->sFilename; else this->sFilename = *psFilename;
941  // nothing loaded: Load now
942  if (!DoLoadContents(pLoader, pFromGrp, *psFilename, fLoadEx)) return false;
943  // sort loaded stuff by name
944  Sort();
945  return true;
946 }
947 
949 {
950  // do a case-insensitive filename comparison
951  for (Entry *pEntry = pFirst; pEntry; pEntry = pEntry->GetNext())
952  if (SEqualNoCase(szFilename, GetFilename(pEntry->GetEntryFilename().getData())))
953  return pEntry;
954  // nothing found
955  return nullptr;
956 }
957 
959 {
960  return StdCopyStrBuf(LoadResStr("IDS_BTN_OPEN"));
961 }
962 
964 {
965  return StdCopyStrBuf(LoadResStr("IDS_DLGTIP_SCENSELNEXT"));
966 }
967 
969 {
970  return false;
971 }
972 
974 {
975  // load folder core if available
976  StdStrBuf sFileContents;
977  if (rGrp.LoadEntryString(C4CFN_FolderCore, &sFileContents))
978  if (!CompileFromBuf_LogWarn<StdCompilerINIRead>(C4F, sFileContents, (rGrp.GetFullName() + DirSep C4CFN_FolderCore).getData()))
979  return false;
980  return true;
981 }
982 
983 // ------------------------------------
984 // SubFolder
985 
986 bool C4ScenarioListLoader::SubFolder::LoadCustom(C4Group &rGrp, bool fNameLoaded, bool fIconLoaded)
987 {
988  // default icon fallback
989  if (!fIconLoaded)
990  {
992  else iIconIndex = C4StartupScenSel_DefaultIcon_Folder;
993  fctIcon.Set(C4Startup::Get()->Graphics.fctScenSelIcons.GetSection(iIconIndex));
994  }
995  // folder index
996  iFolderIndex = C4F.Head.Index;
997  return true;
998 }
999 
1000 bool C4ScenarioListLoader::SubFolder::DoLoadContents(C4ScenarioListLoader *pLoader, C4Group *pFromGrp, const StdStrBuf &sFilename, bool fLoadEx)
1001 {
1002  assert(pLoader);
1003  // clear any previous
1004  ClearChildren();
1005  // group specified: Load as child
1006  C4Group Group;
1007  if (pFromGrp)
1008  {
1009  if (!Group.OpenAsChild(pFromGrp, sFilename.getData())) return false;
1010  }
1011  else
1012  // no parent group: Direct load from filename
1013  if (!Group.Open(sFilename.getData())) return false;
1014  // Load achievement data contained scenarios can fall back to
1015  C4LangStringTable FolderLangStringTable;
1017  AchievementDefs.Load(Group, &FolderLangStringTable);
1018  AchievementGfx.Init(Group);
1019  // get number of entries, to estimate progress
1020  const char *szC4CFN_ScenarioFiles = C4CFN_ScenarioFiles; // assign values for constant comparison
1021  const char *szSearchMask; int32_t iEntryCount=0;
1022  for (szSearchMask = szC4CFN_ScenarioFiles; szSearchMask;)
1023  {
1024  Group.ResetSearch();
1025  while (Group.FindNextEntry(szSearchMask)) ++iEntryCount;
1026  // next search mask
1027  if (szSearchMask == szC4CFN_ScenarioFiles)
1028  szSearchMask = C4CFN_FolderFiles;
1029  else
1030  szSearchMask = nullptr;
1031  }
1032  // initial progress estimate
1033  if (!pLoader->DoProcessCallback(0, iEntryCount, nullptr)) return false;
1034  // iterate through group contents
1035  char ChildFilename[_MAX_FNAME+1]; StdStrBuf sChildFilename; int32_t iLoadCount=0;
1036  for (szSearchMask = szC4CFN_ScenarioFiles; szSearchMask;)
1037  {
1038  Group.ResetSearch();
1039  while (Group.FindNextEntry(szSearchMask, ChildFilename))
1040  {
1041  // mark progress
1042  if (!pLoader->DoProcessCallback(iLoadCount, iEntryCount, ChildFilename)) return false;
1043  sChildFilename.Ref(ChildFilename);
1044  // okay; create this item
1045  Entry *pNewEntry = Entry::CreateEntryForFile(sChildFilename, pLoader, this);
1046  if (pNewEntry)
1047  {
1048  // ...and load it
1049  if (!pNewEntry->Load(&Group, &sChildFilename, fLoadEx))
1050  {
1051  DebugLogF(R"(Error loading entry "%s" in SubFolder "%s"!)", sChildFilename.getData(), Group.GetFullName().getData());
1052  delete pNewEntry;
1053  }
1054  }
1055  ++iLoadCount;
1056  }
1057  // next search mask
1058  if (szSearchMask == szC4CFN_ScenarioFiles)
1059  szSearchMask = C4CFN_FolderFiles;
1060  else
1061  szSearchMask = nullptr;
1062  }
1063  // load map folder data
1064  if (Group.FindEntry(C4CFN_MapFolderData))
1065  {
1066  pMapData = new C4MapFolderData();
1067  if (!pMapData->Load(Group, this))
1068  {
1069  // load error :(
1070  delete pMapData;
1071  pMapData = nullptr;
1072  }
1073  }
1074  // done, success
1075  fContentsLoaded = true;
1076  return true;
1077 }
1078 
1079 
1080 // ------------------------------------
1081 // RegularFolder
1082 
1084 
1085 bool C4ScenarioListLoader::RegularFolder::LoadCustom(C4Group &rGrp, bool fNameLoaded, bool fIconLoaded)
1086 {
1087  // default icon fallback
1088  if (!fIconLoaded)
1089  fctIcon.Set(C4Startup::Get()->Graphics.fctScenSelIcons.GetSection(C4StartupScenSel_DefaultIcon_WinFolder));
1090  // folder index
1091  iFolderIndex = C4F.Head.Index;
1092  return true;
1093 }
1094 
1095 bool C4ScenarioListLoader::RegularFolder::DoLoadContents(C4ScenarioListLoader *pLoader, C4Group *pFromGrp, const StdStrBuf &sFilename, bool fLoadEx)
1096 {
1097  // clear any previous
1098  ClearChildren();
1099  // regular folders must exist and not be within group!
1100  assert(!pFromGrp);
1101  if (sFilename.getData() && sFilename[0])
1102  Merge(sFilename.getData());
1103 
1104  // get number of entries, to estimate progress
1105  int32_t iCountLoaded=0, iCountTotal=0;
1106  NameList::iterator it;
1107  for (it = contents.begin(); it != contents.end(); ++it)
1108  {
1109  if (!DirectoryExists(it->c_str())) continue;
1110  DirectoryIterator DirIter(it->c_str());
1111  const char *szChildFilename;
1112  for (; (szChildFilename = *DirIter); ++DirIter)
1113  {
1114  if (!*szChildFilename || *GetFilename(szChildFilename)=='.') continue;
1115  ++iCountTotal;
1116  }
1117  }
1118  // initial progress estimate
1119  if (!pLoader->DoProcessCallback(iCountLoaded, iCountTotal, nullptr)) return false;
1120 
1121  // do actual loading of files
1122  std::set<std::string> names;
1123  const char *szChildFilename;
1124  for (it = contents.begin(); it != contents.end(); ++it)
1125  {
1126  if (!pLoader->DoProcessCallback(iCountLoaded, iCountTotal, GetFilename(it->c_str()))) return false;
1127  for (DirectoryIterator DirIter(it->c_str()); (szChildFilename = *DirIter); ++DirIter)
1128  {
1129  StdStrBuf sChildFilename(szChildFilename);
1130  szChildFilename = GetFilename(szChildFilename);
1131  // progress callback
1132  if (!pLoader->DoProcessCallback(iCountLoaded, iCountTotal, szChildFilename)) return false;
1133  // Ignore directory navigation entries and CVS folders
1134  if (C4Group_TestIgnore(szChildFilename)) continue;
1135  if (names.find(szChildFilename) != names.end()) continue;
1136  names.insert(szChildFilename);
1137  // filename okay; create this item
1138  Entry *pNewEntry = Entry::CreateEntryForFile(sChildFilename, pLoader, this);
1139  if (pNewEntry)
1140  {
1141  // ...and load it
1142  if (!pNewEntry->Load(nullptr, &sChildFilename, fLoadEx))
1143  {
1144  DebugLogF(R"(Error loading entry "%s" in Folder "%s"!)", szChildFilename, it->c_str());
1145  delete pNewEntry;
1146  }
1147  }
1148  ++iCountLoaded;
1149  }
1150  }
1151  // done, success
1152  fContentsLoaded = true;
1153  return true;
1154 }
1155 
1157 {
1158  contents.emplace_back(szPath);
1159 }
1160 
1161 // ------------------------------------
1162 // C4ScenarioListLoader
1163 
1164 C4ScenarioListLoader::C4ScenarioListLoader(const C4ScenarioParameters &Achievements) : Achievements(Achievements), pRootFolder(nullptr), pCurrFolder(nullptr),
1165  iLoading(0), iProgress(0), iMaxProgress(0), fAbortThis(false), fAbortPrevious(false)
1166 {
1167 }
1168 
1170 {
1171  if (pRootFolder) delete pRootFolder;
1172 }
1173 
1174 bool C4ScenarioListLoader::BeginActivity(bool fAbortPrevious)
1175 {
1176  // if previous activities were running, stop them first if desired
1177  if (iLoading && fAbortPrevious)
1178  this->fAbortPrevious = true;
1179  // mark this activity
1180  ++iLoading;
1181  // progress of activity not yet decided
1182  iProgress = iMaxProgress = 0;
1183  current_load_info.Clear();
1184  // okay; start activity
1185  return true;
1186 }
1187 
1188 void C4ScenarioListLoader::EndActivity()
1189 {
1190  assert(iLoading);
1191  if (!--iLoading)
1192  {
1193  // last activity done: Reset any flags
1194  fAbortThis = false;
1195  fAbortPrevious = false;
1196  iProgress = iMaxProgress = 0;
1197  current_load_info.Clear();
1198  }
1199  else
1200  {
1201  // child activity done: Transfer abort flag for next activity
1202  fAbortThis = fAbortPrevious;
1203  }
1204 }
1205 
1206 bool C4ScenarioListLoader::DoProcessCallback(int32_t iProgress, int32_t iMaxProgress, const char *current_load_info)
1207 {
1208  this->iProgress = iProgress;
1209  this->iMaxProgress = iMaxProgress;
1210  this->current_load_info.Copy(current_load_info);
1211  // callback to dialog
1212  if (C4StartupScenSelDlg::pInstance) C4StartupScenSelDlg::pInstance->ProcessCallback();
1213  // process callback - abort at a few ugly circumstances...
1214  // schedule with 1ms delay to force event processing
1215  // (delay 0 would be nice, but isn't supported properly by our Windows implementation of ScheduleProcs)
1216  if (!Application.ScheduleProcs(1) // WM_QUIT message?
1217  || !C4StartupScenSelDlg::pInstance // host dialog removed?
1218  || !C4StartupScenSelDlg::pInstance->IsShown() // host dialog closed?
1219  ) return false;
1220  // and also abort if flagged
1221  return !fAbortThis;
1222 }
1223 
1224 bool C4ScenarioListLoader::Load(const StdStrBuf &sRootFolder)
1225 {
1226  // (unthreaded) loading of all entries in root folder
1227  if (!BeginActivity(true)) return false;
1228  if (pRootFolder) { delete pRootFolder; pRootFolder = nullptr; }
1229  pCurrFolder = pRootFolder = new RegularFolder(this, nullptr);
1230  // Load regular game data if no explicit path specified
1231  if(!sRootFolder.getData())
1232  for(const auto & iter : Reloc)
1233  pRootFolder->Merge(iter.strBuf.getData());
1234  bool fSuccess = pRootFolder->LoadContents(this, nullptr, &sRootFolder, false, false);
1235  EndActivity();
1236  return fSuccess;
1237 }
1238 
1239 bool C4ScenarioListLoader::Load(Folder *pSpecifiedFolder, bool fReload)
1240 {
1241  // call safety
1242  if (!pRootFolder || !pSpecifiedFolder) return false;
1243  // set new current and load it
1244  if (!BeginActivity(true)) return false;
1245  pCurrFolder = pSpecifiedFolder;
1246  bool fSuccess = pCurrFolder->LoadContents(this, nullptr, nullptr, false, fReload);
1247  EndActivity();
1248  return fSuccess;
1249 }
1250 
1252 {
1253  // call safety
1254  if (!pRootFolder || !pEntry) return false;
1255  // load info of selection
1256  if (!BeginActivity(false)) return false;
1257  bool fSuccess = pEntry->Load(nullptr, nullptr, true);
1258  EndActivity();
1259  return fSuccess;
1260 }
1261 
1263 {
1264  // call safety
1265  if (!pRootFolder || !pCurrFolder) return false;
1266  // already in root: Can't go up
1267  if (pCurrFolder == pRootFolder) return false;
1268  // otherwise, up one level
1269  return Load(pCurrFolder->GetParent(), false);
1270 }
1271 
1273 {
1274  // call safety
1275  if (!pRootFolder || !pCurrFolder) return false;
1276  // reload current
1277  return Load(pCurrFolder, true);
1278 }
1279 
1280 
1281 
1282 
1283 
1284 
1285 // ----------------------------------------------------------------
1286 // Scenario selection GUI
1287 
1288 
1289 // font clrs
1290 const uint32_t ClrScenarioItem = 0xff000000,
1291  ClrScenarioItemXtra = 0x7f000000,
1293 
1294 // ------------------------------------------------
1295 // --- C4StartupScenSelDlg::ScenListItem
1297  : pIcon(nullptr), pNameLabel(nullptr), pScenListEntry(pForEntry)
1298 {
1299  assert(pScenListEntry);
1300  CStdFont &rUseFont = C4Startup::Get()->Graphics.BookFont;
1301  StdStrBuf sIgnore;
1302  bool fEnabled = pScenListEntry->CanOpen(sIgnore) && !pScenListEntry->IsGrayed();
1303  // calc height
1304  int32_t iHeight = rUseFont.GetLineHeight() + 2 * IconLabelSpacing;
1305  // create subcomponents
1306  pIcon = new C4GUI::Picture(C4Rect(0, 0, iHeight, iHeight), true);
1307  pIcon->SetFacet(pScenListEntry->GetIconFacet());
1308  pNameLabel = new C4GUI::Label(pScenListEntry->GetName().getData(), iHeight + IconLabelSpacing, IconLabelSpacing, ALeft, fEnabled ? ClrScenarioItem : ClrScenarioItemDisabled, &rUseFont, false, false);
1309  // achievement components
1310  for (int32_t i=0; i<C4StartupScenSel_MaxAchievements; ++i)
1311  {
1312  C4Facet fct; const char *desc;
1313  if (pForEntry->GetAchievement(i, &fct, &desc))
1314  {
1315  ppAchievements[i] = new C4GUI::Picture(C4Rect(iHeight * (i+2), 0, iHeight, iHeight), true); // position will be adjusted later
1316  ppAchievements[i]->SetFacet(fct);
1317  ppAchievements[i]->SetToolTip(desc);
1318  }
1319  else
1320  {
1321  ppAchievements[i] = nullptr;
1322  }
1323  }
1324  // calc own bounds - use icon bounds only, because only the height is used when the item is added
1325  SetBounds(pIcon->GetBounds());
1326  // add components
1327  AddElement(pIcon); AddElement(pNameLabel);
1328  for (auto & ppAchievement : ppAchievements) if (ppAchievement) AddElement(ppAchievement);
1329  // tooltip by name, so long names can be read via tooltip
1330  SetToolTip(pScenListEntry->GetName().getData());
1331  // add to listbox (will get resized horizontally and moved) - zero indent; no tree structure in this dialog
1332  pForListBox->InsertElement(this, pInsertBeforeElement, 0);
1333  // update name label width to reflect new horizontal size
1334  // name label width must be set so rename edit will take its size
1335  pNameLabel->SetAutosize(false);
1336  C4Rect rcNLB = pNameLabel->GetBounds(); rcNLB.Wdt = GetClientRect().Wdt - rcNLB.x - IconLabelSpacing;
1337  pNameLabel->SetBounds(rcNLB);
1338 }
1339 
1341 {
1342  // parent for client rect
1343  typedef C4GUI::Window ParentClass;
1344  ParentClass::UpdateOwnPos();
1345  // reposition achievement items
1346  C4GUI::ComponentAligner caBounds(GetContainedClientRect(), IconLabelSpacing, IconLabelSpacing);
1347  for (auto & ppAchievement : ppAchievements) if (ppAchievement)
1348  {
1349  ppAchievement->SetBounds(caBounds.GetFromRight(caBounds.GetHeight()));
1350  }
1351 }
1352 
1353 void C4StartupScenSelDlg::ScenListItem::MouseInput(C4GUI::CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam)
1354 {
1355  // double-click opens/starts item - currently processed by ListBox already!
1356  // inherited processing
1357  typedef C4GUI::Window BaseClass;
1358  BaseClass::MouseInput(rMouse, iButton, iX, iY, dwKeyParam);
1359 }
1360 
1362 {
1363  // return whether this item can be selected by entering given char:
1364  // first char of name must match
1365  // FIXME: make unicode-ready
1366  if (!pScenListEntry) return false;
1367  const char *szName = pScenListEntry->GetName().getData();
1368  return szName && (toupper(*szName) == toupper(c[0]));
1369 }
1370 
1372 {
1373  // rename this entry
1375  return true;
1376 }
1377 
1379 {
1380  // no renaming
1381  C4StartupScenSelDlg::pInstance->SetRenamingDone();
1382 }
1383 
1385 {
1386  // check validity for new name
1387  if (!GetEntry()->RenameTo(szNewName)) return C4GUI::RenameEdit::RR_Invalid;
1388  // rename label
1389  pNameLabel->SetText(GetEntry()->GetName().getData());
1390  // main dlg update
1391  C4StartupScenSelDlg::pInstance->SetRenamingDone();
1392  C4StartupScenSelDlg::pInstance->ResortFolder();
1393  C4StartupScenSelDlg::pInstance->UpdateSelection();
1394  C4StartupScenSelDlg::pInstance->FocusScenList();
1395  // done; rename accepted and control deleted by ResortFolder
1397 }
1398 
1399 // ------------------------------------------------
1400 // --- C4StartupScenSelDlg
1401 
1402 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)
1403 {
1404  // ctor
1405  // assign singleton
1406  pInstance = this;
1407 
1408  // screen calculations
1409  UpdateSize();
1410  int32_t iButtonWidth,iCaptionFontHgt;
1411  int iButtonHeight = C4GUI_ButtonHgt;
1412  int iBookPageWidth;
1413  int iExtraHPadding = rcBounds.Wdt >= 700 ? rcBounds.Wdt/50 : 0;
1414  ::GraphicsResource.CaptionFont.GetTextExtent("<< BACK", iButtonWidth, iCaptionFontHgt, true);
1415  iButtonWidth *= 3;
1416  C4GUI::ComponentAligner caMain(GetClientRect(), 0,0, true);
1417  C4GUI::ComponentAligner caButtonArea(caMain.GetFromBottom(caMain.GetHeight()/8),rcBounds.Wdt/(rcBounds.Wdt >= 700 ? 128 : 256),0);
1418  C4Rect rcMap = caMain.GetCentered(caMain.GetWidth(), caMain.GetHeight());
1419 
1420  // tabular for different scenario selection designs
1421  pScenSelStyleTabular = new C4GUI::Tabular(rcMap, C4GUI::Tabular::tbNone);
1422  pScenSelStyleTabular->SetSheetMargin(0);
1423  pScenSelStyleTabular->SetGfx(&C4Startup::Get()->Graphics.fctDlgPaper, &C4Startup::Get()->Graphics.fctOptionsTabClip, &C4Startup::Get()->Graphics.fctOptionsIcons, &C4Startup::Get()->Graphics.BookSmallFont, false);
1424  AddElement(pScenSelStyleTabular);
1425  C4GUI::Tabular::Sheet *pSheetBook = pScenSelStyleTabular->AddSheet(nullptr);
1426  /* C4GUI::Tabular::Sheet *pSheetMap = */ pScenSelStyleTabular->AddSheet(nullptr);
1427 
1428  // scenario selection list
1429  C4GUI::ComponentAligner caBook(pSheetBook->GetClientRect(), caMain.GetWidth()/20, caMain.GetHeight()/20, true);
1430  C4GUI::ComponentAligner caBookLeft(caBook.GetFromLeft(iBookPageWidth=caBook.GetWidth()*4/9+4-iExtraHPadding*2), 0,5);
1431 
1432  CStdFont &rScenSelCaptionFont = C4Startup::Get()->Graphics.BookFontTitle;
1433  pScenSelCaption = new C4GUI::Label("", caBookLeft.GetFromTop(rScenSelCaptionFont.GetLineHeight()), ACenter, ClrScenarioItem, &rScenSelCaptionFont, false);
1434  pSheetBook->AddElement(pScenSelCaption);
1435  pScenSelCaption->SetToolTip(LoadResStr("IDS_DLGTIP_SELECTSCENARIO"));
1436  pScenSelList = new C4GUI::ListBox(caBookLeft.GetAll());
1437  pScenSelList->SetToolTip(LoadResStr("IDS_DLGTIP_SELECTSCENARIO"));
1438  pScenSelList->SetDecoration(false, &C4Startup::Get()->Graphics.sfctBookScroll, true);
1439  pSheetBook->AddElement(pScenSelList);
1442  // scenario selection list progress labels
1443  pScenSelProgressLabel = new C4GUI::Label("", pScenSelList->GetBounds().GetMiddleX(), pScenSelList->GetBounds().GetMiddleY()-iCaptionFontHgt, ACenter, ClrScenarioItem, &(C4Startup::Get()->Graphics.BookFontCapt), false);
1444  pSheetBook->AddElement(pScenSelProgressLabel);
1445  pScenSelProgressInfoLabel = new C4GUI::Label("", pScenSelList->GetBounds().GetMiddleX(), pScenSelList->GetBounds().GetMiddleY(), ACenter, ClrScenarioItemXtra, &(C4Startup::Get()->Graphics.BookFontCapt), false);
1446  pSheetBook->AddElement(pScenSelProgressInfoLabel);
1447 
1448  // right side of book: Displaying current selection
1449  C4Rect bounds = caBook.GetFromRight(iBookPageWidth);
1450  const int32_t AvailWidth = bounds.Wdt;
1451  const int32_t AvailHeight = 2 * bounds.Hgt / 5;
1452  int32_t PictureWidth, PictureHeight;
1454  {
1455  PictureWidth = C4StartupScenSel_TitlePictureWdt * AvailWidth / C4StartupScenSel_TitlePictureWdt;
1457  }
1458  else
1459  {
1460  PictureWidth = C4StartupScenSel_TitlePictureWdt * AvailHeight / C4StartupScenSel_TitlePictureHgt;
1462  }
1463  pSelectionInfo = new C4GUI::TextWindow(bounds, PictureWidth+2*C4StartupScenSel_TitleOverlayMargin, PictureHeight+2*C4StartupScenSel_TitleOverlayMargin,
1464  C4StartupScenSel_TitlePicturePadding, 100, 4096, nullptr, true, &C4Startup::Get()->Graphics.fctScenSelTitleOverlay, C4StartupScenSel_TitleOverlayMargin);
1465  pSelectionInfo->SetDecoration(false, false, &C4Startup::Get()->Graphics.sfctBookScroll, true);
1466  pSheetBook->AddElement(pSelectionInfo);
1467 
1468  // bottom of right side of book: Custom options on selection
1469  // Arbitrary height and invisible by default. Height will be adjusted when options are created.
1470  pSelectionOptions = new C4GameOptionsList(C4Rect(bounds.x, bounds.y+bounds.Hgt-10, bounds.Wdt, 10), false, fNetwork ? C4GameOptionsList::GOLS_PreGameNetwork : C4GameOptionsList::GOLS_PreGameSingle);
1471  pSelectionOptions->SetDecoration(false, &C4Startup::Get()->Graphics.sfctBookScroll, true);
1472  pSelectionOptions->SetVisibility(false);
1473  pSheetBook->AddElement(pSelectionOptions);
1474 
1475  // back button
1477  AddElement(btn = new C4GUI::CallbackButton<C4StartupScenSelDlg>(LoadResStr("IDS_BTN_BACK"), caButtonArea.GetFromLeft(iButtonWidth, iButtonHeight), &C4StartupScenSelDlg::OnBackBtn));
1478  btn->SetToolTip(LoadResStr("IDS_DLGTIP_BACKMAIN"));
1479  AddElement(btn);
1480  // next button
1481  pOpenBtn = new C4GUI::CallbackButton<C4StartupScenSelDlg>(LoadResStr("IDS_BTN_OPEN"), caButtonArea.GetFromRight(iButtonWidth, iButtonHeight), &C4StartupScenSelDlg::OnNextBtn);
1482  pOpenBtn->SetToolTip(LoadResStr("IDS_DLGTIP_SCENSELNEXT"));
1483  // game options boxes
1484  pGameOptionButtons = new C4GameOptionButtons(caButtonArea.GetAll(), fNetwork, true, false);
1485  AddElement(pGameOptionButtons);
1486  // next button adding
1487  AddElement(pOpenBtn);
1488 
1489  // dlg starts with focus on listbox
1490  SetFocus(pScenSelList, false);
1491 
1492  // key bindings
1493  C4CustomKey::CodeList keys;
1494  keys.emplace_back(K_BACK); keys.emplace_back(K_LEFT);
1495  pKeyBack = new C4KeyBinding(keys, "StartupScenSelFolderUp", KEYSCOPE_Gui,
1497  pKeyRefresh = new C4KeyBinding(C4KeyCodeEx(K_F5), "StartupScenSelReload", KEYSCOPE_Gui,
1499  pKeyForward = new C4KeyBinding(C4KeyCodeEx(K_RIGHT), "StartupScenSelNext", KEYSCOPE_Gui,
1501  pKeyRename = new C4KeyBinding(C4KeyCodeEx(K_F2), "StartupScenSelRename", KEYSCOPE_Gui,
1503  pKeyDelete = new C4KeyBinding(C4KeyCodeEx(K_DELETE), "StartupScenSelDelete", KEYSCOPE_Gui,
1505  pKeyCheat = new C4KeyBinding(C4KeyCodeEx(K_M, KEYS_Control), "StartupScenSelCheat", KEYSCOPE_Gui,
1507 }
1508 
1510 {
1511  if (pScenLoader) delete pScenLoader;
1512  if (this == pInstance) pInstance = nullptr;
1513  delete pKeyCheat;
1514  delete pKeyDelete;
1515  delete pKeyRename;
1516  delete pKeyForward;
1517  delete pKeyRefresh;
1518  delete pKeyBack;
1519 }
1520 
1522 {
1523  // draw background
1524  if (pfctBackground)
1525  DrawBackground(cgo, *pfctBackground);
1526 }
1527 
1529 {
1531  // Collect achievements of all activated players
1532  UpdateAchievements();
1533  // init file list
1534  fIsInitialLoading = true;
1535  if (!pScenLoader) pScenLoader = new C4ScenarioListLoader(Achievements);
1536  pScenLoader->Load(StdStrBuf());
1537  UpdateList();
1538  UpdateSelection();
1539  fIsInitialLoading = false;
1540  // network activation by dialog type
1541  Game.NetworkActive = fStartNetworkGame;
1542 }
1543 
1545 {
1546  AbortRenaming();
1547  // clear laoded scenarios
1548  if (pScenLoader)
1549  {
1550  delete pScenLoader;
1551  pScenLoader = nullptr;
1552  UpdateList(); // must clear scenario list, because it points to deleted stuff
1553  UpdateSelection(); // must clear picture facet of selection!
1554  }
1555  // dlg abort: return to main screen
1556  if (!fOK)
1557  {
1558  // clear settings: Password
1559  ::Network.SetPassword(nullptr);
1561  }
1562 }
1563 
1564 void C4StartupScenSelDlg::UpdateList()
1565 {
1566  AbortRenaming();
1567  // default: Show book (also for loading screen)
1568  pMapData = nullptr;
1569  pScenSelStyleTabular->SelectSheet(ShowStyle_Book, false);
1570  // and delete any stuff from map selection
1571  C4GUI::Tabular::Sheet *pMapSheet = pScenSelStyleTabular->GetSheet(ShowStyle_Map);
1572  while (pMapSheet->GetFirst()) delete pMapSheet->GetFirst();
1573  pfctBackground = nullptr;
1574  // for now, all the list is loaded at once anyway
1575  // so just clear and add all loaded items
1576  // remember old selection
1577  C4ScenarioListLoader::Entry *pOldSelection = GetSelectedEntry();
1578  C4GUI::Element *pEl;
1579  while ((pEl = pScenSelList->GetFirst())) delete pEl;
1580  pScenSelCaption->SetText("");
1581  // scen loader still busy: Nothing to add
1582  if (!pScenLoader) return;
1583  if (pScenLoader->IsLoading())
1584  {
1585  StdStrBuf sProgressText;
1586  sProgressText.Format(LoadResStr("IDS_MSG_SCENARIODESC_LOADING"), (int32_t) pScenLoader->GetProgressPercent());
1587  pScenSelProgressLabel->SetText(sProgressText.getData());
1588  pScenSelProgressLabel->SetVisibility(true);
1589  pScenSelProgressInfoLabel->SetText(pScenLoader->GetProgressInfo());
1590  pScenSelProgressInfoLabel->SetVisibility(true);
1591  return;
1592  }
1593  pScenSelProgressLabel->SetVisibility(false);
1594  pScenSelProgressInfoLabel->SetVisibility(false);
1595  // is this a map folder? Then show the map instead
1596  C4ScenarioListLoader::Folder *pFolder = static_cast<C4ScenarioListLoader::Folder *>(pScenLoader->GetCurrFolder());
1597  if ((pMapData = pFolder->GetMapData()))
1598  {
1599  pMapData->ResetSelection();
1600  pMapData->CreateGUIElements(this, *pScenSelStyleTabular->GetSheet(ShowStyle_Map));
1601  pScenSelStyleTabular->SelectSheet(ShowStyle_Map, false);
1602  }
1603  else
1604  {
1605  // book style selection
1606  // add what has been loaded
1607  for (C4ScenarioListLoader::Entry *pEnt = pScenLoader->GetFirstEntry(); pEnt; pEnt = pEnt->GetNext())
1608  {
1609  if (pEnt->IsHidden()) continue; // no UI entry at all for hidden items
1610  ScenListItem *pEntItem = new ScenListItem(pScenSelList, pEnt);
1611  if (pEnt == pOldSelection) pScenSelList->SelectEntry(pEntItem, false);
1612  }
1613  // set title of current folder
1614  // but not root
1615  if (pFolder && pFolder != pScenLoader->GetRootFolder())
1616  pScenSelCaption->SetText(pFolder->GetName().getData());
1617  else
1618  {
1619  // special root title
1620  pScenSelCaption->SetText(LoadResStr("IDS_DLG_SCENARIOS"));
1621  }
1622  // new list has been loaded: Select first entry if nothing else had been selected
1623  if (!pOldSelection) pScenSelList->SelectFirstEntry(false);
1624  }
1625 }
1626 
1627 void C4StartupScenSelDlg::ResortFolder()
1628 {
1629  // if it's still loading, sorting will be done in the end anyway
1630  if (!pScenLoader || pScenLoader->IsLoading()) return;
1631  C4ScenarioListLoader::Folder *pFolder = pScenLoader->GetCurrFolder();
1632  if (!pFolder) return;
1633  pFolder->Resort();
1634  UpdateList();
1635 }
1636 
1637 void C4StartupScenSelDlg::UpdateSelection()
1638 {
1639  AbortRenaming();
1640  if (!pScenLoader)
1641  {
1642  C4Facet fctNoPic;
1643  pSelectionInfo->SetPicture(fctNoPic);
1644  pSelectionInfo->ClearText(false);
1645  SetOpenButtonDefaultText();
1646  return;
1647  }
1648  // determine target text box
1649  C4GUI::TextWindow *pSelectionInfo = pMapData ? pMapData->GetSelectionInfoBox() : this->pSelectionInfo;
1650  // get selected entry
1651  C4ScenarioListLoader::Entry *pSel = GetSelectedEntry();
1652  if (!pSel)
1653  {
1654  // no selection: Display data of current parent folder
1655  pSel = pScenLoader->GetCurrFolder();
1656  // but not root
1657  if (pSel == pScenLoader->GetRootFolder()) pSel = nullptr;
1658  }
1659  // get title image and desc of selected entry
1660  C4Facet fctTitle; StdStrBuf sTitle, sDesc, sVersion, sAuthor;
1661  if (pSel)
1662  {
1663  pScenLoader->LoadExtended(pSel); // 2do: Multithreaded
1664  fctTitle = pSel->GetTitlePicture();
1665  sTitle.Ref(pSel->GetName());
1666  sDesc.Ref(pSel->GetDesc());
1667  sVersion.Ref(pSel->GetVersion());
1668  sAuthor.Ref(pSel->GetAuthor());
1669  // never show a pure title string: There must always be some text or an image
1670  if (!fctTitle.Surface && (!sDesc || !*sDesc.getData()))
1671  sTitle.Clear();
1672  // selection specific open/start button
1673  pOpenBtn->SetText(pSel->GetOpenText().getData());
1674  pOpenBtn->SetToolTip(pSel->GetOpenTooltip().getData());
1675  }
1676  else
1677  SetOpenButtonDefaultText();
1678  // set data in selection component
1679  pSelectionInfo->ClearText(false);
1680  pSelectionInfo->SetPicture(fctTitle);
1681  if (sTitle && (!sDesc || !*sDesc.getData())) pSelectionInfo->AddTextLine(sTitle.getData(), &C4Startup::Get()->Graphics.BookFontCapt, ClrScenarioItem, false, false);
1682  if (sDesc) pSelectionInfo->AddTextLine(sDesc.getData(), &C4Startup::Get()->Graphics.BookFont, ClrScenarioItem, false, false, &C4Startup::Get()->Graphics.BookFontCapt);
1683  if (sAuthor) pSelectionInfo->AddTextLine(FormatString(LoadResStr("IDS_CTL_AUTHOR"), sAuthor.getData()).getData(),
1685  if (sVersion) pSelectionInfo->AddTextLine(FormatString(LoadResStr("IDS_DLG_VERSION"), sVersion.getData()).getData(),
1687  // update custom scenario options panel
1688  if (pSel)
1689  {
1690  pSelectionOptions->SetParameters(pSel->GetParameterDefs(), pSel->GetParameters());
1691  pSelectionOptions->Update();
1692  }
1693  else
1694  pSelectionOptions->SetParameters(nullptr, nullptr);
1695  // update component heights
1696  C4Rect rcSelBounds = pSelectionInfo->GetBounds();
1697  int32_t ymax = pSelectionOptions->GetBounds().GetBottom();
1698  C4GUI::Element *pLastOption = pSelectionOptions->GetLast();
1699  if (pLastOption)
1700  {
1701  // custom options present: Info box reduced; options box at bottom
1702  // set options box max size to 1/3rd of selection info area
1703  int32_t options_hgt = std::min<int32_t>(pLastOption->GetBounds().GetBottom() + pSelectionOptions->GetMarginTop() + pSelectionOptions->GetMarginTop(), rcSelBounds.Hgt/3);
1704  rcSelBounds.Hgt = ymax - options_hgt - rcSelBounds.y;
1705  pSelectionInfo->SetBounds(rcSelBounds);
1706  rcSelBounds.y = ymax - options_hgt;
1707  rcSelBounds.Hgt = options_hgt;
1708  pSelectionOptions->SetBounds(rcSelBounds);
1709  pSelectionOptions->SetVisibility(true);
1710  }
1711  else
1712  {
1713  // custom options absent: Info takes full area
1714  pSelectionOptions->SetVisibility(false);
1715  rcSelBounds.Hgt = ymax - rcSelBounds.y;
1716  pSelectionInfo->SetBounds(rcSelBounds);
1717  }
1718  pSelectionInfo->UpdateHeight();
1719 }
1720 
1721 C4StartupScenSelDlg::ScenListItem *C4StartupScenSelDlg::GetSelectedItem()
1722 {
1723  return static_cast<ScenListItem *>(pScenSelList->GetSelectedItem());
1724 }
1725 
1726 C4ScenarioListLoader::Entry *C4StartupScenSelDlg::GetSelectedEntry()
1727 {
1728  // map layout: Get selection from map
1729  if (pMapData) return pMapData->GetSelectedEntry();
1730  // get selection in listbox
1731  ScenListItem *pSel = static_cast<ScenListItem *>(pScenSelList->GetSelectedItem());
1732  return pSel ? pSel->GetEntry() : nullptr;
1733 }
1734 
1736 {
1737  assert(pStartScen);
1738  if (!pStartScen) return false;
1739  // get required object definitions
1740  if (pStartScen->GetC4S().Definitions.AllowUserChange)
1741  {
1742  // get definitions as user selects them
1743  StdStrBuf sDefinitions;
1744  if (!pStartScen->GetC4S().Definitions.GetModules(&sDefinitions)) sDefinitions.Copy("Objects.ocd");
1745  if (!C4DefinitionSelDlg::SelectDefinitions(GetScreen(), &sDefinitions))
1746  // user aborted during definition selection
1747  return false;
1748  SCopy(sDefinitions.getData(), ::Game.DefinitionFilenames, (sizeof Game.DefinitionFilenames)-1);
1749  }
1750  else
1751  // for no user change, just set default objects. Custom settings will override later anyway
1752  SCopy("Objects.ocd", ::Game.DefinitionFilenames);
1753  // set other default startup parameters
1754  ::Game.fLobby = !!::Game.NetworkActive; // always lobby in network
1755  ::Game.fObserve = false;
1756  C4ScenarioParameters *custom_params = pStartScen->GetParameters();
1757  if (custom_params) ::Game.StartupScenarioParameters = *custom_params; else ::Game.StartupScenarioParameters.Clear();
1758  // start with this set!
1760  return true;
1761 }
1762 
1764 {
1765  // open it through loader
1766  if (!pScenLoader) return false;
1767  bool fSuccess = pScenLoader->Load(pNewFolder, false);
1768  UpdateList();
1769  UpdateSelection();
1770  if (!pMapData) SetFocus(pScenSelList, false);
1771  return fSuccess;
1772 }
1773 
1775 {
1776  AbortRenaming();
1777  // get selected entry
1778  C4ScenarioListLoader::Entry *pSel = GetSelectedEntry();
1779  if (!pSel) return false;
1780  // check if open is possible
1781  StdStrBuf sError;
1782  if (!pSel->CanOpen(sError))
1783  {
1784  GetScreen()->ShowMessage(sError.getData(), LoadResStr("IDS_MSG_CANNOTSTARTSCENARIO"), C4GUI::Ico_Error);
1785  return false;
1786  }
1787  // if CanOpen returned true but set an error message, that means it's a warning. Display it!
1788  if (sError.getLength())
1789  {
1791  // user chose to not start it
1792  return false;
1793  }
1794  // start it!
1795  return pSel->Start();
1796 }
1797 
1798 bool C4StartupScenSelDlg::DoBack(bool fAllowClose)
1799 {
1800  AbortRenaming();
1801  // if in a subfolder, try backtrace folders first
1802  if (pScenLoader && pScenLoader->FolderBack())
1803  {
1804  UpdateList();
1805  UpdateSelection();
1806  return true;
1807  }
1808  // while this isn't multithreaded, the dialog must not be aborted while initial load...
1809  if (pScenLoader && pScenLoader->IsLoading()) return false;
1810  // return to main screen
1811  if (fAllowClose)
1812  {
1813  Close(false);
1814  return true;
1815  }
1816  return false;
1817 }
1818 
1820 {
1821  if (pScenLoader && !pScenLoader->IsLoading())
1822  {
1823  pScenSelList->SelectNone(false);
1824  pScenLoader->ReloadCurrent();
1825  UpdateList();
1826  UpdateSelection();
1827  }
1828 }
1829 
1830 void C4StartupScenSelDlg::SetOpenButtonDefaultText()
1831 {
1832  pOpenBtn->SetText(LoadResStr("IDS_BTN_OPEN"));
1833  pOpenBtn->SetToolTip(LoadResStr("IDS_DLGTIP_SCENSELNEXT"));
1834 }
1835 
1837 {
1838  // no rename in map mode
1839  if (pMapData) return false;
1840  // not if renaming already
1841  if (IsRenaming()) return false;
1842  // forward to selected scenario list item
1843  ScenListItem *pSel = GetSelectedItem();
1844  if (!pSel) return false;
1845  return pSel->KeyRename();
1846 }
1847 
1849 {
1850  // do not delete from map folder
1851  if (pMapData) return false;
1852  // cancel renaming
1853  AbortRenaming();
1854  // delete selected item: Confirmation first
1855  ScenListItem *pSel = GetSelectedItem();
1856  if (!pSel) return false;
1857  C4ScenarioListLoader::Entry *pEnt = pSel->GetEntry();
1858  StdStrBuf sWarning;
1859  sWarning.Format(LoadResStr("IDS_MSG_PROMPTDELETE"), FormatString("%s %s", pEnt->GetTypeName().getData(), pEnt->GetName().getData()).getData(), pEnt->GetEntryFilename().getData());
1860  GetScreen()->ShowRemoveDlg(new C4GUI::ConfirmationDialog(sWarning.getData(), LoadResStr("IDS_MNU_DELETE"),
1862  return true;
1863 }
1864 
1866 {
1867  // deletion confirmed. Do it.
1868  C4ScenarioListLoader::Entry *pEnt = pSel->GetEntry();
1869  if (!C4Group_DeleteItem(pEnt->GetEntryFilename().getData(), true))
1870  {
1871  StdStrBuf sMsg; sMsg.Format("%s", LoadResStr("IDS_FAIL_DELETE"));
1873  return;
1874  }
1875  // remove from scenario list
1876  pScenSelList->SelectEntry(pSel->GetNext(), false);
1877  delete pEnt;
1878  delete pSel;
1879 }
1880 
1882 {
1883  return ::pGUI->ShowRemoveDlg(new C4GUI::InputDialog(LoadResStr("IDS_TEXT_ENTERMISSIONPASSWORD"), LoadResStr("IDS_DLG_MISSIONACCESS"), C4GUI::Ico_Options,
1885  false));
1886 }
1887 
1889 {
1890  // Special character "-": remove mission password(s)
1891  if (SEqual2(rsCheatCode.getData(), "-"))
1892  {
1893  const char *szPass = rsCheatCode.getPtr(1);
1894  if (szPass && *szPass)
1895  {
1896  SRemoveModules(Config.General.MissionAccess, szPass, false);
1897  UpdateList();
1898  UpdateSelection();
1899  return;
1900  }
1901  }
1902 
1903  // No special character: add mission password(s)
1904  const char *szPass = rsCheatCode.getPtr(0);
1905  if (szPass && *szPass)
1906  {
1907  SAddModules(Config.General.MissionAccess, szPass, false);
1908  UpdateList();
1909  UpdateSelection();
1910  return;
1911  }
1912 
1913 }
1914 
1915 void C4StartupScenSelDlg::FocusScenList()
1916 {
1917  SetFocus(pScenSelList, false);
1918 }
1919 
1921 {
1922  // map button was clicked: Update selected scenario
1923  if (!pMapData || !pEl) return;
1924  C4ScenarioListLoader::Entry *pSel = GetSelectedEntry(), *pSel2;
1925  pMapData->OnButtonScenario(pEl);
1926  pSel2 = GetSelectedEntry();
1927  if (pSel && pSel==pSel2)
1928  {
1929  // clicking on the selected scenario again starts it
1930  DoOK();
1931  return;
1932  }
1933  // the first click selects it
1934  SetFocus(pEl, false);
1935  UpdateSelection();
1936 }
1937 
1939 {
1940  // Deselect all so current folder info is displayed
1941  if (GetFocus()) C4GUI::GUISound("UI::Tick");
1942  SetFocus(nullptr, true);
1943  if (pMapData) pMapData->ResetSelection();
1944  UpdateSelection();
1945 }
1946 
1948 {
1949  pRenameEdit = pNewRenameEdit;
1950 }
1951 
1953 {
1954  if (pRenameEdit) pRenameEdit->Abort();
1955 }
1956 
1957 void C4StartupScenSelDlg::UpdateAchievements()
1958 {
1959  // Extract all achievements from activated player files and merge them
1960  Achievements.Clear();
1961  char PlayerFilename[_MAX_FNAME+1];
1962  C4Group PlayerGrp;
1963  for (int i = 0; SCopySegment(Config.General.Participants, i, PlayerFilename, ';', _MAX_FNAME, true); i++)
1964  {
1965  const char *szPlayerFilename = Config.AtUserDataPath(PlayerFilename);
1966  if (!FileExists(szPlayerFilename)) continue;
1967  if (!PlayerGrp.Open(szPlayerFilename)) continue;
1968  C4PlayerInfoCore nfo;
1969  if (!nfo.Load(PlayerGrp)) continue;
1970  Achievements.Merge(nfo.Achievements);
1971  }
1972 }
1973 
1975 {
1976  if (pSelectionOptions) pSelectionOptions->Update();
1977 }
1978 
1979 // NICHT: 9, 7.2.2, 113-114, 8a
1980 
char * GetFilename(char *szPath)
Definition: StdFile.cpp:45
const char * getData() const
Definition: StdBuf.h:442
void MouseInput(C4GUI::CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam) override
bool FindEntry(const char *szWildCard, StdStrBuf *sFileName=nullptr, size_t *iSize=nullptr)
Definition: C4Group.cpp:1774
int32_t GetHeight() const
Definition: C4Gui.h:2801
#define C4CFN_WriteTitle
Definition: C4Components.h:83
void SetText(const char *szToText)
Definition: C4GuiButton.cpp:55
void SetPassword(const char *szToPassword)
Definition: C4Network2.cpp:771
void OpenGame(const char *scenario=nullptr)
CStdFont BookFontTitle
Definition: C4Startup.h:65
int32_t AlphabeticalSorting
Definition: C4Config.h:189
const int32_t C4StartupScenSel_DefaultIcon_WinFolder
#define C4CFN_FolderFiles
Definition: C4Components.h:176
const int32_t C4StartupScenSel_TitlePictureWdt
int32_t GetProgressPercent() const
C4Config Config
Definition: C4Config.cpp:833
char * GrabPointer()
Definition: StdBuf.h:459
const int32_t C4MC_Button_LeftDown
void OnClosed(bool fOK) override
int GetLineHeight() const
Definition: C4FontLoader.h:125
void GUISound(const char *szSound)
Definition: C4Gui.cpp:1175
C4ConfigStartup Startup
Definition: C4Config.h:260
Element * GetLast()
Definition: C4Gui.h:1572
#define C4GUI_ButtonHgt
Definition: C4Gui.h:111
void SCopy(const char *szSource, char *sTarget, size_t iMaxL)
Definition: Standard.cpp:152
bool LoadExtended(Entry *pEntry)
C4SDefinitions Definitions
Definition: C4Scenario.h:231
void OnSelChange(class C4GUI::Element *pEl)
void SetDecoration(bool fDrawBG, bool fDrawFrame, ScrollBarFacets *pToGfx, bool fAutoScroll)
Definition: C4Gui.h:1752
int Replace(const char *szOld, const char *szNew, size_t iStartSearch=0)
Definition: StdBuf.cpp:284
StdNamingCountAdapt< int_t > mkNamingCountAdapt(int_t &iCount, const char *szName)
Definition: StdAdaptors.h:976
void StartRenaming(C4GUI::RenameEdit *pNewRenameEdit)
bool Load(C4Group &hGroup, const char *szFilename, const char *szLanguage=nullptr)
int Wdt
Definition: C4Surface.h:65
void SAppend(const char *szSource, char *szTarget, int iMaxL)
Definition: Standard.cpp:257
bool ItemIdentical(const char *szFilename1, const char *szFilename2)
Definition: StdFile.cpp:855
C4Game Game
Definition: C4Globals.cpp:52
char MissionAccess[CFG_MaxString+1]
Definition: C4Config.h:45
const char * GetProgressInfo() const
float bottom
Definition: C4Rect.h:25
const int32_t C4StartupScenSel_DefaultIcon_OldIconBG
void Clear()
Definition: StdBuf.h:466
C4ConfigGeneral General
Definition: C4Config.h:251
C4GUI::TextWindow * GetSelectionInfoBox() const
bool Load(C4Group &hGroup, const char *szName, int iWdt, int iHgt, bool fNoErrIfNotFound, int iFlags)
Definition: C4FacetEx.cpp:78
float right
Definition: C4Rect.h:25
const char * GetData() const
virtual void OnShown()
Definition: C4Gui.h:2208
bool SCopySegment(const char *szString, int iSegment, char *sTarget, char cSeparator, int iMaxL, bool fSkipWhitespace)
Definition: Standard.cpp:273
int stricmp(const char *s1, const char *s2)
bool Lock()
Definition: C4Surface.cpp:453
float left
Definition: C4Rect.h:25
const uint32_t ClrScenarioItemDisabled
bool SavePNG(C4Group &hGroup, const char *szFilename, bool fSaveAlpha=true, bool fSaveOverlayOnly=false)
void DrawBackground(C4TargetFacet &cgo, C4Facet &rFromFct)
#define C4CFN_GenericGroupFiles
Definition: C4Components.h:167
bool StartScenario(C4ScenarioListLoader::Scenario *pStartScen)
void SetFacet(const C4Facet &fct)
Definition: C4Gui.h:612
void SetToolTip(const char *szNewTooltip, bool is_immediate=false)
Definition: C4Gui.cpp:409
C4GUI::RenameEdit::RenameResult DoRenaming(RenameParams par, const char *szNewName)
static bool SelectDefinitions(C4GUI::Screen *pOnScreen, StdStrBuf *pSelection)
void OnSelDblClick(class C4GUI::Element *pEl)
C4ScenarioListLoader(const C4ScenarioParameters &Achievements)
bool InsertElement(Element *pChild, Element *pInsertBefore, int32_t iIndent=0)
void SetSheetMargin(int32_t iMargin)
Definition: C4Gui.h:1713
bool SEqualNoCase(const char *szStr1, const char *szStr2, int iLen)
Definition: Standard.cpp:207
bool Unlock()
Definition: C4Surface.cpp:464
CStdFont & GetFontByHeight(int32_t iHgt, float *pfZoom=nullptr)
void SetBackground(C4Facet *pNewBG)
void Close(bool fOK)
const int C4FCT_Height
Definition: C4FacetEx.h:27
bool ShowRemoveDlg(Dialog *pDlg)
#define C4CFN_ScenarioParameterDefs
Definition: C4Components.h:43
Definition: C4Rect.h:27
int32_t GetScreenWdt()
Definition: C4Gui.h:2821
Entry * pNext
bool GetTextExtent(const char *szText, int32_t &rsx, int32_t &rsy, bool fCheckMarkup=true)
void OnButtonScenario(C4GUI::Control *pEl)
int GetFolderIndex()
#define C4CFN_Version
Definition: C4Components.h:72
void Format(const char *szFmt,...) GNUC_FORMAT_ATTRIBUTE_O
Definition: StdBuf.cpp:174
void DrawElement(C4TargetFacet &cgo) override
const C4Scenario & GetC4S() const
C4ScenarioParameters * GetParameters() override
void AddElement(Element *pChild)
const StdStrBuf & GetAuthor() const
bool SIsModule(const char *szList, const char *szString, int *ipIndex, bool fCaseSensitive)
Definition: Standard.cpp:541
bool GetModules(StdStrBuf *psOutModules) const
Definition: C4Scenario.cpp:420
const uint32_t ClrScenarioItem
Element * GetFirst()
Definition: C4Gui.h:1571
bool Delete(const char *szFiles, bool fRecursive=false)
Definition: C4Group.cpp:1334
#define _MAX_PATH
#define C4CFN_FolderCore
Definition: C4Components.h:44
void SetSelectionChangeCallbackFn(BaseCallbackHandler *pToHandler)
Definition: C4Gui.h:1548
virtual StdStrBuf GetOpenText()=0
C4Rect GetContainedClientRect()
Definition: C4Gui.h:448
C4GraphicsResource GraphicsResource
void SetDecoration(bool fDrawBG, ScrollBarFacets *pToGfx, bool fAutoScroll, bool fDrawBorder=false)
Definition: C4Gui.h:1566
void SelectSheet(int32_t iIndex, bool fByUser)
bool SEqual(const char *szStr1, const char *szStr2)
Definition: Standard.h:93
void Set(const C4Facet &cpy)
Definition: C4FacetEx.h:46
C4FacetID fctScenSelIcons
Definition: C4Startup.h:61
virtual ~Entry()
bool LoadCustom(C4Group &rGrp, bool fNameLoaded, bool fIconLoaded) override
bool IsShown()
Definition: C4Gui.h:2147
const StdStrBuf & GetVersion() const
bool LoadCustomPre(C4Group &rGrp) override
void CreateGUIElements(C4StartupScenSelDlg *pMainDlg, C4GUI::Window &rContainer)
const char * LoadResStr(const char *id)
Definition: C4Language.h:83
bool SAddModules(char *szList, const char *szModules, bool fCaseSensitive)
Definition: Standard.cpp:570
Element * GetSelectedItem()
Definition: C4Gui.h:1580
const StdStrBuf & GetDesc() const
bool SRemoveModules(char *szList, const char *szModules, bool fCaseSensitive)
Definition: Standard.cpp:599
void MakeFilenameFromTitle(char *szTitle)
Definition: StdFile.cpp:402
C4StartupScenSelDlg(bool fNetwork)
int iDifficulty
void KeyCheat2(const StdStrBuf &rsCheatCode)
StdNamingAdapt< T > mkNamingAdapt(T &&rValue, const char *szName)
Definition: StdAdaptors.h:92
bool GetLanguageString(const char *szLanguage, StdStrBuf &rTarget)
static StdStrBuf AddFilename2ID(const char *filename, const char *id)
#define C4CFN_ScriptStringTbl
Definition: C4Components.h:68
int32_t GetMiddleX() const
Definition: C4Rect.h:56
int32_t Wdt
Definition: C4Rect.h:30
static bool LoadComponentHost(C4ComponentHost *host, C4Group &hGroup, const char *szFilename, const char *szLanguage)
Definition: C4Language.cpp:232
#define C4CFN_MapFolderBG
Definition: C4Components.h:136
std::vector< C4KeyCodeEx > CodeList
const int32_t C4StartupScenSel_TitlePictureHgt
C4GUIScreen * pGUI
Definition: C4Gui.cpp:1191
#define C4CFN_Title
Definition: C4Components.h:82
bool DoLoadContents(C4ScenarioListLoader *pLoader, C4Group *pFromGrp, const StdStrBuf &sFilename, bool fLoadEx) override
void SelectFirstEntry(bool fByUser)
Definition: C4Gui.h:1584
void SetAutosize(bool fToVal)
Definition: C4Gui.h:507
virtual void SetVisibility(bool fToValue)
Definition: C4Gui.cpp:207
bool DebugLogF(const char *strMessage...)
Definition: C4Log.cpp:288
CStdFont BookSmallFont
Definition: C4Startup.h:65
C4Network2 Network
Definition: C4Globals.cpp:53
const int32_t C4StartupScenSel_TitleOverlayMargin
bool Load(C4Group &hGroup)
Definition: C4InfoCore.cpp:78
const char * LoadResStrNoAmp(const char *id)
Definition: StdResStr2.cpp:23
int32_t y
Definition: C4Rect.h:30
void CompileFunc(StdCompiler *pComp)
char Participants[CFG_MaxString+1]
Definition: C4Config.h:38
bool ShowMessageModal(const char *szMessage, const char *szCaption, DWORD dwButtons, Icons icoIcon, int32_t *piConfigDontShowAgainSetting=nullptr)
Element * GetFirst()
Definition: C4Gui.h:829
void OnButtonScenario(C4GUI::Control *pEl)
const int ACenter
Definition: C4Surface.h:41
bool CanOpen(StdStrBuf &sError) override
bool SetPixDw(int iX, int iY, DWORD dwCol)
Definition: C4Surface.cpp:576
const char * GetName() const
Definition: C4Group.cpp:1845
StdStrBuf GetFullName() const
Definition: C4Group.cpp:2078
C4FacetID fctOptionsIcons
Definition: C4Startup.h:71
int32_t GetMarginTop() override
Definition: C4Gui.h:1575
bool Open(const char *szGroupName, bool fCreate=false)
Definition: C4Group.cpp:514
void SetGfx(C4Facet *pafctBack, C4Facet *pafctClip, C4Facet *pafctIcons, CStdFont *paSheetCaptionFont, bool fResizeByAspect)
C4Rect rcBounds
Definition: C4Gui.h:385
bool GetAchievement(int32_t idx, C4Facet *out_facet, const char **out_description) override
void Take(char *pnData)
Definition: StdBuf.h:457
int iIconIndex
int32_t PictureWidth
C4Rect & GetClientRect() override
Definition: C4Gui.h:864
StdPtrAdapt< T > mkPtrAdaptNoNull(T *&rpObj)
Definition: StdAdaptors.h:606
virtual StdStrBuf GetTypeName()=0
bool DoProcessCallback(int32_t iProgress, int32_t iMaxProgress, const char *current_load_info)
void Draw(C4Facet &cgo, bool fAspect=true, int32_t iPhaseX=0, int32_t iPhaseY=0, bool fTransparent=true)
Definition: C4Facet.cpp:154
void UpdateHeight()
Definition: C4Gui.h:1750
bool AllowUserChange
Definition: C4Scenario.h:93
bool C4Group_IsGroup(const char *szFilename)
Definition: C4Group.cpp:94
class C4StartupDlg * SwitchDialog(DialogID eToDlg, bool fFade=true, const char *szSubDialog=nullptr)
Definition: C4Startup.cpp:137
const int32_t C4StartupScenSel_DefaultIcon_SavegamesFolder
bool DirContainsScenarios(const char *szDir)
bool Close()
Definition: C4Group.cpp:755
C4Draw * pDraw
Definition: C4Draw.cpp:42
int SModuleCount(const char *szList)
Definition: Standard.cpp:611
virtual bool SetTitleInGroup(C4Group &rGrp, const char *szNewTitle)
Sheet * GetSheet(int32_t iIndex)
Definition: C4Gui.h:1706
void ReplaceStrings(StdStrBuf &rBuf)
const int32_t C4StartupScenSel_TitlePicturePadding
void UpdateSize() override
static Entry * CreateEntryForFile(const StdStrBuf &sFilename, C4ScenarioListLoader *pLoader, Folder *pParent)
int32_t HideMsgStartDedicated
Definition: C4Config.h:184
#define _MAX_FNAME
bool DoLoadContents(C4ScenarioListLoader *pLoader, C4Group *pFromGrp, const StdStrBuf &sFilename, bool fLoadEx) override
char * GetExtension(char *szFilename)
Definition: StdFile.cpp:121
Folder * GetRootFolder() const
bool NetworkActive
Definition: C4Game.h:122
void Value(const T &rStruct)
Definition: StdCompiler.h:161
int32_t GetMiddleY() const
Definition: C4Rect.h:57
DWORD GetPixDw(int iX, int iY, bool fApplyModulation)
Definition: C4Surface.cpp:491
const char * GetID() const
char LanguageEx[CFG_MaxString+1]
Definition: C4Config.h:37
virtual Folder * GetIsFolder()
Entry(class C4ScenarioListLoader *pLoader, class Folder *pParent)
int32_t GetScreenHgt()
Definition: C4Gui.h:2822
bool DoBack(bool fAllowClose)
CStdFont BookFont
Definition: C4Startup.h:65
virtual StdStrBuf GetOpenTooltip()=0
void RemoveExtension(char *szFilename)
Definition: StdFile.cpp:306
C4Rect & GetBounds()
Definition: C4Gui.h:445
int32_t GetWidth() const
Definition: C4Gui.h:2800
char Language[CFG_MaxString+1]
Definition: C4Config.h:36
C4Reloc Reloc
Definition: C4Reloc.cpp:21
bool fObserve
Definition: C4Game.h:120
#define C4CFN_Achievements
Definition: C4Components.h:90
const char * GetError()
Definition: C4Group.cpp:504
void OnNextBtn(C4GUI::Control *btn)
virtual bool isDeserializer()
Definition: StdCompiler.h:53
void SelectEntry(Element *pNewSel, bool fByUser)
int32_t x
Definition: C4Rect.h:30
bool OpenFolder(C4ScenarioListLoader::Folder *pNewFolder)
bool FileExists(const char *szFileName)
int PreCacheEntries(const char *szSearchPattern, bool cache_previous=false)
Definition: C4Group.cpp:2222
void SelectNone(bool fByUser)
Definition: C4Gui.h:1585
void Set(int32_t iX, int32_t iY, int32_t iWdt, int32_t iHgt)
Definition: C4Rect.cpp:86
bool ShowMessage(const char *szMessage, const char *szCaption, Icons icoIcon, int32_t *piConfigDontShowAgainSetting=nullptr)
bool Load(const StdStrBuf &sRootFolder)
void Ref(const char *pnData)
Definition: StdBuf.h:455
bool C4Group_MoveItem(const char *szSource, const char *szTarget1, bool fNoSort)
Definition: C4Group.cpp:144
bool DirectoryExists(const char *szFilename)
Definition: StdFile.cpp:684
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
const Option * GetOptionByValue(int32_t val) const
#define C4CFN_MapFolderData
Definition: C4Components.h:135
float TargetX
Definition: C4Facet.h:165
Entry * GetFirstEntry() const
void SetFocus(Control *pCtrl, bool fByMouse)
virtual C4ScenarioParameters * GetParameters()
const int32_t C4StartupScenSel_DefaultIcon_Folder
bool LoadEntryString(const char *szEntryName, StdStrBuf *Buf)
Definition: C4Group.cpp:1932
class C4ScenarioParameters & StartupScenarioParameters
Definition: C4Game.h:68
virtual bool IsGrayed()
bool FindNextEntry(const char *szWildCard, StdStrBuf *sFileName=nullptr, size_t *iSize=nullptr, bool fStartAtFilename=false)
Definition: C4Group.cpp:1780
#define C4CFN_IconPNG
Definition: C4Components.h:86
float Hgt
Definition: C4Facet.h:118
void SetBounds(const C4Rect &rcNewBound)
Definition: C4Gui.h:446
void Merge(const C4ScenarioParameters &other)
#define C4CFN_ScenarioCore
Definition: C4Components.h:42
class Folder * GetParent() const
void ClientPos2ScreenPos(int32_t &riX, int32_t &riY)
Definition: C4Gui.cpp:231
void SetVisibility(bool fToValue) override
#define C4CFN_ScenarioDesc
Definition: C4Components.h:88
C4FacetSurface * FindByName(const char *name) const
bool Create(int iWdt, int iHgt, int iWdt2=C4FCT_Full, int iHgt2=C4FCT_Full)
Definition: C4FacetEx.cpp:54
bool LoadContents(C4ScenarioListLoader *pLoader, C4Group *pFromGrp, const StdStrBuf *psFilename, bool fLoadEx, bool fReload)
const char * AtUserDataPath(const char *szFilename)
Definition: C4Config.cpp:526
const C4Facet & GetTitlePicture() const
const int ALeft
Definition: C4Surface.h:41
bool LoadCustomPre(C4Group &rGrp) override
bool WildcardMatch(const char *szWildcard, const char *szString)
Definition: StdFile.cpp:374
C4AchievementGraphics Achievements
ScenListItem(C4GUI::ListBox *pForListBox, C4ScenarioListLoader::Entry *pForEntry, C4GUI::Element *pInsertBeforeElement=nullptr)
bool SEqual2(const char *szStr1, const char *szStr2)
Definition: Standard.cpp:198
const char * GetAchievement() const
#define C4CFN_AnyScriptStringTbl
Definition: C4Components.h:69
static C4Startup * Get()
Definition: C4Startup.h:133
void OnBackBtn(C4GUI::Control *btn)
const char * getPtr(size_t i) const
Definition: StdBuf.h:448
bool Add(const char *szFile, const char *szAddAs)
Definition: C4Group.cpp:1316
char DefinitionFilenames[20 *_MAX_PATH+1]
Definition: C4Game.h:105
bool IsNetworkStart() const
const StdStrBuf & GetName() const
CStdFont & GetBlackFontByHeight(int32_t iHgt, float *pfZoom)
Definition: C4Startup.cpp:96
#define C4CFN_ScenarioFiles
Definition: C4Components.h:175
bool LoadCustom(C4Group &rGrp, bool fNameLoaded, bool fIconLoaded) override
void AddTextLine(const char *szText, CStdFont *pFont, DWORD dwClr, bool fDoUpdate, bool fMakeReadableOnBlack, CStdFont *pCaptionFont=nullptr)
Definition: C4Gui.h:1740
#define C4CFN_DefFiles
Definition: C4Components.h:166
int32_t GetBottom() const
Definition: C4Rect.h:58
StdParameterAdapt< T, P > mkParAdapt(T &&rObj, P &&rPar)
Definition: StdAdaptors.h:458
bool GetFromRight(int32_t iWdt, int32_t iHgt, C4Rect &rcOut)
Definition: C4Gui.cpp:1093
Sheet * AddSheet(const char *szTitle, int32_t icoTitle=Ico_None)
bool Load(C4Group *pFromGrp, const StdStrBuf *psFilename, bool fLoadEx)
size_t getLength() const
Definition: StdBuf.h:445
void ResetSearch(bool reload_contents=false)
Definition: C4Group.cpp:1013
void DeleteConfirm(ScenListItem *pSel)
virtual Screen * GetScreen()
Definition: C4Gui.cpp:289
bool C4Group_TestIgnore(const char *szFilename)
Definition: C4Group.cpp:85
int32_t Hgt
Definition: C4Rect.h:30
bool ScheduleProcs(int iTimeout=1000/36)
C4ScenarioListLoader::Entry * GetSelectedEntry() const
void SetPicture(const C4Facet &rNewPic)
C4Surface * Surface
Definition: C4Facet.h:117
#define DirSep
bool ItemExists(const char *szItemName)
Definition: StdFile.h:75
float TargetY
Definition: C4Facet.h:165
CStdFont BookFontCapt
Definition: C4Startup.h:65
bool C4Group_DeleteItem(const char *szItem, bool fRecycle)
Definition: C4Group.cpp:194
C4ScenarioListLoader::Entry * GetEntry() const
const int32_t C4StartupScenSel_MaxAchievements
#define C4CFN_Savegames
Definition: C4Components.h:35
Folder * GetCurrFolder() const
int GetDifficulty()
C4FacetID fctOptionsTabClip
Definition: C4Startup.h:71
uint32_t DWORD
bool CheckNameHotkey(const char *c) override
StdArrayAdapt< T, M > mkArrayAdaptMap(T *pArray, int iSize, M &&map)
Definition: StdAdaptors.h:313
virtual bool Start()=0
bool fLobby
Definition: C4Game.h:118
const int C4FCT_Full
Definition: C4FacetEx.h:26
Entry * FindEntryByName(const char *szFilename) const
#define C4CFN_ScenarioIcon
Definition: C4Components.h:85
int EntrySortFunc(const void *pEl1, const void *pEl2)
bool LoadCustom(C4Group &rGrp, bool fNameLoaded, bool fIconLoaded) override
void Copy()
Definition: StdBuf.h:467
float Wdt
Definition: C4Facet.h:118
C4Facet GetPhase(int iPhaseX=0, int iPhaseY=0)
Definition: C4Facet.cpp:59
C4ScenarioParameters Achievements
Definition: C4InfoCore.h:108
bool Inside(T ival, U lbound, V rbound)
Definition: Standard.h:43
C4StartupGraphics Graphics
Definition: C4Startup.h:100
int iFolderIndex
void Clear()
Definition: C4FacetEx.h:44
void SetParameters(C4ScenarioParameterDefs *param_defs, C4ScenarioParameters *params)
#define C4CFN_ScenarioTitle
Definition: C4Components.h:84
const C4Facet & GetIconFacet() const
const StdStrBuf & GetEntryFilename() const
C4Application Application
Definition: C4Globals.cpp:44
bool GetCentered(int32_t iWdt, int32_t iHgt, C4Rect &rcOut)
Definition: C4Gui.cpp:1133
void CompileFunc(C4Real &rValue, StdCompiler *pComp)
Definition: C4Real.cpp:9033
void OnLeagueOptionChanged() override
float top
Definition: C4Rect.h:25
bool GetFromBottom(int32_t iHgt, int32_t iWdt, C4Rect &rcOut)
Definition: C4Gui.cpp:1109
bool OpenAsChild(C4Group *pMother, const char *szEntryName, bool fExclusive=false, bool fCreate=false)
Definition: C4Group.cpp:1585
C4MapFolderData * GetMapData() const
virtual bool GetAchievement(int32_t idx, C4Facet *out_facet, const char **out_description)
virtual bool CanOpen(StdStrBuf &sError)
void SetText(const char *szToText, bool fAllowHotkey=true)
Definition: C4GuiLabels.cpp:74
int GetIconIndex()
const uint32_t ClrScenarioItemXtra
bool Load(C4Group &hGroup, C4ScenarioListLoader::Folder *pScenLoaderFolder)
void ClearText(bool fDoUpdate)
Definition: C4Gui.h:1744
Control * GetFocus()
Definition: C4Gui.h:2115
StdStrBuf FormatString(const char *szFmt,...)
Definition: StdBuf.cpp:270
bool RenameTo(const char *szNewName)
virtual C4ScenarioParameterDefs * GetParameterDefs()
void SetSelectionDblClickFn(BaseCallbackHandler *pToHandler)
Definition: C4Gui.h:1553
static C4StartupScenSelDlg * pInstance
Entry * GetNext() const