1 // license:BSD-3-Clause
2 // copyright-holders:Ryan Holtz
3 //============================================================
4 //
5 //  chainentryreader.cpp - BGFX chain entry JSON reader
6 //
7 //============================================================
8 
9 #include <string>
10 
11 #include "emu.h"
12 #include "rendutil.h"
13 #include <modules/render/copyutil.h>
14 
15 #include <modules/lib/osdobj_common.h>
16 
17 #include "chainentryreader.h"
18 
19 #include "texturemanager.h"
20 #include "targetmanager.h"
21 #include "effectmanager.h"
22 #include "chainentry.h"
23 #include "chainmanager.h"
24 #include "slider.h"
25 #include "inputpair.h"
26 #include "entryuniform.h"
27 #include "entryuniformreader.h"
28 #include "suppressor.h"
29 #include "suppressorreader.h"
30 #include "clear.h"
31 #include "clearreader.h"
32 
read_from_value(const Value & value,std::string prefix,chain_manager & chains,std::map<std::string,bgfx_slider * > & sliders,std::map<std::string,bgfx_parameter * > & params,uint32_t screen_index)33 bgfx_chain_entry* chain_entry_reader::read_from_value(const Value& value, std::string prefix, chain_manager& chains, std::map<std::string, bgfx_slider*>& sliders, std::map<std::string, bgfx_parameter*>& params, uint32_t screen_index)
34 {
35 	if (!validate_parameters(value, prefix))
36 	{
37 		printf("Failed validation\n");
38 		return nullptr;
39 	}
40 
41 	bgfx_effect* effect = chains.effects().effect(value["effect"].GetString());
42 	if (effect == nullptr)
43 	{
44 		return nullptr;
45 	}
46 
47 	std::string name = value["name"].GetString();
48 
49 	std::vector<bgfx_input_pair*> inputs;
50 	if (value.HasMember("input"))
51 	{
52 		const Value& input_array = value["input"];
53 		for (uint32_t i = 0; i < input_array.Size(); i++)
54 		{
55 			const Value& input = input_array[i];
56 			if (!READER_CHECK(input.HasMember("sampler"), (prefix + "input[" + std::to_string(i) + ": Must have string value 'sampler' (what sampler are we binding to?)\n").c_str())) return nullptr;
57 			if (!READER_CHECK(input["sampler"].IsString(), (prefix + "input[" + std::to_string(i) + ": Value 'sampler' must be a string\n").c_str())) return nullptr;
58 			bool has_texture = input.HasMember("texture");
59 			bool has_target = input.HasMember("target");
60 			bool has_option = input.HasMember("option");
61 			if (!READER_CHECK(has_texture || has_target || has_option, (prefix + "input[" + std::to_string(i) + ": Must have string value 'target', 'texture' or 'option' (what source are we using?)\n").c_str())) return nullptr;
62 			if (!READER_CHECK(!has_texture || input["texture"].IsString(), (prefix + "input[" + std::to_string(i) + ": Value 'texture' must be a string\n").c_str())) return nullptr;
63 			if (!READER_CHECK(!has_target || input["target"].IsString(), (prefix + "input[" + std::to_string(i) + ": Value 'target' must be a string\n").c_str())) return nullptr;
64 			if (!READER_CHECK(!has_option || input["option"].IsString(), (prefix + "input[" + std::to_string(i) + ": Value 'option' must be a string\n").c_str())) return nullptr;
65 			if (!READER_CHECK(has_target || !input.HasMember("bilinear") || input["bilinear"].IsBool(), (prefix + "input[" + std::to_string(i) + ": Value 'bilinear' must be a boolean\n").c_str())) return nullptr;
66 			if (!READER_CHECK(has_target || !input.HasMember("clamp") || input["clamp"].IsBool(), (prefix + "input[" + std::to_string(i) + ": Value 'clamp' must be a boolean\n").c_str())) return nullptr;
67 			if (!READER_CHECK(has_texture || has_option || !input.HasMember("selection") || input["selection"].IsString(), (prefix + "input[" + std::to_string(i) + ": Value 'selection' must be a string\n").c_str())) return nullptr;
68 			bool bilinear = get_bool(input, "bilinear", true);
69 			bool clamp = get_bool(input, "clamp", false);
70 			std::string selection = get_string(input, "selection", "");
71 
72 			std::vector<std::string> texture_names;
73 			std::string texture_name = "";
74 			if (has_texture || has_option)
75 			{
76 				if (has_texture)
77 				{
78 					texture_name = input["texture"].GetString();
79 				}
80 				if (has_option)
81 				{
82 					std::string option = input["option"].GetString();
83 
84 					texture_name = chains.options().value(option.c_str());
85 				}
86 
87 				if (texture_name != "" && texture_name != "screen" && texture_name != "palette")
88 				{
89 					if (selection == "")
90 					{
91 						// create texture for specified file name
92 						uint32_t flags = bilinear ? 0u : (BGFX_SAMPLER_MIN_POINT | BGFX_SAMPLER_MAG_POINT | BGFX_SAMPLER_MIP_POINT);
93 						flags |= clamp ? (BGFX_SAMPLER_U_CLAMP | BGFX_SAMPLER_V_CLAMP | BGFX_SAMPLER_W_CLAMP) : 0u;
94 						bgfx_texture* texture = chains.textures().create_png_texture(chains.options().art_path(), texture_name, texture_name, flags, screen_index);
95 						if (texture == nullptr)
96 						{
97 							return nullptr;
98 						}
99 					}
100 					else
101 					{
102 						// create texture for specified file name
103 						uint32_t flags = bilinear ? 0u : (BGFX_SAMPLER_MIN_POINT | BGFX_SAMPLER_MAG_POINT | BGFX_SAMPLER_MIP_POINT);
104 						flags |= clamp ? (BGFX_SAMPLER_U_CLAMP | BGFX_SAMPLER_V_CLAMP | BGFX_SAMPLER_W_CLAMP) : 0u;
105 						bgfx_texture* texture = chains.textures().create_png_texture(chains.options().art_path(), texture_name, texture_name, flags, screen_index);
106 						if (texture == nullptr)
107 						{
108 							return nullptr;
109 						}
110 
111 						// get directory of file
112 						std::string directory_path = std::string(chains.options().art_path());
113 						std::string file_directory = "";
114 						const size_t last_slash = texture_name.rfind('/');
115 						if (last_slash != std::string::npos)
116 						{
117 							file_directory = texture_name.substr(0, last_slash);
118 
119 							directory_path += "/" + file_directory;
120 						}
121 
122 						osd::directory::ptr directory = osd::directory::open(directory_path);
123 						if (directory)
124 						{
125 							for (const osd::directory::entry *entry = directory->read(); entry != nullptr; entry = directory->read())
126 							{
127 								if (entry->type == osd::directory::entry::entry_type::FILE)
128 								{
129 									std::string file(entry->name);
130 									std::string extension(".png");
131 
132 									// split into file name and extension
133 									std::string file_name;
134 									std::string file_extension;
135 									const size_t last_dot = file.rfind('.');
136 									if (last_dot != std::string::npos)
137 									{
138 										file_name = file.substr(0, last_dot);
139 										file_extension = file.substr(last_dot, file.length() - last_dot);
140 									}
141 
142 									std::string file_path;
143 									if (file_directory == "")
144 									{
145 										file_path = file;
146 									}
147 									else
148 									{
149 										file_path = file_directory + "/" + file;
150 									}
151 
152 									// check for .png extension
153 									if (file_extension == extension)
154 									{
155 										// create textures for all files containd in the path of the specified file name
156 										uint32_t flags = bilinear ? 0u : (BGFX_SAMPLER_MIN_POINT | BGFX_SAMPLER_MAG_POINT | BGFX_SAMPLER_MIP_POINT);
157 										flags |= clamp ? (BGFX_SAMPLER_U_CLAMP | BGFX_SAMPLER_V_CLAMP | BGFX_SAMPLER_W_CLAMP) : 0u;
158 										bgfx_texture* texture = chains.textures().create_png_texture(chains.options().art_path(), file_path, file_path, flags, screen_index);
159 										if (texture == nullptr)
160 										{
161 											return nullptr;
162 										}
163 										texture_names.push_back(file_path);
164 									}
165 								}
166 							}
167 						}
168 					}
169 				}
170 			}
171 			else if (has_target)
172 			{
173 				texture_name = input["target"].GetString();
174 			}
175 			else
176 			{
177 				return nullptr;
178 			}
179 
180 			std::string sampler = input["sampler"].GetString();
181 			auto* input_pair = new bgfx_input_pair(i, sampler, texture_name, texture_names, selection, chains, screen_index);
182 			inputs.push_back(input_pair);
183 		}
184 	}
185 
186 	// Parse whether or not to apply screen tint in this pass
187 	bool applytint = get_bool(value, "applytint", false);
188 
189 	// Parse uniforms
190 	std::vector<bgfx_entry_uniform*> uniforms;
191 	if (value.HasMember("uniforms"))
192 	{
193 		const Value& uniform_array = value["uniforms"];
194 		for (uint32_t i = 0; i < uniform_array.Size(); i++)
195 		{
196 			bgfx_entry_uniform* uniform = entry_uniform_reader::read_from_value(uniform_array[i], prefix + "uniforms[" + std::to_string(i) + "]: ", effect, sliders, params);
197 			if (uniform == nullptr)
198 			{
199 				for (bgfx_entry_uniform* existing_uniform : uniforms) delete existing_uniform;
200 				return nullptr;
201 			}
202 			uniforms.push_back(uniform);
203 		}
204 	}
205 
206 	std::vector<bgfx_suppressor*> suppressors;
207 	if (value.HasMember("disablewhen"))
208 	{
209 		const Value& suppressor_array = value["disablewhen"];
210 		for (uint32_t i = 0; i < suppressor_array.Size(); i++)
211 		{
212 			bgfx_suppressor* suppressor = suppressor_reader::read_from_value(suppressor_array[i], prefix, sliders);
213 			if (suppressor == nullptr)
214 			{
215 				for (bgfx_entry_uniform* uniform : uniforms) delete uniform;
216 				for (bgfx_suppressor* existing_suppressor : suppressors) delete existing_suppressor;
217 				return nullptr;
218 			}
219 			suppressors.push_back(suppressor);
220 		}
221 	}
222 
223 	// Parse clear state
224 	clear_state* clear = nullptr;
225 	if (value.HasMember("clear"))
226 	{
227 		clear = clear_reader::read_from_value(value["clear"], prefix + "clear state: ");
228 	}
229 	else
230 	{
231 		clear = new clear_state(BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH, 0x00000000, 1.0f, 0);
232 	}
233 
234 	std::string output = value["output"].GetString();
235 	return new bgfx_chain_entry(name, effect, clear, suppressors, inputs, uniforms, chains.targets(), output, applytint);
236 }
237 
validate_parameters(const Value & value,std::string prefix)238 bool chain_entry_reader::validate_parameters(const Value& value, std::string prefix)
239 {
240 	if (!READER_CHECK(value.HasMember("effect"), (prefix + "Must have string value 'effect' (what effect does this entry use?)\n").c_str())) return false;
241 	if (!READER_CHECK(value["effect"].IsString(), (prefix + "Value 'effect' must be a string\n").c_str())) return false;
242 	if (!READER_CHECK(value.HasMember("name"), (prefix + "Must have string value 'effect' (what effect does this entry use?)\n").c_str())) return false;
243 	if (!READER_CHECK(value["name"].IsString(), (prefix + "Value 'name' must be a string\n").c_str())) return false;
244 	if (!READER_CHECK(value.HasMember("output"), (prefix + "Must have string value 'offset' (what target are we rendering to?)\n").c_str())) return false;
245 	if (!READER_CHECK(value["output"].IsString(), (prefix + "Value 'output' must be a string\n").c_str())) return false;
246 	if (!READER_CHECK(!value.HasMember("input") || value["input"].IsArray(), (prefix + "Value 'input' must be an array\n").c_str())) return false;
247 	if (!READER_CHECK(!value.HasMember("uniforms") || value["uniforms"].IsArray(), (prefix + "Value 'uniforms' must be an array\n").c_str())) return false;
248 	if (!READER_CHECK(!value.HasMember("disablewhen") || value["disablewhen"].IsArray(), (prefix + "Value 'disablewhen' must be an array\n").c_str())) return false;
249 	if (!READER_CHECK(!value.HasMember("applytint") || value["applytint"].IsBool(), (prefix + "Value 'applytint' must be a bool\n").c_str())) return false;
250 	return true;
251 }
252