1 /*
2     SPDX-FileCopyrightText: 2011 Stefan Majewsky <majewsky@gmx.net>
3 
4     SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #include "puzzle.h"
8 
9 #include <QFileInfo>
10 #include <QHash>
11 
12 //BEGIN Palapeli::PuzzleComponent
13 
PuzzleComponent()14 Palapeli::PuzzleComponent::PuzzleComponent()
15 	: m_puzzle(nullptr)
16 {
17 }
18 
~PuzzleComponent()19 Palapeli::PuzzleComponent::~PuzzleComponent()
20 {
21 }
22 
cast(Palapeli::PuzzleComponent::Type type) const23 Palapeli::PuzzleComponent* Palapeli::PuzzleComponent::cast(Palapeli::PuzzleComponent::Type type) const
24 {
25 	Q_UNUSED(type)
26 	return nullptr;
27 }
28 
puzzle() const29 Palapeli::Puzzle* Palapeli::PuzzleComponent::puzzle() const
30 {
31 	return m_puzzle;
32 }
33 
34 //END Palapeli::PuzzleComponent
35 //BEGIN Palapeli::Puzzle
36 
37 //See Private::get() for the whole story.
38 struct Component
39 {
40 	Palapeli::PuzzleComponent *component = nullptr;
41 
ComponentComponent42 	Component() {}
ComponentComponent43 	explicit Component(Palapeli::PuzzleComponent* component) : component(component) {}
~ComponentComponent44 	~Component() { delete component; }
45 };
46 struct Palapeli::Puzzle::Private
47 {
48 	Palapeli::Puzzle* q;
49 	QHash<Palapeli::PuzzleComponent::Type, Component*> m_components;
50 	Palapeli::PuzzleComponent *m_mainComponent;
51 	QString m_location;
52 	QString m_identifier;
53 
54 	Private(Palapeli::Puzzle* q, Palapeli::PuzzleComponent* mainComponent, const QString& location, const QString& identifier);
55 	const Palapeli::PuzzleComponent* get(Palapeli::PuzzleComponent::Type type);
56 };
57 
Puzzle(Palapeli::PuzzleComponent * mainComponent,const QString & location,const QString & identifier)58 Palapeli::Puzzle::Puzzle(Palapeli::PuzzleComponent* mainComponent, const QString& location, const QString& identifier)
59 	: d(new Private(this, mainComponent, location, identifier))
60 {
61 	qRegisterMetaType<Palapeli::Puzzle*>();
62 }
63 
Private(Palapeli::Puzzle * q,Palapeli::PuzzleComponent * mainComponent,const QString & location,const QString & identifier)64 Palapeli::Puzzle::Private::Private(Palapeli::Puzzle* q, Palapeli::PuzzleComponent* mainComponent, const QString& location, const QString& identifier)
65 	: q(q)
66 	, m_mainComponent(mainComponent)
67 	, m_location(location)
68 	, m_identifier(identifier)
69 {
70 	m_mainComponent->m_puzzle = q;
71 	m_components.insert(mainComponent->type(), new Component(mainComponent));
72 }
73 
~Puzzle()74 Palapeli::Puzzle::~Puzzle()
75 {
76 	QHashIterator<Palapeli::PuzzleComponent::Type, Component*> iter(d->m_components);
77 	while (iter.hasNext())
78 		delete iter.next().value();
79 	delete d;
80 }
81 
component(Palapeli::PuzzleComponent::Type type) const82 const Palapeli::PuzzleComponent* Palapeli::Puzzle::component(Palapeli::PuzzleComponent::Type type) const
83 {
84 	const Component* c = d->m_components.value(type);
85 	return c ? c->component : nullptr;
86 }
87 
get(Palapeli::PuzzleComponent::Type type)88 const Palapeli::PuzzleComponent* Palapeli::Puzzle::get(Palapeli::PuzzleComponent::Type type)
89 {
90 	return d->get (type);
91 }
92 
get(Palapeli::PuzzleComponent::Type type)93 const Palapeli::PuzzleComponent* Palapeli::Puzzle::Private::get(Palapeli::PuzzleComponent::Type type)
94 {
95 	Component* c = m_components.value(type);
96 	if (c)
97 		return c->component;
98 
99 	m_components.insert(type, c = new Component);
100 	Palapeli::PuzzleComponent* cmp = m_mainComponent->cast(type);
101 	if (cmp)
102 		cmp->m_puzzle = q;
103 	c->component = cmp;
104 	return cmp;
105 }
106 
identifier() const107 QString Palapeli::Puzzle::identifier() const
108 {
109 	return d->m_identifier;
110 }
111 
location() const112 QString Palapeli::Puzzle::location() const
113 {
114 	return d->m_location;
115 }
116 
setLocation(const QString & location)117 void Palapeli::Puzzle::setLocation(const QString& location)
118 {
119 	d->m_location = location;
120 }
121 
dropComponent(Palapeli::PuzzleComponent::Type type)122 void Palapeli::Puzzle::dropComponent(Palapeli::PuzzleComponent::Type type)
123 {
124 	//DO NEVER EVER USE THIS FUNCTION! THIS FUNCTION IS PURELY DANGEROUS. STUFF WILL BREAK.
125 	Component*& c = d->m_components[type];
126 	delete c;
127 	c = nullptr;
128 }
129 
Q_GLOBAL_STATIC(QList<QString>,g_usedIdentifiers)130 Q_GLOBAL_STATIC(QList<QString>, g_usedIdentifiers)
131 
132 /*static*/ QString Palapeli::Puzzle::fsIdentifier(const QString& location)
133 {
134 	QString puzzleName = QFileInfo(location).fileName();
135 	const char* disallowedChars = "\\:*?\"<>|"; //Windows forbids using these chars in filenames, so we'll strip them
136 	for (const char* c = disallowedChars; *c; ++c)
137 		puzzleName.remove(QLatin1Char(*c));
138 	const QString identifierPattern = QString::fromLatin1("__FSC_%1_%2_").arg(puzzleName);
139 	int uniquifier = 0;
140 	while (g_usedIdentifiers->contains(identifierPattern.arg(uniquifier)))
141 		++uniquifier;
142 	const QString identifier = identifierPattern.arg(uniquifier);
143 	*g_usedIdentifiers << identifier;
144 	return identifier;
145 }
146 
147 //END Palapeli::Puzzle
148 
149