Skip to content

Commit ec59042

Browse files
committed
add make-controller-bundle WIP
1 parent cb0f0ef commit ec59042

File tree

2 files changed

+311
-0
lines changed

2 files changed

+311
-0
lines changed

cli/cmds/make-controller-bundle.js

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/*
2+
* Copyright 2014, Gregg Tavares.
3+
* All rights reserved.
4+
*
5+
* Redistribution and use in source and binary forms, with or without
6+
* modification, are permitted provided that the following conditions are
7+
* met:
8+
*
9+
* * Redistributions of source code must retain the above copyright
10+
* notice, this list of conditions and the following disclaimer.
11+
* * Redistributions in binary form must reproduce the above
12+
* copyright notice, this list of conditions and the following disclaimer
13+
* in the documentation and/or other materials provided with the
14+
* distribution.
15+
* * Neither the name of Gregg Tavares. nor the names of its
16+
* contributors may be used to endorse or promote products derived from
17+
* this software without specific prior written permission.
18+
*
19+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21+
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22+
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23+
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24+
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25+
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26+
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27+
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30+
*/
31+
"use strict";
32+
33+
var path = require('path');
34+
var Promise = require('promise');
35+
var utils = require('../utils');
36+
37+
var makeRelease = function(args) {
38+
return new Promise(function(resolve, reject) {
39+
if (args._.length < 1) {
40+
utils.badArgs(module, "missing dstPath");
41+
reject();
42+
return;
43+
}
44+
45+
if (args._.length > 1) {
46+
utils.badArgs(module, "too many arguments");
47+
reject();
48+
return;
49+
}
50+
51+
var destPath = path.resolve(args._[0]);
52+
var fullPath = args.src ? path.resolve(args.src) : process.cwd();
53+
54+
require('../../management/make-controller-bundle').make(fullPath, destPath, args).then(function(file) {
55+
if (args.json) {
56+
console.log(JSON.stringify(file, undefined, " "));
57+
} else {
58+
console.log("created " + file);
59+
}
60+
resolve();
61+
}).catch(function(err) {
62+
console.error(err);
63+
reject();
64+
});
65+
});
66+
};
67+
68+
exports.usage = {
69+
usage: "dstpath",
70+
prepend: [
71+
"make-controller-bundle makes a controller bundle. Duh!. Example:",
72+
"",
73+
" hft make-controller-bundle /tmp",
74+
"",
75+
"controller bundlers are used to make games optionally support HappyFunTimes",
76+
],
77+
options: [
78+
{ option: 'src', type: 'String', description: "path to source. If not supplied assumes current working directory"},
79+
{ option: 'json', type: 'Boolean', description: "format output as json" },
80+
],
81+
};
82+
exports.cmd = makeRelease;
83+
84+

management/make-controller-bundle.js

Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
/*
2+
* Copyright 2014, Gregg Tavares.
3+
* All rights reserved.
4+
*
5+
* Redistribution and use in source and binary forms, with or without
6+
* modification, are permitted provided that the following conditions are
7+
* met:
8+
*
9+
* * Redistributions of source code must retain the above copyright
10+
* notice, this list of conditions and the following disclaimer.
11+
* * Redistributions in binary form must reproduce the above
12+
* copyright notice, this list of conditions and the following disclaimer
13+
* in the documentation and/or other materials provided with the
14+
* distribution.
15+
* * Neither the name of Gregg Tavares. nor the names of its
16+
* contributors may be used to endorse or promote products derived from
17+
* this software without specific prior written permission.
18+
*
19+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21+
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22+
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23+
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24+
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25+
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26+
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27+
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30+
*/
31+
"use strict";
32+
33+
var debug = require('debug')('make');
34+
var buildInfo = require('./build-info');
35+
var exporter = require('./exporter');
36+
var fs = require('fs');
37+
var gameInfo = require('../lib/gameinfo');
38+
var path = require('path');
39+
var Promise = require('promise');
40+
var readdirtree = require('../lib/readdirtree');
41+
var releaseUtils = require('./release-utils');
42+
var strings = require('../lib/strings');
43+
var ZipWriter = require("moxie-zip").ZipWriter;
44+
45+
var goodNameRE = /^[a-zA-Z0-9_ \.\\\/-]+$/;
46+
47+
var makeZip = function(gameId, baseSrcPath, destPath, filter) {
48+
if (filter === undefined) {
49+
filter = function() {
50+
return true;
51+
};
52+
} else if (filter instanceof RegExp) {
53+
filter = function(filter) {
54+
return function(filename) {
55+
return filter.test(filename);
56+
};
57+
}(filter);
58+
}
59+
60+
var filterFunc = function(filename) {
61+
return filter(filename, baseSrcPath, fs.statSync(path.join(baseSrcPath, filename)).isDirectory());
62+
};
63+
64+
var fileNames = readdirtree.sync(baseSrcPath, {filter: /^(?!\.)/});
65+
fileNames = fileNames.filter(filterFunc);
66+
67+
// check there are no non-casesenative duplicates
68+
var lowerCaseFileNames = fileNames.map(function(f) {
69+
return f.toLowerCase();
70+
}).sort();
71+
for (var ii = 0; ii < lowerCaseFileNames.length - 1; ++ii) {
72+
if (lowerCaseFileNames[ii] === lowerCaseFileNames[ii + 1]) {
73+
return Promise.reject(new Error("two filenames are different only by case which won't work on some platforms: " + lowerCaseFileNames[ii]));
74+
}
75+
}
76+
77+
// check there are no non-ascii names
78+
var badNames = fileNames.filter(function(fileName) {
79+
return !goodNameRE.test(fileName);
80+
});
81+
82+
if (badNames.length > 0) {
83+
return Promise.reject(new Error("only alphanumeric names are allowed a-z 0-9 _ - and space:\n\t" + badNames.join("\n\t")));
84+
}
85+
86+
// check no names are too long. Let's limit to 192 characters so /Users/someusername/happyFunTimes/whatever/ + 192 is less than 256?
87+
var longNames = fileNames.filter(function(fileName) {
88+
return fileName.length > 192;
89+
});
90+
91+
if (longNames.length > 0) {
92+
return Promise.reject(new Error("max path length 192 characters. These are too long\n\t" + longNames.join("\n\t")));
93+
}
94+
95+
var zip = new ZipWriter();
96+
fileNames.forEach(function(fileName) {
97+
var zipName = path.join(gameId, fileName).replace(/\\/g, '/');
98+
var srcPath = path.join(baseSrcPath, fileName);
99+
var stat = fs.statSync(srcPath);
100+
if (stat.isDirectory()) {
101+
debug("adding dir: " + zipName);
102+
zip.addDir(zipName);
103+
} else {
104+
debug("adding file: " + srcPath);
105+
var buffer = fs.readFileSync(srcPath);
106+
zip.addData(zipName, buffer);
107+
}
108+
});
109+
110+
return new Promise(function(fulfill, reject) {
111+
zip.saveAs(destPath, function(err) {
112+
if (err) {
113+
reject(err);
114+
} else {
115+
fulfill([{filename:destPath}]);
116+
}
117+
});
118+
});
119+
};
120+
121+
var makeHTML = function(runtimeInfo, gamePath, destFolder/*, options*/) {
122+
var destPath = path.join(destFolder, releaseUtils.safeishName(runtimeInfo.originalGameId) + "-html.zip");
123+
var ignoreFilter = readdirtree.makeIgnoreFilter(runtimeInfo.info.happyFunTimes.ignore);
124+
var filter = function(filename, filePath, isDir) {
125+
var pass = ignoreFilter(filename, filePath, isDir);
126+
if (!pass) {
127+
debug("ignore: " + filename);
128+
}
129+
return pass;
130+
};
131+
132+
return makeZip(runtimeInfo.originalGameId, gamePath, destPath, filter);
133+
};
134+
135+
var makeUnity3d = function(runtimeInfo, gamePath, destFolder, options) {
136+
var gameId = runtimeInfo.originalGameId;
137+
138+
var ignoreFilter = readdirtree.makeIgnoreFilter(runtimeInfo.info.happyFunTimes.ignore);
139+
var promises = [];
140+
var destPath = path.join(destFolder, releaseUtils.safeishName(gameId) + ".controller.zip");
141+
var binStart = "bin/";
142+
var excludeRE = /^(src|Assets(?!\/WebPlayerTemplates)|Library|ProjectSettings|Temp)\//i;
143+
var extRE = /\.(meta|sln|userprefs|csproj)$/i;
144+
var filter = function(nativeFilename, filePath, isDir) {
145+
var filename = nativeFilename.replace(/\\/g, '/');
146+
if (excludeRE.test(filename) || extRE.test(filename)) {
147+
return false;
148+
}
149+
if (strings.startsWith(filename, binStart)) {
150+
return false;
151+
}
152+
var pass = ignoreFilter(nativeFilename, filePath, isDir);
153+
if (!pass) {
154+
debug("ignore: " + nativeFilename);
155+
}
156+
return pass;
157+
};
158+
return makeZip(gameId, gamePath, destPath, filter);
159+
};
160+
161+
var makers = {
162+
html: makeHTML,
163+
unity3d: makeUnity3d,
164+
};
165+
166+
/**
167+
* @typedef {object} Make~Options
168+
* @property {boolean} export true = run exporters for native
169+
* apps.
170+
* @property {string} exporterPath Path to exporter. We check
171+
* default paths if this is not specified.
172+
*/
173+
174+
/**
175+
* @typedef {object} Make~FileInfo
176+
* @property {string} filename Path to file
177+
*/
178+
179+
/**
180+
* @promise Make~MakePromise
181+
* @reject {Error} error
182+
* @fulfill {Make~FileInfo[]} files array of paths to files
183+
* created
184+
*/
185+
186+
/**
187+
* Makes a release.
188+
*
189+
* I'm not sure this belongs here but since installing a release
190+
* belongs here then it seems prudent to keep the 2 together
191+
* since they need to match.
192+
*
193+
* @param {string} gamePath path to folder of game
194+
* @param {string} destFolder path to save release files.
195+
* @param {Make~Options?} options
196+
* @returns {Make~Promise}
197+
*/
198+
var make = function(gamePath, destFolder, options) {
199+
options = options || {};
200+
201+
// Make sure it's a game!
202+
var runtimeInfo = gameInfo.readGameInfo(gamePath);
203+
if (!runtimeInfo) {
204+
return Promise.reject(new Error("not a game: " + gamePath));
205+
}
206+
207+
var hftInfo = runtimeInfo.info.happyFunTimes;
208+
var maker = makers[hftInfo.gameType.toLowerCase()];
209+
if (!maker) {
210+
return Promise.reject(new Error("unsupported game type:" + hftInfo.gameType));
211+
}
212+
213+
try {
214+
gameInfo.checkRequiredFiles(runtimeInfo, gamePath);
215+
if (!fs.existsSync(destFolder)) {
216+
fs.mkdirSync(destFolder);
217+
}
218+
} catch (e) {
219+
return Promise.reject(new Error(e.toString()));
220+
}
221+
222+
223+
return maker(runtimeInfo, gamePath, destFolder, options);
224+
};
225+
226+
exports.make = make;
227+

0 commit comments

Comments
 (0)