A pixel art painter geared specifically at NES pixel art. Includes export for .chr binary file as well as palette and namespace data.
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

270 行
7.3KB

  1. import GlobalEvents from "/app/js/common/EventCaller.js";
  2. import Utils from "/app/js/common/Utils.js";
  3. import JSONSchema from "/app/js/common/JSONSchema.js";
  4. import NESBank from "/app/js/models/NESBank.js";
  5. import NESPalette from "/app/js/models/NESPalette.js";
  6. import NESNameTable from "/app/js/models/NESNameTable.js";
  7. import CTRLPalettesStore from "/app/js/ctrls/CTRLPalettesStore.js";
  8. import CTRLBanksStore from "/app/js/ctrls/CTRLBanksStore.js";
  9. import CTRLNameTablesStore from "/app/js/ctrls/CTRLNameTablesStore.js";
  10. const SUPPORTED_PROJECT_VERSIONS=[
  11. "0.1"
  12. ];
  13. const SCHEMA_ID = "http://nespaint/Project.json";
  14. JSONSchema.add({
  15. "$schema": "http://json-schema.org/draft-07/schema#",
  16. "$id": SCHEMA_ID,
  17. "type":"object",
  18. "properties":{
  19. "id":{
  20. "type":"string",
  21. "enum":["NESPProj"]
  22. },
  23. "version":{
  24. "type":"string",
  25. "pattern":"^[0-9]{1,}\.[0-9]{1,}$"
  26. },
  27. "paletteStore":{"$ref":"http://nespaint/PalettesStoreSchema.json"},
  28. "bankStore":{"$ref":"http://nespaint/BanksStoreSchema.json"},
  29. "nametableStore":{"$ref":"http://nespaint/NametableStoreSchema.json"}
  30. },
  31. "required":["id","version","paletteStore","bankStore"]
  32. });
  33. var SURF = null;
  34. function JSONFromProject(){
  35. var proj = {
  36. id:"NESPProj",
  37. version:SUPPORTED_PROJECT_VERSIONS[SUPPORTED_PROJECT_VERSIONS.length - 1],
  38. paletteStore:CTRLPalettesStore.obj,
  39. bankStore:CTRLBanksStore.obj
  40. };
  41. if (CTRLNameTablesStore.keys.length > 0)
  42. proj.nametableStore = CTRLNameTablesStore.obj;
  43. return JSON.stringify(proj);
  44. }
  45. function RequestDownload(filename, datblob){
  46. var a = document.createElement("a");
  47. a.href = window.URL.createObjectURL(datblob);
  48. a.download = filename;
  49. var body = document.querySelector("body");
  50. body.appendChild(a);
  51. a.click();
  52. setTimeout(function(){ // fixes firefox html removal bug
  53. window.URL.revokeObjectURL(a.href);
  54. a.remove();
  55. }, 500);
  56. }
  57. function LoadFile(file){
  58. if (SURF !== null){
  59. var reader = new FileReader();
  60. if (SURF instanceof NESBank){
  61. reader.onload = function(e){
  62. try {
  63. SURF.chr = new Uint8Array(e.target.result);
  64. } catch (e) {
  65. console.log(e.toString());
  66. }
  67. }
  68. reader.readAsArrayBuffer(file);
  69. }
  70. }
  71. }
  72. function HANDLE_DragOver(e){
  73. e.stopPropagation();
  74. e.preventDefault();
  75. e.dataTransfer.dropEffect = 'copy';
  76. };
  77. function HANDLE_FileDrop(e){
  78. e.stopPropagation();
  79. e.preventDefault();
  80. var files = e.dataTransfer.files;
  81. for (let i=0; i < files.length; i++){
  82. LoadFile(files[i]);
  83. }
  84. }
  85. function HANDLE_SaveProject(e){
  86. //var a = document.createElement("a");
  87. var file = new Blob([JSONFromProject()], {type: "text/plain"});
  88. RequestDownload("nesproject.json", file);
  89. }
  90. function HANDLE_ExportCHR(e){
  91. e.preventDefault();
  92. var bank = CTRLBanksStore.currentBank;
  93. if (bank !== null){
  94. var dat = null;
  95. var size = document.querySelector('input[name="exportchr-size"]:checked').value;
  96. switch (size){
  97. case "full":
  98. dat = bank.getCHR(0,0);
  99. break;
  100. case "current":
  101. dat = bank.chr;
  102. }
  103. if (dat !== null){
  104. var file = new Blob([dat], {type:"application/octet-stream"});
  105. var filename = CTRLBanksStore.currentBankName.replace(/[^a-z0-9\-_.]/gi, '_').toLowerCase() + ".chr";
  106. RequestDownload(filename, file);
  107. }
  108. }
  109. GlobalEvents.emit("modal-close");
  110. }
  111. function HANDLE_ExportPalASM(e){
  112. var pal = CTRLPalettesStore.currentPalette;
  113. var palname = CTRLPalettesStore.currentPaletteName.replace(/[^a-z0-9\-_.]/gi, '_');
  114. if (pal !== null && palname !== ""){
  115. var asmtxt = pal.to_asm(palname);
  116. var file = new Blob([asmtxt], {type: "text/plain"});
  117. RequestDownload(palname + ".asm", file);
  118. }
  119. }
  120. function HANDLE_ExportNameTableASM(e){
  121. var nt = CTRLNameTablesStore.currentNametable;
  122. if (nt !== null){
  123. var basename = CTRLNameTablesStore.currentNTName.replace(/[^a-z0-9\-_.]/gi, '_');
  124. var mode = document.querySelector('input[name="exportnt-op"]:checked').value;
  125. var asm = "";
  126. switch (mode){
  127. case "both":
  128. asm = nt.to_asm(basename + "_NT", basename + "_AT");
  129. break;
  130. case "nametable":
  131. asm = nt.nametable_asm(basename + "_NT");
  132. break;
  133. case "attribtable":
  134. asm = nt.attribtable_asm(basename + "_AT");
  135. break;
  136. }
  137. if (asm !== ""){
  138. var file = new Blob([asm], {type: "text/plain"});
  139. RequestDownload(basename + ".asm", file);
  140. }
  141. }
  142. }
  143. function HANDLE_LoadProjectRequest(){
  144. var input = document.querySelectorAll("input.project-loader");
  145. if (input.length > 0){
  146. input[0].click();
  147. }
  148. }
  149. function HANDLE_LoadProject(e){
  150. if (this.files && this.files.length > 0){
  151. var reader = new FileReader();
  152. reader.onload = (function(e) {
  153. var o = null;
  154. var validator = JSONSchema.getValidator(SCHEMA_ID);
  155. try {
  156. o = JSON.parse(e.target.result);
  157. } catch (e) {
  158. console.log("Failed to parse JSON string. " + e.toString());
  159. }
  160. if (validator !== null && validator(o)){
  161. // TODO: Validate 'id' and 'version' properties.
  162. CTRLPalettesStore.obj = o.paletteStore;
  163. CTRLBanksStore.obj = o.bankStore;
  164. if ("nametableStore" in o)
  165. CTRLNameTablesStore.obj = o.nametableStore;
  166. }
  167. if (this.parentNode.nodeName.toLowerCase() === "form"){
  168. this.parentNode.reset();
  169. } else {
  170. console.log("WARNING: Parent node is NOT a <form> element.");
  171. }
  172. }).bind(this);
  173. reader.readAsText(this.files[0]);
  174. } else {
  175. console.log("Project file not found or no file selected.");
  176. }
  177. }
  178. function HANDLE_SurfChange(surf){
  179. var enableclass = "";
  180. if (surf instanceof NESBank){
  181. SURF = surf;
  182. enableclass = "surf-bank";
  183. } else {
  184. SURF = null;
  185. if (surf instanceof NESNameTable)
  186. enableclass="surf-nametable";
  187. }
  188. var e = document.querySelectorAll(".surf-export");
  189. for (let i=0; i < e.length; i++){
  190. var ea = e[i].querySelector("a");
  191. if (ea){
  192. if (e[i].classList.contains(enableclass)){
  193. e[i].classList.remove("disable-links");
  194. ea.classList.remove("pure-menu-disabled");
  195. } else {
  196. e[i].classList.add("disable-links");
  197. ea.classList.add("pure-menu-disabled");
  198. }
  199. }
  200. }
  201. }
  202. class CTRLIO{
  203. constructor(){
  204. GlobalEvents.listen("change_surface", HANDLE_SurfChange);
  205. GlobalEvents.listen("save-project", HANDLE_SaveProject);
  206. GlobalEvents.listen("load-project", HANDLE_LoadProjectRequest);
  207. GlobalEvents.listen("export-pal-asm", HANDLE_ExportPalASM);
  208. GlobalEvents.listen("export-nametable", HANDLE_ExportNameTableASM);
  209. var input = document.querySelectorAll("input.project-loader");
  210. if (input.length > 0){
  211. input[0].addEventListener("change", HANDLE_LoadProject);
  212. }
  213. }
  214. initialize(){
  215. // Connecting drag/drop ability.
  216. var e = document.querySelectorAll(".drop-zone");
  217. for (let i=0; i < e.length; e++){
  218. e[i].addEventListener("dragover", HANDLE_DragOver);
  219. e[i].addEventListener("drop", HANDLE_FileDrop);
  220. }
  221. // Connecting to button for export request.
  222. var e = document.querySelectorAll(".export-chr-btn");
  223. for (let i=0; i < e.length; i++){
  224. e[i].addEventListener("click", HANDLE_ExportCHR);
  225. }
  226. CTRLPalettesStore.initialize();
  227. CTRLBanksStore.initialize();
  228. CTRLNameTablesStore.initialize();
  229. }
  230. }
  231. const instance = new CTRLIO();
  232. export default instance;