1/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
2/* If you are missing that file, acquire a complete release at teeworlds.com. */
3#include <base/math.h>
4#include <base/system.h>
5
6#include <engine/console.h>
7#include <engine/graphics.h>
8#include <engine/shared/config.h>
9#include <engine/shared/linereader.h>
10#include <engine/storage.h>
11
12#include "countryflags.h"
13
14#include <game/client/render.h>
15
16void CCountryFlags::LoadCountryflagsIndexfile()
17{
18 const char *pFilename = "countryflags/index.txt";
19 CLineReader LineReader;
20 if(!LineReader.OpenFile(File: Storage()->OpenFile(pFilename, Flags: IOFLAG_READ, Type: IStorage::TYPE_ALL)))
21 {
22 char aBuf[128];
23 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "couldn't open index file '%s'", pFilename);
24 Console()->Print(Level: IConsole::OUTPUT_LEVEL_ADDINFO, pFrom: "countryflags", pStr: aBuf);
25 return;
26 }
27
28 char aOrigin[128];
29 while(const char *pLine = LineReader.Get())
30 {
31 if(!str_length(str: pLine) || pLine[0] == '#') // skip empty lines and comments
32 continue;
33
34 str_copy(dst&: aOrigin, src: pLine);
35 const char *pReplacement = LineReader.Get();
36 if(!pReplacement)
37 {
38 Console()->Print(Level: IConsole::OUTPUT_LEVEL_ADDINFO, pFrom: "countryflags", pStr: "unexpected end of index file");
39 break;
40 }
41
42 if(pReplacement[0] != '=' || pReplacement[1] != '=' || pReplacement[2] != ' ')
43 {
44 char aBuf[128];
45 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "malform replacement for index '%s'", aOrigin);
46 Console()->Print(Level: IConsole::OUTPUT_LEVEL_ADDINFO, pFrom: "countryflags", pStr: aBuf);
47 continue;
48 }
49
50 int CountryCode = str_toint(str: pReplacement + 3);
51 if(CountryCode < CODE_LB || CountryCode > CODE_UB)
52 {
53 char aBuf[128];
54 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "country code '%i' not within valid code range [%i..%i]", CountryCode, CODE_LB, CODE_UB);
55 Console()->Print(Level: IConsole::OUTPUT_LEVEL_ADDINFO, pFrom: "countryflags", pStr: aBuf);
56 continue;
57 }
58
59 // load the graphic file
60 char aBuf[128];
61 CImageInfo Info;
62 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "countryflags/%s.png", aOrigin);
63 if(!Graphics()->LoadPng(Image&: Info, pFilename: aBuf, StorageType: IStorage::TYPE_ALL))
64 {
65 char aMsg[128];
66 str_format(buffer: aMsg, buffer_size: sizeof(aMsg), format: "failed to load '%s'", aBuf);
67 Console()->Print(Level: IConsole::OUTPUT_LEVEL_ADDINFO, pFrom: "countryflags", pStr: aMsg);
68 continue;
69 }
70
71 // add entry
72 CCountryFlag CountryFlag;
73 CountryFlag.m_CountryCode = CountryCode;
74 str_copy(dst&: CountryFlag.m_aCountryCodeString, src: aOrigin);
75 CountryFlag.m_Texture = Graphics()->LoadTextureRawMove(Image&: Info, Flags: 0, pTexName: aBuf);
76
77 if(g_Config.m_Debug)
78 {
79 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "loaded country flag '%s'", aOrigin);
80 Console()->Print(Level: IConsole::OUTPUT_LEVEL_ADDINFO, pFrom: "countryflags", pStr: aBuf);
81 }
82 m_vCountryFlags.push_back(x: CountryFlag);
83 }
84
85 std::sort(first: m_vCountryFlags.begin(), last: m_vCountryFlags.end());
86
87 // find index of default item
88 size_t DefaultIndex = 0;
89 for(size_t Index = 0; Index < m_vCountryFlags.size(); ++Index)
90 if(m_vCountryFlags[Index].m_CountryCode == -1)
91 {
92 DefaultIndex = Index;
93 break;
94 }
95
96 // init LUT
97 if(DefaultIndex != 0)
98 for(size_t &CodeIndexLUT : m_aCodeIndexLUT)
99 CodeIndexLUT = DefaultIndex;
100 else
101 mem_zero(block: m_aCodeIndexLUT, size: sizeof(m_aCodeIndexLUT));
102 for(size_t i = 0; i < m_vCountryFlags.size(); ++i)
103 m_aCodeIndexLUT[maximum(a: 0, b: (m_vCountryFlags[i].m_CountryCode - CODE_LB) % CODE_RANGE)] = i;
104}
105
106void CCountryFlags::OnInit()
107{
108 // load country flags
109 m_vCountryFlags.clear();
110 LoadCountryflagsIndexfile();
111 if(m_vCountryFlags.empty())
112 {
113 Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "countryflags", pStr: "failed to load country flags. folder='countryflags/'");
114 CCountryFlag DummyEntry;
115 DummyEntry.m_CountryCode = -1;
116 mem_zero(block: DummyEntry.m_aCountryCodeString, size: sizeof(DummyEntry.m_aCountryCodeString));
117 m_vCountryFlags.push_back(x: DummyEntry);
118 }
119
120 m_FlagsQuadContainerIndex = Graphics()->CreateQuadContainer(AutomaticUpload: false);
121 Graphics()->SetColor(r: 1.f, g: 1.f, b: 1.f, a: 1.f);
122 Graphics()->QuadsSetSubset(TopLeftU: 0, TopLeftV: 0, BottomRightU: 1, BottomRightV: 1);
123 RenderTools()->QuadContainerAddSprite(QuadContainerIndex: m_FlagsQuadContainerIndex, X: 0, Y: 0, Width: 1, Height: 1);
124 Graphics()->QuadContainerUpload(ContainerIndex: m_FlagsQuadContainerIndex);
125}
126
127size_t CCountryFlags::Num() const
128{
129 return m_vCountryFlags.size();
130}
131
132const CCountryFlags::CCountryFlag *CCountryFlags::GetByCountryCode(int CountryCode) const
133{
134 return GetByIndex(Index: m_aCodeIndexLUT[maximum(a: 0, b: (CountryCode - CODE_LB) % CODE_RANGE)]);
135}
136
137const CCountryFlags::CCountryFlag *CCountryFlags::GetByIndex(size_t Index) const
138{
139 return &m_vCountryFlags[Index % m_vCountryFlags.size()];
140}
141
142void CCountryFlags::Render(const CCountryFlag *pFlag, ColorRGBA Color, float x, float y, float w, float h)
143{
144 if(pFlag->m_Texture.IsValid())
145 {
146 Graphics()->TextureSet(Texture: pFlag->m_Texture);
147 Graphics()->SetColor(Color);
148 Graphics()->RenderQuadContainerEx(ContainerIndex: m_FlagsQuadContainerIndex, QuadOffset: 0, QuadDrawNum: -1, X: x, Y: y, ScaleX: w, ScaleY: h);
149 }
150}
151
152void CCountryFlags::Render(int CountryCode, ColorRGBA Color, float x, float y, float w, float h)
153{
154 Render(pFlag: GetByCountryCode(CountryCode), Color, x, y, w, h);
155}
156