When Microsoft decided to adopt MSBuild on .NET Core platform, project.json was not dropped immediately until first toolchain RTM arrives. Dotnet Development on Universal Windows Platform Development leverages .NET Core too, but the depreciation progress is significantly slower than other .NET Core platforms due to historical reasons. UWP uses project.json for package management and MSBuild for builds.
In Visual Studio 2017 April Update, Microsoft finally migrates new UWP projects to full MSBuild-based project system. But our projects, which creates on early 2015, doesn’t get an auto migration as expected. Hence we decided to migrate them manually for additional benefits like better toolchain stability and advanced customization features.
Reminder: Do not attempt to use “dotnet migrate” CLI command, it won’t work for UWP projects.
Notify all your team members. Make sure everyone has Visual Studio 2017 with April update installed.
If you have continuous integration environment configured, make sure build agents have NuGet 4.1 or higher installed (3.5 or 4.0 won’t work).
Lock VCS during migration to prevent additional incidents. (We’re using TFVC for source management so that it will be easy)
Clean up all projects (including bin and obj directories)
Iterate all project directories
Find C# project file, open with your favorite editor.
Add following property group before project file lists:
Someone asked me if I could extract some images from a popular Chinese mobile game. I accepted the challenge, but things were far more complicated than I expected.
What I knew
This game is Unity3D-based.
Original assets were encrypted with known algorithm and key. DISCLAIMER: I will not tell you details about encryption.
The story began
I thought I could extract assets I needed with existing tools (e.g. Disunity) but I was proved wrong. Disunity has been refactored, and remaining work is still in progress (at least the moment I write this article). Since resource extraction has not been implemented at this moment, Disunity couldn’t be my choice.
Then I turned to a tool called Unity Assets Bundle Extractor. It did a great job extracting resources I needed graphically. However, acquiring thousands of texture assets from 2000+ isolated files is not an easy job. I tried the command line support but failed (maybe I was too stupid).
Luckily this toolkit provides some API and documentation. Since it was compiled with Microsoft Visual C++ 2010, I was unable to use it directly(C++ ABI constantly changes with every MSVC release). And I was too lazy to write a C wrapper for P/Invoke. But these C++ header files point to a perfect solution – parse file and implement my own UnityFS parser/reader.
Special thank to the UABE project – without these generous header, I would not be able to implement my own parsing and compose this article.
UnityFS was a new asset bundle format introduced in Unity 5. I am not a Unity3D developer, and I absolutely didn’t know why Unity introduce a new bundle format. But anyway, let’s analyze it.
Things you need to know
UnityFS is just bundle of several Unity assets. Each asset contains a collection of serialized Unity objects (e.g. 2D texture, text resources, scene objects, etc.).
UnityFS follows a standard Unity file header structure. Let’s call it AssetsBundleHeader06
You have to parse asset files in order to extract what you need. There’s bunch of documentation about this. Look into the old Disunity source code for some idea.
So the header goes like this. There’s a DWORD flags data that matters – it contains some critical information required for decompression and directory parsing. The rule goes like this:
(Flags & 0x3F) is compression mode. 0 means no compression, 1 means LZMA and 2/3 means LZ4/LZ4HC.
(Flags & 0x40) says whether the bundle has directory info.
(Flags & 0x80) says whether the block and directory list is at the end of this bundle file.
C# provides a good BinaryReader that makes things a bit easy. But it can be improved for better Null-terminated String and Big Endian support. Be careful with endianness. Unity utilizes both Big Endian and Little Endian in a single file and personally I didn’t get this. For the sake of convenience, I extended the original BinaryReader for these support. Length of each data type matters – but that’s a basic stuff for CS students.
UnityFS uses optional block-based compression for streaming (you can read a specific bundle without downloading the whole file). Both LZMA and LZ4* (LZ4Hc, etc.) are supported. The Unity’s proprietary parser and Disunity respects this design. But I just wanted these bundle files, so I decided to read all blocks at once and decompress into a single memory stream.
You can implement your own block-based reader – but my time budget didn’t allow me to do this.
There we go…block and file information!
Following a unknown 16 bytes block, there’s a Big-Endian UInt32 value represents block count in a single package. Each block information contains a Big-Endian UInt32 decompressed size, a Big-Endian UInt32 compressed size and a flag that we might not interested in.
Then a BE UInt32 value represents file count in a single package. Each file information contains file offset we need(BE UInt64), the decompressed size(BE UInt64), a BE UInt32 flag and a Null-Terminated string of file name.
Parse your assets now
With sufficient information we retrieved, we were able to extract raw asset files from a UnityFS bundle. Then what you need is search the Internet for ideas of extracting objects(text resources, 2D texture, etc.) from Unity assets. Good luck on exploring!
In this article, we discussed structure and parsing of UnityFS resource bundle file. For more information about UnityFS and Unity asset files, please research these projects I mentioned in this article.
This step is absolutely easy on Ubuntu/Debian and other officially supported distributions. If you are using Archlinux, go to AUR for .NET Core runtime.
Step 2: Prepare files.
In local Visual Studio or other development environment, publish project to a folder. (dotnet build -c Release)
Step 3: Setting services
I wrote a simple systemd service for my application:
Description=A certain ASP.NET Core application
ExecStart=/usr/bin/dotnet /opt/imbushuo/somepath/App.dll --server.urls=http://localhost:5050
Step 4: Setting up reverse proxy
Refer to your frontend server documentation for details.
If you are using per-environment configuration file, make sure the configuration starts with capital letter, like appsettings.Production.json . Otherwise, the Startup class will not load the settings file.
If you want to run multiple applications, make sure add configuration class in Program class and apply it. Then, pass server.urls parameter with address and port, like what I wrote above.
The following Program.cs has been modified to enable parameter support. Feel free to copy it:
public class Program
public static void Main(string args)
var configuration = new ConfigurationBuilder()
var host = new WebHostBuilder()