1- import ctypes
1+ from ctypes import *
2+ from enum import Enum
23import os
34import platform
45from UnityPy .streams import EndianBinaryWriter , EndianBinaryReader
5- # pyfmodex init
6- try :
7- ROOT = os .path .dirname (os .path .dirname (os .path .realpath (__file__ )))
8- LIB_PATH = os .path .join (ROOT , "lib" , "FMOD" , platform .system (), platform .architecture ()[0 ])
9-
10- if platform .system () == 'Windows' :
11- _dll = ctypes .WinDLL (os .path .join (LIB_PATH , "fmod.dll" ))
12- elif platform .system () == "Linux" :
13- _dll = ctypes .CDLL (os .path .join (LIB_PATH , "libfmod.so" ))
14- elif platform .system () == "Darwin" :
15- _dll = ctypes .CDLL (os .path .join (LIB_PATH , "libfmod.dylib" ))
16-
17- import pyfmodex
18- pyfmodex .fmodex ._dll = _dll
19- from pyfmodex .structures import CREATESOUNDEXINFO
20- from pyfmodex .flags import MODE , TIMEUNIT , INIT_FLAGS
21- except Exception as e :
22- print ("Error during the pyfmodex initialisation - AudioClips.samples won't work" , e )
23-
6+ # dll init
7+ ROOT = os .path .dirname (os .path .dirname (os .path .realpath (__file__ )))
8+ LIB_PATH = os .path .join (ROOT , "lib" , "FMOD" ,platform .system (), platform .architecture ()[0 ])
9+ if platform .system () == 'Windows' :
10+ _dll = WinDLL (os .path .join (LIB_PATH , "fmod.dll" ))
11+ elif platform .system () == "Linux" :
12+ _dll = CDLL (os .path .join (LIB_PATH , "libfmod.so" ))
13+ elif platform .system () == "Darwin" :
14+ _dll = CDLL (os .path .join (LIB_PATH , "libfmod.dylib" ))
15+ else :
16+ print ("UnityPy/export/AudioClipConverter - Warning: unknown system\n If you want to export AudioClips, you have to set UnityPy.export.AudioClipConverter._dll yourself." )
2417
2518def extract_audioclip_samples (audio ) -> dict :
2619 """extracts all the samples from an AudioClip
@@ -44,16 +37,19 @@ def extract_audioclip_samples(audio) -> dict:
4437
4538def dump_samples (clip ):
4639 # init system
47- system = pyfmodex .System ()
48- system .init (1 , INIT_FLAGS .NORMAL , None )
49- exinfo = CREATESOUNDEXINFO (length = clip .m_Size ,)
40+ #system = pyfmodex.System()
41+ sys_ptr = c_void_p ()
42+ ckresult (_dll .FMOD_System_Create (byref (sys_ptr )))
43+ #system.init(1, INIT_FLAGS.NORMAL, None)
44+ ckresult (_dll .FMOD_System_Init (sys_ptr , clip .m_Channels , None , None ))
5045
5146 # get sound
52- sound = system .create_sound (
53- bytes (clip .m_AudioData ),
54- mode = MODE .OPENMEMORY ,
55- exinfo = exinfo
56- )
47+ exinfo = byref (CREATESOUNDEXINFO (length = clip .m_Size ))
48+ #sound = system.create_sound(bytes(clip.m_AudioData),mode=MODE.OPENMEMORY,exinfo=exinfo)
49+ snd_ptr = c_void_p ()
50+ ckresult (_dll .FMOD_System_CreateSound (sys_ptr , bytes (
51+ clip .m_AudioData ), 0x00000800 , exinfo , byref (snd_ptr )))
52+ sound = Sound (snd_ptr )
5753 # iterate over subsounds
5854 samples = {}
5955 for i in range (sound .num_subsounds ):
@@ -66,13 +62,14 @@ def dump_samples(clip):
6662 subsound .release ()
6763
6864 sound .release ()
69- system .release ()
65+ # system.release()
66+ ckresult (_dll .FMOD_System_Release (sys_ptr ))
7067 return samples
7168
7269
7370def subsound_to_wav (subsound ):
7471 # get sound settings
75- length = subsound .get_length (TIMEUNIT .PCMBYTES )
72+ length = subsound .get_length (0x00000004 ) # TIMEUNIT.PCMBYTES
7673 channels = subsound .format .channels
7774 bits = subsound .format .bits
7875 sample_rate = int (subsound .default_frequency )
@@ -98,7 +95,186 @@ def subsound_to_wav(subsound):
9895 # data
9996 lock = subsound .lock (0 , length )
10097 for ptr , length in lock :
101- ptr_data = ctypes . string_at (ptr , length .value )
98+ ptr_data = string_at (ptr , length .value )
10299 w .write (ptr_data )
103100 subsound .unlock (* lock )
104101 return w .bytes
102+
103+
104+ # following code is copied from
105+ # https://github.com/tyrylu/pyfmodex
106+ # pyfmodex can't be used by itself
107+ # because it would require adding the libs to the path first
108+
109+ class CREATESOUNDEXINFO (Structure ):
110+ _fields_ = [("cbsize" , c_int ), ("length" , c_uint )]
111+
112+ def __init__ (self , * args , ** kwargs ):
113+ Structure .__init__ (self , * args , ** kwargs )
114+ self .cbsize = sizeof (self )
115+
116+
117+ class so :
118+ def __init__ (self , ** kwargs ):
119+ self .__dict__ .update (** kwargs )
120+
121+
122+ class Sound (object ):
123+ def __init__ (self , ptr ):
124+ """Constructor.
125+ :param ptr: The pointer representing this object.
126+ """
127+ self ._ptr = ptr
128+
129+ def _call_fmod (self , funcname , * args ):
130+ result = getattr (_dll , funcname )(self ._ptr , * args )
131+ ckresult (result )
132+
133+ @property
134+ def num_subsounds (self ):
135+ num = c_int ()
136+ self ._call_fmod ("FMOD_Sound_GetNumSubSounds" , byref (num ))
137+ return num .value
138+
139+ def get_subsound (self , index ):
140+ sh_ptr = c_void_p ()
141+ self ._call_fmod ("FMOD_Sound_GetSubSound" , index , byref (sh_ptr ))
142+ return Sound (sh_ptr )
143+
144+ def get_length (self , ltype ):
145+ len = c_uint ()
146+ self ._call_fmod ("FMOD_Sound_GetLength" , byref (len ), int (ltype ))
147+ return len .value
148+
149+ @property
150+ def format (self ):
151+ type = c_int ()
152+ format = c_int ()
153+ channels = c_int ()
154+ bits = c_int ()
155+ self ._call_fmod ("FMOD_Sound_GetFormat" , byref (type ),
156+ byref (format ), byref (channels ), byref (bits ))
157+ return so (type = type .value , format = format .value , channels = channels .value , bits = bits .value )
158+
159+ @property
160+ def default_frequency (self ):
161+ freq = c_float ()
162+ pri = c_int ()
163+ self ._call_fmod ("FMOD_Sound_GetDefaults" , byref (freq ), byref (pri ))
164+ return freq .value
165+
166+ def lock (self , offset , length ):
167+ ptr1 = c_void_p ()
168+ len1 = c_uint ()
169+ ptr2 = c_void_p ()
170+ len2 = c_uint ()
171+ ckresult (_dll .FMOD_Sound_Lock (self ._ptr , offset , length ,
172+ byref (ptr1 ), byref (ptr2 ), byref (len1 ), byref (len2 )))
173+ return ((ptr1 , len1 ), (ptr2 , len2 ))
174+
175+ def release (self ):
176+ self ._call_fmod ("FMOD_Sound_Release" )
177+
178+ def unlock (self , i1 , i2 ):
179+ """I1 and I2 are tuples of form (ptr, len)."""
180+ ckresult (_dll .FMOD_Sound_Unlock (self ._ptr , i1 [0 ], i2 [0 ], i1 [1 ], i2 [1 ]))
181+
182+
183+ def ckresult (result ):
184+ result = RESULT (result )
185+ if result is not RESULT .OK :
186+ raise FmodError (result )
187+
188+
189+ class FmodError (Exception ):
190+ def __init__ (self , result ):
191+ self .result = result
192+ self .message = result .name .replace ("_" , " " )
193+
194+ def __str__ (self ):
195+ return self .message
196+
197+
198+ class RESULT (Enum ):
199+ OK = 0
200+ BADCOMMAND = 1
201+ CHANNEL_ALLOC = 2
202+ CHANNEL_STOLEN = 3
203+ DMA = 4
204+ DSP_CONNECTION = 5
205+ DSP_DONTPROCESS = 6
206+ DSP_FORMAT = 7
207+ DSP_INUSE = 8
208+ DSP_NOTFOUND = 9
209+ DSP_RESERVED = 10
210+ DSP_SILENCE = 11
211+ DSP_TYPE = 12
212+ FILE_BAD = 13
213+ FILE_COULDNOTSEEK = 14
214+ FILE_DISKEJECTED = 15
215+ FILE_EOF = 16
216+ FILE_ENDOFDATA = 17
217+ FILE_NOTFOUND = 18
218+ FORMAT = 19
219+ HEADER_MISMATCH = 20
220+ HTTP = 21
221+ HTTP_ACCESS = 22
222+ HTTP_PROXY_AUTH = 23
223+ HTTP_SERVER_ERROR = 24
224+ HTTP_TIMEOUT = 25
225+ INITIALIZATION = 26
226+ INITIALIZED = 27
227+ INTERNAL = 28
228+ INVALID_FLOAT = 29
229+ INVALID_HANDLE = 30
230+ INVALID_PARAM = 31
231+ INVALID_POSITION = 32
232+ INVALID_SPEAKER = 33
233+ INVALID_SYNCPOINT = 34
234+ INVALID_THREAD = 35
235+ INVALID_VECTOR = 36
236+ MAXAUDIBLE = 37
237+ MEMORY = 38
238+ MEMORY_CANTPOINT = 39
239+ NEEDS3D = 40
240+ NEEDSHARDWARE = 41
241+ NET_CONNECT = 42
242+ NET_SOCKET_ERROR = 43
243+ NET_URL = 44
244+ NET_WOULD_BLOCK = 45
245+ NOTREADY = 46
246+ OUTPUT_ALLOCATED = 47
247+ OUTPUT_CREATEBUFFER = 48
248+ OUTPUT_DRIVERCALL = 49
249+ OUTPUT_FORMAT = 50
250+ OUTPUT_INIT = 51
251+ OUTPUT_NODRIVERS = 52
252+ PLUGIN = 53
253+ PLUGIN_MISSING = 54
254+ PLUGIN_RESOURCE = 55
255+ PLUGIN_VERSION = 56
256+ RECORD = 57
257+ REVERB_CHANNELGROUP = 58
258+ REVERB_INSTANCE = 59
259+ SUBSOUNDS = 60
260+ SUBSOUND_ALLOCATED = 61
261+ SUBSOUND_CANTMOVE = 62
262+ TAGNOTFOUND = 63
263+ TOOMANYCHANNELS = 64
264+ TRUNCATED = 65
265+ UNIMPLEMENTED = 66
266+ UNINITIALIZED = 67
267+ UNSUPPORTED = 68
268+ VERSION = 69
269+ EVENT_ALREADY_LOADED = 70
270+ EVENT_LIVEUPDATE_BUSY = 71
271+ EVENT_LIVEUPDATE_MISMATCH = 72
272+ EVENT_LIVEUPDATE_TIMEOUT = 73
273+ EVENT_NOTFOUND = 74
274+ STUDIO_UNINITIALIZED = 75
275+ STUDIO_NOT_LOADED = 76
276+ INVALID_STRING = 77
277+ ALREADY_LOCKED = 78
278+ NOT_LOCKED = 79
279+ RECORD_DISCONNECTED = 80
280+ TOOMANYSAMPLES = 81
0 commit comments