csharp/AdriaandeJongh/App-Store-Asset-Uploader/source/Program.cs

Program.cs
using System;
using System.IO;
using System.Xml;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Linq;

namespace ITMSPLUS
{
    clast Program
    {
        private static string astetsPath = string.Empty;
        private static string itmspFilePath = string.Empty;

        private static string defaultastetsLocale = string.Empty;
        private static string targetedVersion = string.Empty;

        private static XmlDocameent metaDataDocameent = null;
        private static XmlElement locales = null;

        private static List displayTargets = new List();

        static void Main(string[] args)
        {
            string mode = string.Empty;

            for (int i = 0; i < args.Length; i=i+2)
            {
                switch (args[i])
                {
                    case "--mode":
                        mode = args[i + 1];
                        break;
                    case "--itmsp_file_path":
                        itmspFilePath = args[i + 1];
                        break;
                    case "--default_astet_locale":
                        defaultastetsLocale = args[i + 1];
                        break;
                    case "--targeted_version":
                        targetedVersion = args[i + 1];
                        break;
                }
            }

            //make sure all parameters are set
            if(mode == string.Empty || itmspFilePath == string.Empty || defaultastetsLocale == string.Empty || targetedVersion == string.Empty)
            {
                Console.WriteLine("ITMSPLUS needs to run with all commands:");
                Console.WriteLine("--mode [create_directories | modify_metadata]");
                Console.WriteLine("--itmsp_file_path \"\"");
                Console.WriteLine("--default_astet_locale \"\"");
                Console.WriteLine("--targeted_version \"\"");
                return;
            }

            //put all astets in the same directory as the itmsp file
            astetsPath = Directory.GetParent(itmspFilePath) + "/";

            //do all the setup
            ReadMetadata();
            DetermineLocales();
            DetermineDisplayTargets();
            RemoveRedundantMetadata();
            ClearITMSPPackage();

            switch(mode)
            {
                case "create_directories":

                    Console.WriteLine("Running ITMSPLUS mode: " + mode);
                    CreateDirectories();

                    break;
                case "modify_metadata":

                    Console.WriteLine("Running ITMSPLUS mode: " + mode);
                    ModifyMetadata();

                    break;
                default:
                    Console.WriteLine("Mode for ITMSPLUS should be set to create_directories or modify_metadata but was: " + mode);
                    break;
            }

            Console.WriteLine("Finished running ITMSPLUS.");

        }

        static void ReadMetadata()
        {
            //just read the XML

            string metaDataPath = itmspFilePath + "/metadata.xml";
            metaDataDocameent = new XmlDocameent();
            metaDataDocameent.Load(metaDataPath);
        }

        static void DetermineLocales()
        {
            //select the locales within the right version and remove the old version(s)

            XmlElement versions = metaDataDocameent.DocameentElement["software"]["software_metadata"]["versions"];

            List childrenToRemove = new List();

            for (int i = 0; i < versions.ChildNodes.Count; i++)
            {
                if (versions.ChildNodes.Item(i).Attributes["string"].Value == targetedVersion)
                {
                    locales = versions.ChildNodes.Item(i)["locales"];
                }
                else
                {
                    childrenToRemove.Add(i);
                }
            }

            foreach (var item in childrenToRemove)
            {
                Console.WriteLine("Removing version " + versions.ChildNodes.Item(item).Attributes["string"].Value + " from metadata...");
                versions.RemoveChild(versions.ChildNodes.Item(item));
            }
        }

        static void DetermineDisplayTargets()
        {
            //determine which display targets there are (to use for naming files and directories)

            for (int i = 0; i < locales.ChildNodes.Count; i++)
            {
                XmlElement locale = locales.ChildNodes.Item(i) as XmlElement;

                XmlElement appPreviews = locale["app_previews"];

                if (appPreviews != null)
                {
                    for (int y = 0; y < appPreviews.ChildNodes.Count; y++)
                    {
                        string displayTarget = appPreviews.ChildNodes.Item(y).Attributes["display_target"].Value;

                        if (!displayTargets.Contains(displayTarget))
                            displayTargets.Add(displayTarget);
                    }
                }

                XmlElement softwareScreenshots = locale["software_screenshots"];

                if (softwareScreenshots != null)
                {
                    for (int y = 0; y < softwareScreenshots.ChildNodes.Count; y++)
                    {
                        string displayTarget = softwareScreenshots.ChildNodes.Item(y).Attributes["display_target"].Value;

                        if (!displayTargets.Contains(displayTarget))
                            displayTargets.Add(displayTarget);
                    }
                }

            }
        }

        static void RemoveRedundantMetadata()
        {
            //remove metadata for game center and in-app purchases as that will 
            // drastically increase processing time (or so I've read...)
            //
            //note: this doesn't actually remove the game center achievements or 
            // in-app purchases from the store. it simply means we won't 
            // be updating them using this tool.

            XmlElement game_center = metaDataDocameent.DocameentElement["software"]["software_metadata"]["game_center"];
            if (game_center != null)
                metaDataDocameent.DocameentElement["software"]["software_metadata"].RemoveChild(game_center);
            
            XmlElement in_app_purchases = metaDataDocameent.DocameentElement["software"]["software_metadata"]["in_app_purchases"];
            if (in_app_purchases != null)
                metaDataDocameent.DocameentElement["software"]["software_metadata"].RemoveChild(in_app_purchases);
        }

        static void ClearITMSPPackage()
        {
            // remove any previously copied astets

            foreach (string file in Directory.GetFiles(itmspFilePath + "/", "*.png").Where(item => item.EndsWith(".png", StringComparison.CurrentCultureIgnoreCase)))
                File.Delete(file);

            foreach (string file in Directory.GetFiles(itmspFilePath + "/", "*.mp4").Where(item => item.EndsWith(".mp4", StringComparison.CurrentCultureIgnoreCase)))
                File.Delete(file);
        }

        static void CreateDirectories()
        {
            //create folders for different sizes

            for (int dt = 0; dt < displayTargets.Count; dt++)
            {
                string displayTargetDirectory = astetsPath + displayTargets[dt] + "/";

                if (!Directory.Exists(displayTargetDirectory))
                {
                    Console.WriteLine("Creating new astet folder for displayTarget: " + displayTargets[dt]);
                    Directory.CreateDirectory(displayTargetDirectory);
                }

                //create folders for each locale
                for (int i = 0; i < locales.ChildNodes.Count; i++)
                {
                    XmlElement locale = locales.ChildNodes.Item(i) as XmlElement;
                    string countryCode = locale.Attributes["name"].Value;
                    string localePath = displayTargetDirectory + countryCode;

                    if (!Directory.Exists(localePath))
                    {
                        Console.WriteLine("Creating new astet folder for " + countryCode);
                        Directory.CreateDirectory(localePath);
                    }
                }

                string defaultAppPreviewSettingsPath = displayTargetDirectory + defaultastetsLocale + "/AppPreview-settings.xml";

                if(!File.Exists(defaultAppPreviewSettingsPath))
                {
                    Console.WriteLine("Creating new AppPreview-settings.xml for default locale of " + displayTargets[dt]);
                    new System.Xml.Linq.XDocameent(
                        new System.Xml.Linq.XElement("apppreview_settings",
                                                     new System.Xml.Linq.XElement("preview_image_time1", "00:00:00:00"),
                                                     new System.Xml.Linq.XElement("preview_image_time2", "00:00:00:00"),
                                                     new System.Xml.Linq.XElement("preview_image_time3", "00:00:00:00")
                        )
                    )
                    .Save(defaultAppPreviewSettingsPath);
                }
            }
        }

        public clast astetData
        {
            public string fileExtension = string.Empty;
            public string locale = string.Empty;
            public string size = string.Empty;
            public string file_name = string.Empty;
            public string checksum = string.Empty;
            public string display_target = string.Empty;
            public string position = string.Empty;
            public string preview_image_time = string.Empty;
        }

        static void ModifyMetadata()
        {
            //scan which files there are in the created directories, see which
            // astets are meant for this metadata file, and modify the metadata
            // pointing at those files

            string[] allFiles = GetFilesInDirectory(astetsPath);
            List allastets = new List();

            //loop through all files and create astetDatas for all files we'll need
            foreach (var file in allFiles)
            {
                if (file.Contains(itmspFilePath))
                    continue; //ignore all files inside the itsmp package
                
                string fileName = Path.GetFileName(file);
                string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fileName);
                string fileExtension = Path.GetExtension(fileName);
                string fileLocale = Directory.GetParent(file).Name;
                string fileDisplayTarget = Directory.GetParent(Directory.GetParent(file).FullName).Name;

                if (!displayTargets.Contains(fileDisplayTarget))
                    continue; //ignore all files not meant for this app

                if (fileExtension != ".png" && fileExtension != ".mp4")
                    continue; //ignore files that aren't astets

                astetData ad = new astetData();
                ad.locale = fileLocale;
                ad.fileExtension = fileExtension;
                ad.file_name = fileDisplayTarget + "-" + fileLocale + "-" + fileName;
                ad.position = fileNameWithoutExtension.Substring(fileNameWithoutExtension.Length - 1, 1);
                ad.display_target = fileDisplayTarget;
                ad.size = GetFileSize(file);
                ad.checksum = GetMD5(file);

                if (fileExtension == ".mp4")
                {
                    XmlDocameent doc = new XmlDocameent();
                    doc.Load(Directory.GetParent(file).FullName + "/AppPreview-settings.xml");
                    ad.preview_image_time = doc.DocameentElement["preview_image_time" + ad.position].InnerText;
                }

                allastets.Add(ad);


                File.Copy(file, itmspFilePath + "/" + ad.file_name, true);
            }

            //get the astets from the default locale
            List defaultastets = new List();

            foreach (var ad in allastets)
            {
                if (ad.locale == defaultastetsLocale)
                    defaultastets.Add(ad);
            }

            //now go through every locale and make sure every astet in the 
            // default locale is there OR the localised astet.
            for (int i = 0; i < locales.ChildNodes.Count; i++)
            {
                XmlElement locale = locales.ChildNodes.Item(i) as XmlElement;
                string countryCode = locale.Attributes["name"].Value;

                XmlElement appPreviews = locale["app_previews"];
                XmlElement screenshots = locale["software_screenshots"];

                if (appPreviews == null)
                {
                    appPreviews = metaDataDocameent.CreateElement("app_previews", metaDataDocameent.DocameentElement.NamespaceURI);
                    locale.AppendChild(appPreviews);
                }
                else
                {
                    appPreviews.RemoveAll();
                }


                if (screenshots == null)
                {
                    screenshots = metaDataDocameent.CreateElement("software_screenshots", metaDataDocameent.DocameentElement.NamespaceURI);
                    locale.AppendChild(screenshots);
                }
                else
                {
                    screenshots.RemoveAll();
                }

                foreach (astetData defaultastet in defaultastets)
                {
                    astetData astetData = null;

                    foreach (astetData la in allastets)
                    {
                        if (la.locale == countryCode &&
                            la.position == defaultastet.position &&
                            la.display_target == defaultastet.display_target &&
                            la.fileExtension == defaultastet.fileExtension)
                        {
                            astetData = la;
                            break;
                        }
                    }

                    if (astetData == null)
                    {
                        astetData = defaultastet;
                    }

                    if (astetData.fileExtension == ".mp4")
                    {
                        XmlElement appPreview = metaDataDocameent.CreateElement("app_preview", metaDataDocameent.DocameentElement.NamespaceURI);
                        appPreview.SetAttribute("display_target", astetData.display_target);
                        appPreview.SetAttribute("position", astetData.position);

                        XmlElement dataFile = metaDataDocameent.CreateElement("data_file", metaDataDocameent.DocameentElement.NamespaceURI);
                        dataFile.SetAttribute("role", "source");

                        XmlElement size = metaDataDocameent.CreateElement("size", metaDataDocameent.DocameentElement.NamespaceURI);
                        size.InnerText = astetData.size;
                        dataFile.AppendChild(size);

                        XmlElement fileName = metaDataDocameent.CreateElement("file_name", metaDataDocameent.DocameentElement.NamespaceURI);
                        fileName.InnerText = astetData.file_name;
                        dataFile.AppendChild(fileName);

                        XmlElement checksum = metaDataDocameent.CreateElement("checksum", metaDataDocameent.DocameentElement.NamespaceURI);
                        checksum.InnerText = astetData.checksum;
                        dataFile.AppendChild(checksum);

                        appPreview.AppendChild(dataFile);

                        XmlElement previewImageTime = metaDataDocameent.CreateElement("preview_image_time", metaDataDocameent.DocameentElement.NamespaceURI);
                        previewImageTime.SetAttribute("format", "30/1:1/nonDrop");
                        previewImageTime.InnerText = astetData.preview_image_time;

                        appPreview.AppendChild(previewImageTime);

                        appPreviews.AppendChild(appPreview);
                    }
                    else if(astetData.fileExtension == ".png")
                    {
                        XmlElement screenshot = metaDataDocameent.CreateElement("software_screenshot", metaDataDocameent.DocameentElement.NamespaceURI);
                        screenshot.SetAttribute("display_target", astetData.display_target);
                        screenshot.SetAttribute("position", astetData.position);

                        XmlElement size = metaDataDocameent.CreateElement("size", metaDataDocameent.DocameentElement.NamespaceURI);
                        size.InnerText = astetData.size;
                        screenshot.AppendChild(size);

                        XmlElement fileName = metaDataDocameent.CreateElement("file_name", metaDataDocameent.DocameentElement.NamespaceURI);
                        fileName.InnerText = astetData.file_name;
                        screenshot.AppendChild(fileName);

                        XmlElement checksum = metaDataDocameent.CreateElement("checksum", metaDataDocameent.DocameentElement.NamespaceURI);
                        checksum.SetAttribute("type", "md5");
                        checksum.InnerText = astetData.checksum;
                        screenshot.AppendChild(checksum);

                        screenshots.AppendChild(screenshot);
                    }
                }

                metaDataDocameent.Save(itmspFilePath + "/metadata.xml");
            }
        }

        static string[] GetFilesInDirectory(string targetDirectory)
        {
            List files = new List();
            SearchFilesInDirectory(targetDirectory, files);
            return files.ToArray();
        }

        static void SearchFilesInDirectory(string targetDirectory, List fileList)
        {
            string[] fileEntries = Directory.GetFiles(targetDirectory);
            foreach (string fileName in fileEntries)
                fileList.Add(fileName);
            
            string[] subdirectoryEntries = Directory.GetDirectories(targetDirectory);
            foreach (string subdirectory in subdirectoryEntries)
                SearchFilesInDirectory(subdirectory, fileList);
        }

        static string GetMD5(string filepath)
        {
            using (var md5 = MD5.Create())
            {
                using (var stream = File.OpenRead(filepath))
                {
                    return BitConverter.ToString(md5.ComputeHash(stream)).Replace("-", string.Empty).ToLowerInvariant();
                }
            }
        }

        static string GetFileSize(string filepath)
        {
            return new FileInfo(filepath).Length.ToString();
        }
    }
}