Copy Elements.cpp 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698
  1. /******************************************************************************/
  2. #include "stdafx.h"
  3. /******************************************************************************/
  4. CopyElements CopyElms;
  5. /******************************************************************************/
  6. State StateCopyElms(UpdateCopyElms, DrawCopyElms, InitCopyElms, ShutCopyElms);
  7. /******************************************************************************/
  8. bool CopyElmsFunc(Thread &thread)
  9. {
  10. ThreadMayUseGPUData();
  11. int total =CopyElms.texs_to_copy.elms()+CopyElms.elms_to_copy.elms();
  12. cchar *suffix=u"@new";
  13. Project &src=*CopyElms.src, &dest=*CopyElms.dest;
  14. Pak &pak= CopyElms.esenthel_project_pak;
  15. // copy textures first because elements rely on them
  16. FREPA(CopyElms.texs_to_copy)
  17. {
  18. if(thread.wantStop())return false;
  19. UID id=CopyElms.texs_to_copy[i];
  20. if(src.texs.binaryHas(id, Compare) && !dest.texs.binaryHas(id, Compare)) // if source has and dest doesn't
  21. {
  22. Str sp=src.texPath(id), dp=dest.texPath(id);
  23. bool ok=false;
  24. if(pak.totalFiles())
  25. {
  26. if(C PakFile *pf=pak.find(sp))ok=FCopy(pak, *pf, dp, FILE_OVERWRITE_ALWAYS, null, suffix);
  27. }else
  28. {
  29. ok=FCopy(sp, dp, FILE_OVERWRITE_ALWAYS, null, null, suffix);
  30. }
  31. if(ok)
  32. {
  33. dest.texs.binaryInclude(id, Compare); // include in dest on success
  34. if(src.texs_update.binaryHas(id, Compare))dest.texs_update.binaryInclude(id, Compare);
  35. }else goto error;
  36. }
  37. UpdateProgress.set(i, total);
  38. }
  39. // copy elements
  40. FREPA(CopyElms.elms_to_copy)
  41. {
  42. if(thread.wantStop())return false;
  43. UID id=CopyElms.elms_to_copy[i];
  44. if(Elm *s=src.findElm(id))
  45. {
  46. Elm *d=dest.findElm(id);
  47. if(!d || d->type==s->type) // don't copy if are of different types
  48. {
  49. Str sep=src.editPath(id), dep=dest.editPath(id),
  50. sgp=src.gamePath(id), dgp=dest.gamePath(id);
  51. // copy files first
  52. if(s->type==ELM_WORLD) // World
  53. {
  54. WorldVer *src_world_ver=null, world_ver_temp;
  55. if(pak.totalFiles())
  56. {
  57. if(C PakFile *pf=pak.find(sep))if(!FCopy(pak, *pf, dep+suffix)){FDelDirs(dep+suffix); goto error;} // copy to temp folder
  58. if(C PakFile *pf=pak.find(sgp))if(!FCopy(pak, *pf, dgp+suffix)){FDelDirs(dep+suffix); FDelDirs(dgp+suffix); goto error;} // copy to temp folder
  59. File f; if(f.readTry(src.worldVerPath(id), pak))if(world_ver_temp.load(f))src_world_ver=&world_ver_temp;
  60. }else
  61. {
  62. if(FExist(sep) && !FCopyDir(sep, dep+suffix)){FDelDirs(dep+suffix); goto error;} // copy to temp folder
  63. if(FExist(sgp) && !FCopyDir(sgp, dgp+suffix)){FDelDirs(dep+suffix); FDelDirs(dgp+suffix); goto error;} // copy to temp folder
  64. src_world_ver=src.worldVerRequire(id);
  65. }
  66. FDelDirs(dep); FRename(dep+suffix, dep); // replace target folder with temp
  67. FDelDirs(dgp); FRename(dgp+suffix, dgp); // replace target folder with temp
  68. if(src_world_ver)if(WorldVer *dest_world_ver=dest.worldVerRequire(id))*dest_world_ver=*src_world_ver;
  69. }else
  70. if(s->type==ELM_MINI_MAP) // Mini Map
  71. {
  72. MiniMapVer *src_mini_map_ver=null, mini_map_ver_temp;
  73. if(pak.totalFiles())
  74. {
  75. if(C PakFile *pf=pak.find(sgp))if(!FCopy(pak, *pf, dgp+suffix )) {FDelDirs(dgp+suffix); goto error;} // copy to temp folder
  76. if(C PakFile *pf=pak.find(sep)){if(FCopy(pak, *pf, dep, FILE_OVERWRITE_ALWAYS, null, suffix))SavedEdit(s->type, dep);else{FDelDirs(dgp+suffix); goto error;}}else FDelFile(dep);
  77. File f; if(f.readTry(src.miniMapVerPath(id), pak))if(mini_map_ver_temp.load(f))src_mini_map_ver=&mini_map_ver_temp;
  78. }else
  79. {
  80. if( FExist(sgp) && !FCopyDir(sgp, dgp+suffix)) {FDelDirs(dgp+suffix); goto error;} // copy to temp folder
  81. if(!FExist(sep))FDelFile(dep);else if(FCopy(sep, dep, FILE_OVERWRITE_ALWAYS, null, null, suffix))SavedEdit(s->type, dep);else{FDelDirs(dgp+suffix); goto error;}
  82. src_mini_map_ver=src.miniMapVerRequire(id);
  83. }
  84. FDelDirs(dgp); FRename(dgp+suffix, dgp); // replace target folder with temp
  85. if(src_mini_map_ver)if(MiniMapVer *dest_mini_map_ver=dest.miniMapVerRequire(id))*dest_mini_map_ver=*src_mini_map_ver;
  86. }else
  87. if(s->type==ELM_CODE && CopyElms.src_ver>=49) // Code (stored separately only since version 49)
  88. {
  89. Str scp=src.codePath(id), dcp=dest.codePath(id);
  90. if(pak.totalFiles())
  91. {
  92. if(C PakFile *pf=pak.find(scp)){if(FCopy(pak, *pf, dcp, FILE_OVERWRITE_ALWAYS, null, suffix))SavedCode(dcp);else goto error;}else FDelFile(dcp);
  93. }else
  94. {
  95. if(!FExist(scp))FDelFile(dcp);else if(FCopy(scp, dcp, FILE_OVERWRITE_ALWAYS, null, null, suffix))SavedCode(dcp);else goto error;
  96. }
  97. // code base shouldn't be copied here
  98. }else // regular file
  99. {
  100. //Str scp=src.codePath(id), dcp=dest.codePath(id); this is performed above
  101. if(pak.totalFiles())
  102. {
  103. //if(C PakFile *pf=pak.find(scp)){if(FCopy(pak, *pf, dcp, FILE_OVERWRITE_ALWAYS, null, suffix))SavedCode( dcp);else goto error;}else FDelFile(dcp);
  104. if(C PakFile *pf=pak.find(sep)){if(FCopy(pak, *pf, dep, FILE_OVERWRITE_ALWAYS, null, suffix))SavedEdit(s->type, dep);else goto error;}else FDelFile(dep);
  105. if(C PakFile *pf=pak.find(sgp)){if(FCopy(pak, *pf, dgp, FILE_OVERWRITE_ALWAYS, null, suffix))SavedGame(s->type, dgp);else goto error;}else FDelFile(dgp);
  106. }else
  107. {
  108. //if(!FExist(scp))FDelFile(dcp);else if(FCopy(scp, dcp, FILE_OVERWRITE_ALWAYS, null, null, suffix))SavedCode( dcp);else goto error;
  109. if(!FExist(sep))FDelFile(dep);else if(FCopy(sep, dep, FILE_OVERWRITE_ALWAYS, null, null, suffix))SavedEdit(s->type, dep);else goto error;
  110. if(!FExist(sgp))FDelFile(dgp);else if(FCopy(sgp, dgp, FILE_OVERWRITE_ALWAYS, null, null, suffix))SavedGame(s->type, dgp);else goto error;
  111. }
  112. }
  113. // setup project element after files have been copied
  114. if(d)
  115. {
  116. CopyElms.elms_replaced.add(id); // if element already exists, then we're replacing it
  117. if(s->type==ELM_MESH) // check if we're changing body
  118. {
  119. UID s_body, d_body; // have to check final body instead of 'meshData().body_id' because this could remain the same, but if that body has a different body in src and dest projects, then we have to make the adjustment
  120. src.getMeshSkels(s->meshData(), null, &s_body); // get body in source project mesh
  121. dest.getMeshSkels(d->meshData(), null, &d_body); // get body in dest project mesh
  122. if(s_body!=d_body)CopyElms.changed_body.add(id);
  123. }
  124. }else d=&dest.getElm(id); // create new element
  125. *d=*s;
  126. if(!d->parent_id.valid() && d->parent_id!=CopyElms.root)d->setParent(CopyElms.root);
  127. CopyElms.elms_copied.add(id);
  128. }
  129. }
  130. UpdateProgress.set(CopyElms.texs_to_copy.elms()+i, total);
  131. }
  132. if(0)
  133. {
  134. error:
  135. Gui.msgBox(S, "Can't copy file from one project to another.\nPlease verify that you have enough free disk space.");
  136. }
  137. return false;
  138. }
  139. /******************************************************************************/
  140. bool InitCopyElms()
  141. {
  142. SetKbExclusive();
  143. Proj.pause();
  144. UpdateProgress.create(Rect_C(0, -0.05f, 1, 0.045f));
  145. UpdateThread .create(CopyElmsFunc); // create thread as last thing
  146. return true;
  147. }
  148. void ShutCopyElms()
  149. {
  150. UpdateThread .del(); // del thread as first thing
  151. UpdateProgress.del();
  152. if(CopyElms.dest)
  153. {
  154. if(Elm *elm=CopyElms.dest->findElm(CopyElms.root))elm->opened(true);
  155. CopyElms.dest->updateVersion(CopyElms.src_ver, false, CopyElms.elms_copied); // update copied elements in target project
  156. FREPA(CopyElms.elms_replaced) // process only replaced elements, to avoid having to rebuild everything when just importing completely new elements to a project
  157. if(Elm *elm=CopyElms.dest->findElm(CopyElms.elms_replaced[i]))switch(elm->type)
  158. {
  159. case ELM_MESH: CopyElms.dest->meshTransformChanged(*elm, CopyElms.changed_body.binaryHas(elm->id, Compare)); break; // need to call 'meshTransformChanged' in case mesh transform was changed and need to setup transform for skeletons/anims/phys/bodies, or if we need to rebuild world embedded objects
  160. case ELM_SKEL: CopyElms.dest->skelTransformChanged( elm->id ); break; // need to call 'skelTransformChanged' because 'meshTransformChanged' will not trigger this, since 'setSkelTransform' calls this only when it detects a transform change, however transform was already adjusted during copying
  161. case ELM_MTRL: CopyElms.dest->mtrlSetAutoTanBin ( elm->id ); break;
  162. }
  163. }
  164. CopyElms.elms_copied .clear();
  165. CopyElms.elms_replaced.clear();
  166. CopyElms.changed_body .clear();
  167. CopyElms.close();
  168. if(!Proj.valid()) // if there was no opened project
  169. { // select the newly created project
  170. Projs.refresh(); // need to refresh first in case it wasn't listed yet
  171. Projs.selectProj(CopyElms.target.id);
  172. }
  173. Proj.resume();
  174. CompareProjs.changed(CopyElms.target.id);
  175. WindowSetNormal();
  176. WindowFlash();
  177. }
  178. bool UpdateCopyElms()
  179. {
  180. if(Kb.bp(KB_ESC)){SetProjectState(); Gui.msgBox(S, "Copying elements breaked on user request");}
  181. if(!UpdateThread.active())SetProjectState();
  182. WindowSetProgress(UpdateProgress());
  183. Time.wait(1000/30);
  184. Gui.update();
  185. Server.update(null, true);
  186. if(Ms.bp(3))WindowToggle();
  187. return true;
  188. }
  189. void DrawCopyElms()
  190. {
  191. D.clear(BackgroundColor());
  192. D.text(0, 0.05f, CopyElms.esenthel_project_pak.totalFiles() ? "Importing Elements" : "Copying Elements");
  193. GuiPC gpc;
  194. gpc.visible=gpc.enabled=true;
  195. gpc.client_rect=gpc.clip.set(-D.w(), -D.h(), D.w(), D.h());
  196. gpc.offset.zero();
  197. UpdateProgress.draw(gpc);
  198. D.clip();
  199. Gui.draw();
  200. }
  201. /******************************************************************************/
  202. /******************************************************************************/
  203. CopyElements::ProjListElm::ProjListElm(C Projects::Elm &src) : id(UIDZero) {name=src.local_name; path=src.path; id=src.id;}
  204. CopyElements::ProjListElm::ProjListElm(C Project &src) : id(UIDZero) {name=src. name; path=src.path; id=src.id;}
  205. CopyElements::ReplaceElms::ElmListElm::ElmListElm(C UID &elm_id) : id(UIDZero) {T.id=elm_id; T.name=CopyElms.src->elmFullName(elm_id);}
  206. int CopyElements::ReplaceElms::ElmListElm::Compare(C ElmListElm &a, C ElmListElm &b) {return ComparePathNumber(a.name, b.name);}
  207. void CopyElements::ReplaceElms::Copy(ReplaceElms &re) {re.copyDo();}
  208. void CopyElements::ReplaceElms::copyDo()
  209. {
  210. hide();
  211. if(CopyElms.canCopy())
  212. {
  213. LOAD_RESULT result; Str error;
  214. if(CopyElms.target.id==Proj.id){CopyElms.dest=&Proj ; result=LOAD_OK;}
  215. else {CopyElms.dest=&CopyElms.temp_dest; result=CopyElms.dest->open(CopyElms.target.id, CopyElms.target.name, CopyElms.target.path, error); CopyElms.root.zero();} // can't use root because we're not copying to 'Proj'
  216. if(result==LOAD_NEWER )Gui.msgBox(S, "Target project was created with a newer version of Esenthel Engine.\nPlease upgrade your Esenthel software and try again.");else
  217. if(result==LOAD_LOCKED )Gui.msgBox(S, "Target project appears to be already opened in another instance of the Editor.\nIf it isn't, then please try re-opening it manually first.");else
  218. if(result==LOAD_ERROR )Gui.msgBox(S, S+"Target project failed to load."+(error.is() ? '\n' : '\0')+error);else
  219. if(CopyElms.dest->needUpdate())Gui.msgBox(S, "Target project needs to be updated first.\nPlease first open the target project normally in order to update it.");else
  220. {
  221. if(result==LOAD_EMPTY)CopyElms.dest->initSettings(*CopyElms.src); // if dest project didn't exist yet, then copy all settings from source
  222. // remove those elements which are not selected
  223. list.sel.sort(Compare);
  224. FREPA(list) // iterate all elements
  225. if(!list.sel.binaryHas(i, Compare)) // if i-th element is not selected
  226. if(ElmListElm *data=list.absToData(i)) // get data of that element
  227. {
  228. CopyElms.elms_to_copy.binaryExclude(data->id , Compare); // don't copy that element
  229. REPA(data->children)CopyElms.elms_to_copy.binaryExclude(data->children[i], Compare); // don't copy its children
  230. }
  231. // start copying
  232. StateCopyElms.set(StateFadeTime);
  233. return; // return so that 'close' is not called below
  234. }
  235. CopyElms.close();
  236. }
  237. }
  238. void CopyElements::ReplaceElms::create()
  239. {
  240. Gui+=::EE::Window::create(Rect_C(0, 0, 1.65f, 1.1f), "Replace elements").hide(); button[2].show();
  241. T +=text .create(Rect_LU(0, 0, clientWidth(), 0.19f).extend(-0.02f), "Following elements already exist in target project.\nPlease select which elements should be copied and replace those in target project.\nThe elements that are not selected will not be copied."); text.auto_line=AUTO_LINE_SPACE_SPLIT;
  242. T +=copy .create(Rect_RD(clientWidth()-0.04f, -clientHeight()+0.04f, 0.25f, 0.06f), "Copy").func(Copy, T);
  243. T +=region.create(Rect(0, copy.rect().max.y, clientWidth(), text.rect().min.y).extend(-0.04f));
  244. ListColumn lc[]=
  245. {
  246. ListColumn(MEMBER(ElmListElm, name), LCW_DATA, "Name"),
  247. };
  248. region+=list.create(lc, Elms(lc), true); list.flag|=LIST_MULTI_SEL; list.cur_mode=LCM_ALWAYS; list.sel_mode=LSM_TOGGLE;
  249. }
  250. void CopyElements::ReplaceElms::check()
  251. {
  252. LOAD_RESULT result; Str error;
  253. if(CopyElms.target.id==Proj.id){ CopyElms.dest=&Proj ; result=LOAD_OK;}
  254. else {int ver; CopyElms.dest=&CopyElms.temp_dest; result=CopyElms.dest->load3(CopyElms.target.path, ver, error);} // just load data without opening it for editing (so we don't have to close it after this stage yet)
  255. if(result==LOAD_NEWER )Gui.msgBox(S, "Target project was created with a newer version of Esenthel Engine.\nPlease upgrade your Esenthel software and try again.");else
  256. if(result==LOAD_LOCKED )Gui.msgBox(S, "Target project appears to be already opened in another instance of the Editor.\nIf it isn't, then please try re-opening it manually first.");else
  257. if(result==LOAD_ERROR )Gui.msgBox(S, S+"Target project failed to load."+(error.is() ? '\n' : '\0')+error);else
  258. if(CopyElms.dest->needUpdate())Gui.msgBox(S, "Target project needs to be updated first.\nPlease first open the target project normally in order to update it.");else
  259. {
  260. data.clear();
  261. REPA(CopyElms.elms_to_copy)
  262. {
  263. Elm *src =CopyElms.src ->findElm(CopyElms.elms_to_copy[i]),
  264. *dest=CopyElms.dest->findElm(CopyElms.elms_to_copy[i]);
  265. if(!src || (src && dest && src->type!=dest->type))CopyElms.elms_to_copy.remove(i, true);else // if source doesn't exist for some reason, or both projects have this element but it's of different type, then delete it and don't copy
  266. if(src && dest)data.add(CopyElms.elms_to_copy[i]); // both exist -> display to select whether it should be copied or not
  267. }
  268. // merge hidden elements with their parent
  269. REPA(data)
  270. if(Elm *elm=CopyElms.src->findElm(data[i].id))
  271. if(!ElmVisible(elm->type))
  272. if(Elm *parent=CopyElms.src->firstVisibleParent(elm))
  273. REPAD(p, data)
  274. if(data[p].id==parent->id) // find first visible parent of that element in data container
  275. {
  276. data[p].children.add(elm->id); // move element to visible parent
  277. data.remove(i); // remove element
  278. break;
  279. }
  280. // setup list
  281. data.sort(ElmListElm::Compare); list.setData(data); list.sel.clear(); FREPA(data)list.sel.add(i); // select all elements by default
  282. if(data.elms())activate(); // if there are elements to overwrite then display the window
  283. else copyDo (); // if all elements are new then proceed with copy
  284. return; // return so that 'close' is not called below
  285. }
  286. CopyElms.close();
  287. }
  288. void CopyElements::Refresh(CopyElements &ce) {ce.refresh();}
  289. void CopyElements::Copy(CopyElements &ce) {ce.copyDo ();}
  290. bool CopyElements::canCopy()C
  291. {
  292. if(StateActive==&StateProject || StateActive==&StateProjectList)return true;
  293. Gui.msgBox(S, "Copying Elements can't be performed in this Editor State");
  294. return false;
  295. }
  296. void CopyElements::create()
  297. {
  298. Gui+=::EE::Window::create(Rect_C(0, 0, 1.8f, 1.1f), "Copy elements").hide(); button[2].func(HideProjAct, SCAST(GuiObj, T)).show();
  299. T+=proj_region.create(Rect_RU(clientWidth()-0.04f, -0.10f, 0.5f, 0.82f));
  300. T+=elms_region.create(Rect (0.04f, proj_region.rect().min.y, proj_region.rect().min.x-0.04f, proj_region.rect().max.y));
  301. T+=elms_text .create(elms_region.rect().up()+Vec2(0, 0.04f), "Following elements will be copied");
  302. T+=proj_text .create(proj_region.rect().up()+Vec2(0, 0.04f), "Select target project");
  303. ListColumn elms_lc[]=
  304. {
  305. ListColumn(DATA_STR, 0, SIZE(Str), LCW_DATA, "Name"),
  306. };
  307. ListColumn proj_lc[]=
  308. {
  309. ListColumn(MEMBER(ProjListElm, name), LCW_DATA, "Name"),
  310. };
  311. elms_region+=elms_list.create(elms_lc, Elms(elms_lc), true); elms_list.cur_mode=LCM_ALWAYS;
  312. proj_region+=proj_list.create(proj_lc, Elms(proj_lc), true); proj_list.cur_mode=LCM_ALWAYS; proj_list.sort_column[0]=0;
  313. T+=include_children .create(Rect_LD( 0.04f, -clientHeight()+0.04f , 0.38f, 0.06f), "Include children" ).func(Refresh, T).focusable(false).desc("If include elements which are assigned as children to selected elements." ); include_children .mode=BUTTON_TOGGLE; include_children .set(true, QUIET);
  314. T+=include_dependencies.create(Rect_LU(include_children .rect().ru()+Vec2(0.03f, 0), 0.48f, 0.06f), "Include dependencies").func(Refresh, T).focusable(false).desc("If include elements on which selected elements depend on.\nFor example if a mesh is selected, then enabling this option will select all materials that the mesh uses."); include_dependencies.mode=BUTTON_TOGGLE; include_dependencies.set(true, QUIET);
  315. T+=include_parents .create(Rect_LU(include_dependencies.rect().ru()+Vec2(0.03f, 0), 0.48f, 0.06f), "Include parents" ).func(Refresh, T).focusable(false).desc("If include elements which are assigned as parents of selected elements." ); include_parents .mode=BUTTON_TOGGLE;
  316. T+=copy .create(Rect_RD(clientWidth()-0.04f, -clientHeight()+0.04f , 0.25f, 0.06f), "Copy" ).func(Copy , T);
  317. replace_elms.create();
  318. }
  319. void CopyElements::floodSelected(ElmNode &node, bool parent_selected, bool parent_removed)
  320. {
  321. REPA(node.children)
  322. {
  323. int child_i =node.children [i];
  324. ElmNode &child =src ->hierarchy[child_i];
  325. Elm &elm =src ->elms [child_i];
  326. bool elm_sel =sel .binaryHas(elm.id, Compare),
  327. elm_removed=(elm.removed() || parent_removed);
  328. if(elm_sel // if this one is specifically selected
  329. || parent_selected && !elm_removed) // or parent is selected and this one is not removed
  330. elms_to_copy.binaryInclude(elm.id, Compare);
  331. floodSelected(child, elm_sel || parent_selected, elm_removed);
  332. }
  333. }
  334. void CopyElements::includeTex(C UID &tex_id)
  335. {
  336. if(tex_id.valid())texs_to_copy.binaryInclude(tex_id, Compare);
  337. }
  338. void CopyElements::includeDep(C UID &elm_id)
  339. {
  340. if(elm_id.valid())if(processed_dep.binaryInclude(elm_id, Compare))if(Elm *elm=src->findElm(elm_id))switch(elm->type)
  341. {
  342. case ELM_APP: if(ElmApp *data=elm->appData())
  343. {
  344. if(include_dependencies()){includeDep(data->icon); includeDep(data->image_portrait); includeDep(data->image_landscape);}
  345. }break;
  346. case ELM_OBJ: if(ElmObj *data=elm->objData())
  347. {
  348. includeDep(data->mesh_id); // always include
  349. if(include_dependencies())includeDep(data->base_id);
  350. }break;
  351. case ELM_MESH: if(ElmMesh *data=elm->meshData())
  352. {
  353. includeDep(data-> obj_id); // always include
  354. includeDep(data->skel_id); // always include
  355. includeDep(data->phys_id); // always include
  356. if(include_dependencies()){includeDep(Proj.meshToObj(data->body_id)); includeDep(data->draw_group_id); REPA(data->mtrl_ids)includeDep(data->mtrl_ids[i]);}
  357. }break;
  358. case ELM_PHYS: if(ElmPhys *data=elm->physData())
  359. {
  360. if(include_dependencies())includeDep(data->mtrl_id);
  361. }break;
  362. case ELM_MTRL: if(ElmMaterial *data=elm->mtrlData())
  363. {
  364. includeTex(data->base_0_tex); includeTex(data->base_1_tex); includeTex(data->detail_tex); includeTex(data->macro_tex); includeTex(data->reflection_tex); includeTex(data->light_tex); // always include
  365. }break;
  366. case ELM_WATER_MTRL: if(ElmWaterMtrl *data=elm->waterMtrlData())
  367. {
  368. includeTex(data->base_0_tex); includeTex(data->base_1_tex); includeTex(data->reflection_tex); // always include
  369. }break;
  370. case ELM_IMAGE_ATLAS: if(ElmImageAtlas *data=elm->imageAtlasData())
  371. {
  372. if(include_dependencies())REPA(data->images)if(!data->images[i].removed)includeDep(data->images[i].id);
  373. }break;
  374. case ELM_ICON: if(ElmIcon *data=elm->iconData())
  375. {
  376. if(include_dependencies()){includeDep(data->icon_settings_id); includeDep(data->obj_id);}
  377. }break;
  378. case ELM_TEXT_STYLE: if(ElmTextStyle *data=elm->textStyleData())
  379. {
  380. if(include_dependencies())includeDep(data->font_id);
  381. }break;
  382. case ELM_PANEL_IMAGE: if(ElmPanelImage *data=elm->panelImageData())
  383. {
  384. if(include_dependencies())REPA(data->image_ids)includeDep(data->image_ids[i]);
  385. }break;
  386. case ELM_PANEL: if(ElmPanel *data=elm->panelData())
  387. {
  388. if(include_dependencies())REPA(data->image_ids)includeDep(data->image_ids[i]);
  389. }break;
  390. case ELM_GUI_SKIN: if(ElmGuiSkin *data=elm->guiSkinData())
  391. {
  392. if(include_dependencies())REPA(data->elm_ids)includeDep(data->elm_ids[i]);
  393. }break;
  394. case ELM_ENV: if(ElmEnv *data=elm->envData())
  395. {
  396. if(include_dependencies())
  397. {
  398. REPA(data->cloud_id)includeDep(data->cloud_id[i]);
  399. includeDep(data->sun_id); includeDep(data->skybox_id); includeDep(data->star_id);
  400. }
  401. }break;
  402. case ELM_WORLD: if(ElmWorld *data=elm->worldData())
  403. {
  404. if(include_dependencies())
  405. {
  406. includeDep(data->env_id);
  407. if(WorldVer *world_ver=src->worldVerGet(elm->id))
  408. {
  409. // copy all existing objects
  410. REPA(world_ver->obj)
  411. {
  412. ObjVer &obj_ver=world_ver->obj.lockedData(i); if(!obj_ver.removed())includeDep(obj_ver.elm_obj_id);
  413. }
  414. // copy all materials from all existing waters
  415. REPA(world_ver->lakes)
  416. {
  417. WaterVer &water_ver=world_ver->lakes.lockedData(i); if(!water_ver.removed())
  418. {
  419. Lake lake; if(lake.load(src->editLakePath(elm->id, world_ver->lakes.lockedKey(i))))includeDep(lake.material);
  420. }
  421. }
  422. REPA(world_ver->rivers)
  423. {
  424. WaterVer &water_ver=world_ver->rivers.lockedData(i); if(!water_ver.removed())
  425. {
  426. River river; if(river.load(src->editRiverPath(elm->id, world_ver->rivers.lockedKey(i))))includeDep(river.material);
  427. }
  428. }
  429. }
  430. }
  431. }break;
  432. case ELM_MINI_MAP: if(ElmMiniMap *data=elm->miniMapData())
  433. {
  434. if(include_dependencies()){includeDep(data->world_id); includeDep(data->env_id);}
  435. }break;
  436. case ELM_GUI: if(ElmGui *data=elm->guiData())
  437. {
  438. if(include_dependencies())
  439. {
  440. GuiObjs objs; if(objs.load(src->gamePath(*elm)))
  441. {
  442. REP(GO_NUM)if(C Memb<const_mem_addr GuiObj> *o=objs.objects(GUI_OBJ_TYPE(i)))REPA(*o)includeDep((*o)[i]);
  443. }
  444. }
  445. }break;
  446. }
  447. }
  448. void CopyElements::includeDep(C GuiObj &obj) // here all objects should include their dependencies
  449. { // don't include children because this function is already called for all elements
  450. switch(obj.type())
  451. {
  452. case GO_BUTTON:
  453. {
  454. includeDep(obj.asButton().image.id());
  455. includeDep(obj.asButton().skin .id());
  456. }break;
  457. case GO_CHECKBOX:
  458. {
  459. includeDep(obj.asCheckBox().skin.id());
  460. }break;
  461. case GO_COMBOBOX:
  462. {
  463. includeDep(obj.asComboBox().image .id());
  464. includeDep(obj.asComboBox().skin().id());
  465. }break;
  466. case GO_IMAGE:
  467. {
  468. includeDep(obj.asImage().image.id());
  469. }break;
  470. case GO_MENU:
  471. {
  472. includeDep(obj.asMenu().skin().id());
  473. }break;
  474. case GO_PROGRESS:
  475. {
  476. includeDep(obj.asProgress().skin.id());
  477. }break;
  478. case GO_REGION:
  479. {
  480. includeDep(obj.asRegion().skin().id());
  481. includeDep(obj.asRegion().view);
  482. REPA(obj.asRegion().slidebar)includeDep(obj.asRegion().slidebar[i]);
  483. }break;
  484. case GO_SLIDEBAR:
  485. {
  486. includeDep(obj.asSlideBar().skin().id());
  487. REPA(obj.asSlideBar().button)includeDep(obj.asSlideBar().button[i]);
  488. }break;
  489. case GO_SLIDER:
  490. {
  491. includeDep(obj.asSlider().skin.id());
  492. }break;
  493. case GO_TAB:
  494. {
  495. includeDep(obj.asTab().image.id());
  496. includeDep(obj.asTab().skin .id());
  497. }break;
  498. case GO_TABS:
  499. {
  500. includeDep(obj.asTabs().skin().id());
  501. REPA(obj.asTabs())includeDep(obj.asTabs().tab(i));
  502. }break;
  503. case GO_TEXT:
  504. {
  505. includeDep(obj.asText().skin.id());
  506. includeDep(obj.asText().text_style.id());
  507. }break;
  508. case GO_TEXTLINE:
  509. {
  510. includeDep(obj.asTextLine().reset.skin.id());
  511. includeDep(obj.asTextLine().skin().id());
  512. }break;
  513. case GO_WINDOW:
  514. {
  515. includeDep(obj.asWindow().skin().id());
  516. REPA(obj.asWindow().button)includeDep(obj.asWindow().button[i]);
  517. }break;
  518. }
  519. }
  520. void CopyElements::refresh()
  521. {
  522. elms_to_copy.clear();
  523. texs_to_copy.clear();
  524. // include children
  525. if(include_children())floodSelected(src->root);else elms_to_copy=sel;
  526. // include parents
  527. if(include_parents())
  528. {
  529. Memc<UID> elms=elms_to_copy;
  530. for(; elms.elms(); )
  531. {
  532. UID elm_id=elms.pop(); if(Elm *elm=Proj.findElm(elm_id))if(Proj.findElm(elm->parent_id))if(elms_to_copy.binaryInclude(elm->parent_id, Compare))elms.add(elm->parent_id);
  533. }
  534. }
  535. // include dependencies
  536. processed_dep.clear(); FREPA(elms_to_copy)includeDep(elms_to_copy[i]); Swap(processed_dep, elms_to_copy);
  537. elms_data.clear(); FREPA(elms_to_copy)if(Elm *elm=src->findElm(elms_to_copy[i]))if(ElmVisible(elm->type))elms_data.add(src->elmFullName(elm)); elms_data.sort(ComparePathNumberCI);
  538. elms_list.setData(elms_data);
  539. }
  540. void CopyElements::resize()
  541. {
  542. elms_list.elmHeight(Proj.list.elmHeight()).textSize(Proj.list.textSizeBase(), Proj.list.textSizeRel());
  543. replace_elms.list.elmHeight(elms_list.elmHeight()).textSize(elms_list.textSizeBase(), elms_list.textSizeRel());
  544. }
  545. void CopyElements::display(C Str &esenthel_project_file)
  546. {
  547. replace_elms.hide();
  548. hide();
  549. if(canCopy())
  550. {
  551. src_ver=-1;
  552. root.zero(); if(ListElm *elm=Proj.list.litToListElm())if(elm->elm)root=elm->elm->id;
  553. if(esenthel_project_file.is())
  554. {
  555. long expected_size, actual_size;
  556. switch(esenthel_project_pak.loadEx(esenthel_project_file, null, 0, &expected_size, &actual_size))
  557. {
  558. case PAK_LOAD_OK : break;
  559. case PAK_LOAD_UNSUPPORTED_VERSION: Gui.msgBox(S, "Selected project was created with a newer version of the engine."); return;
  560. case PAK_LOAD_INCOMPLETE_DATA : Gui.msgBox(S, S+"Selected project file is incomplete:\nFile size is: "+FileSize(actual_size)+"\nIt should be: "+FileSize(expected_size)+"\nIf downloaded from Esenthel Store, please make sure that the download was not interrupted.\nConsider downloading using Esenthel Editor which supports resuming interrupted downloads."); return;
  561. default : Gui.msgBox(S, "Can't load selected project"); return;
  562. }
  563. src=&temp_src;
  564. File f; f.readTry("Data", esenthel_project_pak); switch(src->load(f, src_ver)) // call 'load' always, and not only when 'readTry' succeeded, since we can't update the "EsenthelProject" file, we will update the destination project once it finished copying
  565. {
  566. case LOAD_EMPTY :
  567. case LOAD_ERROR :
  568. case LOAD_LOCKED: Gui.msgBox(S, "Invalid project file"); return;
  569. case LOAD_NEWER : Gui.msgBox(S, "This project was created with a newer version of Esenthel Engine.\nPlease upgrade your Esenthel software and try again."); return;
  570. }
  571. if(src_ver<=34 && f.readTry("Settings", esenthel_project_pak))src->loadOldSettings(f); // ver 34 and below had settings in a separate file
  572. src->setIDPath(UIDZero, S); // set paths too because for example when copying textures, then 'tex_path' is used
  573. src->setHierarchy();
  574. // select all elements and textures to be copied
  575. elms_to_copy.clear(); FREPA(src->elms)elms_to_copy.binaryInclude(src->elms[i].id, Compare);
  576. texs_to_copy.clear(); FREPA(src->texs)texs_to_copy.binaryInclude(src->texs[i] , Compare);
  577. if(Proj.valid()) // if there's a project opened, then import into that one
  578. {
  579. if(Demo){Gui.msgBox(S, "Importing projects into existing projects is not available in the demo version."); return;}
  580. target=Proj;
  581. }else
  582. {
  583. target.id.randomizeValid(); // when no project is opened then always create a new one
  584. target.name=src->name; // re-use original name
  585. target.path=ProjectsPath+EncodeFileName(target.id).tailSlash(true); // path needs to be set
  586. }
  587. replace_elms.check();
  588. }else
  589. {
  590. esenthel_project_pak.del();
  591. src=&Proj; src_ver=ProjectVersion;
  592. Proj.setListSel(sel); sel.sort(Compare);
  593. refresh();
  594. int cur=-1;
  595. proj_data.clear(); FREPA(Projs.proj_data){Projects::Elm &src=Projs.proj_data[i]; if(src.id!=Proj.id){if(src.id==target.id)cur=proj_data.elms(); proj_data.add(src);}} // add all except currently opened project, if there's a project to which we've copied last time, then select it
  596. proj_list.setData(proj_data); proj_list.setCur(proj_list.absToVis(cur)); // !! do not merge into one instruction !!
  597. ::EE::GuiObj::activate();
  598. }
  599. }
  600. }
  601. void CopyElements::copyDo()
  602. {
  603. if(!elms_to_copy.elms())Gui.msgBox(S, "No elements to copy");else
  604. if(ProjListElm *dest=proj_list()){if(src==&Proj)REPA(elms_to_copy)Proj.flushElm(elms_to_copy[i]); hide(); target=*dest; replace_elms.check();}else
  605. Gui.msgBox(S, "No target project selected");
  606. }
  607. void CopyElements::drop(Memc<Str> &names, GuiObj *focus_obj, C Vec2 &screen_pos)
  608. {
  609. REPA(names)if(GetExt(names[i])==EsenthelProjectExt)
  610. {
  611. display(names[i]);
  612. names.remove(i, true);
  613. break;
  614. }
  615. }
  616. void CopyElements::close()
  617. {
  618. esenthel_project_pak.del();
  619. if(dest==&Proj)
  620. {
  621. Proj.setList();
  622. Proj.enumChanged(); // before 'activateSources' because this generates C++ auto header
  623. Proj.fontChanged();
  624. Proj.activateSources(CodeEdit.initialized() ? 1 : -1); // set active sources but without rebuilding so code editor doesn't need to be loaded every time (unless it's already loaded), call after 'enumChanged'
  625. }else if(dest)dest->close();
  626. dest=null;
  627. if(src && src!=&Proj)src->del(); src=null; // we shouldn't 'close' 'src' because it's either 'Proj' or an "EsenthelProject" file
  628. }
  629. CopyElements::CopyElements() : src_ver(-1), src(null), dest(null), root(UIDZero) {}
  630. CopyElements::ProjListElm::ProjListElm() : id(UIDZero) {}
  631. CopyElements::ReplaceElms::ElmListElm::ElmListElm() : id(UIDZero) {}
  632. /******************************************************************************/