classic.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579
  1. /******************************************************************************
  2. Copyright (C) 2019-2020 by Dillon Pentz <[email protected]>
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation, either version 2 of the License, or
  6. (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. ******************************************************************************/
  14. #include "importers.hpp"
  15. #include <QByteArray>
  16. using namespace std;
  17. using namespace json11;
  18. static bool source_name_exists(const Json::array &sources, const string &name)
  19. {
  20. for (size_t i = 0; i < sources.size(); i++) {
  21. Json source = sources[i];
  22. if (name == source["name"].string_value())
  23. return true;
  24. }
  25. return false;
  26. }
  27. #define translate_int(in_key, in, out_key, out, off) \
  28. out[out_key] = in[in_key].int_value() + off;
  29. #define translate_string(in_key, in, out_key, out) out[out_key] = in[in_key];
  30. #define translate_double(in_key, in, out_key, out) \
  31. translate_string(in_key, in, out_key, out);
  32. #define translate_bool(in_key, in, out_key, out) \
  33. out[out_key] = in[in_key].int_value() == 1;
  34. static Json::object translate_scene_item(const Json &in, const Json &source)
  35. {
  36. Json::object item = Json::object{};
  37. translate_string("name", source, "name", item);
  38. translate_int("crop.top", in, "crop_top", item, 0);
  39. translate_int("crop.bottom", in, "crop_bottom", item, 0);
  40. translate_int("crop.left", in, "crop_left", item, 0);
  41. translate_int("crop.right", in, "crop_right", item, 0);
  42. Json::object pos = Json::object{};
  43. translate_int("x", in, "x", pos, 0);
  44. translate_int("y", in, "y", pos, 0);
  45. Json::object bounds = Json::object{};
  46. translate_int("cx", in, "x", bounds, 0);
  47. translate_int("cy", in, "y", bounds, 0);
  48. item["pos"] = pos;
  49. item["bounds"] = bounds;
  50. item["bounds_type"] = 2;
  51. item["visible"] = true;
  52. return item;
  53. }
  54. static int red_blue_swap(int color)
  55. {
  56. int r = color / 256 / 256;
  57. int b = color % 256;
  58. return color - (r * 65536) - b + (b * 65536) + r;
  59. }
  60. static void create_string_obj(const string &data, Json::array &arr);
  61. static Json::object translate_source(const Json &in, const Json &sources)
  62. {
  63. string id = in["class"].string_value();
  64. string name = in["name"].string_value();
  65. Json::array source_arr = sources.array_items();
  66. if (id == "GlobalSource") {
  67. for (size_t i = 0; i < source_arr.size(); i++) {
  68. Json source = source_arr[i];
  69. if (name == source["name"].string_value()) {
  70. Json::object obj = source.object_items();
  71. obj["preexist"] = true;
  72. return obj;
  73. }
  74. }
  75. }
  76. Json in_settings = in["data"];
  77. Json::object settings = Json::object{};
  78. Json::object out = Json::object{};
  79. int i = 0;
  80. string new_name = name;
  81. while (source_name_exists(source_arr, new_name)) {
  82. new_name = name + to_string(++i);
  83. }
  84. out["name"] = new_name;
  85. if (id == "TextSource") {
  86. out["id"] = "text_gdiplus";
  87. int color = in_settings["color"].int_value() + 16777216;
  88. color = red_blue_swap(color) + 4278190080;
  89. settings["color"] = color;
  90. color = in_settings["backgroundColor"].int_value();
  91. color = red_blue_swap(color + 16777216) + 4278190080;
  92. settings["bk_color"] = color;
  93. color = in_settings["outlineColor"].int_value();
  94. color = red_blue_swap(color + 16777216) + 4278190080;
  95. settings["outline_color"] = color;
  96. translate_string("text", in_settings, "text", settings);
  97. translate_int("backgroundOpacity", in_settings, "bk_opacity",
  98. settings, 0);
  99. translate_bool("vertical", in_settings, "vertical", settings);
  100. translate_int("textOpacity", in_settings, "opacity", settings,
  101. 0);
  102. translate_bool("useOutline", in_settings, "outline", settings);
  103. translate_int("outlineOpacity", in_settings, "outline_opacity",
  104. settings, 0);
  105. translate_int("outlineSize", in_settings, "outline_size",
  106. settings, 0);
  107. translate_bool("useTextExtents", in_settings, "extents",
  108. settings);
  109. translate_int("extentWidth", in_settings, "extents_cx",
  110. settings, 0);
  111. translate_int("extentHeight", in_settings, "extents_cy",
  112. settings, 0);
  113. translate_bool("mode", in_settings, "read_from_file", settings);
  114. translate_bool("wrap", in_settings, "extents_wrap", settings);
  115. string str = in_settings["file"].string_value();
  116. settings["file"] = StringReplace(str, "\\\\", "/");
  117. int in_align = in_settings["align"].int_value();
  118. string align = in_align == 0
  119. ? "left"
  120. : (in_align == 1 ? "center" : "right");
  121. settings["align"] = align;
  122. bool bold = in_settings["bold"].int_value() == 1;
  123. bool italic = in_settings["italic"].int_value() == 1;
  124. bool underline = in_settings["underline"].int_value() == 1;
  125. int flags = bold ? OBS_FONT_BOLD : 0;
  126. flags |= italic ? OBS_FONT_ITALIC : 0;
  127. flags |= underline ? OBS_FONT_UNDERLINE : 0;
  128. Json::object font = Json::object{};
  129. font["flags"] = flags;
  130. translate_int("fontSize", in_settings, "size", font, 0);
  131. translate_string("font", in_settings, "face", font);
  132. if (bold && italic) {
  133. font["style"] = "Bold Italic";
  134. } else if (bold) {
  135. font["style"] = "Bold";
  136. } else if (italic) {
  137. font["style"] = "Italic";
  138. } else {
  139. font["style"] = "Regular";
  140. }
  141. settings["font"] = font;
  142. } else if (id == "MonitorCaptureSource") {
  143. out["id"] = "monitor_capture";
  144. translate_int("monitor", in_settings, "monitor", settings, 0);
  145. translate_bool("captureMouse", in_settings, "capture_cursor",
  146. settings);
  147. } else if (id == "BitmapImageSource") {
  148. out["id"] = "image_source";
  149. string str = in_settings["path"].string_value();
  150. settings["file"] = StringReplace(str, "\\\\", "/");
  151. } else if (id == "BitmapTransitionSource") {
  152. out["id"] = "slideshow";
  153. Json files = in_settings["bitmap"];
  154. if (!files.is_array()) {
  155. files = Json::array{in_settings["bitmap"]};
  156. }
  157. settings["files"] = files;
  158. } else if (id == "WindowCaptureSource") {
  159. out["id"] = "window_capture";
  160. string win = in_settings["window"].string_value();
  161. string winClass = in_settings["windowClass"].string_value();
  162. win = StringReplace(win, "/", "\\\\");
  163. win = StringReplace(win, ":", "#3A");
  164. winClass = StringReplace(winClass, ":", "#3A");
  165. settings["window"] = win + ":" + winClass + ":";
  166. settings["priority"] = 0;
  167. } else if (id == "CLRBrowserSource") {
  168. out["id"] = "browser_source";
  169. string browser_dec =
  170. QByteArray::fromBase64(in_settings["sourceSettings"]
  171. .string_value()
  172. .c_str())
  173. .toStdString();
  174. string err;
  175. Json browser = Json::parse(browser_dec, err);
  176. if (err != "")
  177. return Json::object{};
  178. Json::object obj = browser.object_items();
  179. translate_string("CSS", obj, "css", settings);
  180. translate_int("Height", obj, "height", settings, 0);
  181. translate_int("Width", obj, "width", settings, 0);
  182. translate_string("Url", obj, "url", settings);
  183. } else if (id == "DeviceCapture") {
  184. out["id"] = "dshow_input";
  185. string device_id = in_settings["deviceID"].string_value();
  186. string device_name = in_settings["deviceName"].string_value();
  187. settings["video_device_id"] = device_name + ":" + device_id;
  188. int w = in_settings["resolutionWidth"].int_value();
  189. int h = in_settings["resolutionHeight"].int_value();
  190. settings["resolution"] = to_string(w) + "x" + to_string(h);
  191. } else if (id == "GraphicsCapture") {
  192. bool hotkey = in_settings["useHotkey"].int_value() == 1;
  193. if (hotkey) {
  194. settings["capture_mode"] = "hotkey";
  195. } else {
  196. settings["capture_mode"] = "window";
  197. }
  198. string winClass = in_settings["windowClass"].string_value();
  199. string exec = in_settings["executable"].string_value();
  200. settings["window"] = ":" + winClass + ":" + exec;
  201. translate_bool("captureMouse", in_settings, "capture_cursor",
  202. settings);
  203. }
  204. out["settings"] = settings;
  205. return out;
  206. }
  207. #undef translate_int
  208. #undef translate_string
  209. #undef translate_double
  210. #undef translate_bool
  211. static void translate_sc(const Json &in, Json &out)
  212. {
  213. Json::object res = Json::object{};
  214. Json::array out_sources = Json::array{};
  215. Json::array global = in["globals"].array_items();
  216. if (!in["globals"].is_null()) {
  217. for (size_t i = 0; i < global.size(); i++) {
  218. Json source = global[i];
  219. Json out_source = translate_source(source, out_sources);
  220. out_sources.push_back(out_source);
  221. }
  222. }
  223. Json::array scenes = in["scenes"].array_items();
  224. string first_name = "";
  225. for (size_t i = 0; i < scenes.size(); i++) {
  226. Json in_scene = scenes[i];
  227. if (first_name.empty())
  228. first_name = in_scene["name"].string_value();
  229. Json::array items = Json::array{};
  230. Json::array sources = in_scene["sources"].array_items();
  231. for (size_t x = sources.size(); x > 0; x--) {
  232. Json source = sources[x - 1];
  233. Json::object out_source =
  234. translate_source(source, out_sources);
  235. Json::object out_item =
  236. translate_scene_item(source, out_source);
  237. out_item["id"] = (int)x - 1;
  238. items.push_back(out_item);
  239. if (out_source.find("preexist") == out_source.end())
  240. out_sources.push_back(out_source);
  241. }
  242. out_sources.push_back(Json::object{
  243. {"id", "scene"},
  244. {"name", in_scene["name"]},
  245. {"settings",
  246. Json::object{{"items", items},
  247. {"id_counter", (int)items.size()}}}});
  248. }
  249. res["current_scene"] = first_name;
  250. res["current_program_scene"] = first_name;
  251. res["sources"] = out_sources;
  252. res["name"] = in["name"];
  253. out = res;
  254. }
  255. static void create_string(const string &name, Json::object &out,
  256. const string &data)
  257. {
  258. string str = StringReplace(data, "\\\\", "/");
  259. out[name] = str;
  260. }
  261. static void create_string_obj(const string &data, Json::array &arr)
  262. {
  263. Json::object obj = Json::object{};
  264. create_string("value", obj, data);
  265. arr.push_back(obj);
  266. }
  267. static void create_double(const string &name, Json::object &out,
  268. const string &data)
  269. {
  270. double d = atof(data.c_str());
  271. out[name] = d;
  272. }
  273. static void create_int(const string &name, Json::object &out,
  274. const string &data)
  275. {
  276. int i = atoi(data.c_str());
  277. out[name] = i;
  278. }
  279. static void create_data_item(Json::object &out, const string &line)
  280. {
  281. size_t end_pos = line.find(':') - 1;
  282. if (end_pos == string::npos)
  283. return;
  284. size_t start_pos = 0;
  285. while (line[start_pos] == ' ')
  286. start_pos++;
  287. string name = line.substr(start_pos, end_pos - start_pos);
  288. const char *c_name = name.c_str();
  289. string first = line.substr(end_pos + 3);
  290. if ((first[0] >= 'A' && first[0] <= 'Z') ||
  291. (first[0] >= 'a' && first[0] <= 'z') || first[0] == '\\' ||
  292. first[0] == '/') {
  293. if (out.find(c_name) != out.end()) {
  294. Json::array arr = out[c_name].array_items();
  295. if (out[c_name].is_string()) {
  296. Json::array new_arr = Json::array{};
  297. string str = out[c_name].string_value();
  298. create_string_obj(str, new_arr);
  299. arr = std::move(new_arr);
  300. }
  301. create_string_obj(first, arr);
  302. out[c_name] = arr;
  303. } else {
  304. create_string(c_name, out, first);
  305. }
  306. } else if (first[0] == '"') {
  307. string str = first.substr(1, first.size() - 2);
  308. if (out.find(c_name) != out.end()) {
  309. Json::array arr = out[c_name].array_items();
  310. if (out[c_name].is_string()) {
  311. Json::array new_arr = Json::array{};
  312. string str1 = out[c_name].string_value();
  313. create_string_obj(str1, new_arr);
  314. arr = std::move(new_arr);
  315. }
  316. create_string_obj(str, arr);
  317. out[c_name] = arr;
  318. } else {
  319. create_string(c_name, out, str);
  320. }
  321. } else if (first.find('.') != string::npos) {
  322. create_double(c_name, out, first);
  323. } else {
  324. create_int(c_name, out, first);
  325. }
  326. }
  327. static Json::object create_object(Json::object &out, string &line, string &src);
  328. static Json::array create_sources(Json::object &out, string &line, string &src)
  329. {
  330. Json::array res = Json::array{};
  331. line = ReadLine(src);
  332. size_t l_len = line.size();
  333. while (!line.empty() && line[l_len - 1] != '}') {
  334. size_t end_pos = line.find(':');
  335. if (end_pos == string::npos)
  336. return Json::array{};
  337. size_t start_pos = 0;
  338. while (line[start_pos] == ' ')
  339. start_pos++;
  340. string name = line.substr(start_pos, end_pos - start_pos - 1);
  341. Json::object nul = Json::object();
  342. Json::object source = create_object(nul, line, src);
  343. source["name"] = name;
  344. res.push_back(source);
  345. line = ReadLine(src);
  346. l_len = line.size();
  347. }
  348. if (!out.empty())
  349. out["sources"] = res;
  350. return res;
  351. }
  352. static Json::object create_object(Json::object &out, string &line, string &src)
  353. {
  354. size_t end_pos = line.find(':');
  355. if (end_pos == string::npos)
  356. return Json::object{};
  357. size_t start_pos = 0;
  358. while (line[start_pos] == ' ')
  359. start_pos++;
  360. string name = line.substr(start_pos, end_pos - start_pos - 1);
  361. Json::object res = Json::object{};
  362. line = ReadLine(src);
  363. size_t l_len = line.size() - 1;
  364. while (!line.empty() && line[l_len] != '}') {
  365. start_pos = 0;
  366. while (line[start_pos] == ' ')
  367. start_pos++;
  368. if (line.substr(start_pos, 7) == "sources")
  369. create_sources(res, line, src);
  370. else if (line[l_len] == '{')
  371. create_object(res, line, src);
  372. else
  373. create_data_item(res, line);
  374. line = ReadLine(src);
  375. l_len = line.size() - 1;
  376. }
  377. if (!out.empty())
  378. out[name] = res;
  379. return res;
  380. }
  381. string ClassicImporter::Name(const string &path)
  382. {
  383. return GetFilenameFromPath(path);
  384. }
  385. int ClassicImporter::ImportScenes(const string &path, string &name, Json &res)
  386. {
  387. BPtr<char> file_data = os_quick_read_utf8_file(path.c_str());
  388. if (!file_data)
  389. return IMPORTER_FILE_WONT_OPEN;
  390. if (name.empty())
  391. name = GetFilenameFromPath(path);
  392. Json::object data = Json::object{};
  393. data["name"] = name;
  394. string file = file_data.Get();
  395. string line = ReadLine(file);
  396. while (!line.empty() && line[0] != '\0') {
  397. string key = line != "global sources : {" ? "scenes"
  398. : "globals";
  399. Json::array arr = create_sources(data, line, file);
  400. data[key] = arr;
  401. line = ReadLine(file);
  402. }
  403. Json sc = data;
  404. translate_sc(sc, res);
  405. return IMPORTER_SUCCESS;
  406. }
  407. bool ClassicImporter::Check(const string &path)
  408. {
  409. BPtr<char> file_data = os_quick_read_utf8_file(path.c_str());
  410. if (!file_data)
  411. return false;
  412. bool check = false;
  413. if (strncmp(file_data, "scenes : {\r\n", 12) == 0)
  414. check = true;
  415. return check;
  416. }
  417. OBSImporterFiles ClassicImporter::FindFiles()
  418. {
  419. OBSImporterFiles res;
  420. #ifdef _WIN32
  421. char dst[512];
  422. int found = os_get_config_path(dst, 512, "OBS\\sceneCollection\\");
  423. if (found == -1)
  424. return res;
  425. os_dir_t *dir = os_opendir(dst);
  426. struct os_dirent *ent;
  427. while ((ent = os_readdir(dir)) != NULL) {
  428. if (ent->directory || *ent->d_name == '.')
  429. continue;
  430. string name = ent->d_name;
  431. size_t pos = name.find(".xconfig");
  432. if (pos != -1 && pos == name.length() - 8) {
  433. string path = dst + name;
  434. res.push_back(path);
  435. }
  436. }
  437. os_closedir(dir);
  438. #endif
  439. return res;
  440. }