CocosCreator(2.1～3.0)のエディタソースを解凍し、Electronコマンドで起動できるかの検証

# CocosCreatorのapp.asarの解凍
CocosCreatorを使っている方なら、一度目にしたことがあるかもしれません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228366/c72d0865-fd69-e409-fc61-995a7cc89876.png)
このapp.asarという圧縮ファイルの中に、エディタ自体のソースコードが入っています。これを解凍出来れば、改修はもちろん、エンジンの勉強にも役立ちます。

通常のasarファイルなら、
`asar e app.asar app`
で解凍できますが、cocos creatorのこのファイルは暗号化されており、asar コマンドでは解凍できません(以下のようなエラーが発生します)。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228366/bbe1aac3-ef5d-b118-58c6-97c7c5a534fc.png)

cocos creatorオフィシャルフォーラムにも同じような質問があります。
https://forum.cocos.com/t/creator/65966
このスレッドで解決策を見つけました。
https://forum.cocos.com/t/creator/65966/4
以下は解決策になります。
①まずは、Consoleタブをクリックし、すると、画面が下図の状態になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228366/45e56916-8af0-81d9-5f45-19d13e0fa71c.png)

②asarファイル内のファイルを外にコピーする関数を実行 
https://gist.github.com/benloong/586d6c899c2d84cea557d358311ae9d7

```javascript

var fs = require("fs");
var app = require("electron").remote.app;
var fsj = require("fs-jetpack");

// example usage : copyFileOutsideOfElectronAsar( "myFolderInsideTheAsarFile", app.getPath("temp") + "com.bla.bla"

var copyFileOutsideOfElectronAsar = function(sourceInAsarArchive, destOutsideAsarArchive) {
if ( fs.existsSync( app.getAppPath() + "/" + sourceInAsarArchive ) ) {

                  // file will be copied
                  if ( fs.statSync( app.getAppPath() + "/" + sourceInAsarArchive ).isFile() ) {

                      fsj.file( destOutsideAsarArchive , {content: fs.readFileSync( app.getAppPath() + "/" + sourceInAsarArchive ) });

                  }

                  // dir is browsed
                  else if ( fs.statSync( app.getAppPath() + "/" + sourceInAsarArchive ).isDirectory() ) {

                      fs.readdirSync( app.getAppPath() + "/" + sourceInAsarArchive ).forEach(function(fileOrFolderName) {

                         copyFileOutsideOfElectronAsar( sourceInAsarArchive + "/" + fileOrFolderName, destOutsideAsarArchive + "/" + fileOrFolderName );
                      });
                  }
                }

}
```
上記コードをそのままConsoleにコピーし、実行(下図をご参照)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228366/b5a9ba25-afea-3271-a9ac-8c87f8a817a4.png)

③app.asarと同じフォルダに一個appというフォルダを作成します。(mkdir appなどで)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228366/f32c64f2-5833-52e4-9341-dfed292b5934.png)

④実際のコピー処理を実行します。

```javascript
// 二つ目のパラメータはコピー先のフルパスに指定してください。
// 今回は上記で作成したappフォルダのフルパスを指定します。
copyFileOutsideOfElectronAsar("./","C:\\CocosCreator\\resources\\app")
```
これをConsoleで実行します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228366/bab16b14-04dd-ed60-b058-a0c20d20d3f3.png)

実際のappフォルダを確認しましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228366/ffe83b74-d4db-3f67-2ca9-8351ae91f016.png)

コピー出来ました!!

これでやっとapp.asarの中身を取り出せました。

# electronコマンドでcocos creatorを起動
cocos creatorで使われているelectronのバージョンをまず確認します。
app.asarを解凍しない場合、Console上で

```javascript
process.versions
```
で確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228366/671a62a7-c128-e04b-4846-6892827b9453.png)

今回app.asarを解凍しましたので、この中のpackage.jsonからでも確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228366/ea09c496-6749-2c16-f5ee-59335c6da1bd.png)

上記のprocess.versions実行でelectron 1.7.5で使われているnodeのバージョンは7.9.0で、chromiumのバージョンは58.0.3029.110です。
バージョン指定でelectron をインストールするとき、nodeのバージョンはあまり関係ないと思いますが、念のため、以下のようにインストールしました。

```bash
nvm use 7.9.0
npm -g install electron@1.7.5
electron --version
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228366/eb77ded6-a3b6-827b-b9fd-738f9f3d38c0.png)

では本題のelectronコマンド実行でcreator creatorが起動できるかを見てみましょうか。
appフォルダに入り、electron .を実行
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228366/d86ca816-00d9-5f49-bf58-e9954702e284.png)
画面が立ち上がりました!!

今回の検証は今後どのような状況で使えるかはまだ課題として残っています。
例:・内蔵されているnodeが7.9.0で、もっと高いバージョンに上げるにはどうすればいいのか
# CocosCreator3.0についての補足
asarファイル内のファイルを外にコピーする関数ですが、後続のバージョンでは一部モジュールが使えなくなったり、メソッド自体に制限がかかったりすることがありますので、以下の新しいバージョンをおすすめします。
以前と同じように、コピペでコンソールで実行。その後module.exports.copyFileOutsideOfElectronAsar("./","D:\\\\unpack\\\\app")を実行してください・「"D:\\\\unpack\\\\app"」の部分は解凍先フォルダとなります。 


```javascript
var fs = require("fs");
var app = require("electron").remote.app;
var copyFileOutsideOfElectronAsarUtility;
//module.exports.copyFileOutsideOfElectronAsar("./","D:\\unpack\\app")
(function () {
  if (copyFileOutsideOfElectronAsarUtility == null) {
    copyFileOutsideOfElectronAsarUtility = {};
  }
  copyFileOutsideOfElectronAsarUtility.isFile = function (filePath) {
    try {
      let r = fs.readFileSync(filePath);
      if (r != null) {
        return true;
      }
    } catch (error) {}
    return false;
  };
  copyFileOutsideOfElectronAsarUtility.isDirectory = function (filePath) {
    try {
      fs.readFileSync(filePath);
      return false;
    } catch (error) {
      return true;
    }
  };
  copyFileOutsideOfElectronAsarUtility.copyFileOutsideOfElectronAsar = function (
    sourceInAsarArchive,
    destOutsideAsarArchive
  ) {
    // file will be copied
    if (
      copyFileOutsideOfElectronAsarUtility.isFile(
        app.getAppPath() + "/" + sourceInAsarArchive
      )
    ) {
      console.log(app.getAppPath() + "/" + sourceInAsarArchive + ":isFile");
      let content = fs.readFileSync(
        app.getAppPath() + "/" + sourceInAsarArchive
      );
      fs.writeFileSync(destOutsideAsarArchive, content);
    }

    // dir is browsed
    else if (
      copyFileOutsideOfElectronAsarUtility.isDirectory(
        app.getAppPath() + "/" + sourceInAsarArchive
      )
    ) {
      if (sourceInAsarArchive !== "./") {
        fs.mkdirSync(destOutsideAsarArchive);
      }
      console.log(
        app.getAppPath() + "/" + sourceInAsarArchive + ":isDirectory"
      );
      let directory = app.getAppPath() + "/" + sourceInAsarArchive;
      if (sourceInAsarArchive === "./") {
        directory = app.getAppPath();
      }
      fs.readdirSync(directory).forEach(function (fileOrFolderName) {
        console.log(
          app.getAppPath() + "/" + sourceInAsarArchive + ":" + fileOrFolderName
        );

        copyFileOutsideOfElectronAsarUtility.copyFileOutsideOfElectronAsar(
          sourceInAsarArchive + "/" + fileOrFolderName,
          destOutsideAsarArchive + "/" + fileOrFolderName
        );
      });
    }
  };
})();

module.exports = copyFileOutsideOfElectronAsarUtility;


```


# CocosCreator3.0解凍してもファイルの中身が確認できなかったため、対策を実施

上の新しい「module.exports.copyFileOutsideOfElectronAsar("./","D:\\unpack\\app")」を実行すると、解凍先のD:\unpack\app\の中身は確かに確認できるようになりましたが、でも、このindex.cccは何ですか？これでもせっかく解凍できても、javascript処理の確認が出来ないですね。
見れるソースコードを確認した限り、たぶんzlibで圧縮されたものらしいです。(←もちろん違う可能性もあります)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228366/5f3f1416-2a9f-9ae4-c224-1ed528f40cb8.png)

cocosの起動時、必ずこの.cccを有効のモジュールに変換する処理がありますので、ここはいくつの処理を拝借して、.cccファイルを同じ処理の.jsに変換し、.cccも削除する処理を作りました。



```javascript　

var fs = require("fs");
var convertCCCFileTojsUtility;
//module.exports.convertCCCFileTojsUtility("D:\\unpack\\app");
(function () {
  if (convertCCCFileTojsUtility == null) {
    convertCCCFileTojsUtility = {};
  }
  var compiler = null;
  try {
    require("fix-electron-require");
    compiler = process._linkedBinding("electron_common_compile");
  } catch (e) {
    console.log(e);
  }
  convertCCCFileTojsUtility.getBaseNameWithOutExt = function (filePath) {
    return filePath
      .split(".")
      .filter((c, i, a) => i < a.length - 1)
      .join(".");
  };

  convertCCCFileTojsUtility.loadCCC = function (filePath) {
    try {
      let compiler2Ret = Buffer.from(compiler.test2(filePath)).toString();
      return compiler2Ret;
    } catch (error) {
      console.log(error);
    }
  };

  convertCCCFileTojsUtility.convertCCCFileTojsUtility = function (
    cccSourceListPath
  ) {
    (async () => {
      let cccFileList = await new Promise((resolve) =>
        require("glob")(cccSourceListPath + "/**/*.ccc", {}, (err, files) => {
          resolve(files);
        })
      );
      let editorcccList = await new Promise((resolve) =>
        require("glob")(
          cccSourceListPath + "/**/.editor.ccc",
          {},
          (err, files) => {
            resolve(files);
          }
        )
      );
      let workflowcccList = await new Promise((resolve) =>
        require("glob")(
          cccSourceListPath + "/**/.workflow.ccc",
          {},
          (err, files) => {
            resolve(files);
          }
        )
      );
      cccFileList = cccFileList.concat(editorcccList).concat(workflowcccList);
      try {
        for (let index = 0; index < cccFileList.length; index++) {
          const cccFilePath = cccFileList[index];
          let jsVersionPath =
            convertCCCFileTojsUtility.getBaseNameWithOutExt(cccFilePath) +
            ".js";
          let cccFileContent = convertCCCFileTojsUtility.loadCCC(cccFilePath);
          fs.writeFileSync(jsVersionPath, cccFileContent);
          console.info(
            (index + 1).toString() + "/" + cccFileList.length + ":done"
          );
          if (fs.existsSync(jsVersionPath)) {
            // 以前の.cccファイル削除
            try {
              fs.unlinkSync(cccFilePath);
            } catch (error) {
              console.warn(error);
            }
          }
        }
      } catch (e) {
        console.error(e);
      }

      console.info("all ccc file convert process is over");
    })();
  };
})();
module.exports = convertCCCFileTojsUtility;


```

.cccファイルが入っているappフォルダー(もしcocoscreatorのフォルダー配下の場合、別の場所に移動しましょう)で、このキャプチャの場合、D:\unpack\appとなります。
CocosCreator3.0を起動し、メニューの「Toggle DevTools」を選択すると、出てきたコンソールに先ほどのconvertCCCFileTojsUtilityのソースコード全体をコピペして実行します。
その後、module.exports.convertCCCFileTojsUtility("D:\\unpack\\app");を実行すると、D:\unpack\app配下の.cccが全て.jsになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228366/c8547cab-1bf6-e45f-4380-59c55aacee65.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228366/9c388e0f-92a2-ccd7-8355-8b8a8bd5d8a9.png)

以上となります。これで.asar圧縮ファイルなしでも、.cccがなくても動くことが出来ます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228366/e1e28f99-1cb9-5b9e-50e4-cb0acabef083.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228366/4e634730-4254-8766-3994-cd774c028ef1.png)



参考資料:
https://forum.cocos.com/t/creator/65966
https://discuss.cocos2d-x.org/t/successfully-run-cocos-creator-on-linux-and-released-it-as-docker-image/40755
https://github.com/atom/atom/issues/9800