Search Unity

Unzipping ZIP files containing .app files in OSX (Manually vs Programmatically/C#)

Discussion in 'General Discussion' started by DavidxK, Feb 1, 2015.

  1. DavidxK

    DavidxK

    Joined:
    May 5, 2014
    Posts:
    8
    Hey everyone! My name is David and I hope that you are having a great day!

    Before I move on to why I started this thread, I did already attempt to google for the answer to my situation (several times) but to no avail.

    I'm currently in the midst of creating a self-updating tool for my game. It basically downloads the latest version of the game in a .zip file, unzips it and launches the game.

    I have the standalone working in Windows but on Mac (OSX), the unzipping of the game's .app file somehow corrupts it (i.e it crashes right upon you starting the game/.app file)

    So to find out why that is happening, I dived into the package contents. On Mac OSX, you can right click on an .app file and click on "Show Package Contents" to see what's inside.

    So from diving into Contents->MacOS, I found out that the binary in this folder is different depending on how the unzipping was done.

    So if I use the self-updating tool (still a work-in-progress) to download the zip and unzip it automatically, the binary in Contents->MacOS shows this when quick-looked at:



    Which is wrong :(

    If I just unzip it manually myself, it comes out alright like this:



    I'm assuming that the unzipping via C# is somehow not identifying it as a binary so I am trying to resolve that.

    By the way, this is the script that I am using for the unzipping part (works for Windows but I have changed the strings just for testing with OSX). I am using SharpZipLib (a 3rd party ZIP library to do the unzipping)

    Code (CSharp):
    1.          
    2. using ICSharpCode.SharpZipLib.Core;
    3. using ICSharpCode.SharpZipLib.Zip;
    4. using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
    5.  
    6. string docPath = "game-osx.zip";
    7. string programNameToRun = "game-demo.app";
    8.  
    9. ZipFile zf = null;
    10.  
    11. try {
    12.  
    13.          FileStream fs = File.OpenRead(docPath);
    14.          zf = new ZipFile(fs);
    15.  
    16.          foreach (ZipEntry zipEntry in zf) {
    17.  
    18.                 if (!zipEntry.IsFile) {
    19.                         continue;           // Ignore directories
    20.                 }
    21.  
    22.                 string entryFileName = zipEntry.Name;
    23.                
    24.                 byte[] buffer = new byte[4096];     // 4K is optimum
    25.                 Stream zipStream = zf.GetInputStream(zipEntry);
    26.                
    27.                 string fullZipToPath = Path.Combine("", entryFileName);
    28.                 string directoryName = Path.GetDirectoryName(fullZipToPath);
    29.                 if (directoryName.Length > 0) {
    30.                         Directory.CreateDirectory(directoryName);
    31.                 }
    32.                
    33.                 using (FileStream streamWriter = File.Create(fullZipToPath)) {
    34.                         StreamUtils.Copy(zipStream, streamWriter, buffer);
    35.                 }
    36.         }
    37.  
    38. } finally {
    39.  
    40.         if (zf != null) {
    41.  
    42.                   zf.IsStreamOwner = true; // Makes close also shut the underlying stream
    43.                   zf.Close(); // Ensure we release resources
    44.         }
    45. }
    46.  
    47. //once done, remove the zip file
    48. System.IO.File.Delete(fileNameToUnzip);
    49.  
    50. //close this updater and start the game
    51. Application.Quit();
    52. System.Diagnostics.Process.Start(Directory.GetCurrentDirectory() + programNameToRun);

    Any ideas?
     
    Last edited: Feb 1, 2015
  2. boolfone

    boolfone

    Joined:
    Oct 2, 2014
    Posts:
    289
    You may want to go into Terminal on Mac and do an “ls -l” in a directory with a .app.

    Here’s what Xcode looks like in my /Applications:

    drwxr-xr-x 3 root wheel 102 Jan 1 09:45 Xcode.app

    One point of interest is that it is actually a directory.

    I am not sure how your code even creates the .app “file” since your code claims it skips directories.

    You may want to look at this:

    http://en.wikipedia.org/wiki/Application_bundle

    Excerpt:

    Application bundles are directory hierarchies, with the top-level directory having a name that ends with a .app extension.
     
  3. DavidxK

    DavidxK

    Joined:
    May 5, 2014
    Posts:
    8
    oh my bad! I didn't mention this in the 1st post but everything is extracted and created on OSX (yes even the .app file and even all the files in its package contents are in the right places)

    it's just that when I dived into the .app's package contents, the binary in Contents->MacOS doesn't come out as a binary. That is the reason why I'm thinking that the .app file doesn't run ~
     
  4. Dameon_

    Dameon_

    Joined:
    Apr 11, 2014
    Posts:
    542
    Here's another thing to consider: how much space do you actually save with a zip file? Most modern file formats take advantage of compression, and many of them do a better job than zip. Zip files can actually increase the collective size when other pre-compressed formats are involved.
     
  5. DavidxK

    DavidxK

    Joined:
    May 5, 2014
    Posts:
    8
    hey thanks for the input! Before trying zipping, I have already tried to have the self-updater tool download the .app file directly from the server.

    But since the .app file is a directory itself, you can't really download the .app file as it-is without putting it in a zip file. (i.e it shows up as a directory when it is uploaded onto the server)

    If there are any other ways to go about doing this, I am all-ears :D
     
  6. boolfone

    boolfone

    Joined:
    Oct 2, 2014
    Posts:
    289
    I’m pretty sure it is because the executable bit is not being set.

    On Unix-type systems like MacOs, files typically have permissions such as “drwxr-xr-x” in:

    drwxr-xr-x 3 root wheel 102 Jan 1 09:45 Xcode.app

    Those x’s right there are executable bits indicating the program can be run. It is kind of like the Unix equivalent of something ending in .EXE.

    Anyhow, your code does not seem to set such permissions. So, they are probably getting set to defaults, and the default most likely does not have the executable bit set.
     
  7. DavidxK

    DavidxK

    Joined:
    May 5, 2014
    Posts:
    8
    hey thanks for the tip! So in order to change the file permission for the file, I googled and got it to work!

    So if anyone came across this same issue, this is the script/code that I used to make the binary executable. Btw the snippet below is within the loop where the unzipping is done and must be after each file is unzipped (check above)

    Code (CSharp):
    1.                  
    2. using System.Diagnostics;
    3.  
    4. #if UNITY_STANDALONE_OSX
    5.  
    6.         if (entryFileName == "Game.app/Contents/MacOS/Game") {
    7.  
    8.                 ProcessStartInfo startInfo = new ProcessStartInfo() {
    9.                         FileName = "chmod",
    10.                         Arguments = "+x " + entryFileName,
    11.                 };
    12.              
    13.                 Process proc = new Process() { StartInfo = startInfo, };
    14.                 proc.Start();
    15.         }
    16.  
    17. #endif
     
    Last edited: Feb 2, 2015
    Ellernate likes this.
  8. Sly88

    Sly88

    Joined:
    Feb 22, 2016
    Posts:
    73
    Hi guys,
    I have some problem after uzip process, when I try to start my game I got the error the application “TEST” can’t be opened.

    any advice on how to fix the problem?

    Code (CSharp):
    1.  private void Unzip()
    2.         {
    3.                
    4.             string docPath = "test.zip";
    5.             ZipFile zf = null;
    6.             try {
    7.                 FileStream fs = File.OpenRead(docPath);
    8.                 zf = new ZipFile(fs);
    9.                 foreach (ZipEntry zipEntry in zf) {
    10.                     if (!zipEntry.IsFile) {
    11.                         continue;           // Ignore directories
    12.                     }
    13.                     string entryFileName = zipEntry.Name;
    14.                    
    15.                         byte[] buffer = new byte[4096];     // 4K is optimum
    16.                         Stream zipStream = zf.GetInputStream(zipEntry);
    17.              
    18.  
    19.                         string fullZipToPath = Path.Combine("", entryFileName);
    20.                         string directoryName = Path.GetDirectoryName(fullZipToPath);
    21.                         if (directoryName.Length > 0) {
    22.                             Directory.CreateDirectory(directoryName);
    23.                         }        
    24.                        
    25.                         using (FileStream streamWriter = File.Create(fullZipToPath)) {                    
    26.                             StreamUtils.Copy(zipStream, streamWriter, buffer);
    27.                         }
    28.                    
    29. #if UNITY_STANDALONE_OSX
    30.                         if (entryFileName == "Test.app/Contents/MacOS/Test") {
    31.                          
    32.                             ProcessStartInfo startInfo = new ProcessStartInfo() {
    33.                                 FileName = "chmod",
    34.                                 Arguments = "+x " + entryFileName                              
    35.                             };
    36.            
    37.                             Process proc = new Process() { StartInfo = startInfo, };
    38.                             proc.Start();
    39.                         }
    40. #endif
    41.                 }
    42.             } finally {
    43.                 if (zf != null) {
    44.                     zf.IsStreamOwner = true; // Makes close also shut the underlying stream
    45.                     zf.Close(); // Ensure we release resources
    46.                 }
    47.             }
    48.        
    49.         }
     
  9. RacketRebel

    RacketRebel

    Joined:
    Jun 3, 2017
    Posts:
    7
    Hi,
    for anyone who has the same problem, it's just all about spaces in your path or app name.
    Try this function it will handle all app's in your zip file

    Code (CSharp):
    1. #if UNITY_STANDALONE_OSX
    2.                     OnMacOSX(entryFileName, fullZipToPath);
    3. #endif
    Code (CSharp):
    1. private static void OnMacOSX(string entryFileName, string fullZipToPath) {
    2.             if (entryFileName.Contains(".app")) {
    3.                 string appName = entryFileName.Substring(0, entryFileName.IndexOf(".app"));
    4.                 if (entryFileName.EndsWith(appName)) {
    5.                     string procesPath = fullZipToPath.Contains(" ") && (!fullZipToPath.StartsWith("\"") && !fullZipToPath.EndsWith("\"")) ?
    6.                          "\"" + fullZipToPath + "\"" : fullZipToPath;
    7.                     try {
    8.                         var procStartInfo = new ProcessStartInfo() {
    9.                             FileName = "chmod",
    10.                             Arguments = "+x " + procesPath,
    11.                             RedirectStandardError = true,
    12.                             RedirectStandardOutput = true,
    13.                             UseShellExecute = false,
    14.                             CreateNoWindow = false
    15.                         };
    16.                         var proc = new Process { StartInfo = procStartInfo };
    17.                         proc.Start();
    18.  
    19.                     } catch (Exception e) {
    20.                         UnityEngine.Debug.Log(e.Message);
    21.                     }
    22.                 }
    23.             }
    24.         }