在Android上将游戏资源下载到SD卡

3
我正在开发一款Android游戏,需要下载一些资源到SD卡上,以尽可能减小应用程序的大小。我考虑使用未压缩的zip文件来捆绑所有资源。
客户的要求是尽可能保护这些资产。将其作为apk的一部分被认为已经足够保护,但如果这样做,apk的大小将会非常大。如果我只是把一个zip包放在SD卡中,那么任何人都可以解压它并浏览其中的内容。
有没有一种简单的方法来实现这一点而不必采取可怕的DRM措施?
显然,如果有人真的想检查Android游戏的资源,他们可以。我只是寻找一个简单的解决方案,以避免使这个过程变得非常容易。

1
你知道 APK 文件只是一个重命名的 zip 文件吗?它可以用任何能打开 zip 的程序打开,而且许多 Android 文件浏览器甚至不需要你更改文件扩展名。因此,将要保护的资源包含在 apk 中并不能提供任何保护,无论文件大小如何。你的很多代码和清单都是使用开发密钥编译/加密的,但像图片这样的东西是原始可用的。 - Steve Haley
如果你启用了拷贝保护,我相信用户很难轻易地访问APK文件的内容。虽然并不是说这是一种百分百可靠的方法。 - hpique
@Steve H - 如果在上传到市场时勾选相关框以“前向锁定”应用程序,则用户无法在未经root的手机上轻松找到.apk。这不会阻止有决心的人,但它将防止随意浏览者找到资产,我认为这是意图所在。 - David Webb
啊,应用程序的复制保护是否阻止备份程序在SD卡上备份APK?如果没有,那么获取APK仍然相当容易。我承认我对该选项并不了解,除了它会使您的APK大小加倍,这可能会对大型游戏造成限制。无论如何,Dave已经提出了一个很好的建议,所以听他的,而不是我:p - Steve Haley
1个回答

4

你可以使用AES加密ZIP文件,但提取资产时会有一些额外的开销,这可能是一个问题,因为在编写游戏时通常性能非常重要。

然而,正如你自己所说,使用安全加密并不值得,因为如果有人真的感兴趣,他们可以在应用程序的源代码中找到密钥。因此,你可以自己编写InputStreamOutputStream来对ZIP文件的字节进行一些微不足道的转换。这样读取文件时很快就可以撤消,但会“破坏”ZIP文件,足以阻止某些人只是浏览他们的SD卡。

在使用它时,你需要将流放置在FileZIPInputStream之间,例如:

File assets = new File(Environment.getExternalStorageDirectory(),"assets");
ZipInputStream zip = new ZipInputStream(new TranslateInputStream(assets));

作为一个例子翻译,您可以将所有字节与已知值进行异或操作:
private static final byte MAGIC_NUMBER = 13;

private void translateBuffer(byte[] buffer) {
    for (int i = 0;i < buffer.length;i++) {
        buffer[i] ^= MAGIC_NUMBER;
    }
}

private class TranslateOutputStream extends FileOutputStream {
    public TranslateOutputStream(File file) throws FileNotFoundException {
        super(file);
    }

    @Override
    public void write(int oneByte) throws IOException {
        oneByte ^= MAGIC_NUMBER;
        super.write(oneByte);
    }

    //In Android write(byte[]) calls this method so we don't need to override both        
    @Override
    public void write(byte[] buffer, int offset, int count)
            throws IOException {
        translateBuffer(buffer);
        super.write(buffer, offset, count);
    }
}

private class TranslateInputStream extends FileInputStream {

    public TranslateInputStream(File file) throws FileNotFoundException {
        super(file);
    }

    @Override
    public int read() throws IOException {
        return super.read() ^ MAGIC_NUMBER;
    }

    //In Android read(byte[]) calls this method so we don't need to override both        
    @Override
    public int read(byte[] buffer, int offset, int count)
            throws IOException {
        int bytesRead = super.read(buffer, offset, count);
        translateBuffer(buffer);
        return bytesRead;
    }
}

或者,如果您不关心压缩,可以不使用ZIP。只需将所有资产连接在一起形成一个大的数据块。创建数据块时,将每个资产的开始和结束位置写入索引文件。然后,在您的.apk文件中包含该索引文件,以便从应用程序中获取所需的数据。由于大型数据块不是标准格式,因此人们无法轻松地从中提取文件。为了确保安全性,您始终可以在文件开头填充其他数据(例如纯文本),使数据块看起来像其他东西。


这看起来是一个简单而高效的解决方案。我会试一试。谢谢 Dave! - hpique
我认为我更喜欢流转换解决方案而不是 Blob 解决方案。前者可以让我轻松更新资产,而后者需要更多的工作来重新生成和更新索引文件。 - hpique

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接