1
0

classic.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581
  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_bool(in_key, in, out_key, out) \
  31. out[out_key] = in[in_key].int_value() == 1;
  32. static Json::object translate_scene_item(const Json &in, const Json &source)
  33. {
  34. Json::object item = Json::object{};
  35. translate_string("name", source, "name", item);
  36. translate_int("crop.top", in, "crop_top", item, 0);
  37. translate_int("crop.bottom", in, "crop_bottom", item, 0);
  38. translate_int("crop.left", in, "crop_left", item, 0);
  39. translate_int("crop.right", in, "crop_right", item, 0);
  40. Json::object pos = Json::object{};
  41. translate_int("x", in, "x", pos, 0);
  42. translate_int("y", in, "y", pos, 0);
  43. Json::object bounds = Json::object{};
  44. translate_int("cx", in, "x", bounds, 0);
  45. translate_int("cy", in, "y", bounds, 0);
  46. item["pos"] = pos;
  47. item["bounds"] = bounds;
  48. item["bounds_type"] = 2;
  49. item["visible"] = true;
  50. return item;
  51. }
  52. static int red_blue_swap(int color)
  53. {
  54. int r = color / 256 / 256;
  55. int b = color % 256;
  56. return color - (r * 65536) - b + (b * 65536) + r;
  57. }
  58. static void create_string_obj(const string &data, Json::array &arr);
  59. static Json::object translate_source(const Json &in, const Json &sources)
  60. {
  61. string id = in["class"].string_value();
  62. string name = in["name"].string_value();
  63. Json::array source_arr = sources.array_items();
  64. if (id == "GlobalSource") {
  65. for (size_t i = 0; i < source_arr.size(); i++) {
  66. Json source = source_arr[i];
  67. if (name == source["name"].string_value()) {
  68. Json::object obj = source.object_items();
  69. obj["preexist"] = true;
  70. return obj;
  71. }
  72. }
  73. }
  74. Json in_settings = in["data"];
  75. Json::object settings = Json::object{};
  76. Json::object out = Json::object{};
  77. int i = 0;
  78. string new_name = name;
  79. while (source_name_exists(source_arr, new_name)) {
  80. new_name = name + to_string(++i);
  81. }
  82. out["name"] = new_name;
  83. if (id == "TextSource") {
  84. out["id"] = "text_gdiplus";
  85. int color = in_settings["color"].int_value() + 16777216;
  86. color = red_blue_swap(color) + 4278190080;
  87. settings["color"] = color;
  88. color = in_settings["backgroundColor"].int_value();
  89. color = red_blue_swap(color + 16777216) + 4278190080;
  90. settings["bk_color"] = color;
  91. color = in_settings["outlineColor"].int_value();
  92. color = red_blue_swap(color + 16777216) + 4278190080;
  93. settings["outline_color"] = color;
  94. translate_string("text", in_settings, "text", settings);
  95. translate_int("backgroundOpacity", in_settings, "bk_opacity",
  96. settings, 0);
  97. translate_bool("vertical", in_settings, "vertical", settings);
  98. translate_int("textOpacity", in_settings, "opacity", settings,
  99. 0);
  100. translate_bool("useOutline", in_settings, "outline", settings);
  101. translate_int("outlineOpacity", in_settings, "outline_opacity",
  102. settings, 0);
  103. translate_int("outlineSize", in_settings, "outline_size",
  104. settings, 0);
  105. translate_bool("useTextExtents", in_settings, "extents",
  106. settings);
  107. translate_int("extentWidth", in_settings, "extents_cx",
  108. settings, 0);
  109. translate_int("extentHeight", in_settings, "extents_cy",
  110. settings, 0);
  111. translate_bool("mode", in_settings, "read_from_file", settings);
  112. translate_bool("wrap", in_settings, "extents_wrap", settings);
  113. string str = in_settings["file"].string_value();
  114. settings["file"] = StringReplace(str, "\\\\", "/");
  115. int in_align = in_settings["align"].int_value();
  116. string align = in_align == 0
  117. ? "left"
  118. : (in_align == 1 ? "center" : "right");
  119. settings["align"] = align;
  120. bool bold = in_settings["bold"].int_value() == 1;
  121. bool italic = in_settings["italic"].int_value() == 1;
  122. bool underline = in_settings["underline"].int_value() == 1;
  123. int flags = bold ? OBS_FONT_BOLD : 0;
  124. flags |= italic ? OBS_FONT_ITALIC : 0;
  125. flags |= underline ? OBS_FONT_UNDERLINE : 0;
  126. Json::object font = Json::object{};
  127. font["flags"] = flags;
  128. translate_int("fontSize", in_settings, "size", font, 0);
  129. translate_string("font", in_settings, "face", font);
  130. if (bold && italic) {
  131. font["style"] = "Bold Italic";
  132. } else if (bold) {
  133. font["style"] = "Bold";
  134. } else if (italic) {
  135. font["style"] = "Italic";
  136. } else {
  137. font["style"] = "Regular";
  138. }
  139. settings["font"] = font;
  140. } else if (id == "MonitorCaptureSource") {
  141. out["id"] = "monitor_capture";
  142. translate_int("monitor", in_settings, "monitor", settings, 0);
  143. translate_bool("captureMouse", in_settings, "capture_cursor",
  144. settings);
  145. } else if (id == "BitmapImageSource") {
  146. out["id"] = "image_source";
  147. string str = in_settings["path"].string_value();
  148. settings["file"] = StringReplace(str, "\\\\", "/");
  149. } else if (id == "BitmapTransitionSource") {
  150. out["id"] = "slideshow";
  151. Json files = in_settings["bitmap"];
  152. if (!files.is_array()) {
  153. files = Json::array{in_settings["bitmap"]};
  154. }
  155. settings["files"] = files;
  156. } else if (id == "WindowCaptureSource") {
  157. out["id"] = "window_capture";
  158. string win = in_settings["window"].string_value();
  159. string winClass = in_settings["windowClass"].string_value();
  160. win = StringReplace(win, "/", "\\\\");
  161. win = StringReplace(win, ":", "#3A");
  162. winClass = StringReplace(winClass, ":", "#3A");
  163. settings["window"] = win + ":" + winClass + ":";
  164. settings["priority"] = 0;
  165. } else if (id == "CLRBrowserSource") {
  166. out["id"] = "browser_source";
  167. string browser_dec =
  168. QByteArray::fromBase64(in_settings["sourceSettings"]
  169. .string_value()
  170. .c_str())
  171. .toStdString();
  172. string err;
  173. Json browser = Json::parse(browser_dec, err);
  174. if (err != "")
  175. return Json::object{};
  176. Json::object obj = browser.object_items();
  177. translate_string("CSS", obj, "css", settings);
  178. translate_int("Height", obj, "height", settings, 0);
  179. translate_int("Width", obj, "width", settings, 0);
  180. translate_string("Url", obj, "url", settings);
  181. } else if (id == "DeviceCapture") {
  182. out["id"] = "dshow_input";
  183. string device_id = in_settings["deviceID"].string_value();
  184. string device_name = in_settings["deviceName"].string_value();
  185. settings["video_device_id"] = device_name + ":" + device_id;
  186. int w = in_settings["resolutionWidth"].int_value();
  187. int h = in_settings["resolutionHeight"].int_value();
  188. settings["resolution"] = to_string(w) + "x" + to_string(h);
  189. } else if (id == "GraphicsCapture") {
  190. bool hotkey = in_settings["useHotkey"].int_value() == 1;
  191. if (hotkey) {
  192. settings["capture_mode"] = "hotkey";
  193. } else {
  194. settings["capture_mode"] = "window";
  195. }
  196. string winClass = in_settings["windowClass"].string_value();
  197. string exec = in_settings["executable"].string_value();
  198. settings["window"] = ":" + winClass + ":" + exec;
  199. translate_bool("captureMouse", in_settings, "capture_cursor",
  200. settings);
  201. }
  202. out["settings"] = settings;
  203. return out;
  204. }
  205. #undef translate_int
  206. #undef translate_string
  207. #undef translate_bool
  208. static void translate_sc(const Json &in, Json &out)
  209. {
  210. Json::object res = Json::object{};
  211. Json::array out_sources = Json::array{};
  212. Json::array global = in["globals"].array_items();
  213. if (!in["globals"].is_null()) {
  214. for (size_t i = 0; i < global.size(); i++) {
  215. Json source = global[i];
  216. Json out_source = translate_source(source, out_sources);
  217. out_sources.push_back(out_source);
  218. }
  219. }
  220. Json::array scenes = in["scenes"].array_items();
  221. string first_name = "";
  222. for (size_t i = 0; i < scenes.size(); i++) {
  223. Json in_scene = scenes[i];
  224. if (first_name.empty())
  225. first_name = in_scene["name"].string_value();
  226. Json::array items = Json::array{};
  227. Json::array sources = in_scene["sources"].array_items();
  228. for (size_t x = sources.size(); x > 0; x--) {
  229. Json source = sources[x - 1];
  230. Json::object out_source =
  231. translate_source(source, out_sources);
  232. Json::object out_item =
  233. translate_scene_item(source, out_source);
  234. out_item["id"] = (int)x - 1;
  235. items.push_back(out_item);
  236. if (out_source.find("preexist") == out_source.end())
  237. out_sources.push_back(out_source);
  238. }
  239. out_sources.push_back(Json::object{
  240. {"id", "scene"},
  241. {"name", in_scene["name"]},
  242. {"settings",
  243. Json::object{{"items", items},
  244. {"id_counter", (int)items.size()}}}});
  245. }
  246. res["current_scene"] = first_name;
  247. res["current_program_scene"] = first_name;
  248. res["sources"] = out_sources;
  249. res["name"] = in["name"];
  250. out = res;
  251. }
  252. static void create_string(const string &name, Json::object &out,
  253. const string &data)
  254. {
  255. string str = StringReplace(data, "\\\\", "/");
  256. out[name] = str;
  257. }
  258. static void create_string_obj(const string &data, Json::array &arr)
  259. {
  260. Json::object obj = Json::object{};
  261. create_string("value", obj, data);
  262. arr.push_back(obj);
  263. }
  264. static void create_double(const string &name, Json::object &out,
  265. const string &data)
  266. {
  267. double d = atof(data.c_str());
  268. out[name] = d;
  269. }
  270. static void create_int(const string &name, Json::object &out,
  271. const string &data)
  272. {
  273. int i = atoi(data.c_str());
  274. out[name] = i;
  275. }
  276. static void create_data_item(Json::object &out, const string &line)
  277. {
  278. size_t end_pos = line.find(':') - 1;
  279. if (end_pos == string::npos)
  280. return;
  281. size_t start_pos = 0;
  282. while (line[start_pos] == ' ')
  283. start_pos++;
  284. string name = line.substr(start_pos, end_pos - start_pos);
  285. const char *c_name = name.c_str();
  286. string first = line.substr(end_pos + 3);
  287. if ((first[0] >= 'A' && first[0] <= 'Z') ||
  288. (first[0] >= 'a' && first[0] <= 'z') || first[0] == '\\' ||
  289. first[0] == '/') {
  290. if (out.find(c_name) != out.end()) {
  291. Json::array arr = out[c_name].array_items();
  292. if (out[c_name].is_string()) {
  293. Json::array new_arr = Json::array{};
  294. string str = out[c_name].string_value();
  295. create_string_obj(str, new_arr);
  296. arr = std::move(new_arr);
  297. }
  298. create_string_obj(first, arr);
  299. out[c_name] = arr;
  300. } else {
  301. create_string(c_name, out, first);
  302. }
  303. } else if (first[0] == '"') {
  304. string str = first.substr(1, first.size() - 2);
  305. if (out.find(c_name) != out.end()) {
  306. Json::array arr = out[c_name].array_items();
  307. if (out[c_name].is_string()) {
  308. Json::array new_arr = Json::array{};
  309. string str1 = out[c_name].string_value();
  310. create_string_obj(str1, new_arr);
  311. arr = std::move(new_arr);
  312. }
  313. create_string_obj(str, arr);
  314. out[c_name] = arr;
  315. } else {
  316. create_string(c_name, out, str);
  317. }
  318. } else if (first.find('.') != string::npos) {
  319. create_double(c_name, out, first);
  320. } else {
  321. create_int(c_name, out, first);
  322. }
  323. }
  324. static Json::object create_object(Json::object &out, string &line, string &src);
  325. static Json::array create_sources(Json::object &out, string &line, string &src)
  326. {
  327. Json::array res = Json::array{};
  328. line = ReadLine(src);
  329. size_t l_len = line.size();
  330. while (!line.empty() && line[l_len - 1] != '}') {
  331. size_t end_pos = line.find(':');
  332. if (end_pos == string::npos)
  333. return Json::array{};
  334. size_t start_pos = 0;
  335. while (line[start_pos] == ' ')
  336. start_pos++;
  337. string name = line.substr(start_pos, end_pos - start_pos - 1);
  338. Json::object nul = Json::object();
  339. Json::object source = create_object(nul, line, src);
  340. source["name"] = name;
  341. res.push_back(source);
  342. line = ReadLine(src);
  343. l_len = line.size();
  344. }
  345. if (!out.empty())
  346. out["sources"] = res;
  347. return res;
  348. }
  349. static Json::object create_object(Json::object &out, string &line, string &src)
  350. {
  351. size_t end_pos = line.find(':');
  352. if (end_pos == string::npos)
  353. return Json::object{};
  354. size_t start_pos = 0;
  355. while (line[start_pos] == ' ')
  356. start_pos++;
  357. string name = line.substr(start_pos, end_pos - start_pos - 1);
  358. Json::object res = Json::object{};
  359. line = ReadLine(src);
  360. size_t l_len = line.size() - 1;
  361. while (!line.empty() && line[l_len] != '}') {
  362. start_pos = 0;
  363. while (line[start_pos] == ' ')
  364. start_pos++;
  365. if (line.substr(start_pos, 7) == "sources")
  366. create_sources(res, line, src);
  367. else if (line[l_len] == '{')
  368. create_object(res, line, src);
  369. else
  370. create_data_item(res, line);
  371. line = ReadLine(src);
  372. l_len = line.size() - 1;
  373. }
  374. if (!out.empty())
  375. out[name] = res;
  376. return res;
  377. }
  378. string ClassicImporter::Name(const string &path)
  379. {
  380. return GetFilenameFromPath(path);
  381. }
  382. int ClassicImporter::ImportScenes(const string &path, string &name, Json &res)
  383. {
  384. BPtr<char> file_data = os_quick_read_utf8_file(path.c_str());
  385. if (!file_data)
  386. return IMPORTER_FILE_WONT_OPEN;
  387. if (name.empty())
  388. name = GetFilenameFromPath(path);
  389. Json::object data = Json::object{};
  390. data["name"] = name;
  391. string file = file_data.Get();
  392. string line = ReadLine(file);
  393. while (!line.empty() && line[0] != '\0') {
  394. string key = line != "global sources : {" ? "scenes"
  395. : "globals";
  396. Json::array arr = create_sources(data, line, file);
  397. data[key] = arr;
  398. line = ReadLine(file);
  399. }
  400. Json sc = data;
  401. translate_sc(sc, res);
  402. QDir dir(path.c_str());
  403. TranslateOSStudio(res);
  404. TranslatePaths(res, QDir::cleanPath(dir.filePath("..")).toStdString());
  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. }