task_processor.js 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. import { parentPort } from 'node:worker_threads';
  2. import fs from 'node:fs';
  3. import md5 from "./src/misc/md5.js";
  4. import path from "path";
  5. import defaultProcess from "./src/defaultProcess.js";
  6. import npcInit from "./src/npcInit.js";
  7. import skillDefinitions from "./src/skillDefinitions.js";
  8. import wardrobeItems from "./src/wardrobeItems.js";
  9. //https://stackoverflow.com/a/25221100/7200161
  10. function baseFileName(fullpath){
  11. return fullpath.split('\\').pop().split('/').pop();
  12. }
  13. function convertFile(task){
  14. const VERSION = task.VERSION;
  15. const filePath = task.filePath;
  16. const outPath = task.outPath;
  17. const generatedFilesPrefix = task.generatedFilesPrefix;
  18. const options = task.options;
  19. try{
  20. const data = fs.readFileSync(filePath,{encoding: 'utf8'});
  21. const startTime = (new Date()).getTime();
  22. const baseFileNameStr = baseFileName(filePath);
  23. const outFilePath = path.join(outPath,generatedFilesPrefix,baseFileNameStr.split('.')[0]+'.tw');
  24. const outFilePathTS = path.join(outPath,generatedFilesPrefix,baseFileNameStr.split('.')[0]+'.ts');
  25. const qsp2twOptions = data.split('\n')?.[1];
  26. if(qsp2twOptions.startsWith("!! SKIP_QSRC2TW")){
  27. if(fs.existsSync(outFilePath))
  28. fs.unlink(outFilePath,(err) => {if (err) throw err;});
  29. if(fs.existsSync(outFilePathTS))
  30. fs.unlink(outFilePathTS,(err) => {if (err) throw err;});
  31. return [0,"SKIP"];
  32. }
  33. //#region Skip File Output if outfile exists, is based on the same codebase (determined by hash) and has used the same compier-version
  34. const codeHash = md5(data);
  35. if (fs.existsSync(outFilePath)) {
  36. try{
  37. const secondLineData = fs.readFileSync(outFilePath, "utf-8").split('\n')[1];
  38. const qsrc2twResultMatch = secondLineData.match(/<!--\s*qsrc2twResult=({.*})\s*-->/);
  39. if(qsrc2twResultMatch){
  40. const qsrc2twResult = JSON.parse(qsrc2twResultMatch[1]);
  41. if((qsrc2twResult.code && codeHash == qsrc2twResult.code) &&
  42. (qsrc2twResult.version && VERSION == qsrc2twResult.version)){
  43. return [0,"EXISTS"];
  44. }
  45. }
  46. }
  47. catch(e){
  48. //Something unexpected happens. No need to handle this, because we'll just run the default file-processing.
  49. }
  50. }
  51. //#endregion
  52. var convertMode = "default";
  53. var convertModeOptions = {};
  54. /*if(qsp2twOptions.startsWith("!! QSRC2TW_module")){
  55. convertMode = qsp2twOptions.trim().split(" ").toReversed()[0];
  56. }*/
  57. const modeLookupRegex = /\s*!!\s*QSRC2TW_module\s+(\w+)(\s+{[^}]+})?/;
  58. let modeLookupResult;
  59. try{
  60. if(modeLookupResult = modeLookupRegex.exec(qsp2twOptions)){
  61. convertMode = modeLookupResult[1];
  62. if(modeLookupResult[2])
  63. convertModeOptions = JSON.parse(modeLookupResult[2]);
  64. }
  65. }
  66. catch(e){
  67. console.log(e.toString());
  68. }
  69. /**
  70. * Return value is Array [TwineCode, TSCode]. TwineCode must not be null.
  71. */
  72. var convertFunction = (code,options)=>[null,null];
  73. switch (convertMode) {
  74. case "default": convertFunction = (code,options) => [defaultProcess(code),null]; break;
  75. case "npcInit": convertFunction = (code,options) => npcInit(code); break;
  76. case "stat_sklattrib_lvlset": convertFunction = (code,options) => skillDefinitions(code); break;
  77. case "wardrobeItems": convertFunction = (code,options) => wardrobeItems(code,options); break;
  78. default:
  79. console.warn("Unreckognized Convert Mode");
  80. break;
  81. }
  82. try{
  83. let twineCode = "";
  84. let twineCodeRaw = undefined;
  85. let tsCodeRaw = undefined;
  86. const defaultConsole = console;
  87. if(!options.verboseErrors)
  88. console = {log:(...args)=>{}, debug: (...args)=>{},warn:(...args)=>{}, error: (...args)=>{}};
  89. try{
  90. [twineCodeRaw,tsCodeRaw] = convertFunction(data,convertModeOptions);
  91. }
  92. catch(e){
  93. throw e;
  94. }
  95. finally{
  96. console = defaultConsole;
  97. }
  98. if(!twineCodeRaw){
  99. console.error("Twine Code must be generated by every convertMode");
  100. return [2,"Invalid convertFunction"];
  101. }else{
  102. twineCode = twineCodeRaw.split('\n')
  103. .toSpliced(1,0,`<!--qsrc2twResult={"version":${VERSION},"code":"${codeHash}","time":"${(new Date().toISOString())}"}-->`)
  104. .join('\n');
  105. fs.writeFile(outFilePath, twineCode, err => {
  106. if (err) {
  107. console.error(err);
  108. } else {
  109. }
  110. });
  111. }
  112. if(!tsCodeRaw){
  113. if(fs.existsSync(outFilePathTS))
  114. fs.unlink(outFilePathTS,(err) => {if (err) throw err;});
  115. }else{
  116. fs.writeFile(outFilePathTS, tsCodeRaw, err => {
  117. if (err) {
  118. console.error(err);
  119. } else {
  120. }
  121. });
  122. }
  123. const executionTime = (new Date()).getTime() - startTime;
  124. console.log(`${baseFileNameStr.padEnd(30,'.')} ${executionTime} ms`);
  125. return [0,"SUCCESS"];
  126. }catch(e){
  127. //console.error(`Error in "${baseFileNameStr}". No output was generated`);
  128. return [1,`Error in "${baseFileNameStr}". No output was generated`,e];
  129. }
  130. }
  131. catch(e){
  132. //console.error();
  133. return [1,`Critical Error in "${filePath}". No output was generated`, e];
  134. }
  135. }
  136. parentPort.on('message', (task) => {
  137. parentPort.postMessage(convertFile(task));
  138. });