diff --git a/docs/changelist.md b/docs/changelist.md index a4e29aea..24667568 100644 --- a/docs/changelist.md +++ b/docs/changelist.md @@ -1,6 +1,10 @@ Changelist ========== +* 0.0.40 + + * don't allow games for a different platform to be installed. + * 0.0.39 * Games have context menu with "GetInfo", "Uninstall" and/or "Remove" diff --git a/lib/gameinfo.js b/lib/gameinfo.js index 74b9df7b..04cfc5b1 100644 --- a/lib/gameinfo.js +++ b/lib/gameinfo.js @@ -287,7 +287,7 @@ GameInfo.prototype.checkRequiredFiles = (function() { ].concat(controllerChecks), }; - return function(runtimeInfo) { + return function(runtimeInfo, fileNames) { var info = runtimeInfo.info; var found = []; var foundCount = 0; @@ -298,7 +298,7 @@ GameInfo.prototype.checkRequiredFiles = (function() { checks = checks.concat(gameChecks); } - var fileNames = readdirtree.sync(runtimeInfo.htmlPath); + var fileNames = fileNames || readdirtree.sync(runtimeInfo.htmlPath); for (var ii = 0; ii < fileNames.length && foundCount < checks.length; ++ii) { var fileName = fileNames[ii].replace(/\\/g, '/'); checks.forEach(function(check, ndx) { // eslint-disable-line diff --git a/lib/games.js b/lib/games.js index 4ac8892f..df971ca0 100644 --- a/lib/games.js +++ b/lib/games.js @@ -127,7 +127,7 @@ var InstalledGamesList = function() { if (!info) { throw ""; } - gameInfo.checkRequiredFiles(info, info.rootPath); + gameInfo.checkRequiredFiles(info); getInstalledGames(); var index = indexByPath(gamePath); diff --git a/management/install.js b/management/install.js index 2a47ff6f..6c8ad52a 100644 --- a/management/install.js +++ b/management/install.js @@ -78,6 +78,7 @@ var install = function(releasePath, opt_destPath, opt_options) { }); var runtimeInfo; var zipRootPath; + var zipPackagePath; var packageLocations = gameInfo.getPackageLocations(); var checkPackageLocations = function(entry) { @@ -106,18 +107,50 @@ var install = function(releasePath, opt_destPath, opt_options) { if (!packageEntry) { return reject("no package.json found in " + releasePath); } - zipRootPath = packageEntry.name.replace(/\\/g, "/"); - zipRootPath = zipRootPath.substring(0, zipRootPath.indexOf("/")); + zipPackagePath = packageEntry.name.replace(/\\/g, "/"); + zipRootPath = zipPackagePath.substring(0, zipPackagePath.indexOf("/")); runtimeInfo = gameInfo.parseGameInfo(packageEntry.asText(), packageEntry.name, zipRootPath); } catch (e) { return reject("could not parse package.json. Maybe this is not a HappyFunTimes game?"); } + // Check it's got the required files + try { + var zipPackageDir = path.dirname(zipPackagePath); + var fileNames = entries.map(function(entry) { + return path.relative(zipPackageDir, entry.name.replace(/[\\/]/g, path.sep)).replace(/\\/g, "/"); + }); + gameInfo.checkRequiredFiles(runtimeInfo, fileNames); + } catch (e) { + return reject(e); + } + var info = runtimeInfo.info; var hftInfo = info.happyFunTimes; var gameId = runtimeInfo.originalGameId; + var safeGameId = releaseUtils.safeishName(gameId); var destBasePath; + var fileExists = {}; + entries.forEach(function(entry) { + var filename = entry.name.replace(/\\/g, "/").replace(/.*?\//, ""); + fileExists[filename] = true; + }); + + // Check for more files that should exist. + // Games that are "added" don't need this but games + // that are "installed" do. + log("checking gametype: " + hftInfo.gameType); + if (hftInfo.gameType.toLowerCase() === "unity3d") { + var exePath = platformInfo.exePath; + if (exePath) { + exePath = strings.replaceParams(exePath, { gameId: safeGameId }); + if (!fileExists[exePath]) { + return reject("path not found: " + exePath + "\nIs this the correct zip file for " + platformInfo.id + "?"); + } + } + } + // is it already installed? var installedGame = gameDB.getGameById(gameId); if (installedGame) { @@ -130,8 +163,6 @@ var install = function(releasePath, opt_destPath, opt_options) { } } - var safeGameId = releaseUtils.safeishName(gameId); - if (!destBasePath) { // make the dir after we're sure we're ready to install destBasePath = path.join(config.getConfig().gamesDir, safeGameId); @@ -139,14 +170,32 @@ var install = function(releasePath, opt_destPath, opt_options) { destBasePath = opt_destPath ? opt_destPath : destBasePath; + // Check for bad paths + var bad = false; + var errors = []; + entries.forEach(function(entry) { + if (bad) { + return; + } + var filePath = entry.name.substring(zipRootPath.length + 1); + var destPath = path.resolve(path.join(destBasePath, filePath)); + if (destPath.substring(0, destBasePath.length) !== destBasePath) { + errors.push("ERROR: bad zip file. Path would write outside game folder"); + bad = true; + } + }); + + if (bad) { + // Should delete all work here? + return reject(errors.join("\n")); + } + console.log("installing to: " + destBasePath); if (!options.dryRun) { mkdirp.sync(destBasePath); } var files = []; - var errors = []; - var bad = false; entries.forEach(function(entry) { if (bad) { return; @@ -154,31 +203,26 @@ var install = function(releasePath, opt_destPath, opt_options) { var filePath = entry.name.substring(zipRootPath.length + 1); files.push(filePath); var destPath = path.resolve(path.join(destBasePath, filePath)); - if (destPath.substring(0, destBasePath.length) !== destBasePath) { - errors.push("ERROR: bad zip file. Path would write outside game folder"); - bad = true; - } else { - var isDir = entry.dir; - if (destPath.substr(-1) === "/" || destPath.substr(-1) === "\\") { - destPath = destPath.substr(0, destPath.length - 1); - isDir = true; + var isDir = entry.dir; + if (destPath.substr(-1) === "/" || destPath.substr(-1) === "\\") { + destPath = destPath.substr(0, destPath.length - 1); + isDir = true; + } + //?? + if (isDir) { + log("mkdir : " + destPath); + if (!options.dryRun) { + mkdirp.sync(destPath); } - //?? - if (isDir) { - log("mkdir : " + destPath); - if (!options.dryRun) { - mkdirp.sync(destPath); - } - } else { - log("install: " + entry.name + " -> " + destPath); - if (!options.dryRun) { - var dirPath = path.dirname(destPath); - if (!fs.existsSync(dirPath)) { - log("mkdir : " + dirPath); - mkdirp.sync(dirPath); - } - fs.writeFileSync(destPath, entry.asNodeBuffer()); + } else { + log("install: " + entry.name + " -> " + destPath); + if (!options.dryRun) { + var dirPath = path.dirname(destPath); + if (!fs.existsSync(dirPath)) { + log("mkdir : " + dirPath); + mkdirp.sync(dirPath); } + fs.writeFileSync(destPath, entry.asNodeBuffer()); } } }); @@ -188,8 +232,7 @@ var install = function(releasePath, opt_destPath, opt_options) { return reject(errors.join("\n")); } - // Should this be in the zip? - log("checking gametype: " + hftInfo.gameType); + // Set the executable bit if (hftInfo.gameType.toLowerCase() === "unity3d") { var exePath = platformInfo.exePath; if (exePath) { diff --git a/management/make-controller-bundle.js b/management/make-controller-bundle.js index 76f4616b..7e1b5a3c 100644 --- a/management/make-controller-bundle.js +++ b/management/make-controller-bundle.js @@ -208,7 +208,7 @@ var make = function(gamePath, destFolder, options) { } try { - gameInfo.checkRequiredFiles(runtimeInfo, gamePath); + gameInfo.checkRequiredFiles(runtimeInfo); if (!fs.existsSync(destFolder)) { fs.mkdirSync(destFolder); } diff --git a/management/make.js b/management/make.js index b4a1c09c..9102f13a 100644 --- a/management/make.js +++ b/management/make.js @@ -320,7 +320,7 @@ var make = function(gamePath, destFolder, options) { } try { - gameInfo.checkRequiredFiles(runtimeInfo, gamePath); + gameInfo.checkRequiredFiles(runtimeInfo); if (!fs.existsSync(destFolder)) { fs.mkdirSync(destFolder); }