This is kinda messy but here is how i implemented a ContentManager derived that supports expansion files. Code is a bit messy but i will update it depending on the interest/suggestions.
using Android.Content.PM;
using Android.Content;
using Android.App;
using Android;
using System;
using System.IO;
using System.Linq;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Path = System.IO.Path;
using System.Diagnostics;
using System.IO.Compression;
using ICSharpCode.SharpZipLib.Zip;
#if !WINRT
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Media;
#endif
namespace Microsoft.Xna.Framework.Content
{
public class Android_ContentManager:ContentManager
{
// Keep this static so we only call Game.Activity.Assets.List() once
// No need to call it for each file if the list will never change.
// We do need one file list per folder though.
static ZipFile zif;
static ApplicationInfo ainfo;
static PackageInfo pinfo;
static string obbPath;
static Dictionary<string,Dictionary<string,int>> entries;
public Android_ContentManager (IServiceProvider serviceProvider) : base ( serviceProvider)
{
init ();
}
public Android_ContentManager (IServiceProvider serviceProvider, string rootDirectory) : base(serviceProvider,rootDirectory)
{
init ();
}
public static int expansionPackVersion;
void init ()
{
if (entries == null) {
Activity activity = Game.Activity;
ainfo = activity.ApplicationInfo;
pinfo = activity.PackageManager.GetPackageInfo (ainfo.PackageName, PackageInfoFlags.MetaData);
#if DEBUG
obbPath = Path.Combine (Android.OS.Environment.ExternalStorageDirectory.AbsolutePath, "Android", "obb", ainfo.PackageName+".debug", String.Format ("main.{0}.{1}.obb", expansionPackVersion, ainfo.PackageName));
#else
obbPath = Path.Combine (Android.OS.Environment.ExternalStorageDirectory.AbsolutePath, "Android", "obb", ainfo.PackageName, String.Format ("main.{0}.{1}.obb", expansionPackVersion, ainfo.PackageName));
#endif
try {
zif = new ZipFile (obbPath);
entries = new Dictionary<string,Dictionary<string,int>> ();
Dictionary<string,int> name_to_index = null;
for (int i = 0; i < zif.Count; i++) {
if (Path.GetExtension (zif [i].Name).Length > 0) {
var dirname = Path.GetDirectoryName (zif [i].Name);
name_to_index = null;
if (!entries.TryGetValue (dirname, out name_to_index)) {
name_to_index = new Dictionary<string, int> ();
entries [dirname] = name_to_index;
}
name_to_index [Path.GetFileName (zif [i].Name)] = i;
}
}
} catch (Exception e) {
System.Diagnostics.Debug.WriteLine ("++ Zip expansion file could not be opened ++ {0}", e.Message);
zif = null;
}
}
}
object ContentManagerLock=(object)1;
public override Stream OpenStream(string assetName)
{
if (zif == null)
return base.OpenStream (assetName);
Stream stream;
try {
string assetPath = Path.Combine (RootDirectory, assetName) + ".xnb";
Activity activity = Game.Activity;
string filePath = assetPath;
lock (ContentManagerLock) {
ZipEntry ze = zif.GetEntry (filePath);
stream = zif.GetInputStream (ze);
MemoryStream mstream = new MemoryStream ((int)ze.Size);
stream.CopyTo (mstream);
mstream.Seek (0, SeekOrigin.Begin);
stream.Close ();
stream = mstream;
}
} catch (FileNotFoundException fileNotFound) {
return base.OpenStream (assetName); // if file or folder are not found we can try with the standard read method
}
#if !WINRT
catch (DirectoryNotFoundException directoryNotFound) {
return base.OpenStream (assetName); // if file or folder are not found we can try with the standard read method
}
#endif
catch (Exception exception) {
return base.OpenStream (assetName); // if file or folder are not found we can try with the standard read method
}
return stream;
}
protected override void Dispose (bool disposing)
{
base.Dispose (disposing);
}
static string[] textureExtensions = new string[] { ".jpg", ".bmp", ".jpeg", ".png", ".gif" };
static string[] songExtensions = new string[] { ".mp3", ".ogg", ".mid" };
static string[] soundEffectExtensions = new string[] { ".wav", ".mp3", ".ogg", ".mid" };
protected override string Normalize<T>(string assetName)
{
string result = null;
if (zif == null)
return base.Normalize<T>(assetName);
if (typeof(T) == typeof(Texture2D) || typeof(T) == typeof(Texture))
{
result = Normalize(assetName,textureExtensions);
}
else if ((typeof(T) == typeof(Song)))
{
result = Normalize(assetName,songExtensions);
}
else if ((typeof(T) == typeof(SoundEffect)))
{
result = Normalize(assetName,soundEffectExtensions);
}
if (result == null) { //item might not be in the package or be an unsupported file type
result = base.Normalize<T>(assetName);
}
return result;
}
protected override object ReadRawAsset<T>(string assetName, string originalAssetName)
{
if (zif == null)
return base.ReadRawAsset<T>(assetName, originalAssetName);
ZipEntry ze = zif.GetEntry (assetName);
if (ze == null) {
return base.ReadRawAsset<T>(assetName, originalAssetName);
}
if (typeof(T) == typeof(Texture2D) || typeof(T) == typeof(Texture)) {
lock (ContentManagerLock) {
using (MemoryStream mstream = new MemoryStream ((int)ze.Size)) {
using (var stream = zif.GetInputStream (ze)) {
stream.CopyTo (mstream);
mstream.Seek (0, SeekOrigin.Begin);
}
Texture2D texture = Texture2D.FromStream (
graphicsDeviceService.GraphicsDevice, mstream);
texture.Name = originalAssetName;
return texture;
}
}
}
else if ((typeof(T) == typeof(Song)))
{
return new Song(obbPath,zif.LocateEntry(ze),ze.CompressedSize);
}
else if ((typeof(T) == typeof(SoundEffect)))
{
return new SoundEffect(obbPath,zif.LocateEntry(ze),ze.CompressedSize);
}
throw new NotImplementedException ("This format of file is not supported as raw file");
}
internal string Normalize(string fileName, string[] extensions)
{
if (zif == null)
return null;
int index = fileName.LastIndexOf(Path.DirectorySeparatorChar);
string path = string.Empty;
string file = fileName;
if (index >= 0)
{
path = fileName.Substring (0, index);
file = fileName.Substring(index + 1, fileName.Length - index - 1);
}
Dictionary<string,int> files = null;
if (!entries.TryGetValue (path,out files))
return null;
bool found = false;
index=-1;
foreach (string s in extensions) {
if (files.TryGetValue (file + s, out index)) {
found = true;
break;
}
}
if(!found)
return null;
return zif[index].Name;
}
}
}