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 
21 #include "game/C4Application.h"
22 #include "gui/C4GameOptions.h"
24 #include "gui/C4StartupMainDlg.h"
25 #include "gui/C4StartupNetDlg.h"
27 #include "c4group/C4Components.h"
28 #include "lib/C4Log.h"
29 #include "game/C4Game.h"
30 #include "gui/C4GameDialogs.h"
31 #include "c4group/C4Language.h"
32 #include "gui/C4FileSelDlg.h"
33 #include "gui/C4MouseControl.h"
35 #include "graphics/C4Draw.h"
36 #include <set>
37 
38 // singleton
40 
41 
42 // ----------------------------------------------------------------
43 // Map folder data
44 
46 {
47  pComp->Value(mkNamingAdapt( sFilename, "File" , StdStrBuf()));
48  pComp->Value(mkNamingAdapt( sBaseImage, "BaseImage" , StdStrBuf()));
49  pComp->Value(mkNamingAdapt( sOverlayImage, "OverlayImage" , StdStrBuf()));
50  pComp->Value(mkNamingAdapt( rcOverlayPos, "Area", C4Rect()));
51  pComp->Value(mkNamingAdapt( sTitle, "Title" , StdStrBuf()));
52  pComp->Value(mkNamingAdapt( iTitleFontSize, "TitleFontSize", 20));
53  pComp->Value(mkNamingAdapt( dwTitleInactClr, "TitleColorInactive", 0x7fffffffu));
54  pComp->Value(mkNamingAdapt( dwTitleActClr, "TitleColorActive", 0x0fffffffu));
55  pComp->Value(mkNamingAdapt( iTitleOffX, "TitleOffX", 0));
56  pComp->Value(mkNamingAdapt( iTitleOffY, "TitleOffY", 0));
57  pComp->Value(mkNamingAdapt( byTitleAlign, "TitleAlign", ACenter));
58  pComp->Value(mkNamingAdapt( fTitleBookFont, "TitleUseBookFont", true));
59  pComp->Value(mkNamingAdapt( fImgDump, "ImageDump", false)); // developer help
60 }
61 
63 {
64  pComp->Value(mkNamingAdapt( sPassword, "Access", StdStrBuf()));
65  pComp->Value(mkNamingAdapt( sOverlayImage, "OverlayImage" , StdStrBuf()));
66  pComp->Value(mkNamingAdapt( rcOverlayPos, "Area", C4Rect()));
67 }
68 
69 C4MapFolderData::MapPic::MapPic(const FLOAT_RECT &rcfBounds, const C4Facet &rfct) : C4GUI::Picture(C4Rect(rcfBounds), false), rcfBounds(rcfBounds)
70 {
71  // ctor
72  SetFacet(rfct);
73  SetToolTip(LoadResStr("IDS_MSG_MAP_DESC"));
74 }
75 
76 void C4MapFolderData::MapPic::MouseInput(C4GUI::CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam)
77 {
78  typedef C4GUI::Picture Parent;
79  Parent::MouseInput(rMouse, iButton, iX, iY, dwKeyParam);
80  // input: mouse movement or buttons - deselect everything if clicked
81  if (iButton == C4MC_Button_LeftDown && C4StartupScenSelDlg::pInstance)
82  {
83  C4StartupScenSelDlg::pInstance->DeselectAll();
84  }
85 }
86 
87 void C4MapFolderData::MapPic::DrawElement(C4TargetFacet &cgo)
88 {
89  // get drawing bounds
90  float x0 = rcfBounds.left + cgo.TargetX, y0 = rcfBounds.top + cgo.TargetY;
91  // draw the image
92  GetFacet().DrawXFloat(cgo.Surface, x0, y0, rcfBounds.right-rcfBounds.left, rcfBounds.bottom-rcfBounds.top);
93 }
94 
96 {
97  fCoordinatesAdjusted = false;
98  fctBackgroundPicture.Clear();
99  pScenarioFolder = nullptr;
100  pSelectedEntry = nullptr;
101  pSelectionInfoBox = nullptr;
102  rcScenInfoArea.Set(0,0,0,0);
103  MinResX=MinResY=0;
104  fUseFullscreenMap=false;
105  int i;
106  for (i=0; i<iScenCount; ++i) delete ppScenList[i];
107  iScenCount=0;
108  delete [] ppScenList; ppScenList=nullptr;
109  for (i=0; i<iAccessGfxCount; ++i) delete ppAccessGfxList[i];
110  iAccessGfxCount=0;
111  delete [] ppAccessGfxList; ppAccessGfxList=nullptr;
112  pMainDlg = nullptr;
113 }
114 
116 {
117  // clear previous
118  Clear();
119  // load localization info
120  C4LangStringTable LangTable;
121  bool fHasLangTable = C4Language::LoadComponentHost(&LangTable, hGroup, C4CFN_ScriptStringTbl, Config.General.LanguageEx);
122  // load core data
123  StdStrBuf Buf;
124  if (!hGroup.LoadEntryString(C4CFN_MapFolderData, &Buf)) return false;
125  if (fHasLangTable) LangTable.ReplaceStrings(Buf);
126  if (!CompileFromBuf_LogWarn<StdCompilerINIRead>(mkNamingAdapt(*this, "FolderMap"), Buf, C4CFN_MapFolderData)) return false;
127  // check resolution requirement
128  if (MinResX && MinResX>C4GUI::GetScreenWdt()) return false;
129  if (MinResY && MinResY>C4GUI::GetScreenHgt()) return false;
130  // load images
131  if (!fctBackgroundPicture.Load(hGroup, C4CFN_MapFolderBG, C4FCT_Full, C4FCT_Full, false, 0))
132  {
133  DebugLogF("C4MapFolderData::Load(%s): Could not load background graphic \"%s\"", hGroup.GetName(), C4CFN_MapFolderBG);
134  return false;
135  }
136  int i;
137  for (i=0; i<iScenCount; ++i)
138  {
139  // init scenario entry stuff
140  Scenario *pScen = ppScenList[i];
141  pScen->pScenEntry = pScenLoaderFolder->FindEntryByName(pScen->sFilename.getData());
142  pScen->pBtn = nullptr;
143  pScen->sTitle.Replace("TITLE", pScen->pScenEntry ? pScen->pScenEntry->GetName().getData() : "<c ff0000>ERROR</c>" /* scenario not loaded; title cannot be referenced */);
144  // developer image dump
145  if (pScen->fImgDump)
146  {
147  C4FacetSurface fctDump; bool fSuccess=false;
148  if (fctDump.Create(pScen->rcOverlayPos.Wdt, pScen->rcOverlayPos.Hgt, C4FCT_Full, C4FCT_Full))
149  {
150  pDraw->Blit(fctBackgroundPicture.Surface,
151  (float) pScen->rcOverlayPos.x, (float) pScen->rcOverlayPos.y,
152  (float) pScen->rcOverlayPos.Wdt, (float) pScen->rcOverlayPos.Hgt,
153  fctDump.Surface,
154  0, 0,
155  fctDump.Wdt, fctDump.Hgt);
156  fSuccess = fctDump.Surface->SavePNG(pScen->sBaseImage.getData(), true, false, false);
157  }
158  if (!fSuccess)
159  DebugLogF("C4MapFolderData::Load(%s): Could not dump graphic \"%s\"", hGroup.GetName(), pScen->sBaseImage.getData());
160  continue;
161  }
162  // load images
163  if (pScen->sBaseImage.getLength()>0) if (!pScen->fctBase.Load(hGroup, pScen->sBaseImage.getData(), C4FCT_Full, C4FCT_Full, false, 0))
164  {
165  DebugLogF("C4MapFolderData::Load(%s): Could not load base graphic \"%s\"", hGroup.GetName(), pScen->sBaseImage.getData());
166  return false;
167  }
168  if (pScen->sOverlayImage.getLength()>0) if (!pScen->fctOverlay.Load(hGroup, pScen->sOverlayImage.getData(), C4FCT_Full, C4FCT_Full, false, 0))
169  {
170  DebugLogF("C4MapFolderData::Load(%s): Could not load graphic \"%s\"", hGroup.GetName(), pScen->sOverlayImage.getData());
171  return false;
172  }
173  }
174  for (i=0; i<iAccessGfxCount; ++i)
175  {
176  AccessGfx *pGfx= ppAccessGfxList[i];
177  if (pGfx->sOverlayImage.getLength()>0) if (!pGfx->fctOverlay.Load(hGroup, pGfx->sOverlayImage.getData(), C4FCT_Full, C4FCT_Full, false, 0))
178  {
179  DebugLogF("C4MapFolderData::Load(%s): Could not load graphic \"%s\"", hGroup.GetName(), pGfx->sOverlayImage.getData());
180  return false;
181  }
182  }
183  // all loaded
184  pScenarioFolder = pScenLoaderFolder;
185  return true;
186 }
187 
189 {
190  // core values
191  pComp->Value(mkNamingAdapt( rcScenInfoArea, "ScenInfoArea", C4Rect(0,0,0,0)));
192  pComp->Value(mkNamingAdapt( MinResX, "MinResX", 0));
193  pComp->Value(mkNamingAdapt( MinResY, "MinResY", 0));
194  pComp->Value(mkNamingAdapt( fUseFullscreenMap,"FullscreenBG", false));
195  // compile scenario list
196  int32_t iOldScenCount = iScenCount;
197  pComp->Value(mkNamingCountAdapt(iScenCount, "Scenario"));
198  if (pComp->isDeserializer())
199  {
200  while (iOldScenCount--) delete ppScenList[iOldScenCount];
201  delete [] ppScenList;
202  if (iScenCount)
203  {
204  ppScenList = new Scenario *[iScenCount];
205  memset(ppScenList, 0, sizeof(Scenario *)*iScenCount);
206  }
207  else
208  ppScenList = nullptr;
209  }
210  if (iScenCount)
211  {
212  mkPtrAdaptNoNull(*ppScenList);
213  pComp->Value(mkNamingAdapt(mkArrayAdaptMap(ppScenList, iScenCount, mkPtrAdaptNoNull<Scenario>), "Scenario"));
214  }
215  // compile access gfx list
216  int32_t iOldAccesGfxCount = iAccessGfxCount;
217  pComp->Value(mkNamingCountAdapt(iAccessGfxCount, "AccessGfx"));
218  if (pComp->isDeserializer())
219  {
220  while (iOldAccesGfxCount--) delete ppAccessGfxList[iOldAccesGfxCount];
221  delete [] ppAccessGfxList;
222  if (iAccessGfxCount)
223  {
224  ppAccessGfxList = new AccessGfx *[iAccessGfxCount];
225  memset(ppAccessGfxList, 0, sizeof(AccessGfx *)*iAccessGfxCount);
226  }
227  else
228  ppAccessGfxList = nullptr;
229  }
230  if (iAccessGfxCount)
231  {
232  mkPtrAdaptNoNull(*ppAccessGfxList);
233  pComp->Value(mkNamingAdapt(mkArrayAdaptMap(ppAccessGfxList, iAccessGfxCount, mkPtrAdaptNoNull<AccessGfx>), "AccessGfx"));
234  }
235 }
236 
237 void C4MapFolderData::ConvertFacet2ScreenCoord(const C4Rect &rc, FLOAT_RECT *pfrc, float fBGZoomX, float fBGZoomY, int iOffX, int iOffY)
238 {
239  pfrc->left = (fBGZoomX * rc.x) + iOffX;
240  pfrc->top = (fBGZoomY * rc.y) + iOffY;
241  pfrc->right = pfrc->left + (fBGZoomX * rc.Wdt);
242  pfrc->bottom = pfrc->top + (fBGZoomY * rc.Hgt);
243 }
244 
245 void C4MapFolderData::ConvertFacet2ScreenCoord(int32_t *piValue, float fBGZoom, int iOff)
246 {
247  *piValue = int32_t(floorf(fBGZoom * *piValue + 0.5f)) + iOff;
248 }
249 
250 void C4MapFolderData::ConvertFacet2ScreenCoord(C4Rect &rcMapArea, bool fAspect)
251 {
252  if (!fctBackgroundPicture.Wdt || !fctBackgroundPicture.Hgt) return; // invalid BG - should not happen
253  // get zoom of background image
254  float fBGZoomX = 1.0f, fBGZoomY = 1.0f; int iOffX=0, iOffY=0;
255  if (fAspect)
256  {
257  if (fctBackgroundPicture.Wdt * rcMapArea.Hgt > rcMapArea.Wdt * fctBackgroundPicture.Hgt)
258  {
259  // background image is limited by width
260  fBGZoomX = fBGZoomY = (float) rcMapArea.Wdt / fctBackgroundPicture.Wdt;
261  iOffY = std::max<int>(0, (int)(rcMapArea.Hgt - (fBGZoomX * fctBackgroundPicture.Hgt)))/2;
262  }
263  else
264  {
265  // background image is limited by height
266  fBGZoomX = fBGZoomY = (float) rcMapArea.Hgt / fctBackgroundPicture.Hgt;
267  iOffX = std::max<int>(0, (int)(rcMapArea.Wdt - (fBGZoomY * fctBackgroundPicture.Wdt)))/2;
268  }
269  }
270  else
271  {
272  // do not keep aspect: Independant X and Y zoom
273  fBGZoomX = (float) rcMapArea.Wdt / fctBackgroundPicture.Wdt;;
274  fBGZoomY = (float) rcMapArea.Hgt / fctBackgroundPicture.Hgt;;
275  }
276  iOffX -= rcMapArea.x; iOffY -= rcMapArea.y;
277  C4Rect rcBG; rcBG.Set(0,0,fctBackgroundPicture.Wdt, fctBackgroundPicture.Hgt);
278  ConvertFacet2ScreenCoord(rcBG, &rcfBG, fBGZoomX, fBGZoomY, iOffX, iOffY);
279  // default for scenario info area: 1/3rd of right area
280  if (!rcScenInfoArea.Wdt)
281  rcScenInfoArea.Set((int32_t)(fctBackgroundPicture.Wdt*2/3), (int32_t)(fctBackgroundPicture.Hgt/16), (int32_t)(fctBackgroundPicture.Wdt/3), (int32_t)(fctBackgroundPicture.Hgt*7/8));
282  // assume all facet coordinates are referring to background image zoom; convert them to screen coordinates by applying zoom and offset
283  FLOAT_RECT rcfScenInfoArea;
284  ConvertFacet2ScreenCoord(rcScenInfoArea, &rcfScenInfoArea, fBGZoomX, fBGZoomY, iOffX, iOffY);
285  rcScenInfoArea.x = (int32_t) rcfScenInfoArea.left; rcScenInfoArea.y = (int32_t) rcfScenInfoArea.top;
286  rcScenInfoArea.Wdt = (int32_t) (rcfScenInfoArea.right - rcfScenInfoArea.left);
287  rcScenInfoArea.Hgt = (int32_t) (rcfScenInfoArea.bottom - rcfScenInfoArea.top);
288  int i;
289  for (i=0; i<iScenCount; ++i)
290  {
291  Scenario *pScen = ppScenList[i];
292  ConvertFacet2ScreenCoord(pScen->rcOverlayPos, &(pScen->rcfOverlayPos), fBGZoomX, fBGZoomY, iOffX, iOffY);
293  // title sizes
294  ConvertFacet2ScreenCoord(&(pScen->iTitleFontSize), fBGZoomY, 0);
295  // title position: Relative to title rect; so do not add offset here
296  ConvertFacet2ScreenCoord(&(pScen->iTitleOffX), fBGZoomX, 0);
297  ConvertFacet2ScreenCoord(&(pScen->iTitleOffY), fBGZoomY, 0);
298  }
299  for (i=0; i<iAccessGfxCount; ++i) ConvertFacet2ScreenCoord(ppAccessGfxList[i]->rcOverlayPos, &(ppAccessGfxList[i]->rcfOverlayPos), fBGZoomX, fBGZoomY, iOffX, iOffY);
300  // done
301  fCoordinatesAdjusted = true;
302 }
303 
305 {
306  this->pMainDlg = pMainDlg;
307  // convert all coordinates to match the container sizes
308  // do this only once; assume container won't change between loads
309  if (!fCoordinatesAdjusted)
310  {
311  if (!fUseFullscreenMap)
312  ConvertFacet2ScreenCoord(rContainer.GetClientRect(), true);
313  else
314  {
315  C4Rect rcMapRect = pMainDlg->GetBounds();
316  rContainer.ClientPos2ScreenPos(rcMapRect.x, rcMapRect.y);
317  ConvertFacet2ScreenCoord(rcMapRect, false);
318  }
319  }
320  // empty any previous stuff in container
321  while (rContainer.GetFirst()) delete rContainer.GetFirst();
322  // create background image
323  if (!fUseFullscreenMap)
324  rContainer.AddElement(new MapPic(rcfBG, fctBackgroundPicture));
325  else
326  {
327  pMainDlg->SetBackground(&fctBackgroundPicture);
328  }
329  // create mission access overlays
330  int i;
331  for (i=0; i<iAccessGfxCount; ++i)
332  {
333  AccessGfx *pGfx = ppAccessGfxList[i];
334  const char *szPassword = pGfx->sPassword.getData();
335  if (!szPassword || !*szPassword || SIsModule(Config.General.MissionAccess, szPassword))
336  {
337  // ACCESS TO GFX GRANTED: draw it
338  rContainer.AddElement(new MapPic(pGfx->rcfOverlayPos, pGfx->fctOverlay));
339  }
340  }
341  // create buttons for scenarios
342  C4GUI::Button *pBtnFirst = nullptr;
343  for (i=0; i<iScenCount; ++i)
344  {
345  Scenario *pScen = ppScenList[i];
346  if (pScen->pScenEntry && !pScen->pScenEntry->HasMissionAccess())
347  {
348  // no access to this scenario: Do not create a button at all; not even base image. The scenario is "invisible".
349  }
350  else
351  {
353  (pScen->fctBase, pScen->fctOverlay, pScen->rcfOverlayPos, 0, pMainDlg, &C4StartupScenSelDlg::OnButtonScenario);
354  ppScenList[i]->pBtn = pBtn;
355  if (pScen->pScenEntry)
356  pBtn->SetToolTip(FormatString(LoadResStr("IDS_MSG_MAP_STARTSCEN"), pScen->pScenEntry->GetName().getData()).getData());
357  if (pScen->sTitle.getLength()>0)
358  {
359  pBtn->SetText(pScen->sTitle.getData());
360  pBtn->SetTextColors(pScen->dwTitleInactClr, pScen->dwTitleActClr);
361  pBtn->SetTextPos(pScen->iTitleOffX, pScen->iTitleOffY, pScen->byTitleAlign);
362  CStdFont *pUseFont; float fFontZoom=1.0f;
363  if (pScen->fTitleBookFont)
364  pUseFont = &(C4Startup::Get()->Graphics.GetBlackFontByHeight(pScen->iTitleFontSize, &fFontZoom));
365  else
366  pUseFont = &(::GraphicsResource.GetFontByHeight(pScen->iTitleFontSize, &fFontZoom));
367  if (Inside<float>(fFontZoom, 0.8f, 1.25f)) fFontZoom = 1.0f; // some tolerance for font zoom
368  pBtn->SetTextFont(pUseFont, fFontZoom);
369  }
370  rContainer.AddElement(pBtn);
371  if (!pBtnFirst) pBtnFirst = pBtn;
372  }
373  }
374  // create scenario info listbox
375  pSelectionInfoBox = new C4GUI::TextWindow(rcScenInfoArea,
377  C4StartupScenSel_TitlePicturePadding, 100, 4096, nullptr, true, &C4Startup::Get()->Graphics.fctScenSelTitleOverlay, C4StartupScenSel_TitleOverlayMargin);
378  pSelectionInfoBox->SetDecoration(false, false, &C4Startup::Get()->Graphics.sfctBookScroll, true);
379  rContainer.AddElement(pSelectionInfoBox);
380 }
381 
383 {
384  // get associated scenario entry
385  int i;
386  for (i=0; i<iScenCount; ++i)
387  if (pEl == ppScenList[i]->pBtn)
388  break;
389  if (i == iScenCount) return;
390  // select the associated entry
391  pSelectedEntry = ppScenList[i]->pScenEntry;
392 }
393 
395 {
396  pSelectedEntry = nullptr;
397 }
398 
399 
400 
401 // ----------------------------------------------------------------
402 // Scenario list loader
403 
404 // ------------------------------------
405 // Entry
406 
407 C4ScenarioListLoader::Entry::Entry(class C4ScenarioListLoader *pLoader, Folder *pParent) : pLoader(pLoader), pNext(nullptr), pParent(pParent), fBaseLoaded(false), fExLoaded(false)
408 {
409  // ctor: Put into parent tree node
410  if (pParent)
411  {
412  pNext = pParent->pFirst;
413  pParent->pFirst = this;
414  }
415  iIconIndex = -1;
416  iDifficulty = 0;
417  iFolderIndex = 0;
418 }
419 
421 {
422  // dtor: unlink from parent list (MUST be in there)
423  if (pParent)
424  {
425  Entry **ppCheck = &(pParent->pFirst);
426  while (*ppCheck != this)
427  {
428  ppCheck = &(*ppCheck)->pNext;
429  }
430  *ppCheck = pNext;
431  }
432 }
433 
434 
435 bool C4ScenarioListLoader::Entry::Load(C4Group *pFromGrp, const StdStrBuf *psFilename, bool fLoadEx)
436 {
437  // nothing to do if already loaded
438  if (fBaseLoaded && (fExLoaded || !fLoadEx)) return true;
439  C4Group Group;
440  // group specified: Load as child
441  if (pFromGrp)
442  {
443  assert(psFilename);
444  if (!Group.OpenAsChild(pFromGrp, psFilename->getData())) return false;
445  // set FN by complete entry name
446  this->sFilename.Take(Group.GetFullName());
447  }
448  else
449  {
450  // set FN by complete entry name
451  if (psFilename) this->sFilename.Copy(*psFilename);
452  // no parent group: Direct load from filename
453  if (!Group.Open(sFilename.getData())) return false;
454  }
455  // okay; load standard stuff from group
456  bool fNameLoaded=false, fIconLoaded=false;
457  if (fBaseLoaded)
458  {
459  fNameLoaded = fIconLoaded = true;
460  }
461  else
462  {
463  // Set default name as filename without extension
464  sName.Copy(GetFilename(sFilename.getData()));
465  char *szBuf = sName.GrabPointer();
466  RemoveExtension(szBuf);
467  sName.Take(szBuf);
468  // load entry specific stuff that's in the front of the group
469  if (!LoadCustomPre(Group))
470  return false;
471  // Load entry name
472  C4ComponentHost DefNames;
474  if (DefNames.GetLanguageString(Config.General.LanguageEx, sName))
475  fNameLoaded = true;
476  // load entry icon
477  if (Group.FindEntry(C4CFN_IconPNG) && fctIcon.Load(Group, C4CFN_IconPNG, C4FCT_Full, C4FCT_Full, false, 0))
478  fIconLoaded = true;
479  else
480  {
481  C4FacetSurface fctTemp;
482  if (Group.FindEntry(C4CFN_ScenarioIcon) && fctTemp.Load(Group, C4CFN_ScenarioIcon, C4FCT_Full, C4FCT_Full, true, 0))
483  {
484  // old style icon: Blit it on a pieace of paper
485  fctTemp.Surface->Lock();
486  for (int y=0; y<fctTemp.Hgt; ++y)
487  for (int x=0; x<fctTemp.Wdt; ++x)
488  {
489  uint32_t dwPix = fctTemp.Surface->GetPixDw(x,y, false);
490  // transparency has some tolerance...
491  if (Inside<uint8_t>(dwPix & 0xff, 0xb8, 0xff))
492  if (Inside<uint8_t>((dwPix>>0x08) & 0xff, 0x00, 0x0f))
493  if (Inside<uint8_t>((dwPix>>0x10) & 0xff, 0xb8, 0xff))
494  fctTemp.Surface->SetPixDw(x,y,0x00ffffff);
495  }
496  fctTemp.Surface->Unlock();
497  int iIconSize = C4Startup::Get()->Graphics.fctScenSelIcons.Hgt;
498  fctIcon.Create(iIconSize, iIconSize, C4FCT_Full, C4FCT_Full);
500  fctTemp.Draw(fctIcon.Surface, (fctIcon.Wdt-fctTemp.Wdt)/2, (fctIcon.Hgt-fctTemp.Hgt)/2);
501  fctTemp.Clear();
502  fIconLoaded = true;
503  }
504  }
505  // load any entryx-type-specific custom data (e.g. fallbacks for scenario title, and icon)
506  if (!LoadCustom(Group, fNameLoaded, fIconLoaded)) return false;
507  fBaseLoaded = true;
508  }
509  // load extended stuff: title picture
510  if (fLoadEx && !fExLoaded)
511  {
512  // load desc
513  C4ComponentHost DefDesc;
515  {
516  sDesc.Copy(DefDesc.GetData());
517  }
518  // load title
519  fctTitle.Load(Group, C4CFN_ScenarioTitle,C4FCT_Full,C4FCT_Full,false,true);
520  fExLoaded = true;
521  // load version
522  Group.LoadEntryString(C4CFN_Version, &sVersion);
523  }
524  // done, success
525  return true;
526 }
527 
528 // helper func: Recursive check whether a directory contains a .ocs or .ocf file
529 bool DirContainsScenarios(const char *szDir)
530 {
531  // create iterator on free store to avoid stack overflow with deeply recursed folders
532  DirectoryIterator *pIter = new DirectoryIterator(szDir);
533  const char *szChildFilename;
534  for (; (szChildFilename = **pIter); ++*pIter)
535  {
536  // Ignore directory navigation entries and CVS folders
537  if (C4Group_TestIgnore(szChildFilename)) continue;
538  if (WildcardMatch(C4CFN_ScenarioFiles, szChildFilename) || WildcardMatch(C4CFN_FolderFiles, szChildFilename)) break;
539  if (DirectoryExists(szChildFilename))
540  if (DirContainsScenarios(szChildFilename))
541  break;
542  }
543  delete pIter;
544  // return true if loop was broken, in which case a matching entry was found
545  return !!szChildFilename;
546 }
547 
549 {
550  // determine entry type by file type
551  const char *szFilename = sFilename.getData();
552  if (!szFilename || !*szFilename) return nullptr;
553  if (WildcardMatch(C4CFN_ScenarioFiles, sFilename.getData())) return new Scenario(pLoader, pParent);
554  if (WildcardMatch(C4CFN_FolderFiles, sFilename.getData())) return new SubFolder(pLoader, pParent);
555  // regular, open folder (C4Group-packed folders without extensions are not regarded, because they could contain anything!)
556  const char *szExt = GetExtension(szFilename);
557  if ((!szExt || !*szExt) && DirectoryExists(sFilename.getData()))
558  {
559  // open folders only if they contain a scenario or folder
560  if (DirContainsScenarios(szFilename))
561  return new RegularFolder(pLoader, pParent);
562  }
563  // type not recognized
564  return nullptr;
565 }
566 
567 bool C4ScenarioListLoader::Entry::RenameTo(const char *szNewName)
568 {
569  // change name+filename
570  // some name sanity validation
571  if (!szNewName || !*szNewName) return false;
572  if (SEqual(szNewName, sName.getData())) return true;
573  char fn[_MAX_PATH+1];
574  SCopy(szNewName, fn, _MAX_PATH);
575  // generate new file name
577  if (!*fn) return false;
578  const char *szExt = GetDefaultExtension();
579  if (szExt) { SAppend(".", fn, _MAX_PATH); SAppend(szExt, fn, _MAX_PATH); }
580  char fullfn[_MAX_PATH+1];
581  SCopy(sFilename.getData(), fullfn, _MAX_PATH);
582  char *fullfn_fn = GetFilename(fullfn);
583  SCopy(fn, fullfn_fn, _MAX_PATH - (fullfn_fn - fullfn));
584  StdCopyStrBuf strErr(LoadResStr("IDS_FAIL_RENAME"));
585  // check if a rename is due
586  if (!ItemIdentical(sFilename.getData(), fullfn))
587  {
588  // check for duplicate filename
589  if (ItemExists(fullfn))
590  {
591  StdStrBuf sMsg; sMsg.Format(LoadResStr("IDS_ERR_FILEEXISTS"), fullfn);
593  return false;
594  }
595  // OK; then rename
596  if (!C4Group_MoveItem(sFilename.getData(), fullfn, true))
597  {
598  StdStrBuf sMsg; sMsg.Format(LoadResStr("IDS_ERR_RENAMEFILE"), sFilename.getData(), fullfn);
600  return false;
601  }
602  sFilename.Copy(fullfn);
603  }
604  // update real name in group, if this is a group
605  if (C4Group_IsGroup(fullfn))
606  {
607  C4Group Grp;
608  if (!Grp.Open(fullfn))
609  {
610  StdStrBuf sMsg; sMsg.Format(LoadResStr("IDS_ERR_OPENFILE"), sFilename.getData(), Grp.GetError());
612  return false;
613  }
614  if (!Grp.Delete(C4CFN_Title))
615  {
616  StdStrBuf sMsg; sMsg.Format(LoadResStr("IDS_ERR_DELOLDTITLE"), sFilename.getData(), Grp.GetError());
618  return false;
619  }
620  if (!SetTitleInGroup(Grp, szNewName)) return false;
621  if (!Grp.Close())
622  {
623  StdStrBuf sMsg; sMsg.Format(LoadResStr("IDS_ERR_WRITENEWTITLE"), sFilename.getData(), Grp.GetError());
625  return false;
626  }
627  }
628  // update title
629  sName.Copy(szNewName);
630  // done
631  return true;
632 }
633 
634 bool C4ScenarioListLoader::Entry::SetTitleInGroup(C4Group &rGrp, const char *szNewTitle)
635 {
636  // default for group files: Create a title text file and set the title in there
637  // no title needed if filename is sufficient - except for scenarios, where a Scenario.txt could overwrite the title
638  if (!IsScenario())
639  {
640  StdStrBuf sNameByFile; sNameByFile.Copy(GetFilename(sFilename.getData()));
641  char *szBuf = sNameByFile.GrabPointer();
642  RemoveExtension(szBuf);
643  sNameByFile.Take(szBuf);
644  if (SEqual(szNewTitle, sNameByFile.getData())) return true;
645  }
646  // okay, make a title
647  StdStrBuf sTitle; sTitle.Format("%s:%s", Config.General.Language, szNewTitle);
648  if (!rGrp.Add(C4CFN_WriteTitle, sTitle, false, true))
649  {
650  StdStrBuf sMsg; sMsg.Format(LoadResStr("IDS_ERR_ERRORADDINGNEWTITLEFORFIL"), sFilename.getData(), rGrp.GetError());
652  return false;
653  }
654  return true;
655 }
656 
657 
658 // ------------------------------------
659 // Scenario
660 
662 {
663  // load scenario core first
664  StdStrBuf sFileContents;
665  if (!rGrp.LoadEntryString(C4CFN_ScenarioCore, &sFileContents)) return false;
666  if (!CompileFromBuf_LogWarn<StdCompilerINIRead>(mkParAdapt(C4S, false), sFileContents, (rGrp.GetFullName() + DirSep C4CFN_ScenarioCore).getData()))
667  return false;
668  // Mission access
669  fNoMissionAccess = (!C4S.Head.MissionAccess.empty() && !SIsModule(Config.General.MissionAccess, C4S.Head.MissionAccess.c_str()));
670  // Localized parameter definitions. needed for achievements and parameter input boxes.
671  // Only show them for "real" scenarios
672  if (!C4S.Head.SaveGame && !C4S.Head.Replay)
673  {
674  // Skipping ahead in regular reading list, so keep other entries in memory
677  C4LangStringTable ScenarioLangStringTable;
679  ParameterDefs.Load(rGrp, &ScenarioLangStringTable);
680  // achievement images: Loaded from this entry and parent folder
681  nAchievements = 0;
682  const C4ScenarioParameterDefs *deflists[] = { pParent ? pParent->GetAchievementDefs() : nullptr, &ParameterDefs };
683  for (size_t def_list_idx=0; def_list_idx<2; ++def_list_idx)
684  {
685  const C4ScenarioParameterDefs *deflist = deflists[def_list_idx];
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)) 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  sChildFilename.Ref(ChildFilename);
1042  // okay; create this item
1043  Entry *pNewEntry = Entry::CreateEntryForFile(sChildFilename, pLoader, this);
1044  if (pNewEntry)
1045  {
1046  // ...and load it
1047  if (!pNewEntry->Load(&Group, &sChildFilename, fLoadEx))
1048  {
1049  DebugLogF("Error loading entry \"%s\" in SubFolder \"%s\"!", sChildFilename.getData(), Group.GetFullName().getData());
1050  delete pNewEntry;
1051  }
1052  }
1053  // mark progress
1054  if (!pLoader->DoProcessCallback(++iLoadCount, iEntryCount)) return false;
1055  }
1056  // next search mask
1057  if (szSearchMask == szC4CFN_ScenarioFiles)
1058  szSearchMask = C4CFN_FolderFiles;
1059  else
1060  szSearchMask = nullptr;
1061  }
1062  // load map folder data
1063  if (Group.FindEntry(C4CFN_MapFolderData))
1064  {
1065  pMapData = new C4MapFolderData();
1066  if (!pMapData->Load(Group, this))
1067  {
1068  // load error :(
1069  delete pMapData;
1070  pMapData = nullptr;
1071  }
1072  }
1073  // done, success
1074  fContentsLoaded = true;
1075  return true;
1076 }
1077 
1078 
1079 // ------------------------------------
1080 // RegularFolder
1081 
1083 {}
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)) 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  for (DirectoryIterator DirIter(it->c_str()); (szChildFilename = *DirIter); ++DirIter)
1127  {
1128  StdStrBuf sChildFilename(szChildFilename);
1129  szChildFilename = GetFilename(szChildFilename);
1130 
1131  // Ignore directory navigation entries and CVS folders
1132  if (C4Group_TestIgnore(szChildFilename)) continue;
1133  if (names.find(szChildFilename) != names.end()) continue;
1134  names.insert(szChildFilename);
1135  // filename okay; create this item
1136  Entry *pNewEntry = Entry::CreateEntryForFile(sChildFilename, pLoader, this);
1137  if (pNewEntry)
1138  {
1139  // ...and load it
1140  if (!pNewEntry->Load(nullptr, &sChildFilename, fLoadEx))
1141  {
1142  DebugLogF("Error loading entry \"%s\" in Folder \"%s\"!", szChildFilename, it->c_str());
1143  delete pNewEntry;
1144  }
1145  }
1146  // progress callback
1147  if (!pLoader->DoProcessCallback(++iCountLoaded, iCountTotal)) return false;
1148  }
1149  }
1150  // done, success
1151  fContentsLoaded = true;
1152  return true;
1153 }
1154 
1156 {
1157  contents.push_back(szPath);
1158 }
1159 
1160 // ------------------------------------
1161 // C4ScenarioListLoader
1162 
1163 C4ScenarioListLoader::C4ScenarioListLoader(const C4ScenarioParameters &Achievements) : Achievements(Achievements), pRootFolder(nullptr), pCurrFolder(nullptr),
1164  iLoading(0), iProgress(0), iMaxProgress(0), fAbortThis(false), fAbortPrevious(false)
1165 {
1166 }
1167 
1169 {
1170  if (pRootFolder) delete pRootFolder;
1171 }
1172 
1173 bool C4ScenarioListLoader::BeginActivity(bool fAbortPrevious)
1174 {
1175  // if previous activities were running, stop them first if desired
1176  if (iLoading && fAbortPrevious)
1177  this->fAbortPrevious = true;
1178  // mark this activity
1179  ++iLoading;
1180  // progress of activity not yet decided
1181  iProgress = iMaxProgress = 0;
1182  // okay; start activity
1183  return true;
1184 }
1185 
1186 void C4ScenarioListLoader::EndActivity()
1187 {
1188  assert(iLoading);
1189  if (!--iLoading)
1190  {
1191  // last activity done: Reset any flags
1192  fAbortThis = false;
1193  fAbortPrevious = false;
1194  iProgress = iMaxProgress = 0;
1195  }
1196  else
1197  {
1198  // child activity done: Transfer abort flag for next activity
1199  fAbortThis = fAbortPrevious;
1200  }
1201 }
1202 
1203 bool C4ScenarioListLoader::DoProcessCallback(int32_t iProgress, int32_t iMaxProgress)
1204 {
1205  this->iProgress = iProgress;
1206  this->iMaxProgress = iMaxProgress;
1207  // callback to dialog
1208  if (C4StartupScenSelDlg::pInstance) C4StartupScenSelDlg::pInstance->ProcessCallback();
1209  // process callback - abort at a few ugly circumstances...
1210  if (!Application.ScheduleProcs() // WM_QUIT message?
1211  || !C4StartupScenSelDlg::pInstance // host dialog removed?
1212  || !C4StartupScenSelDlg::pInstance->IsShown() // host dialog closed?
1213  ) return false;
1214  // and also abort if flagged
1215  return !fAbortThis;
1216 }
1217 
1218 bool C4ScenarioListLoader::Load(const StdStrBuf &sRootFolder)
1219 {
1220  // (unthreaded) loading of all entries in root folder
1221  if (!BeginActivity(true)) return false;
1222  if (pRootFolder) { delete pRootFolder; pRootFolder = nullptr; }
1223  pCurrFolder = pRootFolder = new RegularFolder(this, nullptr);
1224  // Load regular game data if no explicit path specified
1225  if(!sRootFolder.getData())
1226  for(C4Reloc::iterator iter = Reloc.begin(); iter != Reloc.end(); ++iter)
1227  pRootFolder->Merge(iter->strBuf.getData());
1228  bool fSuccess = pRootFolder->LoadContents(this, nullptr, &sRootFolder, false, false);
1229  EndActivity();
1230  return fSuccess;
1231 }
1232 
1233 bool C4ScenarioListLoader::Load(Folder *pSpecifiedFolder, bool fReload)
1234 {
1235  // call safety
1236  if (!pRootFolder || !pSpecifiedFolder) return false;
1237  // set new current and load it
1238  if (!BeginActivity(true)) return false;
1239  pCurrFolder = pSpecifiedFolder;
1240  bool fSuccess = pCurrFolder->LoadContents(this, nullptr, nullptr, false, fReload);
1241  EndActivity();
1242  return fSuccess;
1243 }
1244 
1246 {
1247  // call safety
1248  if (!pRootFolder || !pEntry) return false;
1249  // load info of selection
1250  if (!BeginActivity(false)) return false;
1251  bool fSuccess = pEntry->Load(nullptr, nullptr, true);
1252  EndActivity();
1253  return fSuccess;
1254 }
1255 
1257 {
1258  // call safety
1259  if (!pRootFolder || !pCurrFolder) return false;
1260  // already in root: Can't go up
1261  if (pCurrFolder == pRootFolder) return false;
1262  // otherwise, up one level
1263  return Load(pCurrFolder->GetParent(), false);
1264 }
1265 
1267 {
1268  // call safety
1269  if (!pRootFolder || !pCurrFolder) return false;
1270  // reload current
1271  return Load(pCurrFolder, true);
1272 }
1273 
1274 
1275 
1276 
1277 
1278 
1279 // ----------------------------------------------------------------
1280 // Scenario selection GUI
1281 
1282 
1283 // font clrs
1284 const uint32_t ClrScenarioItem = 0xff000000,
1285  ClrScenarioItemXtra = 0x7f000000,
1287 
1288 // ------------------------------------------------
1289 // --- C4StartupScenSelDlg::ScenListItem
1291  : pIcon(nullptr), pNameLabel(nullptr), pScenListEntry(pForEntry)
1292 {
1293  assert(pScenListEntry);
1294  CStdFont &rUseFont = C4Startup::Get()->Graphics.BookFont;
1295  StdStrBuf sIgnore;
1296  bool fEnabled = pScenListEntry->CanOpen(sIgnore) && !pScenListEntry->IsGrayed();
1297  // calc height
1298  int32_t iHeight = rUseFont.GetLineHeight() + 2 * IconLabelSpacing;
1299  // create subcomponents
1300  pIcon = new C4GUI::Picture(C4Rect(0, 0, iHeight, iHeight), true);
1301  pIcon->SetFacet(pScenListEntry->GetIconFacet());
1302  pNameLabel = new C4GUI::Label(pScenListEntry->GetName().getData(), iHeight + IconLabelSpacing, IconLabelSpacing, ALeft, fEnabled ? ClrScenarioItem : ClrScenarioItemDisabled, &rUseFont, false, false);
1303  // achievement components
1304  for (int32_t i=0; i<C4StartupScenSel_MaxAchievements; ++i)
1305  {
1306  C4Facet fct; const char *desc;
1307  if (pForEntry->GetAchievement(i, &fct, &desc))
1308  {
1309  ppAchievements[i] = new C4GUI::Picture(C4Rect(iHeight * (i+2), 0, iHeight, iHeight), true); // position will be adjusted later
1310  ppAchievements[i]->SetFacet(fct);
1311  ppAchievements[i]->SetToolTip(desc);
1312  }
1313  else
1314  {
1315  ppAchievements[i] = nullptr;
1316  }
1317  }
1318  // calc own bounds - use icon bounds only, because only the height is used when the item is added
1319  SetBounds(pIcon->GetBounds());
1320  // add components
1321  AddElement(pIcon); AddElement(pNameLabel);
1322  for (int32_t i=0; i<C4StartupScenSel_MaxAchievements; ++i) if (ppAchievements[i]) AddElement(ppAchievements[i]);
1323  // tooltip by name, so long names can be read via tooltip
1324  SetToolTip(pScenListEntry->GetName().getData());
1325  // add to listbox (will get resized horizontally and moved) - zero indent; no tree structure in this dialog
1326  pForListBox->InsertElement(this, pInsertBeforeElement, 0);
1327  // update name label width to reflect new horizontal size
1328  // name label width must be set so rename edit will take its size
1329  pNameLabel->SetAutosize(false);
1330  C4Rect rcNLB = pNameLabel->GetBounds(); rcNLB.Wdt = GetClientRect().Wdt - rcNLB.x - IconLabelSpacing;
1331  pNameLabel->SetBounds(rcNLB);
1332 }
1333 
1335 {
1336  // parent for client rect
1337  typedef C4GUI::Window ParentClass;
1338  ParentClass::UpdateOwnPos();
1339  // reposition achievement items
1340  C4GUI::ComponentAligner caBounds(GetContainedClientRect(), IconLabelSpacing, IconLabelSpacing);
1341  for (int32_t i=0; i<C4StartupScenSel_MaxAchievements; ++i) if (ppAchievements[i])
1342  {
1343  ppAchievements[i]->SetBounds(caBounds.GetFromRight(caBounds.GetHeight()));
1344  }
1345 }
1346 
1347 void C4StartupScenSelDlg::ScenListItem::MouseInput(C4GUI::CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam)
1348 {
1349  // double-click opens/starts item - currently processed by ListBox already!
1350  // inherited processing
1351  typedef C4GUI::Window BaseClass;
1352  BaseClass::MouseInput(rMouse, iButton, iX, iY, dwKeyParam);
1353 }
1354 
1356 {
1357  // return whether this item can be selected by entering given char:
1358  // first char of name must match
1359  // FIXME: make unicode-ready
1360  if (!pScenListEntry) return false;
1361  const char *szName = pScenListEntry->GetName().getData();
1362  return szName && (toupper(*szName) == toupper(c[0]));
1363 }
1364 
1366 {
1367  // rename this entry
1369  return true;
1370 }
1371 
1373 {
1374  // no renaming
1375  C4StartupScenSelDlg::pInstance->SetRenamingDone();
1376 }
1377 
1379 {
1380  // check validity for new name
1381  if (!GetEntry()->RenameTo(szNewName)) return C4GUI::RenameEdit::RR_Invalid;
1382  // rename label
1383  pNameLabel->SetText(GetEntry()->GetName().getData());
1384  // main dlg update
1385  C4StartupScenSelDlg::pInstance->SetRenamingDone();
1386  C4StartupScenSelDlg::pInstance->ResortFolder();
1387  C4StartupScenSelDlg::pInstance->UpdateSelection();
1388  C4StartupScenSelDlg::pInstance->FocusScenList();
1389  // done; rename accepted and control deleted by ResortFolder
1391 }
1392 
1393 // ------------------------------------------------
1394 // --- C4StartupScenSelDlg
1395 
1396 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)
1397 {
1398  // ctor
1399  // assign singleton
1400  pInstance = this;
1401 
1402  // screen calculations
1403  UpdateSize();
1404  int32_t iButtonWidth,iCaptionFontHgt;
1405  int iButtonHeight = C4GUI_ButtonHgt;
1406  int iBookPageWidth;
1407  int iExtraHPadding = rcBounds.Wdt >= 700 ? rcBounds.Wdt/50 : 0;
1408  ::GraphicsResource.CaptionFont.GetTextExtent("<< BACK", iButtonWidth, iCaptionFontHgt, true);
1409  iButtonWidth *= 3;
1410  C4GUI::ComponentAligner caMain(GetClientRect(), 0,0, true);
1411  C4GUI::ComponentAligner caButtonArea(caMain.GetFromBottom(caMain.GetHeight()/8),rcBounds.Wdt/(rcBounds.Wdt >= 700 ? 128 : 256),0);
1412  C4Rect rcMap = caMain.GetCentered(caMain.GetWidth(), caMain.GetHeight());
1413 
1414  // tabular for different scenario selection designs
1415  pScenSelStyleTabular = new C4GUI::Tabular(rcMap, C4GUI::Tabular::tbNone);
1416  pScenSelStyleTabular->SetSheetMargin(0);
1417  pScenSelStyleTabular->SetGfx(&C4Startup::Get()->Graphics.fctDlgPaper, &C4Startup::Get()->Graphics.fctOptionsTabClip, &C4Startup::Get()->Graphics.fctOptionsIcons, &C4Startup::Get()->Graphics.BookSmallFont, false);
1418  AddElement(pScenSelStyleTabular);
1419  C4GUI::Tabular::Sheet *pSheetBook = pScenSelStyleTabular->AddSheet(nullptr);
1420  /* C4GUI::Tabular::Sheet *pSheetMap = */ pScenSelStyleTabular->AddSheet(nullptr);
1421 
1422  // scenario selection list
1423  C4GUI::ComponentAligner caBook(pSheetBook->GetClientRect(), caMain.GetWidth()/20, caMain.GetHeight()/20, true);
1424  C4GUI::ComponentAligner caBookLeft(caBook.GetFromLeft(iBookPageWidth=caBook.GetWidth()*4/9+4-iExtraHPadding*2), 0,5);
1425 
1426  CStdFont &rScenSelCaptionFont = C4Startup::Get()->Graphics.BookFontTitle;
1427  pScenSelCaption = new C4GUI::Label("", caBookLeft.GetFromTop(rScenSelCaptionFont.GetLineHeight()), ACenter, ClrScenarioItem, &rScenSelCaptionFont, false);
1428  pSheetBook->AddElement(pScenSelCaption);
1429  pScenSelCaption->SetToolTip(LoadResStr("IDS_DLGTIP_SELECTSCENARIO"));
1430  pScenSelList = new C4GUI::ListBox(caBookLeft.GetAll());
1431  pScenSelList->SetToolTip(LoadResStr("IDS_DLGTIP_SELECTSCENARIO"));
1432  pScenSelList->SetDecoration(false, &C4Startup::Get()->Graphics.sfctBookScroll, true);
1433  pSheetBook->AddElement(pScenSelList);
1436  // scenario selection list progress label
1437  pScenSelProgressLabel = new C4GUI::Label("", pScenSelList->GetBounds().GetMiddleX(), pScenSelList->GetBounds().GetMiddleX()-iCaptionFontHgt/2, ACenter, ClrScenarioItem, &(C4Startup::Get()->Graphics.BookFontCapt), false);
1438  pSheetBook->AddElement(pScenSelProgressLabel);
1439 
1440  // right side of book: Displaying current selection
1441  C4Rect bounds = caBook.GetFromRight(iBookPageWidth);
1442  const int32_t AvailWidth = bounds.Wdt;
1443  const int32_t AvailHeight = 2 * bounds.Hgt / 5;
1444  int32_t PictureWidth, PictureHeight;
1446  {
1447  PictureWidth = C4StartupScenSel_TitlePictureWdt * AvailWidth / C4StartupScenSel_TitlePictureWdt;
1449  }
1450  else
1451  {
1452  PictureWidth = C4StartupScenSel_TitlePictureWdt * AvailHeight / C4StartupScenSel_TitlePictureHgt;
1454  }
1455  pSelectionInfo = new C4GUI::TextWindow(bounds, PictureWidth+2*C4StartupScenSel_TitleOverlayMargin, PictureHeight+2*C4StartupScenSel_TitleOverlayMargin,
1456  C4StartupScenSel_TitlePicturePadding, 100, 4096, nullptr, true, &C4Startup::Get()->Graphics.fctScenSelTitleOverlay, C4StartupScenSel_TitleOverlayMargin);
1457  pSelectionInfo->SetDecoration(false, false, &C4Startup::Get()->Graphics.sfctBookScroll, true);
1458  pSheetBook->AddElement(pSelectionInfo);
1459 
1460  // bottom of right side of book: Custom options on selection
1461  // Arbitrary height and invisible by default. Height will be adjusted when options are created.
1462  pSelectionOptions = new C4GameOptionsList(C4Rect(bounds.x, bounds.y+bounds.Hgt-10, bounds.Wdt, 10), false, fNetwork ? C4GameOptionsList::GOLS_PreGameNetwork : C4GameOptionsList::GOLS_PreGameSingle);
1463  pSelectionOptions->SetDecoration(false, &C4Startup::Get()->Graphics.sfctBookScroll, true);
1464  pSelectionOptions->SetVisibility(false);
1465  pSheetBook->AddElement(pSelectionOptions);
1466 
1467  // back button
1469  AddElement(btn = new C4GUI::CallbackButton<C4StartupScenSelDlg>(LoadResStr("IDS_BTN_BACK"), caButtonArea.GetFromLeft(iButtonWidth, iButtonHeight), &C4StartupScenSelDlg::OnBackBtn));
1470  btn->SetToolTip(LoadResStr("IDS_DLGTIP_BACKMAIN"));
1471  AddElement(btn);
1472  // next button
1473  pOpenBtn = new C4GUI::CallbackButton<C4StartupScenSelDlg>(LoadResStr("IDS_BTN_OPEN"), caButtonArea.GetFromRight(iButtonWidth, iButtonHeight), &C4StartupScenSelDlg::OnNextBtn);
1474  pOpenBtn->SetToolTip(LoadResStr("IDS_DLGTIP_SCENSELNEXT"));
1475  // game options boxes
1476  pGameOptionButtons = new C4GameOptionButtons(caButtonArea.GetAll(), fNetwork, true, false);
1477  AddElement(pGameOptionButtons);
1478  // next button adding
1479  AddElement(pOpenBtn);
1480 
1481  // dlg starts with focus on listbox
1482  SetFocus(pScenSelList, false);
1483 
1484  // key bindings
1485  C4CustomKey::CodeList keys;
1486  keys.push_back(C4KeyCodeEx(K_BACK)); keys.push_back(C4KeyCodeEx(K_LEFT));
1487  pKeyBack = new C4KeyBinding(keys, "StartupScenSelFolderUp", KEYSCOPE_Gui,
1489  pKeyRefresh = new C4KeyBinding(C4KeyCodeEx(K_F5), "StartupScenSelReload", KEYSCOPE_Gui,
1491  pKeyForward = new C4KeyBinding(C4KeyCodeEx(K_RIGHT), "StartupScenSelNext", KEYSCOPE_Gui,
1493  pKeyRename = new C4KeyBinding(C4KeyCodeEx(K_F2), "StartupScenSelRename", KEYSCOPE_Gui,
1495  pKeyDelete = new C4KeyBinding(C4KeyCodeEx(K_DELETE), "StartupScenSelDelete", KEYSCOPE_Gui,
1497  pKeyCheat = new C4KeyBinding(C4KeyCodeEx(K_M, KEYS_Control), "StartupScenSelCheat", KEYSCOPE_Gui,
1499 }
1500 
1502 {
1503  if (pScenLoader) delete pScenLoader;
1504  if (this == pInstance) pInstance = nullptr;
1505  delete pKeyCheat;
1506  delete pKeyDelete;
1507  delete pKeyRename;
1508  delete pKeyForward;
1509  delete pKeyRefresh;
1510  delete pKeyBack;
1511 }
1512 
1514 {
1515  // draw background
1516  if (pfctBackground)
1517  DrawBackground(cgo, *pfctBackground);
1518 }
1519 
1521 {
1523  // Collect achievements of all activated players
1524  UpdateAchievements();
1525  // init file list
1526  fIsInitialLoading = true;
1527  if (!pScenLoader) pScenLoader = new C4ScenarioListLoader(Achievements);
1528  pScenLoader->Load(StdStrBuf());
1529  UpdateList();
1530  UpdateSelection();
1531  fIsInitialLoading = false;
1532  // network activation by dialog type
1533  Game.NetworkActive = fStartNetworkGame;
1534 }
1535 
1537 {
1538  AbortRenaming();
1539  // clear laoded scenarios
1540  if (pScenLoader)
1541  {
1542  delete pScenLoader;
1543  pScenLoader = nullptr;
1544  UpdateList(); // must clear scenario list, because it points to deleted stuff
1545  UpdateSelection(); // must clear picture facet of selection!
1546  }
1547  // dlg abort: return to main screen
1548  if (!fOK)
1549  {
1550  // clear settings: Password
1551  ::Network.SetPassword(nullptr);
1553  }
1554 }
1555 
1556 void C4StartupScenSelDlg::UpdateList()
1557 {
1558  AbortRenaming();
1559  // default: Show book (also for loading screen)
1560  pMapData = nullptr;
1561  pScenSelStyleTabular->SelectSheet(ShowStyle_Book, false);
1562  // and delete any stuff from map selection
1563  C4GUI::Tabular::Sheet *pMapSheet = pScenSelStyleTabular->GetSheet(ShowStyle_Map);
1564  while (pMapSheet->GetFirst()) delete pMapSheet->GetFirst();
1565  pfctBackground = nullptr;
1566  // for now, all the list is loaded at once anyway
1567  // so just clear and add all loaded items
1568  // remember old selection
1569  C4ScenarioListLoader::Entry *pOldSelection = GetSelectedEntry();
1570  C4GUI::Element *pEl;
1571  while ((pEl = pScenSelList->GetFirst())) delete pEl;
1572  pScenSelCaption->SetText("");
1573  // scen loader still busy: Nothing to add
1574  if (!pScenLoader) return;
1575  if (pScenLoader->IsLoading())
1576  {
1577  StdStrBuf sProgressText;
1578  sProgressText.Format(LoadResStr("IDS_MSG_SCENARIODESC_LOADING"), (int32_t) pScenLoader->GetProgressPercent());
1579  pScenSelProgressLabel->SetText(sProgressText.getData());
1580  pScenSelProgressLabel->SetVisibility(true);
1581  return;
1582  }
1583  pScenSelProgressLabel->SetVisibility(false);
1584  // is this a map folder? Then show the map instead
1585  C4ScenarioListLoader::Folder *pFolder = static_cast<C4ScenarioListLoader::Folder *>(pScenLoader->GetCurrFolder());
1586  if ((pMapData = pFolder->GetMapData()))
1587  {
1588  pMapData->ResetSelection();
1589  pMapData->CreateGUIElements(this, *pScenSelStyleTabular->GetSheet(ShowStyle_Map));
1590  pScenSelStyleTabular->SelectSheet(ShowStyle_Map, false);
1591  }
1592  else
1593  {
1594  // book style selection
1595  // add what has been loaded
1596  for (C4ScenarioListLoader::Entry *pEnt = pScenLoader->GetFirstEntry(); pEnt; pEnt = pEnt->GetNext())
1597  {
1598  if (pEnt->IsHidden()) continue; // no UI entry at all for hidden items
1599  ScenListItem *pEntItem = new ScenListItem(pScenSelList, pEnt);
1600  if (pEnt == pOldSelection) pScenSelList->SelectEntry(pEntItem, false);
1601  }
1602  // set title of current folder
1603  // but not root
1604  if (pFolder && pFolder != pScenLoader->GetRootFolder())
1605  pScenSelCaption->SetText(pFolder->GetName().getData());
1606  else
1607  {
1608  // special root title
1609  pScenSelCaption->SetText(LoadResStr("IDS_DLG_SCENARIOS"));
1610  }
1611  // new list has been loaded: Select first entry if nothing else had been selected
1612  if (!pOldSelection) pScenSelList->SelectFirstEntry(false);
1613  }
1614 }
1615 
1616 void C4StartupScenSelDlg::ResortFolder()
1617 {
1618  // if it's still loading, sorting will be done in the end anyway
1619  if (!pScenLoader || pScenLoader->IsLoading()) return;
1620  C4ScenarioListLoader::Folder *pFolder = pScenLoader->GetCurrFolder();
1621  if (!pFolder) return;
1622  pFolder->Resort();
1623  UpdateList();
1624 }
1625 
1626 void C4StartupScenSelDlg::UpdateSelection()
1627 {
1628  AbortRenaming();
1629  if (!pScenLoader)
1630  {
1631  C4Facet fctNoPic;
1632  pSelectionInfo->SetPicture(fctNoPic);
1633  pSelectionInfo->ClearText(false);
1634  SetOpenButtonDefaultText();
1635  return;
1636  }
1637  // determine target text box
1638  C4GUI::TextWindow *pSelectionInfo = pMapData ? pMapData->GetSelectionInfoBox() : this->pSelectionInfo;
1639  // get selected entry
1640  C4ScenarioListLoader::Entry *pSel = GetSelectedEntry();
1641  if (!pSel)
1642  {
1643  // no selection: Display data of current parent folder
1644  pSel = pScenLoader->GetCurrFolder();
1645  // but not root
1646  if (pSel == pScenLoader->GetRootFolder()) pSel = nullptr;
1647  }
1648  // get title image and desc of selected entry
1649  C4Facet fctTitle; StdStrBuf sTitle, sDesc, sVersion, sAuthor;
1650  if (pSel)
1651  {
1652  pScenLoader->LoadExtended(pSel); // 2do: Multithreaded
1653  fctTitle = pSel->GetTitlePicture();
1654  sTitle.Ref(pSel->GetName());
1655  sDesc.Ref(pSel->GetDesc());
1656  sVersion.Ref(pSel->GetVersion());
1657  sAuthor.Ref(pSel->GetAuthor());
1658  // never show a pure title string: There must always be some text or an image
1659  if (!fctTitle.Surface && (!sDesc || !*sDesc.getData()))
1660  sTitle.Clear();
1661  // selection specific open/start button
1662  pOpenBtn->SetText(pSel->GetOpenText().getData());
1663  pOpenBtn->SetToolTip(pSel->GetOpenTooltip().getData());
1664  }
1665  else
1666  SetOpenButtonDefaultText();
1667  // set data in selection component
1668  pSelectionInfo->ClearText(false);
1669  pSelectionInfo->SetPicture(fctTitle);
1670  if (sTitle && (!sDesc || !*sDesc.getData())) pSelectionInfo->AddTextLine(sTitle.getData(), &C4Startup::Get()->Graphics.BookFontCapt, ClrScenarioItem, false, false);
1671  if (sDesc) pSelectionInfo->AddTextLine(sDesc.getData(), &C4Startup::Get()->Graphics.BookFont, ClrScenarioItem, false, false, &C4Startup::Get()->Graphics.BookFontCapt);
1672  if (sAuthor) pSelectionInfo->AddTextLine(FormatString(LoadResStr("IDS_CTL_AUTHOR"), sAuthor.getData()).getData(),
1674  if (sVersion) pSelectionInfo->AddTextLine(FormatString(LoadResStr("IDS_DLG_VERSION"), sVersion.getData()).getData(),
1676  // update custom scenario options panel
1677  if (pSel)
1678  {
1679  pSelectionOptions->SetParameters(pSel->GetParameterDefs(), pSel->GetParameters());
1680  pSelectionOptions->Update();
1681  }
1682  else
1683  pSelectionOptions->SetParameters(nullptr, nullptr);
1684  // update component heights
1685  C4Rect rcSelBounds = pSelectionInfo->GetBounds();
1686  int32_t ymax = pSelectionOptions->GetBounds().GetBottom();
1687  C4GUI::Element *pLastOption = pSelectionOptions->GetLast();
1688  if (pLastOption)
1689  {
1690  // custom options present: Info box reduced; options box at bottom
1691  // set options box max size to 1/3rd of selection info area
1692  int32_t options_hgt = std::min<int32_t>(pLastOption->GetBounds().GetBottom() + pSelectionOptions->GetMarginTop() + pSelectionOptions->GetMarginTop(), rcSelBounds.Hgt/3);
1693  rcSelBounds.Hgt = ymax - options_hgt - rcSelBounds.y;
1694  pSelectionInfo->SetBounds(rcSelBounds);
1695  rcSelBounds.y = ymax - options_hgt;
1696  rcSelBounds.Hgt = options_hgt;
1697  pSelectionOptions->SetBounds(rcSelBounds);
1698  pSelectionOptions->SetVisibility(true);
1699  }
1700  else
1701  {
1702  // custom options absent: Info takes full area
1703  pSelectionOptions->SetVisibility(false);
1704  rcSelBounds.Hgt = ymax - rcSelBounds.y;
1705  pSelectionInfo->SetBounds(rcSelBounds);
1706  }
1707  pSelectionInfo->UpdateHeight();
1708 }
1709 
1710 C4StartupScenSelDlg::ScenListItem *C4StartupScenSelDlg::GetSelectedItem()
1711 {
1712  return static_cast<ScenListItem *>(pScenSelList->GetSelectedItem());
1713 }
1714 
1715 C4ScenarioListLoader::Entry *C4StartupScenSelDlg::GetSelectedEntry()
1716 {
1717  // map layout: Get selection from map
1718  if (pMapData) return pMapData->GetSelectedEntry();
1719  // get selection in listbox
1720  ScenListItem *pSel = static_cast<ScenListItem *>(pScenSelList->GetSelectedItem());
1721  return pSel ? pSel->GetEntry() : nullptr;
1722 }
1723 
1725 {
1726  assert(pStartScen);
1727  if (!pStartScen) return false;
1728  // get required object definitions
1729  if (pStartScen->GetC4S().Definitions.AllowUserChange)
1730  {
1731  // get definitions as user selects them
1732  StdStrBuf sDefinitions;
1733  if (!pStartScen->GetC4S().Definitions.GetModules(&sDefinitions)) sDefinitions.Copy("Objects.ocd");
1734  if (!C4DefinitionSelDlg::SelectDefinitions(GetScreen(), &sDefinitions))
1735  // user aborted during definition selection
1736  return false;
1737  SCopy(sDefinitions.getData(), ::Game.DefinitionFilenames, (sizeof Game.DefinitionFilenames)-1);
1738  }
1739  else
1740  // for no user change, just set default objects. Custom settings will override later anyway
1741  SCopy("Objects.ocd", ::Game.DefinitionFilenames);
1742  // set other default startup parameters
1743  ::Game.fLobby = !!::Game.NetworkActive; // always lobby in network
1744  ::Game.fObserve = false;
1745  C4ScenarioParameters *custom_params = pStartScen->GetParameters();
1746  if (custom_params) ::Game.StartupScenarioParameters = *custom_params; else ::Game.StartupScenarioParameters.Clear();
1747  // start with this set!
1749  return true;
1750 }
1751 
1753 {
1754  // open it through loader
1755  if (!pScenLoader) return false;
1756  bool fSuccess = pScenLoader->Load(pNewFolder, false);
1757  UpdateList();
1758  UpdateSelection();
1759  if (!pMapData) SetFocus(pScenSelList, false);
1760  return fSuccess;
1761 }
1762 
1764 {
1765  AbortRenaming();
1766  // get selected entry
1767  C4ScenarioListLoader::Entry *pSel = GetSelectedEntry();
1768  if (!pSel) return false;
1769  // check if open is possible
1770  StdStrBuf sError;
1771  if (!pSel->CanOpen(sError))
1772  {
1773  GetScreen()->ShowMessage(sError.getData(), LoadResStr("IDS_MSG_CANNOTSTARTSCENARIO"), C4GUI::Ico_Error);
1774  return false;
1775  }
1776  // if CanOpen returned true but set an error message, that means it's a warning. Display it!
1777  if (sError.getLength())
1778  {
1780  // user chose to not start it
1781  return false;
1782  }
1783  // start it!
1784  return pSel->Start();
1785 }
1786 
1787 bool C4StartupScenSelDlg::DoBack(bool fAllowClose)
1788 {
1789  AbortRenaming();
1790  // if in a subfolder, try backtrace folders first
1791  if (pScenLoader && pScenLoader->FolderBack())
1792  {
1793  UpdateList();
1794  UpdateSelection();
1795  return true;
1796  }
1797  // while this isn't multithreaded, the dialog must not be aborted while initial load...
1798  if (pScenLoader && pScenLoader->IsLoading()) return false;
1799  // return to main screen
1800  if (fAllowClose)
1801  {
1802  Close(false);
1803  return true;
1804  }
1805  return false;
1806 }
1807 
1809 {
1810  if (pScenLoader && !pScenLoader->IsLoading())
1811  {
1812  pScenSelList->SelectNone(false);
1813  pScenLoader->ReloadCurrent();
1814  UpdateList();
1815  UpdateSelection();
1816  }
1817 }
1818 
1819 void C4StartupScenSelDlg::SetOpenButtonDefaultText()
1820 {
1821  pOpenBtn->SetText(LoadResStr("IDS_BTN_OPEN"));
1822  pOpenBtn->SetToolTip(LoadResStr("IDS_DLGTIP_SCENSELNEXT"));
1823 }
1824 
1826 {
1827  // no rename in map mode
1828  if (pMapData) return false;
1829  // not if renaming already
1830  if (IsRenaming()) return false;
1831  // forward to selected scenario list item
1832  ScenListItem *pSel = GetSelectedItem();
1833  if (!pSel) return false;
1834  return pSel->KeyRename();
1835 }
1836 
1838 {
1839  // do not delete from map folder
1840  if (pMapData) return false;
1841  // cancel renaming
1842  AbortRenaming();
1843  // delete selected item: Confirmation first
1844  ScenListItem *pSel = GetSelectedItem();
1845  if (!pSel) return false;
1846  C4ScenarioListLoader::Entry *pEnt = pSel->GetEntry();
1847  StdStrBuf sWarning;
1848  sWarning.Format(LoadResStr("IDS_MSG_PROMPTDELETE"), FormatString("%s %s", pEnt->GetTypeName().getData(), pEnt->GetName().getData()).getData(), pEnt->GetEntryFilename().getData());
1849  GetScreen()->ShowRemoveDlg(new C4GUI::ConfirmationDialog(sWarning.getData(), LoadResStr("IDS_MNU_DELETE"),
1851  return true;
1852 }
1853 
1855 {
1856  // deletion confirmed. Do it.
1857  C4ScenarioListLoader::Entry *pEnt = pSel->GetEntry();
1858  if (!C4Group_DeleteItem(pEnt->GetEntryFilename().getData(), true))
1859  {
1860  StdStrBuf sMsg; sMsg.Format("%s", LoadResStr("IDS_FAIL_DELETE"));
1862  return;
1863  }
1864  // remove from scenario list
1865  pScenSelList->SelectEntry(pSel->GetNext(), false);
1866  delete pEnt;
1867  delete pSel;
1868 }
1869 
1871 {
1872  return ::pGUI->ShowRemoveDlg(new C4GUI::InputDialog(LoadResStr("IDS_TEXT_ENTERMISSIONPASSWORD"), LoadResStr("IDS_DLG_MISSIONACCESS"), C4GUI::Ico_Options,
1874  false));
1875 }
1876 
1878 {
1879  // Special character "-": remove mission password(s)
1880  if (SEqual2(rsCheatCode.getData(), "-"))
1881  {
1882  const char *szPass = rsCheatCode.getPtr(1);
1883  if (szPass && *szPass)
1884  {
1885  SRemoveModules(Config.General.MissionAccess, szPass, false);
1886  UpdateList();
1887  UpdateSelection();
1888  return;
1889  }
1890  }
1891 
1892  // No special character: add mission password(s)
1893  const char *szPass = rsCheatCode.getPtr(0);
1894  if (szPass && *szPass)
1895  {
1896  SAddModules(Config.General.MissionAccess, szPass, false);
1897  UpdateList();
1898  UpdateSelection();
1899  return;
1900  }
1901 
1902 }
1903 
1904 void C4StartupScenSelDlg::FocusScenList()
1905 {
1906  SetFocus(pScenSelList, false);
1907 }
1908 
1910 {
1911  // map button was clicked: Update selected scenario
1912  if (!pMapData || !pEl) return;
1913  C4ScenarioListLoader::Entry *pSel = GetSelectedEntry(), *pSel2;
1914  pMapData->OnButtonScenario(pEl);
1915  pSel2 = GetSelectedEntry();
1916  if (pSel && pSel==pSel2)
1917  {
1918  // clicking on the selected scenario again starts it
1919  DoOK();
1920  return;
1921  }
1922  // the first click selects it
1923  SetFocus(pEl, false);
1924  UpdateSelection();
1925 }
1926 
1928 {
1929  // Deselect all so current folder info is displayed
1930  if (GetFocus()) C4GUI::GUISound("UI::Tick");
1931  SetFocus(nullptr, true);
1932  if (pMapData) pMapData->ResetSelection();
1933  UpdateSelection();
1934 }
1935 
1937 {
1938  pRenameEdit = pNewRenameEdit;
1939 }
1940 
1942 {
1943  if (pRenameEdit) pRenameEdit->Abort();
1944 }
1945 
1946 void C4StartupScenSelDlg::UpdateAchievements()
1947 {
1948  // Extract all achievements from activated player files and merge them
1949  Achievements.Clear();
1950  char PlayerFilename[_MAX_FNAME+1];
1951  C4Group PlayerGrp;
1952  for (int i = 0; SCopySegment(Config.General.Participants, i, PlayerFilename, ';', _MAX_FNAME, true); i++)
1953  {
1954  const char *szPlayerFilename = Config.AtUserDataPath(PlayerFilename);
1955  if (!FileExists(szPlayerFilename)) continue;
1956  if (!PlayerGrp.Open(szPlayerFilename)) continue;
1957  C4PlayerInfoCore nfo;
1958  if (!nfo.Load(PlayerGrp)) continue;
1959  Achievements.Merge(nfo.Achievements);
1960  }
1961 }
1962 
1964 {
1965  if (pSelectionOptions) pSelectionOptions->Update();
1966 }
1967 
1968 // NICHT: 9, 7.2.2, 113-114, 8a
1969 
char * GetFilename(char *szPath)
Definition: StdFile.cpp:55
const char * getData() const
Definition: StdBuf.h:450
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:794
CStdFont BookFontTitle
Definition: C4Startup.h:65
int32_t AlphabeticalSorting
Definition: C4Config.h:190
const int32_t C4StartupScenSel_DefaultIcon_WinFolder
#define C4CFN_FolderFiles
Definition: C4Components.h:175
const C4ScenarioParameterDef * GetParameterDefByIndex(size_t idx) const
const int32_t C4StartupScenSel_TitlePictureWdt
int32_t GetProgressPercent() const
C4Config Config
Definition: C4Config.cpp:837
char * GrabPointer()
Definition: StdBuf.h:467
const int32_t C4MC_Button_LeftDown
int GetLineHeight() const
Definition: C4FontLoader.h:132
void GUISound(const char *szSound)
Definition: C4Gui.cpp:1178
C4ConfigStartup Startup
Definition: C4Config.h:261
virtual bool GetAchievement(int32_t idx, C4Facet *out_facet, const char **out_description)
Element * GetLast()
Definition: C4Gui.h:1572
#define C4GUI_ButtonHgt
Definition: C4Gui.h:111
virtual void OnLeagueOptionChanged()
void SCopy(const char *szSource, char *sTarget, size_t iMaxL)
Definition: Standard.cpp:129
bool LoadExtended(Entry *pEntry)
C4SDefinitions Definitions
Definition: C4Scenario.h:231
virtual int32_t GetMarginTop()
Definition: C4Gui.h:1575
virtual bool DoLoadContents(C4ScenarioListLoader *pLoader, C4Group *pFromGrp, const StdStrBuf &sFilename, bool fLoadEx)
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:291
StdNamingCountAdapt< int_t > mkNamingCountAdapt(int_t &iCount, const char *szName)
Definition: StdAdaptors.h:977
void StartRenaming(C4GUI::RenameEdit *pNewRenameEdit)
bool Load(C4Group &hGroup, const char *szFilename, const char *szLanguage=nullptr)
int Wdt
Definition: C4Surface.h:67
void SAppend(const char *szSource, char *szTarget, int iMaxL)
Definition: Standard.cpp:234
bool ItemIdentical(const char *szFilename1, const char *szFilename2)
Definition: StdFile.cpp:865
C4Game Game
Definition: C4Globals.cpp:52
char MissionAccess[CFG_MaxString+1]
Definition: C4Config.h:46
float bottom
Definition: C4Rect.h:27
const int32_t C4StartupScenSel_DefaultIcon_OldIconBG
void Clear()
Definition: StdBuf.h:474
C4ConfigGeneral General
Definition: C4Config.h:252
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:27
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:250
int stricmp(const char *s1, const char *s2)
bool Lock()
Definition: C4Surface.cpp:463
float left
Definition: C4Rect.h:27
const uint32_t ClrScenarioItemDisabled
bool SavePNG(C4Group &hGroup, const char *szFilename, bool fSaveAlpha=true, bool fSaveOverlayOnly=false)
void DrawBackground(C4TargetFacet &cgo, C4Facet &rFromFct)
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:410
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)
PathList::const_iterator iterator
Definition: C4Reloc.h:38
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:184
bool Unlock()
Definition: C4Surface.cpp:474
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:29
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:181
const C4Scenario & GetC4S() const
virtual C4Rect & GetClientRect()
Definition: C4Gui.h:864
void AddElement(Element *pChild)
const StdStrBuf & GetAuthor() const
bool SIsModule(const char *szList, const char *szString, int *ipIndex, bool fCaseSensitive)
Definition: Standard.cpp:518
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:97
void Set(const C4Facet &cpy)
Definition: C4FacetEx.h:46
C4FacetID fctScenSelIcons
Definition: C4Startup.h:61
virtual ~Entry()
bool IsShown()
Definition: C4Gui.h:2147
const StdStrBuf & GetVersion() const
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:547
Element * GetSelectedItem()
Definition: C4Gui.h:1580
const StdStrBuf & GetDesc() const
bool SRemoveModules(char *szList, const char *szModules, bool fCaseSensitive)
Definition: Standard.cpp:576
void MakeFilenameFromTitle(char *szTitle)
Definition: StdFile.cpp:412
C4StartupScenSelDlg(bool fNetwork)
int iDifficulty
virtual void SetVisibility(bool fToValue)
void KeyCheat2(const StdStrBuf &rsCheatCode)
StdNamingAdapt< T > mkNamingAdapt(T &&rValue, const char *szName)
Definition: StdAdaptors.h:93
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:58
int32_t Wdt
Definition: C4Rect.h:32
static bool LoadComponentHost(C4ComponentHost *host, C4Group &hGroup, const char *szFilename, const char *szLanguage)
Definition: C4Language.cpp:235
virtual void DrawElement(C4TargetFacet &cgo)
#define C4CFN_MapFolderBG
Definition: C4Components.h:136
std::vector< C4KeyCodeEx > CodeList
virtual bool LoadCustomPre(C4Group &rGrp)
const int32_t C4StartupScenSel_TitlePictureHgt
C4GUIScreen * pGUI
Definition: C4Gui.cpp:1194
#define C4CFN_Title
Definition: C4Components.h:82
void SelectFirstEntry(bool fByUser)
Definition: C4Gui.h:1584
void SetAutosize(bool fToVal)
Definition: C4Gui.h:507
virtual void SetVisibility(bool fToValue)
Definition: C4Gui.cpp:208
bool DebugLogF(const char *strMessage...)
Definition: C4Log.cpp:281
CStdFont BookSmallFont
Definition: C4Startup.h:65
C4Network2 Network
Definition: C4Globals.cpp:53
const int32_t C4StartupScenSel_TitleOverlayMargin
bool Load(C4Group &hGroup)
Definition: C4InfoCore.cpp:81
const char * LoadResStrNoAmp(const char *id)
Definition: StdResStr2.cpp:24
int32_t y
Definition: C4Rect.h:32
void CompileFunc(StdCompiler *pComp)
bool DoProcessCallback(int32_t iProgress, int32_t iMaxProgress)
char Participants[CFG_MaxString+1]
Definition: C4Config.h:39
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:43
virtual bool LoadCustom(C4Group &rGrp, bool fNameLoaded, bool fIconLoaded)
bool SetPixDw(int iX, int iY, DWORD dwCol)
Definition: C4Surface.cpp:586
const char * GetName() const
Definition: C4Group.cpp:1845
StdStrBuf GetFullName() const
Definition: C4Group.cpp:2078
C4FacetID fctOptionsIcons
Definition: C4Startup.h:71
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
void Take(char *pnData)
Definition: StdBuf.h:465
int iIconIndex
int32_t PictureWidth
StdPtrAdapt< T > mkPtrAdaptNoNull(T *&rpObj)
Definition: StdAdaptors.h:607
virtual StdStrBuf GetTypeName()=0
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
virtual bool CheckNameHotkey(const char *c)
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:139
const int32_t C4StartupScenSel_DefaultIcon_SavegamesFolder
bool DirContainsScenarios(const char *szDir)
bool Close()
Definition: C4Group.cpp:755
C4Draw * pDraw
Definition: C4Draw.cpp:45
int SModuleCount(const char *szList)
Definition: Standard.cpp:588
virtual bool SetTitleInGroup(C4Group &rGrp, const char *szNewTitle)
Sheet * GetSheet(int32_t iIndex)
Definition: C4Gui.h:1706
void ReplaceStrings(StdStrBuf &rBuf)
virtual bool DoLoadContents(C4ScenarioListLoader *pLoader, C4Group *pFromGrp, const StdStrBuf &sFilename, bool fLoadEx)
const int32_t C4StartupScenSel_TitlePicturePadding
static Entry * CreateEntryForFile(const StdStrBuf &sFilename, C4ScenarioListLoader *pLoader, Folder *pParent)
int32_t HideMsgStartDedicated
Definition: C4Config.h:185
#define _MAX_FNAME
char * GetExtension(char *szFilename)
Definition: StdFile.cpp:131
Folder * GetRootFolder() const
bool NetworkActive
Definition: C4Game.h:124
void Value(const T &rStruct)
Definition: StdCompiler.h:171
DWORD GetPixDw(int iX, int iY, bool fApplyModulation)
Definition: C4Surface.cpp:501
const char * GetID() const
char LanguageEx[CFG_MaxString+1]
Definition: C4Config.h:38
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:316
C4Rect & GetBounds()
Definition: C4Gui.h:445
iterator begin() const
Definition: C4Reloc.cpp:59
int32_t GetWidth() const
Definition: C4Gui.h:2800
char Language[CFG_MaxString+1]
Definition: C4Config.h:37
C4Reloc Reloc
Definition: C4Reloc.cpp:22
bool fObserve
Definition: C4Game.h:122
virtual void MouseInput(C4GUI::CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam)
#define C4CFN_Achievements
Definition: C4Components.h:90
const char * GetError()
Definition: C4Group.cpp:504
virtual bool LoadCustom(C4Group &rGrp, bool fNameLoaded, bool fIconLoaded)
void OnNextBtn(C4GUI::Control *btn)
virtual bool isDeserializer()
Definition: StdCompiler.h:63
void SelectEntry(Element *pNewSel, bool fByUser)
int32_t x
Definition: C4Rect.h:32
bool OpenFolder(C4ScenarioListLoader::Folder *pNewFolder)
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:463
virtual bool LoadCustom(C4Group &rGrp, bool fNameLoaded, bool fIconLoaded)
bool C4Group_MoveItem(const char *szSource, const char *szTarget1, bool fNoSort)
Definition: C4Group.cpp:144
bool DirectoryExists(const char *szFilename)
Definition: StdFile.cpp:694
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:304
const Option * GetOptionByValue(int32_t val) const
#define C4CFN_MapFolderData
Definition: C4Components.h:135
float TargetX
Definition: C4Facet.h:167
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:70
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:120
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:232
#define C4CFN_ScenarioDesc
Definition: C4Components.h:88
C4FacetSurface * FindByName(const char *name) const
virtual bool LoadCustomPre(C4Group &rGrp)
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:530
virtual bool CanOpen(StdStrBuf &sError)
const C4Facet & GetTitlePicture() const
const int ALeft
Definition: C4Surface.h:43
bool WildcardMatch(const char *szWildcard, const char *szString)
Definition: StdFile.cpp:384
C4AchievementGraphics Achievements
ScenListItem(C4GUI::ListBox *pForListBox, C4ScenarioListLoader::Entry *pForEntry, C4GUI::Element *pInsertBeforeElement=nullptr)
bool FileExists(const char *szFilename)
Definition: StdFile.cpp:439
bool SEqual2(const char *szStr1, const char *szStr2)
Definition: Standard.cpp:175
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:456
bool Add(const char *szFile, const char *szAddAs)
Definition: C4Group.cpp:1316
char DefinitionFilenames[20 *_MAX_PATH+1]
Definition: C4Game.h:107
bool IsNetworkStart() const
const StdStrBuf & GetName() const
CStdFont & GetBlackFontByHeight(int32_t iHgt, float *pfZoom)
Definition: C4Startup.cpp:98
#define C4CFN_ScenarioFiles
Definition: C4Components.h:174
iterator end() const
Definition: C4Reloc.cpp:64
void AddTextLine(const char *szText, CStdFont *pFont, DWORD dwClr, bool fDoUpdate, bool fMakeReadableOnBlack, CStdFont *pCaptionFont=nullptr)
Definition: C4Gui.h:1740
int32_t GetBottom() const
Definition: C4Rect.h:60
StdParameterAdapt< T, P > mkParAdapt(T &&rObj, P &&rPar)
Definition: StdAdaptors.h:459
bool GetFromRight(int32_t iWdt, int32_t iHgt, C4Rect &rcOut)
Definition: C4Gui.cpp:1096
virtual void OnClosed(bool fOK)
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:453
void ResetSearch(bool reload_contents=false)
Definition: C4Group.cpp:1013
void DeleteConfirm(ScenListItem *pSel)
virtual Screen * GetScreen()
Definition: C4Gui.cpp:290
bool C4Group_TestIgnore(const char *szFilename)
Definition: C4Group.cpp:85
int32_t Hgt
Definition: C4Rect.h:32
bool ScheduleProcs(int iTimeout=1000/36)
C4ScenarioListLoader::Entry * GetSelectedEntry() const
void SetPicture(const C4Facet &rNewPic)
C4Surface * Surface
Definition: C4Facet.h:119
#define DirSep
bool ItemExists(const char *szItemName)
Definition: StdFile.h:75
float TargetY
Definition: C4Facet.h:167
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
StdArrayAdapt< T, M > mkArrayAdaptMap(T *pArray, int iSize, M &&map)
Definition: StdAdaptors.h:314
virtual bool Start()=0
bool fLobby
Definition: C4Game.h:120
virtual C4ScenarioParameters * GetParameters()
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)
void Copy()
Definition: StdBuf.h:475
float Wdt
Definition: C4Facet.h:120
C4Facet GetPhase(int iPhaseX=0, int iPhaseY=0)
Definition: C4Facet.cpp:59
C4ScenarioParameters Achievements
Definition: C4InfoCore.h:109
bool Inside(T ival, U lbound, V rbound)
Definition: Standard.h:45
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:1136
void CompileFunc(C4Real &rValue, StdCompiler *pComp)
Definition: C4Real.cpp:9033
float top
Definition: C4Rect.h:27
bool GetFromBottom(int32_t iHgt, int32_t iWdt, C4Rect &rcOut)
Definition: C4Gui.cpp:1112
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 void UpdateSize()
void OpenGame(const char *scenario=0)
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:277
bool RenameTo(const char *szNewName)
virtual C4ScenarioParameterDefs * GetParameterDefs()
void SetSelectionDblClickFn(BaseCallbackHandler *pToHandler)
Definition: C4Gui.h:1553
static C4StartupScenSelDlg * pInstance
Entry * GetNext() const