classic.cpp 15 KB

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