| 1 | /* |
| 2 | * LegacyClonk |
| 3 | * |
| 4 | * Copyright (c) 1998-2000, Matthes Bender (RedWolf Design) |
| 5 | * Copyright (c) 2017-2020, The LegacyClonk 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 | |
| 17 | /* Loads all standard graphics from Graphics.c4g */ |
| 18 | |
| 19 | #include "C4GuiResource.h" |
| 20 | #include <C4Include.h> |
| 21 | #include <C4GraphicsResource.h> |
| 22 | |
| 23 | #include <C4Gui.h> |
| 24 | #include <C4Log.h> |
| 25 | #include <C4Game.h> |
| 26 | |
| 27 | #include <StdGL.h> |
| 28 | |
| 29 | #include <array> |
| 30 | |
| 31 | C4GraphicsResource::C4GraphicsResource() |
| 32 | { |
| 33 | Default(); |
| 34 | } |
| 35 | |
| 36 | C4GraphicsResource::~C4GraphicsResource() |
| 37 | { |
| 38 | Clear(); |
| 39 | } |
| 40 | |
| 41 | void C4GraphicsResource::Default() |
| 42 | { |
| 43 | fInitialized = false; |
| 44 | |
| 45 | sfcControl.Default(); |
| 46 | idSfcControl = 0; |
| 47 | idPalGrp = 0; |
| 48 | |
| 49 | fctPlayer.Default(); |
| 50 | fctFlag.Default(); |
| 51 | fctCrew.Default(); |
| 52 | fctScore.Default(); |
| 53 | fctWealth.Default(); |
| 54 | fctRank.Default(); |
| 55 | fctFire.Default(); |
| 56 | fctBackground.Default(); |
| 57 | sfcLiquidAnimation.Default(); idSfcLiquidAnimation = 0; |
| 58 | fctCaptain.Default(); |
| 59 | fctMouseCursor.Default(); |
| 60 | fctSelectMark.Default(); |
| 61 | fctMenu.Default(); |
| 62 | fctUpperBoard.Default(); |
| 63 | fctLogo.Default(); |
| 64 | fctConstruction.Default(); |
| 65 | fctEnergy.Default(); |
| 66 | fctMagic.Default(); |
| 67 | fctArrow.Default(); |
| 68 | fctExit.Default(); |
| 69 | fctHand.Default(); |
| 70 | fctGamepad.Default(); |
| 71 | fctBuild.Default(); |
| 72 | fctEnergyBars.Default(); |
| 73 | |
| 74 | std::fill(first: GamePalette, last: std::end(arr&: GamePalette), value: 0); |
| 75 | std::fill(first: AlphaPalette, last: std::end(arr&: AlphaPalette), value: 0); |
| 76 | fctCrewClr.Default(); |
| 77 | fctFlagClr.Default(); |
| 78 | fctPlayerClr.Default(); |
| 79 | |
| 80 | fctCursor.Default(); |
| 81 | fctDropTarget.Default(); |
| 82 | fctInsideSymbol.Default(); |
| 83 | fctKeyboard.Default(); |
| 84 | fctGamepad.Default(); |
| 85 | fctCommand.Default(); |
| 86 | fctKey.Default(); |
| 87 | fctOKCancel.Default(); |
| 88 | fctMouse.Default(); |
| 89 | |
| 90 | iNumRanks = 1; |
| 91 | idRegisteredMainGroupSetFiles = -1; |
| 92 | fOldStyleCursor = false; |
| 93 | } |
| 94 | |
| 95 | void C4GraphicsResource::Clear() |
| 96 | { |
| 97 | fInitialized = false; |
| 98 | // GUI data |
| 99 | C4GUI::Resource::Unload(); |
| 100 | |
| 101 | sfcControl.Clear(); |
| 102 | idSfcControl = 0; |
| 103 | idPalGrp = 0; |
| 104 | |
| 105 | fctCrewClr.Clear(); |
| 106 | fctFlagClr.Clear(); |
| 107 | fctPlayerClr.Clear(); |
| 108 | fctPlayerGray.Clear(); |
| 109 | |
| 110 | fctPlayer.Clear(); |
| 111 | fctFlag.Clear(); |
| 112 | fctCrew.Clear(); |
| 113 | fctScore.Clear(); |
| 114 | fctWealth.Clear(); |
| 115 | fctRank.Clear(); |
| 116 | fctFire.Clear(); |
| 117 | fctBackground.Clear(); |
| 118 | sfcLiquidAnimation.Clear(); |
| 119 | fctCaptain.Clear(); |
| 120 | fctMouseCursor.Clear(); |
| 121 | fctSelectMark.Clear(); |
| 122 | fctMenu.Clear(); |
| 123 | fctUpperBoard.Clear(); |
| 124 | fctLogo.Clear(); |
| 125 | fctConstruction.Clear(); |
| 126 | fctEnergy.Clear(); |
| 127 | fctMagic.Clear(); |
| 128 | fctArrow.Clear(); |
| 129 | fctExit.Clear(); |
| 130 | fctHand.Clear(); |
| 131 | fctGamepad.Clear(); |
| 132 | fctBuild.Clear(); |
| 133 | fctEnergyBars.Clear(); |
| 134 | |
| 135 | // unhook deflist from font |
| 136 | FontRegular.SetCustomImages(nullptr); |
| 137 | |
| 138 | // closing the group set will also close the graphics.c4g |
| 139 | // this is just for games that failed to init |
| 140 | // normally, this is done after successful init anyway |
| 141 | CloseFiles(); |
| 142 | } |
| 143 | |
| 144 | bool C4GraphicsResource::InitFonts() |
| 145 | { |
| 146 | // update group set |
| 147 | if (!RegisterMainGroups()) |
| 148 | { |
| 149 | LogFatal(id: C4ResStrTableKey::IDS_ERR_GFX_REGISTERMAIN); |
| 150 | return false; |
| 151 | } |
| 152 | // reinit main font |
| 153 | // this regards scenario-specific fonts or overloads in Extra.c4g |
| 154 | const char *szFont; |
| 155 | if (*Game.C4S.Head.Font) szFont = Game.C4S.Head.Font; else szFont = Config.General.RXFontName; |
| 156 | #ifndef USE_CONSOLE |
| 157 | if (!Game.FontLoader.InitFont(rFont&: FontRegular, szFontName: szFont, eType: C4FontLoader::C4FT_Main, iSize: Config.General.RXFontSize, pGfxGroups: &Files)) |
| 158 | return false; |
| 159 | // assign def list as custom image source |
| 160 | FontRegular.SetCustomImages(&Game.Defs); |
| 161 | // load additional fonts |
| 162 | if (!Game.FontLoader.InitFont(rFont&: FontTitle, szFontName: szFont, eType: C4FontLoader::C4FT_Title, iSize: Config.General.RXFontSize, pGfxGroups: &Game.GraphicsResource.Files)) return false; |
| 163 | if (!Game.FontLoader.InitFont(rFont&: FontCaption, szFontName: szFont, eType: C4FontLoader::C4FT_Caption, iSize: Config.General.RXFontSize, pGfxGroups: &Game.GraphicsResource.Files)) return false; |
| 164 | if (!Game.FontLoader.InitFont(rFont&: FontTiny, szFontName: szFont, eType: C4FontLoader::C4FT_Log, iSize: Config.General.RXFontSize, pGfxGroups: &Game.GraphicsResource.Files)) return false; |
| 165 | if (!Game.FontLoader.InitFont(rFont&: FontTooltip, szFontName: szFont, eType: C4FontLoader::C4FT_Main, iSize: Config.General.RXFontSize, pGfxGroups: &Game.GraphicsResource.Files, fDoShadow: false)) return false; |
| 166 | #endif |
| 167 | // done, success |
| 168 | return true; |
| 169 | } |
| 170 | |
| 171 | bool C4GraphicsResource::Init() |
| 172 | { |
| 173 | // Init fonts (double init will never if groups didnt change) |
| 174 | if (!InitFonts()) |
| 175 | return false; |
| 176 | // Game palette - could perhaps be eliminated... |
| 177 | int32_t idNewPalGrp; |
| 178 | C4Group *pPalGrp = Files.FindEntry(szWildcard: "C4.pal" , pPriority: nullptr, pID: &idNewPalGrp); |
| 179 | if (!pPalGrp) { LogNTr(level: spdlog::level::err, fmt: "{}: {}" , args: LoadResStr(id: C4ResStrTableKey::IDS_PRC_FILENOTFOUND), args: "C4.pal" ); return false; } |
| 180 | if (idPalGrp != idNewPalGrp) |
| 181 | { |
| 182 | if (!pPalGrp->AccessEntry(szWildCard: "C4.pal" )) { LogFatalNTr(message: "Pal error!" ); return false; } |
| 183 | if (!pPalGrp->Read(pBuffer: GamePalette, iSize: 256 * 3)) { LogFatalNTr(message: "Pal error!" ); return false; } |
| 184 | for (int32_t cnt = 0; cnt < 256 * 3; cnt++) GamePalette[cnt] <<= 2; |
| 185 | std::fill_n(first: AlphaPalette, n: 256, value: 0); |
| 186 | // Set default force field color |
| 187 | GamePalette[191 * 3 + 0] = 0; |
| 188 | GamePalette[191 * 3 + 1] = 0; |
| 189 | GamePalette[191 * 3 + 2] = 255; |
| 190 | // color 0 is transparent |
| 191 | GamePalette[0] = GamePalette[1] = GamePalette[2] = 0; |
| 192 | AlphaPalette[0] = 255; |
| 193 | AlphaPalette[191] = 127; |
| 194 | // update game pal |
| 195 | if (!Game.GraphicsSystem.SetPalette()) { LogFatalNTr(message: "Pal error (2)!" ); return false; } |
| 196 | idPalGrp = idNewPalGrp; |
| 197 | } |
| 198 | |
| 199 | // Control |
| 200 | if (!LoadFile(sfc&: sfcControl, szName: "Control" , rGfxSet&: Files, ridCurrSfc&: idSfcControl)) return false; |
| 201 | fctKeyboard.Set(nsfc: &sfcControl, nx: 0, ny: 0, nwdt: 80, nhgt: 36); |
| 202 | fctCommand .Set(nsfc: &sfcControl, nx: 0, ny: 36, nwdt: 32, nhgt: 32); |
| 203 | fctKey .Set(nsfc: &sfcControl, nx: 0, ny: 100, nwdt: 64, nhgt: 64); |
| 204 | fctOKCancel.Set(nsfc: &sfcControl, nx: 128, ny: 100, nwdt: 32, nhgt: 32); |
| 205 | fctMouse .Set(nsfc: &sfcControl, nx: 198, ny: 100, nwdt: 32, nhgt: 32); |
| 206 | |
| 207 | // Facet bitmap resources |
| 208 | if (!LoadFile(fct&: fctFire, szName: "Fire" , rGfxSet&: Files, iWdt: C4FCT_Height)) return false; |
| 209 | if (!LoadFile(fct&: fctBackground, szName: "Background" , rGfxSet&: Files)) return false; |
| 210 | if (!LoadFile(fct&: fctFlag, szName: "Flag" , rGfxSet&: Files)) return false; // (new format) |
| 211 | if (!LoadFile(fct&: fctCrew, szName: "Crew" , rGfxSet&: Files)) return false; // (new format) |
| 212 | if (!LoadFile(fct&: fctScore, szName: "Score" , rGfxSet&: Files)) return false; // (new) |
| 213 | if (!LoadFile(fct&: fctWealth, szName: "Wealth" , rGfxSet&: Files)) return false; // (new) |
| 214 | if (!LoadFile(fct&: fctPlayer, szName: "Player" , rGfxSet&: Files)) return false; // (new format) |
| 215 | if (!LoadFile(fct&: fctRank, szName: "Rank" , rGfxSet&: Files, iWdt: C4FCT_Height)) return false; |
| 216 | if (!LoadFile(fct&: fctCaptain, szName: "Captain" , rGfxSet&: Files)) return false; |
| 217 | if (!LoadCursorGfx()) return false; |
| 218 | if (!LoadFile(fct&: fctSelectMark, szName: "SelectMark" , rGfxSet&: Files, iWdt: C4FCT_Height)) return false; |
| 219 | if (!LoadFile(fct&: fctMenu, szName: "Menu" , rGfxSet&: Files, iWdt: 35, iHgt: 35)) return false; |
| 220 | if (!LoadFile(fct&: fctLogo, szName: "Logo" , rGfxSet&: Files)) return false; |
| 221 | if (!LoadFile(fct&: fctConstruction, szName: "Construction" , rGfxSet&: Files)) return false; // (new) |
| 222 | if (!LoadFile(fct&: fctEnergy, szName: "Energy" , rGfxSet&: Files)) return false; // (new) |
| 223 | if (!LoadFile(fct&: fctMagic, szName: "Magic" , rGfxSet&: Files)) return false; // (new) |
| 224 | if (!LoadFile(fct&: fctOptions, szName: "Options" , rGfxSet&: Files, iWdt: C4FCT_Height)) return false; |
| 225 | if (!LoadFile(fct&: fctUpperBoard, szName: "UpperBoard" , rGfxSet&: Files)) return false; |
| 226 | if (!LoadFile(fct&: fctArrow, szName: "Arrow" , rGfxSet&: Files, iWdt: C4FCT_Height)) return false; |
| 227 | if (!LoadFile(fct&: fctExit, szName: "Exit" , rGfxSet&: Files)) return false; |
| 228 | if (!LoadFile(fct&: fctHand, szName: "Hand" , rGfxSet&: Files, iWdt: C4FCT_Height)) return false; |
| 229 | if (!LoadFile(fct&: fctGamepad, szName: "Gamepad" , rGfxSet&: Files, iWdt: 80)) return false; |
| 230 | if (!LoadFile(fct&: fctBuild, szName: "Build" , rGfxSet&: Files)) return false; |
| 231 | if (!LoadFile(fct&: fctEnergyBars, szName: "EnergyBars" , rGfxSet&: Files)) return false; |
| 232 | if (!LoadFile(sfc&: sfcLiquidAnimation, szName: "Liquid" , rGfxSet&: Files, ridCurrSfc&: idSfcLiquidAnimation)) return false; |
| 233 | if (!ReloadResolutionDependentFiles()) return false; |
| 234 | // life bar facets |
| 235 | if (fctEnergyBars.Surface) |
| 236 | { |
| 237 | int32_t bar_wdt = fctEnergyBars.Surface->Wdt / 6; |
| 238 | int32_t bar_hgt = fctEnergyBars.Surface->Hgt / 3; |
| 239 | if (!bar_wdt || !bar_hgt) { LogFatalNTr(message: "EnergyBars.png invalid or too small!" ); return false; } |
| 240 | fctEnergyBars.Set(nsfc: fctEnergyBars.Surface, nx: 0, ny: 0, nwdt: bar_wdt, nhgt: bar_hgt); |
| 241 | } |
| 242 | |
| 243 | // create ColorByOwner overlay surfaces |
| 244 | if (fctCrew.idSourceGroup != fctCrewClr.idSourceGroup) |
| 245 | { |
| 246 | if (!fctCrewClr.CreateClrByOwner(pBySurface: fctCrew.Surface)) { LogFatalNTr(message: "ClrByOwner error! (1)" ); return false; } |
| 247 | fctCrewClr.Wdt = fctCrew.Wdt; |
| 248 | fctCrewClr.Hgt = fctCrew.Hgt; |
| 249 | fctCrewClr.idSourceGroup = fctCrew.idSourceGroup; |
| 250 | } |
| 251 | if (fctFlag.idSourceGroup != fctFlagClr.idSourceGroup) |
| 252 | { |
| 253 | if (!fctFlagClr.CreateClrByOwner(pBySurface: fctFlag.Surface)) { LogFatalNTr(message: "ClrByOwner error! (1)" ); return false; } |
| 254 | fctFlagClr.Wdt = fctFlag.Wdt; |
| 255 | fctFlagClr.Hgt = fctFlag.Hgt; |
| 256 | fctFlagClr.idSourceGroup = fctFlag.idSourceGroup; |
| 257 | } |
| 258 | if (fctPlayer.idSourceGroup != fctPlayerGray.idSourceGroup) |
| 259 | { |
| 260 | fctPlayerGray.Create(iWdt: fctPlayer.Wdt, iHgt: fctPlayer.Hgt); |
| 261 | fctPlayer.Draw(cgo&: fctPlayerGray); |
| 262 | fctPlayerGray.Grayscale(iOffset: 30); |
| 263 | fctPlayerGray.idSourceGroup = fctPlayer.idSourceGroup; |
| 264 | } |
| 265 | if (fctPlayer.idSourceGroup != fctPlayerClr.idSourceGroup) |
| 266 | { |
| 267 | if (!fctPlayerClr.CreateClrByOwner(pBySurface: fctPlayer.Surface)) { LogFatalNTr(message: "ClrByOwner error! (1)" ); return false; } |
| 268 | fctPlayerClr.Wdt = fctPlayer.Wdt; |
| 269 | fctPlayerClr.Hgt = fctPlayer.Hgt; |
| 270 | fctPlayerClr.idSourceGroup = fctPlayer.idSourceGroup; |
| 271 | } |
| 272 | |
| 273 | // get number of ranks |
| 274 | int32_t Q; fctRank.GetPhaseNum(rX&: iNumRanks, rY&: Q); |
| 275 | if (!iNumRanks) iNumRanks = 1; |
| 276 | |
| 277 | // load GUI files |
| 278 | C4GUI::Resource *pRes = C4GUI::GetRes(); |
| 279 | if (!pRes) pRes = new C4GUI::Resource(FontCaption, FontTitle, FontRegular, FontTiny, FontTooltip); |
| 280 | if (!pRes->Load(rFromGroup&: Files)) { delete pRes; return false; } |
| 281 | |
| 282 | // CloseFiles() must not be called now: |
| 283 | // The sky still needs to be loaded from the global graphics |
| 284 | // group in C4Game::InitGame -> C4Sky::Init so we need to keep the group(s) open. |
| 285 | // In activated NETWORK2, the files mustn't be closed either, because there will be |
| 286 | // multiple calls to this function to allow overloadings |
| 287 | |
| 288 | // mark initialized |
| 289 | fInitialized = true; |
| 290 | |
| 291 | return true; |
| 292 | } |
| 293 | |
| 294 | bool C4GraphicsResource::LoadCursorGfx() |
| 295 | { |
| 296 | // old-style cursor file overloads new-stye, because old scenarios might want to have their own cursors |
| 297 | if (!LoadFile(fct&: fctMouseCursor, szName: "Cursor" , rGfxSet&: Files, iWdt: C4FCT_Height, iHgt: C4FCT_Full, fNoWarnIfNotFound: true)) |
| 298 | { |
| 299 | struct CursorSize |
| 300 | { |
| 301 | const char *filename; |
| 302 | std::size_t facetIndex; |
| 303 | }; |
| 304 | // the order needs to match the order defined in the sorting list in C4Components.h |
| 305 | static constexpr CursorSize cursors[] |
| 306 | { |
| 307 | {.filename: "CursorSmall" , .facetIndex: 7}, |
| 308 | {.filename: "CursorMedium" , .facetIndex: 6}, |
| 309 | {.filename: "CursorLarge" , .facetIndex: 5}, |
| 310 | {.filename: "CursorXLarge" , .facetIndex: 4}, |
| 311 | {.filename: "CursorXXLarge" , .facetIndex: 3}, |
| 312 | {.filename: "CursorXXXLarge" , .facetIndex: 2}, |
| 313 | {.filename: "CursorXXXXLarge" , .facetIndex: 1}, |
| 314 | {.filename: "CursorXXXXXLarge" , .facetIndex: 0}, |
| 315 | }; |
| 316 | for (const auto &cursor : cursors) |
| 317 | { |
| 318 | if (!LoadFile(fct&: fctCursors[cursor.facetIndex], szName: cursor.filename, rGfxSet&: Files, iWdt: C4FCT_Height, iHgt: C4FCT_Full)) |
| 319 | return false; |
| 320 | } |
| 321 | } |
| 322 | return true; |
| 323 | } |
| 324 | |
| 325 | void C4GraphicsResource::ApplyCursorGfx() |
| 326 | { |
| 327 | // adjust dependent faces |
| 328 | int32_t iCursorSize = fctMouseCursor.Hgt; |
| 329 | if (iCursorSize == 13) |
| 330 | { |
| 331 | fctCursor.Set(nsfc: fctMouseCursor.Surface, nx: 455, ny: 0, nwdt: 13, nhgt: 13); |
| 332 | fOldStyleCursor = true; |
| 333 | } |
| 334 | else |
| 335 | { |
| 336 | fctCursor.Set(nsfc: fctMouseCursor.Surface, nx: 35 * iCursorSize, ny: 0, nwdt: iCursorSize, nhgt: iCursorSize); |
| 337 | fOldStyleCursor = false; |
| 338 | } |
| 339 | if (iCursorSize == 13) |
| 340 | { |
| 341 | fctInsideSymbol.Set(nsfc: fctMouseCursor.Surface, nx: 468, ny: 0, nwdt: 13, nhgt: 13); |
| 342 | fctDropTarget .Set(nsfc: fctMouseCursor.Surface, nx: 494, ny: 0, nwdt: 13, nhgt: 13); |
| 343 | } |
| 344 | else |
| 345 | { |
| 346 | fctInsideSymbol.Set(nsfc: fctMouseCursor.Surface, nx: 36 * iCursorSize, ny: 0, nwdt: iCursorSize, nhgt: iCursorSize); |
| 347 | fctDropTarget .Set(nsfc: fctMouseCursor.Surface, nx: 38 * iCursorSize, ny: 0, nwdt: iCursorSize, nhgt: iCursorSize); |
| 348 | } |
| 349 | } |
| 350 | |
| 351 | bool C4GraphicsResource::RegisterGlobalGraphics() |
| 352 | { |
| 353 | // Create main gfx group - register with fixed ID 1, to prevent unnecessary font reloading. |
| 354 | // FontLoader-initializations always check whether the font has already been initialized |
| 355 | // with the same parameters. If the game is simply reloaded in console-mode, this means |
| 356 | // that non-bitmap-fonts are not reinitialized. This will also apply for InGame-scenario |
| 357 | // switches yet to be implemented. |
| 358 | // Bitmap fonts from other groups are always reloaded, because the group indices of the gfx |
| 359 | // group set are not reset, and will then differ for subsequent group registrations. |
| 360 | // Resetting the group index of the gfx group set at game reset would cause problems if a |
| 361 | // scenario with its own font face is being closed, and then another scenario with another, |
| 362 | // overloaded font face is opened. The group indices could match and the old font would |
| 363 | // then be kept. |
| 364 | // The cleanest alternative would be to reinit all the fonts whenever a scenario is reloaded |
| 365 | C4Group *pMainGfxGrp = new C4Group(); |
| 366 | if (!pMainGfxGrp->Open(C4CFN_Graphics) || !Files.RegisterGroup(rGroup&: *pMainGfxGrp, fOwnGrp: true, C4GSPrio_Base, C4GSCnt_Graphics, fCheckContent: 1)) |
| 367 | { |
| 368 | // error |
| 369 | LogFatal(id: C4ResStrTableKey::IDS_PRC_NOGFXFILE, C4CFN_Graphics, args: pMainGfxGrp->GetError()); |
| 370 | delete pMainGfxGrp; |
| 371 | return false; |
| 372 | } |
| 373 | return true; |
| 374 | } |
| 375 | |
| 376 | bool C4GraphicsResource::RegisterMainGroups() |
| 377 | { |
| 378 | // register main groups |
| 379 | Files.RegisterGroups(rCopy&: Game.GroupSet, C4GSCnt_Graphics, C4CFN_Graphics, iMaxSkipID: idRegisteredMainGroupSetFiles); |
| 380 | idRegisteredMainGroupSetFiles = Game.GroupSet.GetLastID(); |
| 381 | return true; |
| 382 | } |
| 383 | |
| 384 | void C4GraphicsResource::CloseFiles() |
| 385 | { |
| 386 | // closes main gfx group; releases dependencies into game group set |
| 387 | Files.Clear(); |
| 388 | idRegisteredMainGroupSetFiles = -1; |
| 389 | } |
| 390 | |
| 391 | C4Group *FindSuitableFile(const char *szName, C4GroupSet &rGfxSet, char *szFileName, int32_t &rGroupID) |
| 392 | { |
| 393 | const char *const extensions[] = { "bmp" , "jpeg" , "jpg" , "png" }; |
| 394 | |
| 395 | C4Group *pGrp = nullptr; |
| 396 | C4Group *pGrp2; |
| 397 | int32_t iPrio = -1; |
| 398 | int32_t iPrio2; |
| 399 | int32_t GroupID; |
| 400 | char FileName[_MAX_FNAME]; |
| 401 | SCopy(szSource: szName, sTarget: FileName); |
| 402 | for (int i = 0; i < 4; ++i) |
| 403 | { |
| 404 | EnforceExtension(szFileName: FileName, szExtension: extensions[i]); |
| 405 | pGrp2 = rGfxSet.FindEntry(szWildcard: FileName, pPriority: &iPrio2, pID: &GroupID); |
| 406 | if ((!pGrp || iPrio2 >= iPrio) && pGrp2) |
| 407 | { |
| 408 | rGroupID = GroupID; |
| 409 | pGrp = pGrp2; |
| 410 | SCopy(szSource: FileName, sTarget: szFileName); |
| 411 | } |
| 412 | } |
| 413 | // return found group, if any |
| 414 | return pGrp; |
| 415 | } |
| 416 | |
| 417 | bool C4GraphicsResource::LoadFile(C4FacetExID &fct, const char *szName, C4GroupSet &rGfxSet, int32_t iWdt, int32_t iHgt, bool fNoWarnIfNotFound) |
| 418 | { |
| 419 | char FileName[_MAX_FNAME]; int32_t ID; |
| 420 | C4Group *pGrp = FindSuitableFile(szName, rGfxSet, szFileName: FileName, rGroupID&: ID); |
| 421 | if (!pGrp) |
| 422 | { |
| 423 | // FIXME: Use LogFatal here |
| 424 | if (!fNoWarnIfNotFound) |
| 425 | { |
| 426 | Log(id: C4ResStrTableKey::IDS_PRC_NOGFXFILE, args&: szName, args: LoadResStr(id: C4ResStrTableKey::IDS_PRC_FILENOTFOUND)); |
| 427 | } |
| 428 | return false; |
| 429 | } |
| 430 | // check group |
| 431 | if (fct.idSourceGroup == ID) |
| 432 | // already up-to-date |
| 433 | return true; |
| 434 | // load |
| 435 | if (!fct.Load(hGroup&: *pGrp, szName: FileName, iWdt, iHgt)) |
| 436 | { |
| 437 | Log(id: C4ResStrTableKey::IDS_PRC_NOGFXFILE, args: +FileName, args: LoadResStr(id: C4ResStrTableKey::IDS_ERR_NOFILE)); |
| 438 | return false; |
| 439 | } |
| 440 | fct.idSourceGroup = ID; |
| 441 | return true; |
| 442 | } |
| 443 | |
| 444 | bool C4GraphicsResource::LoadFile(C4Surface &sfc, const char *szName, C4GroupSet &rGfxSet, int32_t &ridCurrSfc) |
| 445 | { |
| 446 | // find |
| 447 | char FileName[_MAX_FNAME]; int32_t ID; |
| 448 | C4Group *pGrp = FindSuitableFile(szName, rGfxSet, szFileName: FileName, rGroupID&: ID); |
| 449 | if (!pGrp) |
| 450 | { |
| 451 | Log(id: C4ResStrTableKey::IDS_PRC_NOGFXFILE, args&: szName, args: LoadResStr(id: C4ResStrTableKey::IDS_PRC_FILENOTFOUND)); |
| 452 | return false; |
| 453 | } |
| 454 | // check group |
| 455 | if (ID == ridCurrSfc) |
| 456 | // already up-to-date |
| 457 | return true; |
| 458 | // load |
| 459 | if (!sfc.Load(hGroup&: *pGrp, szFilename: FileName)) |
| 460 | { |
| 461 | Log(id: C4ResStrTableKey::IDS_PRC_NOGFXFILE, args: +FileName, args: LoadResStr(id: C4ResStrTableKey::IDS_ERR_NOFILE)); |
| 462 | return false; |
| 463 | } |
| 464 | ridCurrSfc = ID; |
| 465 | return true; |
| 466 | } |
| 467 | |
| 468 | bool C4GraphicsResource::ReloadResolutionDependentFiles() |
| 469 | { |
| 470 | // reload any files that depend on the current resolution |
| 471 | // reloads the cursor |
| 472 | const auto scale = Application.GetScale(); |
| 473 | const auto resX = Config.Graphics.ResX * scale; |
| 474 | constexpr std::array<int32_t, 2> breakPoints |
| 475 | { |
| 476 | 1280, |
| 477 | 800 |
| 478 | }; |
| 479 | size_t index = 5; |
| 480 | if (resX > breakPoints.front()) |
| 481 | { |
| 482 | index -= std::min(a: index, b: static_cast<size_t>(std::max(a: 1.f, b: Application.GetScale()) - 0.5f)); |
| 483 | } |
| 484 | else |
| 485 | { |
| 486 | for (const auto selectX : breakPoints) |
| 487 | { |
| 488 | if (resX >= selectX) |
| 489 | { |
| 490 | break; |
| 491 | } |
| 492 | ++index; |
| 493 | } |
| 494 | } |
| 495 | if (!fctCursors[index].Wdt && !LoadCursorGfx()) return false; |
| 496 | if (fctCursors[index].Wdt) |
| 497 | { |
| 498 | fctMouseCursor.idSourceGroup = 0; |
| 499 | fctMouseCursor.Set(fctCursors[index]); |
| 500 | ApplyCursorGfx(); |
| 501 | return true; |
| 502 | } |
| 503 | return false; |
| 504 | } |
| 505 | |