Skip to content

Commit

Permalink
Add OBB (zip) support (cocos2d#15515)
Browse files Browse the repository at this point in the history
* Add OBB support.
OBB must be a zip file with audio and video files stored uncompressed.

* Use introspection to be able to build with Android API level 10.
Using the new audio engine to play sounds from the OBB will only work with API level 12 (Android 3.1) or above.
  • Loading branch information
adesentenac authored and minggo committed May 4, 2016
1 parent e11a7fd commit 1a76c9e
Show file tree
Hide file tree
Showing 12 changed files with 423 additions and 26 deletions.
29 changes: 17 additions & 12 deletions cocos/audio/android/AudioEngine-inl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#include "base/CCDirector.h"
#include "base/CCScheduler.h"
#include "platform/android/CCFileUtils-android.h"
#include "platform/android/jni/Java_org_cocos2dx_lib_Cocos2dxHelper.h"

using namespace cocos2d;
using namespace cocos2d::experimental;
Expand Down Expand Up @@ -104,27 +105,31 @@ bool AudioPlayer::init(SLEngineItf engineEngine, SLObjectItf outputMixObject,con
SLDataFormat_MIME format_mime = {SL_DATAFORMAT_MIME, NULL, SL_CONTAINERTYPE_UNSPECIFIED};
audioSrc.pFormat = &format_mime;

if (fileFullPath[0] != '/'){
std::string relativePath = "";

if (fileFullPath[0] != '/') {
off_t start, length;
std::string relativePath;
size_t position = fileFullPath.find("assets/");

if (0 == position) {
// "assets/" is at the beginning of the path and we don't want it
relativePath += fileFullPath.substr(strlen("assets/"));
relativePath = fileFullPath.substr(strlen("assets/"));
} else {
relativePath += fileFullPath;
relativePath = fileFullPath;
}

auto asset = AAssetManager_open(cocos2d::FileUtilsAndroid::getAssetManager(), relativePath.c_str(), AASSET_MODE_UNKNOWN);

// open asset as file descriptor
off_t start, length;
_assetFd = AAsset_openFileDescriptor(asset, &start, &length);
if (_assetFd <= 0){
if (cocos2d::FileUtilsAndroid::getObbFile() != nullptr) {
_assetFd = getObbAssetFileDescriptorJNI(relativePath.c_str(), &start, &length);
} else {
auto asset = AAssetManager_open(cocos2d::FileUtilsAndroid::getAssetManager(), relativePath.c_str(), AASSET_MODE_UNKNOWN);
// open asset as file descriptor
_assetFd = AAsset_openFileDescriptor(asset, &start, &length);
AAsset_close(asset);
}

if (_assetFd <= 0) {
CCLOGERROR("Failed to open file descriptor for '%s'", fileFullPath.c_str());
break;
}
AAsset_close(asset);

// configure audio source
loc_fd = {SL_DATALOCATOR_ANDROIDFD, _assetFd, start, length};
Expand Down
29 changes: 29 additions & 0 deletions cocos/base/ZipUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -645,6 +645,35 @@ unsigned char *ZipFile::getFileData(const std::string &fileName, ssize_t *size)
return buffer;
}

bool ZipFile::getFileData(const std::string &fileName, ResizableBuffer* buffer)
{
bool res = false;
do
{
CC_BREAK_IF(!_data->zipFile);
CC_BREAK_IF(fileName.empty());

ZipFilePrivate::FileListContainer::const_iterator it = _data->fileList.find(fileName);
CC_BREAK_IF(it == _data->fileList.end());

ZipEntryInfo fileInfo = it->second;

int nRet = unzGoToFilePos(_data->zipFile, &fileInfo.pos);
CC_BREAK_IF(UNZ_OK != nRet);

nRet = unzOpenCurrentFile(_data->zipFile);
CC_BREAK_IF(UNZ_OK != nRet);

buffer->resize(fileInfo.uncompressed_size);
int CC_UNUSED nSize = unzReadCurrentFile(_data->zipFile, buffer->buffer(), static_cast<unsigned int>(fileInfo.uncompressed_size));
CCASSERT(nSize == 0 || nSize == (int)fileInfo.uncompressed_size, "the file size is wrong");
unzCloseCurrentFile(_data->zipFile);
res = true;
} while (0);

return res;
}

std::string ZipFile::getFirstFilename()
{
if (unzGoToFirstFile(_data->zipFile) != UNZ_OK) return emptyFilename;
Expand Down
9 changes: 9 additions & 0 deletions cocos/base/ZipUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ THE SOFTWARE.
#include "platform/CCPlatformConfig.h"
#include "platform/CCPlatformMacros.h"
#include "platform/CCPlatformDefine.h"
#include "platform/CCFileUtils.h"

#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
#include "platform/android/CCFileUtils-android.h"
Expand Down Expand Up @@ -275,6 +276,14 @@ typedef struct unz_file_info_s unz_file_info;
* @since v2.0.5
*/
unsigned char *getFileData(const std::string &fileName, ssize_t *size);

/**
* Get resource file data from a zip file.
* @param fileName File name
* @param[out] buffer If the file read operation succeeds, if will contain the file data.
* @return True if successful.
*/
bool getFileData(const std::string &fileName, ResizableBuffer* buffer);

std::string getFirstFilename();
std::string getNextFilename();
Expand Down
35 changes: 30 additions & 5 deletions cocos/platform/android/CCFileUtils-android.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,10 @@ THE SOFTWARE.
#include "platform/android/CCFileUtils-android.h"
#include "platform/CCCommon.h"
#include "platform/android/jni/JniHelper.h"
#include "platform/android/jni/Java_org_cocos2dx_lib_Cocos2dxHelper.h"
#include "android/asset_manager.h"
#include "android/asset_manager_jni.h"
#include "base/ZipUtils.h"
#include <stdlib.h>
#include <sys/stat.h>

Expand All @@ -42,6 +44,7 @@ using namespace std;
NS_CC_BEGIN

AAssetManager* FileUtilsAndroid::assetmanager = nullptr;
ZipFile* FileUtilsAndroid::obbfile = nullptr;

void FileUtilsAndroid::setassetmanager(AAssetManager* a) {
if (nullptr == a) {
Expand All @@ -57,7 +60,7 @@ FileUtils* FileUtils::getInstance()
if (s_sharedFileUtils == nullptr)
{
s_sharedFileUtils = new FileUtilsAndroid();
if(!s_sharedFileUtils->init())
if (!s_sharedFileUtils->init())
{
delete s_sharedFileUtils;
s_sharedFileUtils = nullptr;
Expand All @@ -73,11 +76,22 @@ FileUtilsAndroid::FileUtilsAndroid()

FileUtilsAndroid::~FileUtilsAndroid()
{
if (obbfile)
{
delete obbfile;
obbfile = nullptr;
}
}

bool FileUtilsAndroid::init()
{
_defaultResRootPath = "assets/";

std::string assetsPath(getApkPath());
if (assetsPath.find("/obb/") != std::string::npos)
{
obbfile = new ZipFile(assetsPath);
}

return FileUtils::init();
}
Expand Down Expand Up @@ -149,9 +163,14 @@ bool FileUtilsAndroid::isFileExistInternal(const std::string& strFilePath) const
const char* s = strFilePath.c_str();

// Found "assets/" at the beginning of the path and we don't want it
if (strFilePath.find(_defaultResRootPath) == 0) s += strlen("assets/");

if (FileUtilsAndroid::assetmanager) {
if (strFilePath.find(_defaultResRootPath) == 0) s += _defaultResRootPath.length();

if (obbfile && obbfile->fileExists(s))
{
bFound = true;
}
else if (FileUtilsAndroid::assetmanager)
{
AAsset* aa = AAssetManager_open(FileUtilsAndroid::assetmanager, s, AASSET_MODE_UNKNOWN);
if (aa)
{
Expand All @@ -165,7 +184,7 @@ bool FileUtilsAndroid::isFileExistInternal(const std::string& strFilePath) const
else
{
FILE *fp = fopen(strFilePath.c_str(), "r");
if(fp)
if (fp)
{
bFound = true;
fclose(fp);
Expand Down Expand Up @@ -249,6 +268,12 @@ FileUtils::Status FileUtilsAndroid::getContents(const std::string& filename, Res
} else {
relativePath = fullPath;
}

if (obbfile)
{
if (obbfile->getFileData(relativePath, buffer))
return FileUtils::Status::OK;
}

if (nullptr == assetmanager) {
LOGD("... FileUtilsAndroid::assetmanager is nullptr");
Expand Down
4 changes: 4 additions & 0 deletions cocos/platform/android/CCFileUtils-android.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ Copyright (c) 2013-2014 Chukong Technologies Inc.

NS_CC_BEGIN

class ZipFile;

/**
* @addtogroup platform
* @{
Expand All @@ -57,6 +59,7 @@ class CC_DLL FileUtilsAndroid : public FileUtils

static void setassetmanager(AAssetManager* a);
static AAssetManager* getAssetManager() { return assetmanager; }
static ZipFile* getObbFile() { return obbfile; }

/* override functions */
bool init() override;
Expand All @@ -73,6 +76,7 @@ class CC_DLL FileUtilsAndroid : public FileUtils
virtual bool isDirectoryExistInternal(const std::string& dirPath) const override;

static AAssetManager* assetmanager;
static ZipFile* obbfile;
};

// end of platform group
Expand Down
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -31,19 +31,31 @@ of this software and associated documentation files (the "Software"), to deal
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.Vibrator;
import android.preference.PreferenceManager.OnActivityResultListener;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Display;
import android.view.WindowManager;

import com.android.vending.expansion.zipfile.APKExpansionSupport;
import com.android.vending.expansion.zipfile.ZipResourceFile;

import com.enhance.gameservice.IGameTuningService;

import java.io.IOException;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.LinkedHashSet;
import java.util.Locale;
import java.util.Map;
Expand All @@ -55,6 +67,7 @@ public class Cocos2dxHelper {
// ===========================================================
private static final String PREFS_NAME = "Cocos2dxPrefsFile";
private static final int RUNNABLES_PER_FRAME = 5;
private static final String TAG = Cocos2dxHelper.class.getSimpleName();

// ===========================================================
// Fields
Expand All @@ -77,6 +90,12 @@ public class Cocos2dxHelper {
private static final int BOOST_TIME = 7;
//Enhance API modification end

// The absolute path to the OBB if it exists, else the absolute path to the APK.
private static String sAssetsPath = "";

// The OBB file
private static ZipResourceFile sOBBFile = null;

// ===========================================================
// Constructors
// ===========================================================
Expand All @@ -91,11 +110,11 @@ public static void init(final Activity activity) {
Cocos2dxHelper.sCocos2dxHelperListener = (Cocos2dxHelperListener)activity;
if (!sInited) {
final ApplicationInfo applicationInfo = activity.getApplicationInfo();

Cocos2dxHelper.sPackageName = applicationInfo.packageName;
Cocos2dxHelper.sFileDirectory = activity.getFilesDir().getAbsolutePath();

Cocos2dxHelper.nativeSetApkPath(applicationInfo.sourceDir);
Cocos2dxHelper.nativeSetApkPath(Cocos2dxHelper.getAssetsPath());

Cocos2dxHelper.sCocos2dxAccelerometer = new Cocos2dxAccelerometer(activity);
Cocos2dxHelper.sCocos2dMusic = new Cocos2dxMusic(activity);
Expand All @@ -114,7 +133,46 @@ public static void init(final Activity activity) {
serviceIntent.setPackage("com.enhance.gameservice");
boolean suc = activity.getApplicationContext().bindService(serviceIntent, connection, Context.BIND_AUTO_CREATE);
//Enhance API modification end

int versionCode = 1;
try {
versionCode = Cocos2dxActivity.getContext().getPackageManager().getPackageInfo(Cocos2dxHelper.getCocos2dxPackageName(), 0).versionCode;
} catch (NameNotFoundException e) {
e.printStackTrace();
}
try {
Cocos2dxHelper.sOBBFile = APKExpansionSupport.getAPKExpansionZipFile(Cocos2dxActivity.getContext(), versionCode, 0);
} catch (IOException e) {
e.printStackTrace();
}
}
}

// This function returns the absolute path to the OBB if it exists,
// else it returns the absolute path to the APK.
public static String getAssetsPath()
{
if (Cocos2dxHelper.sAssetsPath == "") {
int versionCode = 1;
try {
versionCode = Cocos2dxHelper.sActivity.getPackageManager().getPackageInfo(Cocos2dxHelper.sPackageName, 0).versionCode;
} catch (NameNotFoundException e) {
e.printStackTrace();
}
String pathToOBB = Environment.getExternalStorageDirectory().getAbsolutePath() + "/Android/obb/" + Cocos2dxHelper.sPackageName + "/main." + versionCode + "." + Cocos2dxHelper.sPackageName + ".obb";
File obbFile = new File(pathToOBB);
if (obbFile.exists())
Cocos2dxHelper.sAssetsPath = pathToOBB;
else
Cocos2dxHelper.sAssetsPath = Cocos2dxHelper.sActivity.getApplicationInfo().sourceDir;
}

return Cocos2dxHelper.sAssetsPath;
}

public static ZipResourceFile getObbFile()
{
return Cocos2dxHelper.sOBBFile;
}

//Enhance API modification begin
Expand Down Expand Up @@ -226,6 +284,29 @@ public static boolean openURL(String url) {
}
return ret;
}

public static long[] getObbAssetFileDescriptor(final String path) {
long[] array = new long[3];
if (Cocos2dxHelper.sOBBFile != null) {
AssetFileDescriptor descriptor = Cocos2dxHelper.sOBBFile.getAssetFileDescriptor(path);
if (descriptor != null) {
try {
ParcelFileDescriptor parcel = descriptor.getParcelFileDescriptor();
Method method = parcel.getClass().getMethod("getFd", new Class[] {});
array[0] = (Integer)method.invoke(parcel);
array[1] = descriptor.getStartOffset();
array[2] = descriptor.getLength();
} catch (NoSuchMethodException e) {
Log.e(Cocos2dxHelper.TAG, "Accessing file descriptor directly from the OBB is only supported from Android 3.1 (API level 12) and above.");
} catch (IllegalAccessException e) {
Log.e(Cocos2dxHelper.TAG, e.toString());
} catch (InvocationTargetException e) {
Log.e(Cocos2dxHelper.TAG, e.toString());
}
}
}
return array;
}

public static void preloadBackgroundMusic(final String pPath) {
Cocos2dxHelper.sCocos2dMusic.preloadBackgroundMusic(pPath);
Expand Down
Loading

0 comments on commit 1a76c9e

Please sign in to comment.