Browse Source

Adding Subprocess system, build system (web only atm), to cli build

Josh Engebretson 10 years ago
parent
commit
de9edefe2a

+ 117 - 0
Source/ToolCore/Build/BuildBase.cpp

@@ -0,0 +1,117 @@
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#include "AtomicEditor.h"
+#include <Atomic/IO/Log.h>
+#include <Atomic/IO/FileSystem.h>
+
+#include "BuildBase.h"
+#include "ResourcePackager.h"
+
+namespace ToolCore
+{
+
+BuildBase::BuildBase(Context * context) : Object(context), containsMDL_(false)
+{
+
+    if (UseResourcePackager())
+        resourcePackager_ = new ResourcePackager(context, this);
+
+    BuildLog("Build Started");
+
+}
+
+BuildBase::~BuildBase()
+{
+    for (unsigned i = 0; i < resourceEntries_.Size(); i++)
+    {
+        delete resourceEntries_[i];
+    }
+}
+
+void BuildBase::BuildLog(const String& message)
+{
+    buildLog_.Push(message);
+}
+
+void BuildBase::BuildWarn(const String& warning)
+{
+    buildWarnings_.Push(warning);
+}
+
+void BuildBase::BuildError(const String& error)
+{
+    buildErrors_.Push(error);
+}
+
+
+void BuildBase::ScanResourceDirectory(const String& resourceDir)
+{
+    Vector<String> fileNames;
+    FileSystem* fileSystem = GetSubsystem<FileSystem>();
+    fileSystem->ScanDir(fileNames, resourceDir, "*.*", SCAN_FILES, true);
+
+    for (unsigned i = 0; i < fileNames.Size(); i++)
+    {
+        const String& filename = fileNames[i];
+
+        for (unsigned j = 0; j < resourceEntries_.Size(); j++)
+        {
+            const BuildResourceEntry* entry = resourceEntries_[j];
+            if (entry->packagePath_ == filename)
+            {
+                BuildWarn(ToString("Resource Path: %s already exists", filename.CString()));
+                continue;
+            }
+        }
+
+        BuildResourceEntry* newEntry = new BuildResourceEntry;
+
+// BEGIN LICENSE MANAGEMENT
+        if (GetExtension(filename) == ".mdl")
+        {
+            containsMDL_ = true;
+        }
+// END LICENSE MANAGEMENT
+
+        newEntry->absolutePath_ = resourceDir + filename;
+        newEntry->packagePath_ = filename;
+        newEntry->resourceDir_ = resourceDir;
+
+        resourceEntries_.Push(newEntry);
+    }
+}
+
+void BuildBase::BuildResourceEntries()
+{
+    for (unsigned i = 0; i < resourceDirs_.Size(); i++)
+    {
+        ScanResourceDirectory(resourceDirs_[i]);
+    }
+
+    if (resourcePackager_.NotNull())
+    {
+        for (unsigned i = 0; i < resourceEntries_.Size(); i++)
+        {
+            BuildResourceEntry* entry = resourceEntries_[i];
+            resourcePackager_->AddResourceEntry(entry);
+        }
+
+    }
+
+}
+
+void BuildBase::GenerateResourcePackage(const String& resourcePackagePath)
+{
+    resourcePackager_->GeneratePackage(resourcePackagePath);
+}
+
+void BuildBase::AddResourceDir(const String& dir)
+{
+    assert(!resourceDirs_.Contains(dir));
+    resourceDirs_.Push(dir);
+}
+
+
+}

+ 63 - 0
Source/ToolCore/Build/BuildBase.h

@@ -0,0 +1,63 @@
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#pragma once
+
+#include <Atomic/Core/Object.h>
+#include "BuildTypes.h"
+
+using namespace Atomic;
+
+namespace ToolCore
+{
+
+class ResourcePackager;
+
+class BuildBase : public Object
+{
+    OBJECT(BuildBase);
+
+public:
+
+    BuildBase(Context* context);
+    virtual ~BuildBase();
+
+    virtual void Build(const String& buildPath) = 0;
+
+    // some platforms may want to do their own packaging of resources
+    virtual bool UseResourcePackager() { return true; }
+
+    // add in search order, first added is first searched
+    // will warn on name conflicts
+    void AddResourceDir(const String& dir);
+
+    void BuildLog(const String& message);
+    void BuildWarn(const String& warning);
+    void BuildError(const String& error);
+
+protected:
+
+    void GenerateResourcePackage(const String& resourcePackagePath);
+
+    void BuildResourceEntries();
+
+    String buildPath_;
+    PODVector<BuildResourceEntry*> resourceEntries_;
+
+    bool containsMDL_;
+
+private:
+
+    Vector<String> buildLog_;
+    Vector<String> buildWarnings_;
+    Vector<String> buildErrors_;
+
+    void ScanResourceDirectory(const String& resourceDir);
+
+    SharedPtr<ResourcePackager> resourcePackager_;
+    Vector<String> resourceDirs_;
+
+};
+
+}

+ 202 - 0
Source/ToolCore/Build/BuildSettings.cpp

@@ -0,0 +1,202 @@
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#include "AtomicEditor.h"
+#include <Atomic/Core/CoreEvents.h>
+#include "AtomicEditor.h"
+#include "BuildSettings.h"
+
+namespace ToolCore
+{
+
+BuildSettings::BuildSettings(Context* context) :
+    Object(context)
+{
+
+}
+
+BuildSettings::~BuildSettings()
+{
+
+}
+
+String BuildSettings::GetStringMember(rapidjson::Value::Member* jobject, const String& name)
+{
+    rapidjson::Value::Member* member = jobject->value.FindMember(name.CString());
+
+    if (!member || !member->value.IsString())
+        return "";
+
+    return member->value.GetString();
+}
+
+void BuildSettings::Load(rapidjson::Value::Member* jobject)
+{
+    // ANDROID ------
+    rapidjson::Value::Member* jandroid = jobject->value.FindMember("android");
+    if (jandroid && jandroid->value.IsObject())
+    {
+        android_.appName = GetStringMember(jandroid, "app_name");
+        android_.package = GetStringMember(jandroid, "package");
+        android_.targetSDKVersion = GetStringMember(jandroid, "target_sdk_version");
+        android_.minSDKVersion = GetStringMember(jandroid, "min_sdk_version");
+        android_.activityName = GetStringMember(jandroid, "activity_name");
+        android_.companyName = GetStringMember(jandroid, "company_name");
+        android_.productName = GetStringMember(jandroid, "product_name");
+    }
+    // END ANDROID ------
+
+    // IOS ------
+    rapidjson::Value::Member* jios = jobject->value.FindMember("ios");
+    if (jios && jios->value.IsObject())
+    {
+        ios_.appName = GetStringMember(jios, "app_name");
+        ios_.package = GetStringMember(jios, "package");
+        ios_.provisionFile = GetStringMember(jios, "provision_file");
+        ios_.companyName = GetStringMember(jios, "company_name");
+        ios_.productName = GetStringMember(jios, "product_name");
+        ios_.appidPrefix = GetStringMember(jios, "appid_prefix");
+    }
+    // END IOS ------
+
+    // Mac ------
+    rapidjson::Value::Member* jmac = jobject->value.FindMember("mac");
+    if (jmac && jmac->value.IsObject())
+    {
+        mac_.appName = GetStringMember(jmac, "app_name");
+        mac_.package = GetStringMember(jmac, "package");
+        mac_.companyName = GetStringMember(jmac, "company_name");
+        mac_.productName = GetStringMember(jmac, "product_name");
+    }
+    // END Mac ------
+
+    // Windows ------
+    rapidjson::Value::Member* jwindows = jobject->value.FindMember("windows");
+    if (jwindows && jwindows->value.IsObject())
+    {
+        windows_.appName = GetStringMember(jwindows, "app_name");
+        windows_.package = GetStringMember(jwindows, "package");
+        windows_.companyName = GetStringMember(jwindows, "company_name");
+        windows_.productName = GetStringMember(jwindows, "product_name");
+    }
+    // END Windows ------
+
+    // WebGL ------
+    rapidjson::Value::Member* jwebgl = jobject->value.FindMember("webgl");
+    if (jwebgl && jwebgl->value.IsObject())
+    {
+        webgl_.appName = GetStringMember(jwebgl, "app_name");
+        webgl_.package = GetStringMember(jwebgl, "package");
+        webgl_.companyName = GetStringMember(jwebgl, "company_name");
+        webgl_.productName = GetStringMember(jwebgl, "product_name");
+    }
+    // END WebGL ------
+
+}
+
+void BuildSettings::Save(rapidjson::PrettyWriter<rapidjson::FileStream>& writer)
+{
+    writer.String("build_settings");
+    writer.StartObject();
+
+    writer.String("version");
+    writer.Int(1);
+
+    // ANDROID ------
+    writer.String("android");
+    writer.StartObject();
+
+    writer.String("app_name");
+    writer.String(android_.appName.CString());
+    writer.String("package");
+    writer.String(android_.package.CString());
+    writer.String("target_sdk_version");
+    writer.String(android_.targetSDKVersion.CString());
+    writer.String("min_sdk_version");
+    writer.String(android_.minSDKVersion.CString());
+    writer.String("activity_name");
+    writer.String(android_.activityName.CString());
+    writer.String("company_name");
+    writer.String(android_.companyName.CString());
+    writer.String("product_name");
+    writer.String(android_.productName.CString());
+
+    writer.EndObject();
+    // END ANDROID ------
+
+    // IOS ------
+    writer.String("ios");
+    writer.StartObject();
+
+    writer.String("app_name");
+    writer.String(ios_.appName.CString());
+    writer.String("package");
+    writer.String(ios_.package.CString());
+    writer.String("provision_file");
+    writer.String(ios_.provisionFile.CString());
+    writer.String("company_name");
+    writer.String(ios_.companyName.CString());
+    writer.String("product_name");
+    writer.String(ios_.productName.CString());
+    writer.String("appid_prefix");
+    writer.String(ios_.appidPrefix.CString());
+
+    writer.EndObject();
+    // END IOS ------
+
+    // Mac ------
+    writer.String("mac");
+    writer.StartObject();
+
+    writer.String("app_name");
+    writer.String(mac_.appName.CString());
+    writer.String("package");
+    writer.String(mac_.package.CString());
+    writer.String("company_name");
+    writer.String(mac_.companyName.CString());
+    writer.String("product_name");
+    writer.String(mac_.productName.CString());
+
+    writer.EndObject();
+    // END Mac ------
+
+    // Windows ------
+    writer.String("windows");
+    writer.StartObject();
+
+    writer.String("app_name");
+    writer.String(windows_.appName.CString());
+    writer.String("package");
+    writer.String(windows_.package.CString());
+    writer.String("company_name");
+    writer.String(windows_.companyName.CString());
+    writer.String("product_name");
+    writer.String(windows_.productName.CString());
+
+    writer.EndObject();
+    // END Windows ------
+
+    // WebGL ------
+    writer.String("webgl");
+    writer.StartObject();
+
+    writer.String("app_name");
+    writer.String(webgl_.appName.CString());
+    writer.String("package");
+    writer.String(webgl_.package.CString());
+    writer.String("company_name");
+    writer.String(webgl_.companyName.CString());
+    writer.String("product_name");
+    writer.String(webgl_.productName.CString());
+
+    writer.EndObject();
+    // END WebGL ------
+
+
+    writer.EndObject();
+
+}
+
+}
+

+ 105 - 0
Source/ToolCore/Build/BuildSettings.h

@@ -0,0 +1,105 @@
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#pragma once
+
+#include <rapidjson/document.h>
+#include <rapidjson/filestream.h>
+#include <rapidjson/prettywriter.h>
+
+#include <Atomic/Container/Str.h>
+#include <Atomic/Core/Object.h>
+
+using namespace Atomic;
+
+namespace ToolCore
+{
+
+struct AndroidBuildSettings
+{
+    String appName;
+    String package;
+    String targetSDKVersion;
+    String minSDKVersion;
+    String activityName;
+    String companyName;
+    String productName;
+};
+
+struct IOSBuildSettings
+{
+    String appName;
+    String package;
+    String companyName;
+    String productName;
+    String provisionFile;
+    String appidPrefix;
+};
+
+struct MacBuildSettings
+{
+    String appName;
+    String package;
+    String companyName;
+    String productName;
+};
+
+struct WindowsBuildSettings
+{
+    String appName;
+    String package;
+    String companyName;
+    String productName;
+};
+
+struct WebGLSettings
+{
+    String appName;
+    String package;
+    String companyName;
+    String productName;
+};
+
+
+class BuildSettings : public Object
+{
+    OBJECT(BuildSettings);
+
+public:
+    /// Construct.
+    BuildSettings(Context* context);
+    /// Destruct.
+    virtual ~BuildSettings();
+
+    const AndroidBuildSettings& GetAndroidSettings() { return android_; }
+    void SetAndroidSettings(const AndroidBuildSettings& settings) { android_ = settings; }
+
+    const IOSBuildSettings& GetIOSSettings() { return ios_; }
+    void SetIOSSettings(const IOSBuildSettings& settings) { ios_ = settings; }
+
+    const MacBuildSettings& GetMacSettings() { return mac_; }
+    void SetMacSettings(const MacBuildSettings& settings) { mac_ = settings; }
+
+    const WindowsBuildSettings& GetWindowsSettings() { return windows_; }
+    void SetWindowsSettings(const WindowsBuildSettings& settings) { windows_ = settings; }
+
+    const WebGLSettings& GetWebGLSettings() { return webgl_; }
+    void SetWebGLSettings(const WebGLSettings& settings) { webgl_ = settings; }
+
+    void Load(rapidjson::Value::Member* jobject);
+    void Save(rapidjson::PrettyWriter<rapidjson::FileStream>& writer);
+
+private:
+
+    String GetStringMember(rapidjson::Value::Member* jobject, const String& name);
+
+    AndroidBuildSettings android_;
+    IOSBuildSettings ios_;
+    MacBuildSettings mac_;
+    WindowsBuildSettings windows_;
+    WebGLSettings webgl_;
+
+};
+
+}

+ 61 - 0
Source/ToolCore/Build/BuildSystem.cpp

@@ -0,0 +1,61 @@
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#include "AtomicEditor.h"
+#include <Atomic/Core/Context.h>
+#include <Atomic/Core/StringUtils.h>
+#include <Atomic/IO/FileSystem.h>
+#include <Atomic/IO/Log.h>
+
+#include "BuildSystem.h"
+#include "BuildWeb.h"
+
+
+namespace ToolCore
+{
+
+BuildSystem::BuildSystem(Context* context) :
+    Object(context)
+{
+    buildSettings_ = new BuildSettings(context);
+}
+
+BuildSystem::~BuildSystem()
+{
+
+}
+
+void BuildSystem::BuildComplete(PlatformID platform, const String &buildFolder, bool success, bool fail3D)
+{
+
+    if (success)
+        LOGINFOF("Build Success");
+    else
+        LOGINFOF("Build Failed");
+
+    currentBuild_ = 0;
+}
+
+
+void BuildSystem::DoBuildWeb(const String& buildPath)
+{
+    currentBuild_ = SharedPtr<BuildBase>(new BuildWeb(context_));
+    currentBuild_->Build(buildPath);
+
+}
+
+void BuildSystem::LoadBuildSettings(rapidjson::Value::Member* jobject)
+{
+    buildSettings_->Load(jobject);
+}
+
+void BuildSystem::SaveBuildSettings(rapidjson::PrettyWriter<rapidjson::FileStream>& writer)
+{
+    buildSettings_->Save(writer);
+}
+
+
+
+
+}

+ 48 - 0
Source/ToolCore/Build/BuildSystem.h

@@ -0,0 +1,48 @@
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#pragma once
+
+#include <Atomic/Core/Object.h>
+#include "BuildSettings.h"
+#include "BuildBase.h"
+
+#include "../Platform/Platform.h"
+
+using namespace Atomic;
+
+namespace ToolCore
+{
+
+class BuildSystem : public Object
+{
+    OBJECT(BuildSystem);
+
+public:
+    /// Construct.
+    BuildSystem(Context* context);
+    /// Destruct.
+    virtual ~BuildSystem();
+
+    BuildSettings* GetBuildSettings() { return buildSettings_; }
+
+    void LoadBuildSettings(rapidjson::Value::Member* jobject);
+    void SaveBuildSettings(rapidjson::PrettyWriter<rapidjson::FileStream>& writer);
+
+    void ClearBuildCompleteUI();
+    void BuildComplete(PlatformID platform, const String& buildFolder, bool success = true, bool fail3D = false);
+
+private:
+
+    void DoBuildWeb(const String& buildPath);
+
+    SharedPtr<BuildSettings> buildSettings_;
+    SharedPtr<BuildBase> currentBuild_;
+
+    void HandleEditorBuild(StringHash eventType, VariantMap& eventData);
+    void HandleEditorShutdown(StringHash eventType, VariantMap& eventData);
+};
+
+
+}

+ 40 - 0
Source/ToolCore/Build/BuildTypes.h

@@ -0,0 +1,40 @@
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#include <Atomic/Container/Str.h>
+
+#pragma once
+
+using namespace Atomic;
+
+namespace ToolCore
+{
+
+struct BuildResourceEntry
+{
+    // the resource directory this resource came from
+    String resourceDir_;
+
+    // the absolute path to the resource
+    String absolutePath_;
+
+    // the path within the package
+    String packagePath_;
+
+    // the offset in the package file
+    unsigned offset_;
+
+    // the size in the resource
+    unsigned size_;
+
+    // the checksum_
+    unsigned checksum_;
+
+    BuildResourceEntry()
+    {
+        offset_ = size_ = checksum_ = 0;
+    }
+};
+
+}

+ 94 - 0
Source/ToolCore/Build/BuildWeb.cpp

@@ -0,0 +1,94 @@
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#include <Atomic/IO/FileSystem.h>
+#include <Atomic/IO/File.h>
+
+#include "../ToolSystem.h"
+#include "../Project/Project.h"
+#include "BuildSystem.h"
+#include "BuildWeb.h"
+
+namespace ToolCore
+{
+
+BuildWeb::BuildWeb(Context * context) : BuildBase(context)
+{
+
+}
+
+BuildWeb::~BuildWeb()
+{
+
+}
+
+void BuildWeb::Initialize()
+{
+    ToolSystem* tools = GetSubsystem<ToolSystem>();
+    Project* project = tools->GetProject();
+
+    FileSystem* fileSystem = GetSubsystem<FileSystem>();
+    String bundleResources = fileSystem->GetAppBundleResourceFolder();
+
+    String projectResources = project->GetResourcePath();
+    String coreDataFolder = bundleResources + "CoreData/";
+
+    AddResourceDir(coreDataFolder);
+    AddResourceDir(projectResources);
+
+    BuildResourceEntries();
+}
+void BuildWeb::Build(const String& buildPath)
+{
+    buildPath_ = buildPath + "/Web-Build";
+
+    Initialize();
+
+    FileSystem* fileSystem = GetSubsystem<FileSystem>();
+    if (fileSystem->DirExists(buildPath_))
+        fileSystem->RemoveDir(buildPath_, true);
+
+    String buildSourceDir = fileSystem->GetAppBundleResourceFolder();
+    buildSourceDir += "Deployment/Web";
+
+    fileSystem->CreateDir(buildPath_);
+
+    String resourcePackagePath = buildPath_ + "/AtomicResources.data";
+    GenerateResourcePackage(resourcePackagePath);
+
+    fileSystem->Copy(buildSourceDir + "/AtomicPlayer.html", buildPath_ + "/AtomicPlayer.html");
+    fileSystem->Copy(buildSourceDir + "/AtomicPlayer.js", buildPath_ + "/AtomicPlayer.js");
+
+    File file(context_, buildSourceDir + "/AtomicResources_js.template", FILE_READ);
+    unsigned size = file.GetSize();
+
+    SharedArrayPtr<char> buffer(new char[size + 1]);
+    file.Read(buffer.Get(), size);
+    buffer[size] = '\0';
+
+    String resourcejs = (const char*) buffer.Get();
+
+    file.Close();
+
+    file.Open(buildPath_ + "/AtomicResources.data", FILE_READ);
+    int rsize = (int) file.GetSize();
+    file.Close();
+
+    String request;
+    request.AppendWithFormat("new DataRequest(0, %i, 0, 0).open('GET', '/AtomicResources.pak');", rsize);
+
+    resourcejs.Replace("$$ATOMIC_RESOURCES_DATA_REQUEST$$", request);
+
+    file.Open(buildPath_ + "/AtomicResources.js", FILE_WRITE);
+    file.Write(resourcejs.CString(), resourcejs.Length());
+    file.Close();
+
+    BuildSystem* buildSystem = GetSubsystem<BuildSystem>();
+    buildSystem->BuildComplete(PLATFORMID_WEB, buildPath_);
+
+    //fileSystem->SystemCommandAsync("/Applications/Firefox.app/Contents/MacOS/firefox");
+
+}
+
+}

+ 30 - 0
Source/ToolCore/Build/BuildWeb.h

@@ -0,0 +1,30 @@
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#pragma once
+
+#include "BuildBase.h"
+
+namespace ToolCore
+{
+
+class BuildWeb : public BuildBase
+{
+    OBJECT(BuildWeb);
+
+public:
+
+    BuildWeb(Context* context);
+    virtual ~BuildWeb();
+
+    void Build(const String& buildPath);
+
+protected:
+
+    void Initialize();
+
+
+};
+
+}

+ 219 - 0
Source/ToolCore/Build/ResourcePackager.cpp

@@ -0,0 +1,219 @@
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+//
+// Portions Copyright (c) 2008-2014 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include "AtomicEditor.h"
+
+#include "Atomic/Core/StringUtils.h"
+#include <Atomic/IO/FileSystem.h>
+#include <Atomic/Container/ArrayPtr.h>
+
+#include <LZ4/lz4.h>
+#include <LZ4/lz4hc.h>
+
+#include "BuildBase.h"
+#include "ResourcePackager.h"
+
+namespace ToolCore
+{
+
+ResourcePackager::ResourcePackager(Context* context, BuildBase* buildBase) : Object(context)
+  , buildBase_(buildBase)
+  , checksum_(0)
+{
+
+
+}
+
+ResourcePackager::~ResourcePackager()
+{
+
+}
+
+bool ResourcePackager::WritePackageFile(const String& destFilePath)
+{
+    buildBase_->BuildLog("Writing package");
+
+    SharedPtr<File> dest(new File(context_, destFilePath, FILE_WRITE));
+    if (!dest->IsOpen())
+    {
+        buildBase_->BuildError("Could not open output file " + destFilePath);
+        return false;
+    }
+
+    // Write ID, number of files & placeholder for checksum
+    WriteHeader(dest);
+
+    for (unsigned i = 0; i < resourceEntries_.Size(); i++)
+    {
+        BuildResourceEntry* entry = resourceEntries_[i];
+
+        // Write entry (correct offset is still unknown, will be filled in later)
+        dest->WriteString(entry->packagePath_);
+        dest->WriteUInt(entry->offset_);
+        dest->WriteUInt(entry->size_);
+        dest->WriteUInt(entry->checksum_);
+    }
+
+    unsigned totalDataSize = 0;
+
+    // Write file data, calculate checksums & correct offsets
+    for (unsigned i = 0; i < resourceEntries_.Size(); i++)
+    {
+        BuildResourceEntry* entry = resourceEntries_[i];
+
+        entry->offset_ = dest->GetSize();
+
+        File srcFile(context_, entry->absolutePath_);
+        if (!srcFile.IsOpen())
+        {
+            buildBase_->BuildError("Could not open input file " + entry->absolutePath_);
+            return false;
+        }
+
+        unsigned dataSize = entry->size_;
+        totalDataSize += dataSize;
+        SharedArrayPtr<unsigned char> buffer(new unsigned char[dataSize]);
+
+        if (srcFile.Read(&buffer[0], dataSize) != dataSize)
+        {
+            buildBase_->BuildError("Could not read input file " + entry->absolutePath_);
+            return false;
+        }
+
+        srcFile.Close();
+
+        for (unsigned j = 0; j < dataSize; ++j)
+        {
+            checksum_ = SDBMHash(checksum_, buffer[j]);
+            entry->checksum_ = SDBMHash(entry->checksum_, buffer[j]);
+        }
+
+        // might not want to compress for WebGL
+        //if (!compress_)
+        //{
+        //    PrintLine(entries_[i].name_ + " size " + String(dataSize));
+        //    dest.Write(&buffer[0], entries_[i].size_);
+        //}
+        //else
+        //{
+
+        unsigned compressedBlockSize_ = 32768;
+
+        SharedArrayPtr<unsigned char> compressBuffer(new unsigned char[LZ4_compressBound(compressedBlockSize_)]);
+
+        unsigned pos = 0;
+        unsigned totalPackedBytes = 0;
+
+        while (pos < dataSize)
+        {
+            unsigned unpackedSize = compressedBlockSize_;
+            if (pos + unpackedSize > dataSize)
+                unpackedSize = dataSize - pos;
+
+            unsigned packedSize = LZ4_compressHC((const char*)&buffer[pos], (char*)compressBuffer.Get(), unpackedSize);
+            if (!packedSize)
+            {
+                buildBase_->BuildError("LZ4 compression failed for file " + entry->absolutePath_ + " at offset " + pos);
+                return false;
+            }
+
+            dest->WriteUShort(unpackedSize);
+            dest->WriteUShort(packedSize);
+            dest->Write(compressBuffer.Get(), packedSize);
+            totalPackedBytes += 6 + packedSize;
+
+            pos += unpackedSize;
+        }
+
+        buildBase_->BuildLog(entry->absolutePath_ + " in " + String(dataSize) + " out " + String(totalPackedBytes));
+        }
+    //}
+
+    // Write package size to the end of file to allow finding it linked to an executable file
+    unsigned currentSize = dest->GetSize();
+    dest->WriteUInt(currentSize + sizeof(unsigned));
+
+    // Write header again with correct offsets & checksums
+    dest->Seek(0);
+    WriteHeader(dest);
+
+    for (unsigned i = 0; i < resourceEntries_.Size(); i++)
+    {
+        BuildResourceEntry* entry = resourceEntries_[i];
+
+        dest->WriteString(entry->packagePath_);
+        dest->WriteUInt(entry->offset_);
+        dest->WriteUInt(entry->size_);
+        dest->WriteUInt(entry->checksum_);
+    }
+
+    buildBase_->BuildLog("Number of files " + String(resourceEntries_.Size()));
+    buildBase_->BuildLog("File data size " + String(totalDataSize));
+    buildBase_->BuildLog("Package size " + String(dest->GetSize()));
+
+    return true;
+}
+
+void ResourcePackager::WriteHeader(File* dest)
+{
+    dest->WriteFileID("ULZ4");
+    dest->WriteUInt(resourceEntries_.Size());
+    dest->WriteUInt(checksum_);
+}
+
+
+void ResourcePackager::GeneratePackage(const String& destFilePath)
+{
+    for (unsigned i = 0; i < resourceEntries_.Size(); i++)
+    {
+        BuildResourceEntry* entry = resourceEntries_[i];
+
+        File file(context_);
+
+        if (!file.Open(entry->absolutePath_))
+        {
+            buildBase_->BuildError(Atomic::ToString("Could not open resource file %s", entry->absolutePath_.CString()));
+            return;
+        }
+
+        if (!file.GetSize())
+        {
+            return;
+        }
+
+        entry->size_ = file.GetSize();
+    }
+
+    WritePackageFile(destFilePath);
+
+}
+
+void ResourcePackager::AddResourceEntry(BuildResourceEntry* entry)
+{
+    resourceEntries_.Push(entry);
+}
+
+}

+ 46 - 0
Source/ToolCore/Build/ResourcePackager.h

@@ -0,0 +1,46 @@
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#pragma once
+
+#include <Atomic/Core/Object.h>
+#include "Atomic/Container/Vector.h"
+#include <Atomic/IO/File.h>
+
+#include "BuildTypes.h"
+
+using namespace Atomic;
+
+namespace ToolCore
+{
+
+class BuildBase;
+
+class ResourcePackager : public Object
+{
+    OBJECT(ResourcePackager);
+
+public:
+
+    ResourcePackager(Context* context, BuildBase* buildBase);
+    virtual ~ResourcePackager();
+
+    void AddResourceEntry(BuildResourceEntry* entry);
+
+    void GeneratePackage(const String& destFilePath);
+
+private:
+
+    void WriteHeader(File* dest);
+    bool WritePackageFile(const String& destFilePath);
+
+    PODVector<BuildResourceEntry*> resourceEntries_;
+
+    WeakPtr<BuildBase> buildBase_;
+
+    unsigned checksum_;
+
+};
+
+}

+ 154 - 0
Source/ToolCore/Subprocess/Subprocess.cpp

@@ -0,0 +1,154 @@
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#include <Atomic/IO/Log.h>
+#include <Atomic/Core/Timer.h>
+
+#include "Subprocess.h"
+#include "SubprocessSystem.h"
+
+namespace ToolCore
+{
+
+Subprocess::Subprocess(Context* context) :
+    Object(context),
+    handle_(NULL),
+    inStream_(pipeOut_),
+    errorStream_(pipeError_),
+    returnCode_(-1)
+{
+
+}
+
+Subprocess::~Subprocess()
+{
+    if (handle_)
+        delete handle_;
+}
+
+
+void Subprocess::ThreadFunction()
+{
+    while (shouldRun_)
+    {
+        int read;
+        char data[129];
+
+        if (!Poco::Process::isRunning(*handle_))
+        {
+            shouldRun_ = false;
+            break;
+        }
+
+        inStream_.read(data, 128);
+        read = inStream_.gcount();
+        data[read] = 0;
+
+        if (read)
+        {
+            mutex_.Acquire();
+            output_ += (const char*) data;
+            mutex_.Release();
+
+        }
+
+        if (inStream_.eof())
+        {
+            returnCode_ = handle_->wait();
+
+            if (returnCode_)
+            {
+                while (true)
+                {
+                    errorStream_.read(data, 128);
+                    read = errorStream_.gcount();
+                    data[read] = 0;
+
+                    if (read)
+                    {
+                        mutex_.Acquire();
+                        errorOutput_ += (const char*) data;
+                        mutex_.Release();
+                    }
+                    else
+                    {
+                        break;
+                    }
+                }
+            }
+
+            shouldRun_ = false;
+            break;
+        }
+
+    }
+}
+
+void Subprocess::ProcessOutput(SubprocessSystem* system)
+{
+    String output, errorOutput;
+
+    GetOutput(output, errorOutput);
+    if (output.Length())
+    {
+        VariantMap eventData;        
+        eventData[SubprocessOutput::P_TEXT] = output;
+        SendEvent(E_SUBPROCESSOUTPUT, eventData);
+    }
+
+    if (errorOutput.Length())
+    {
+        VariantMap eventData;
+        eventData[SubprocessOutput::P_TEXT] = errorOutput;
+        SendEvent(E_SUBPROCESSOUTPUT, eventData);
+    }
+
+}
+
+bool Subprocess::Update(SubprocessSystem* system)
+{
+    ProcessOutput(system);
+
+    if (!shouldRun_) {
+
+        ProcessOutput(system);
+
+        VariantMap eventData;
+        eventData[SubprocessComplete::P_RETCODE] = returnCode_;
+        SendEvent(E_SUBPROCESSCOMPLETE, eventData);
+        return false;
+    }
+
+
+    return true;
+
+}
+
+bool Subprocess::Launch(const String& command, const Vector<String>& args, const String& initialDirectory)
+{
+    Poco::Process::Env env;
+    return Launch(command, args, initialDirectory, env);
+}
+
+bool Subprocess::Launch(const String& command, const Vector<String>& args, const String& initialDirectory, const Poco::Process::Env& env)
+{
+    Poco::Process::Args pargs;
+
+    for (unsigned i = 0; i < args.Size(); i++)
+        pargs.push_back(args[i].CString());
+
+    std::string pcommand = command.CString();
+    std::string pinitialDirectory = initialDirectory.CString();
+
+    // this can take an ENV as well, may come in useful
+    handle_ = new Poco::ProcessHandle(Poco::Process::launch(pcommand, pargs, pinitialDirectory, &pipeIn_, &pipeOut_, &pipeError_, env));
+
+    if (!Poco::Process::isRunning(*handle_))
+        return false;
+
+    return Run();
+
+}
+
+}

+ 69 - 0
Source/ToolCore/Subprocess/Subprocess.h

@@ -0,0 +1,69 @@
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#pragma once
+
+#include <Poco/Process.h>
+#include <Poco/Pipe.h>
+#include <Poco/PipeStream.h>
+
+#include <Atomic/Container/Str.h>
+#include <Atomic/Core/Mutex.h>
+#include <Atomic/Core/Thread.h>
+#include <Atomic/Core/Object.h>
+
+using namespace Atomic;
+
+namespace ToolCore
+{
+
+class SubprocessSystem;
+
+class Subprocess : public Object, public Thread
+{
+    friend class SubprocessSystem;
+
+    OBJECT(Subprocess);
+
+public:
+    /// Construct.
+    Subprocess(Context* context);
+    /// Destruct.
+    ~Subprocess();
+
+    void GetOutput(String& outputText, String& errorText)
+    {
+        mutex_.Acquire();
+        outputText = output_;
+        output_.Clear();
+        errorText = errorOutput_;
+        errorOutput_.Clear();
+        mutex_.Release(); }
+
+private:
+
+    void ThreadFunction();
+
+    mutable Mutex mutex_;
+
+    void ProcessOutput(SubprocessSystem* system);
+    bool Update(SubprocessSystem* system);
+
+
+    bool Launch(const String& command, const Vector<String>& args, const String& initialDirectory = "");
+    bool Launch(const String& command, const Vector<String>& args, const String& initialDirectory, const Poco::Process::Env& env);
+
+    String output_;
+    String errorOutput_;
+    int returnCode_;
+
+    Poco::ProcessHandle* handle_;
+    Poco::Pipe pipeIn_;
+    Poco::Pipe pipeOut_;
+    Poco::Pipe pipeError_;
+    Poco::PipeInputStream inStream_;
+    Poco::PipeInputStream errorStream_;
+};
+
+}

+ 73 - 0
Source/ToolCore/Subprocess/SubprocessSystem.cpp

@@ -0,0 +1,73 @@
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#include <Atomic/Core/CoreEvents.h>
+#include <Atomic/Core/Context.h>
+#include <Atomic/IO/FileSystem.h>
+
+#include "SubprocessSystem.h"
+
+namespace ToolCore
+{
+
+SubprocessSystem::SubprocessSystem(Context* context) :
+    Object(context),
+    updateTimer_(0.0f)
+{
+    SubscribeToEvent(E_UPDATE, HANDLER(SubprocessSystem, HandleUpdate));    
+}
+
+SubprocessSystem::~SubprocessSystem()
+{
+    for (unsigned i = 0; i < processes_.Size(); i++)
+        processes_[i]->Stop();
+
+    processes_.Clear();
+
+}
+
+Subprocess* SubprocessSystem::Launch(const String& command, const Vector<String>& args, const String& initialDirectory)
+{
+    Poco::Process::Env env;
+    return Launch(command, args, initialDirectory, env);
+}
+
+Subprocess* SubprocessSystem::Launch(const String& command, const Vector<String>& args, const String& initialDirectory, const Poco::Process::Env& env)
+{
+    SharedPtr<Subprocess> process(new Subprocess(context_));
+
+    if (process->Launch(GetNativePath(command), args, GetNativePath(initialDirectory), env))
+    {
+        processes_.Push(process);
+        return process;
+    }
+
+    return 0;
+
+}
+
+
+void SubprocessSystem::HandleUpdate(StringHash eventType, VariantMap& eventData)
+{
+    Vector<Subprocess*> remove;
+
+    for (unsigned i = 0; i < processes_.Size(); i++)
+    {
+        Subprocess* process = processes_[i];
+        if (!process->Update(this))
+        {
+            remove.Push(process);
+        }
+    }
+
+    for (unsigned i = 0; i < remove.Size(); i++)
+    {
+        processes_.Remove(SharedPtr<Subprocess>(remove[i]));
+    }
+
+}
+
+
+}
+

+ 50 - 0
Source/ToolCore/Subprocess/SubprocessSystem.h

@@ -0,0 +1,50 @@
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#pragma once
+
+#include <Atomic/Core/Object.h>
+
+#include "Subprocess.h"
+
+using namespace Atomic;
+
+namespace ToolCore
+{
+
+EVENT(E_SUBPROCESSOUTPUT, SubprocessOutput)
+{
+    PARAM(P_TEXT, Text);      // string
+}
+
+EVENT(E_SUBPROCESSCOMPLETE, SubprocessComplete)
+{
+    PARAM(P_PROCESSKEY, ProcessKey); // unsigned
+    PARAM(P_RETCODE, RetCode);      // int (return code of process)
+}
+
+class SubprocessSystem : public Object
+{
+    OBJECT(SubprocessSystem);
+
+public:
+    /// Construct.
+    SubprocessSystem(Context* context);
+    /// Destruct.
+    virtual ~SubprocessSystem();
+
+
+    Subprocess* Launch(const String& command, const Vector<String>& args, const String& initialDirectory = "");
+    Subprocess* Launch(const String& command, const Vector<String>& args, const String& initialDirectory, const Poco::Process::Env& env);
+
+private:
+
+    void HandleUpdate(StringHash eventType, VariantMap& eventData);
+
+    Vector<SharedPtr<Subprocess> > processes_;
+    float updateTimer_;
+
+};
+
+}

+ 25 - 1
Source/ToolCore/ToolSystem.cpp

@@ -1,11 +1,16 @@
 
 #include <Atomic/Core/Context.h>
+#include <Atomic/IO/FileSystem.h>
+#include <Atomic/Resource/ResourceCache.h>
 
 #include "Platform/PlatformWeb.h"
 #include "Net/CurlManager.h"
 #include "License/LicenseSystem.h"
+#include "Build/BuildSystem.h"
 #include "ToolSystem.h"
 
+#include "Project/Project.h"
+
 
 namespace ToolCore
 {
@@ -15,7 +20,7 @@ ToolSystem::ToolSystem(Context* context) : Object(context)
 
     context_->RegisterSubsystem(new CurlManager(context_));
     context_->RegisterSubsystem(new LicenseSystem(context_));
-
+    context_->RegisterSubsystem(new BuildSystem(context_));
 
     // platform registration
     RegisterPlatform(new PlatformWeb(context));
@@ -26,6 +31,25 @@ ToolSystem::~ToolSystem()
 
 }
 
+bool ToolSystem::LoadProject(const String& fullpath)
+{
+    String path = GetPath(fullpath);
+
+    ResourceCache* cache = GetSubsystem<ResourceCache>();
+    cache->AddResourceDir(path, 0);
+
+    String resourcePath = path;
+    resourcePath += "Resources";
+    cache->AddResourceDir(resourcePath, 0);
+
+    project_ = new Project(context_);
+    project_->SetResourcePath(resourcePath);
+    project_->Load(fullpath);
+
+    return true;
+
+}
+
 void ToolSystem::SetCurrentPlatform(PlatformID platform)
 {
     if (!platforms_.Contains((unsigned) platform))

+ 7 - 1
Source/ToolCore/ToolSystem.h

@@ -11,6 +11,7 @@ namespace ToolCore
 {
 
 class Platform;
+class Project;
 
 class ToolSystem : public Object
 {
@@ -21,8 +22,11 @@ public:
     ToolSystem(Context* context);
     virtual ~ToolSystem();
 
-    void RegisterPlatform(Platform* platform);
+    bool LoadProject(const String& fullpath);
+    Project* GetProject() { return project_; }
 
+    // Platforms
+    void RegisterPlatform(Platform* platform);
     void SetCurrentPlatform(PlatformID platform);
     PlatformID GetCurrentPlatform();
 
@@ -33,6 +37,8 @@ private:
     // PlatformID -> platform
     HashMap<unsigned, SharedPtr<Platform> > platforms_;
 
+    SharedPtr<Project> project_;
+
 };
 
 }