2018-07-25 22:53:32

Hi everyone,
WITH BGT on its way out, at least in my eyes, I've been moving into more main stream development platforms, EG python.
One major sticking point though is game data or sounds.
We all know, the first thing a game player does upon downloading a game is goes, "where's the sounds."
And if they're in a nice snug sounds folder, and playable, the following thing they do is go, "why thank you very much *steel*."
I am therefore seeking advice. I am using pygame for my games, and wx for non sound games.
In regards to pygame then, what is the best way to make sounds... well, hidden, or unsteelable.
Don't get me wrong, I know if someone is serious enough about it, nothing can stop them.
But if sounds are hidden, or unplayable to most but the game dev and his game. It puts most people off.
So. How.
That is my question. How do I hide, compress, or otherwise make sounds inaccessible to the user, but usable by pygame.
Thanks very much.

Nathan Smith
Managing Director of Nathan Tech
It's not disability
It's ability!

2018-07-25 23:37:02

Generally, packing them in a zip file and renaming it something like "sounds.dat" is sufficient, as the extension doesn't matter with zip files. You could also put a password on it too, compile your script as a pyc file to obscure your source and use a loader script to import and run it.

Fun fact: python egg files are just renamed zip files.

-BrushTone v1.3.3: Accessible Paint Tool
-AudiMesh3D v1.0.0: Accessible 3D Model Viewer

2018-07-25 23:47:33

Hi.
You could encrypt and compress them, like how must games do it. For that purpose, I've made a freely available ResourceManager, that can help in your kind of situation: https://github.com/NicklasMCHD/ResourceManager
Hope it helps smile

If you like what I do, Feel free to check me out on GitHub, or follow me on Twitter

2018-07-26 09:19:26

hi
if i understood correctly, you want hide sounds.dat
first, put sounds.dat in incloods file
then, go to your game.bgt, remove set sound key bla bla bla
well, finish

2018-07-26 12:15:20

@mahdi-abedi:

The original poster stated in post 1 that they are no longer using BGT, so your post isn't very helpful.

2018-07-26 13:16:23

Hi there!
It's funny, I came up with all these various ideas, using pickle, using buffers, this, that and the other. All while worrying about size.
Compression is such an obvious answer, and I feel like a complete durp now!
Out of interest, is there a better way to load the file from the zip, rather than using, for example, extra file, put it in temporary files, play, delete?

Nathan Smith
Managing Director of Nathan Tech
It's not disability
It's ability!

2018-07-26 15:13:16

Dude i recommend you not to do that because it needs cpu and probably RAM. Assume that you have to load a 1 mb ogg file every ten seconds onece, So you extract it, load it, play it, delete it. All of these. You know how bad bad it can be.

---
Co-founder of Sonorous Arts.
Check out Sonorous Arts on github: https://github.com/sonorous-arts/
my Discord: kianoosh.shakeri2#2988

2018-07-26 15:18:41

@6, can't you read the file from the zipfile without storing in a temporary directory?
I think there's a read method in the zipfile module that allows to read certain files inside the archive. Do not quote me on that though

Paul

2018-07-26 23:37:18

hi there
there is yes. I got compression through zipfile to work very nicely. and yes there is a zipfile.reda(), but I'm not sure how to get that to work with pygame so as to play the sound, because if you do something like the below code, it comes out deformed:
r=z.read("mysound.ogg")
pygame.mixer.Channel(1).play(pygame.sound(r))

Nathan Smith
Managing Director of Nathan Tech
It's not disability
It's ability!

2018-07-26 23:37:26

@pauliyobo
zipfile.read() returns a string with the files contents as opposed to a file object, most libraries like pygame and pyglet require a file like object like those returned from zipfile.load(). This isn't to say you can't do it, and its a neat idea, but you'd have to convert the data into a usable file like object to be loaded in memory first, which depending on the type of data can involve different processes and an understanding of how the data is formatted.

@dardar
Ideally you should only have to load the files into memory once, as a static not streaming source, to use repeatedly as needed while the program is running, then clear them from memory when the program ends. As for the actual files themselves you'd need to uncompress them to load them, unless using pauliyobo's suggestion, so you would have to delete them right afterwards. Now, if you pack the zip file with the sounds inside the executable, the archive and the resulting files will be automatically dumped to a temporary folder thats deleted when the program is finished so should handle things for you, if your streaming particularly large sounds into memory this might be a good solution.

-BrushTone v1.3.3: Accessible Paint Tool
-AudiMesh3D v1.0.0: Accessible 3D Model Viewer

2018-07-27 00:29:39

@10, Just is just an idea, but can't you like read the content of the file with wave? if it's wav format, or with pyogg for example?

Paul

2018-07-27 00:35:40

if you make an executeable version of your game, I think there is a way in python to make it one single .exe instead of having all the separate folders as well. I think it's a bit more complicated and the loading time may be longer but I think it extracts the sounds etc in to a tempory folder. people who are really determined can probably track that tempory folder down while the game is running but it's a kind of solution.

Who's that trip trapping over My bridge? Come find out.

2018-07-27 00:57:52 (edited by magurp244 2018-07-27 01:01:31)

@11
Same problem, wave and pyogg take file like objects, not string data. You'd need to parse the raw data yourself, which would probably involve trimming and processing the header data at the front end of the string for playback settings, then building a file object. I've done something similar when working with sonifiers in memory, though in that case I was generating the raw sound wave data myself before loading it into an openal buffer so I predetermined the settings and already knew the sound parameters. A better idea here might be to procedurally generate sounds in memory on startup and eliminate the need for external files altogether, but the complexity, quality, and types of sounds could prove problematic.

Here's an example of my openal buffer used for playing raw data:

#OpenAL Sound Buffer
class buffer_sound(object):
    def __init__(self):
        self.channels = 1
        self.bitrate = 16
        self.samplerate = 21000
        self.wavbuf = None
        self.alformat = al.AL_FORMAT_MONO16
        self.length = None

        self.buf = al.ALuint(0)
        al.alGenBuffers(1, self.buf)

#load raw data into buffer with preset parameters defined in init()
    def load(self,data):
        self.wavbuf = data
        self.length = len(data)
    #allocate buffer space to: buffer, format, data, len(data), and samplerate
        al.alBufferData(self.buf, self.alformat, self.wavbuf, len(self.wavbuf), self.samplerate)

    def delete(self):
        al.alDeleteBuffers(1, self.buf)

-BrushTone v1.3.3: Accessible Paint Tool
-AudiMesh3D v1.0.0: Accessible 3D Model Viewer

2018-07-27 04:07:27

Another option is encryption+compression, and not with zipfile either. Have you guys heard of Zstandard? Its fast and quick like ZIPs are but just as good as LZMA. And its multithreaded too - the first compression ever to be fully multithreaded. For something like that, you could try something like:

import cryptography.fernet, os, os.path
def encrypt(filename):
    encrypter1=cryptography.fernet.Fernet(b'your-encryption-key-here')
    compressor=zstandard.ZstdCompressor(level=22, write_checksum=True, write_content_size=True, threads=-1)
    file = open(os.path.abspath("")+"/"+filename, "rb")
    encrypted_uncompressed_data=encrypter1.encrypt(file.read())
    compressed_encrypted_data=compressor.compress(encrypted_uncompressed_data)
    encrypter2=cryptography.fernet.Fernet(b'another-cryptographic-key')
    double_encrypted_compressed_data=encrypter2.encrypt(compressed_encrypted_data)
    compressor2=zstandard.ZstdCompressor(level=22, write_checksum=True, write_content_size=True, threads=-1)
    final_data=compressor2.compress(double_encrypted_compressed_data)
    file.close()
    return final_data

What this does:

  1. We first create a Fernet object that holds a 32-byte (256-bit) key for AES-256 encryption. You could construct this data manually but you'd need to pass around nonces and things.

  2. We then create a Zstandard.ZstdCompressor object that is used during our first encryption/compression pass.

  3. We open the file passed into the function and read it in to the first encryptors encrypt() method.

  4. We then pass the encrypted data through the compressor object which, while compressing the data, writes a checksum and the content length at the start of the compressed data, ensuring its not modifiable, then compresses it.

  5. We then redo this again (encryption and compression but not rereading the file) and return the final data.

You can modify this to your liking, but its certainly one way to secure your data. Packing it into a zipfile could also be done this way: run all your maps and data through this function, zip it, then run the zipped raw data through this function and you've pretty much got inaccessible data unless someone can get the key from your Python code, and if you modify the Python bytecode structure in Python's source code, you can make decompilation and disassembly pretty much impossible.

"On two occasions I have been asked [by members of Parliament!]: 'Pray, Mr. Babbage, if you put into the machine wrong figures, will the right answers come out ?' I am not able rightly to apprehend the kind of confusion of ideas that could provoke such a question."    — Charles Babbage.
My Github

2018-07-27 21:06:25

Is Pygame mixer just a Python implementation of SDL_Mixer in C? If this is the case, just as a suggestion, if you're willing to do this, you can just write your own archiver. I'm writing my own AudioGame system in C/C++, and I found that writing a custom format for storing assets doesn't take too long. Basically, I have a system where I list the files I want to store in a text file, I run my little archiver program, and the program generates a custom file as well as corresponding loading procedures I can insert wherever I want in the game code. I apply some relatively simple encryption. But as you've already stated, if someone really wants to take the time to decrypt and reconstruct the files from memory, they would have to know quite a bit about memory dumps and debugging and such. No system is full-proof, so might as well stick with the simple solutions. However, if I were to make my product commercially available, my system is flexible enough to add extra encryption so as to make it a bit more challenging. Oh, and this all assumes that you are familiar with loading data from memory as per the memory procedures you can implement in your custom archiving library.

2018-07-28 12:59:49

The question you need to ask yourself is how much work do you want to put into it? you could use a method as simple as changing the file extension or as complicated as encrypting the sounds, obfescating the code and swapping python bytecodes if that' s the correct term. Python was never designed to be obfescated, compilers like py2exe only package the source up conveniently, they don' t obfescate. Also keep in mind that people who found the sounds will also share them with others, so someone can just copy them from someone who already has the sounds.

Roel
golfing in the kitchen

2018-07-29 11:19:47 (edited by Munawar 2018-07-29 11:21:32)

Ethin mentioned one method of encryption, but I think it's overkill for sound encryption especially if you want to load files fast.

I used a simple bitwise XOR encryption scheme. Nothing complex, but it will turn away the casual hacker. Every byte of data is XOR'd with 10000001:

        private static byte key = 129;
        private static void encryptDecrypt(byte[] data)
        {
            byte c = 0;
            int i = 0;
            for (i = 0; i < data.Length; i++)
            {
                c = data[i];
                c = (byte)(c ^ key);
                data[i] = c;
            }
        }

The advantage to this method is that you use the exact same logic to encrypt and decrypt your sounds. And it takes only

O(n)

time to execute.