]> Devoid-pointer.net GitWeb - ffe-parser.git/commitdiff
Initial commit.
authorMichal Malý <madcatxster@devoid-pointer.net>
Tue, 17 Jun 2014 14:13:25 +0000 (16:13 +0200)
committerMichal Malý <madcatxster@devoid-pointer.net>
Tue, 17 Jun 2014 14:13:25 +0000 (16:13 +0200)
ffe_parse.py [new file with mode: 0644]

diff --git a/ffe_parse.py b/ffe_parse.py
new file mode 100644 (file)
index 0000000..4f7613e
--- /dev/null
@@ -0,0 +1,548 @@
+# Script to parse the FFE files
+#
+# This script requires 'Construct' to run:
+# https://pypi.python.org/pypi/construct
+
+from construct import *
+import argparse
+
+try:
+       import os
+       import sys
+       import ctypes
+       import sdl2
+       _hasPySDL2 = True
+except ImportError:
+       _hasPySDL2 = False 
+
+guid_constant = ' \x1cT\x133\x8e\xd0\x11\x9a\xd0\x00\xa0\xc9\xa0n5'
+guid_ramp     = '!\x1cT\x133\x8e\xd0\x11\x9a\xd0\x00\xa0\xc9\xa0n5'
+guid_square   = '"\x1cT\x133\x8e\xd0\x11\x9a\xd0\x00\xa0\xc9\xa0n5'
+guid_sine     = '#\x1cT\x133\x8e\xd0\x11\x9a\xd0\x00\xa0\xc9\xa0n5'
+guid_triangle = '$\x1cT\x133\x8e\xd0\x11\x9a\xd0\x00\xa0\xc9\xa0n5'
+guid_sawup    = '%\x1cT\x133\x8e\xd0\x11\x9a\xd0\x00\xa0\xc9\xa0n5'
+guid_sawdown  = '&\x1cT\x133\x8e\xd0\x11\x9a\xd0\x00\xa0\xc9\xa0n5'
+guid_spring   = "'\x1cT\x133\x8e\xd0\x11\x9a\xd0\x00\xa0\xc9\xa0n5"
+guid_damper   = '(\x1cT\x133\x8e\xd0\x11\x9a\xd0\x00\xa0\xc9\xa0n5'
+guid_inertia  = ')\x1cT\x133\x8e\xd0\x11\x9a\xd0\x00\xa0\xc9\xa0n5'
+guid_friction = '*\x1cT\x133\x8e\xd0\x11\x9a\xd0\x00\xa0\xc9\xa0n5'
+guid_custom   = '+\x1cT\x133\x8e\xd0\x11\x9a\xd0\x00\xa0\xc9\xa0n5'
+#---
+
+header = Struct("header",
+       Const(Bytes("magic", 4), "RIFF"),
+       UBInt32("length"),
+
+       Const(Bytes("magic2", 4), "FORC"),
+       Bytes("header", 16),
+
+       OptionalGreedyRange(LazyBound("listBlock", lambda: listBlock)),
+)
+
+#---
+
+listBlock = Struct("listBlock",
+       Const(Bytes("magic", 4), "LIST"),
+       ULInt32("length"),
+
+       LazyBound("effectBlock", lambda: effectBlock),
+
+       Bytes("Guid", 16),
+       ULInt32("lpDiEffect"),                                  # name is guessed
+
+       LazyBound("dataAxis", lambda: dataAxis),
+       LazyBound("dataDirection", lambda: dataDirection ),
+
+       If(this.effectBlock.lpEnvelope,
+               LazyBound("dataEnvelope", lambda: dataEnvelope),
+       ),
+
+       # Is using Guid the only way?
+       Switch("dataSpecific" , this.Guid,
+               {
+               guid_constant : LazyBound("dataSpecificConstant", lambda: dataSpecificConstant),
+               guid_ramp     : LazyBound("dataSpecificRamp",     lambda: dataSpecificRamp),
+               guid_square   : LazyBound("dataSpecificPeriodic", lambda: dataSpecificPeriodic),
+               guid_sine     : LazyBound("dataSpecificPeriodic", lambda: dataSpecificPeriodic),
+               guid_triangle : LazyBound("dataSpecificPeriodic", lambda: dataSpecificPeriodic),
+               guid_sawup    : LazyBound("dataSpecificPeriodic", lambda: dataSpecificPeriodic),
+               guid_sawdown  : LazyBound("dataSpecificPeriodic", lambda: dataSpecificPeriodic),
+               guid_spring   : LazyBound("dataSpecificSpring",   lambda: dataSpecificSpring),
+               guid_damper   : LazyBound("dataSpecificSpring",   lambda: dataSpecificSpring),
+               guid_inertia  : LazyBound("dataSpecificSpring",   lambda: dataSpecificSpring),
+               guid_friction : LazyBound("dataSpecificSpring",   lambda: dataSpecificSpring),
+               guid_custom   : LazyBound("dataSpecificCustom",   lambda: dataSpecificCustom),
+               },
+               default = Pass
+       ),
+)
+
+effectBlock = Struct("effectBlock",
+       Const(Bytes("magic", 4), "efct"),
+
+       CString("szFriendlyName"),
+       Padding(lambda ctx: 63 - len(ctx["szFriendlyName"])),   # pad name to fill 64 bytes
+
+       # Reference:
+       # http://msdn.microsoft.com/en-us/library/windows/desktop/microsoft.directx_sdk.reference.dieffect%28v=vs.85%29.aspx
+
+       ULInt32("dwSize"),
+
+       BitStruct("dwFlags",                                    # Low Byte MSB's first
+               Padding(1),
+               BitField("DIEFF_SPHERICAL", 1),
+               BitField("DIEFF_POLAR", 1),
+               BitField("DIEFF_CARTESIAN", 1),
+               Padding(2),
+               BitField("DIEFF_OBJECTOFFSETS", 1),
+               BitField("DIEFF_OBJECTIDS", 1),
+               Padding(24),
+       ),
+
+       ULInt32("dwDuration"),
+       ULInt32("dwSamplePeriod"),
+       ULInt32("dwGain"),
+       ULInt32("dwTriggerButton"),
+       ULInt32("dwTriggerRepeatInterval"),
+       ULInt32("cAxes"),
+       ULInt32("rgdwAxes"),
+       ULInt32("rglDirection"),
+       ULInt32("lpEnvelope"),
+       ULInt32("cbTypeSpecificParams"),
+       ULInt32("lpvTypeSepecificParams"),
+       ULInt32("dwStartDelay"),
+)
+
+#---
+
+dataAxis = Struct("dataAxis",
+       Const(Bytes("magic", 4), "data"),
+       ULInt32("length"),
+
+       Array(this._.effectBlock.cAxes, ULInt32("axis")),       # 'this._.' refers to parent
+)
+
+dataDirection = Struct("dataDirection",
+       Const(Bytes("magic", 4), "data"),
+       ULInt32("length"),
+
+       Array(this._.effectBlock.cAxes, SLInt32("direction")),
+)
+
+dataEnvelope = Struct("dataEnvelope",
+       Const(Bytes("magic", 4), "data"),
+       ULInt32("length"),
+
+       ULInt32("dwSize"),
+       ULInt32("dwAttackLevel"),
+       ULInt32("dwAttackTime"),
+       ULInt32("dwFadeLevel"),
+       ULInt32("dwFadeTime"),
+)
+
+#---
+
+dataSpecificConstant = Struct("dataSpecificConstant",
+       Const(Bytes("magic", 4), "data"),
+       ULInt32("length"),
+
+       SLInt16("lMagnitude"),
+)
+
+dataSpecificRamp = Struct("dataSpecificRamp",
+       Const(Bytes("magic", 4), "data"),
+       ULInt32("length"),
+
+       SLInt32("lStart"),
+       SLInt32("lEnd"),
+)
+
+dataSpecificPeriodic = Struct("dataSpecificPeriodic",
+       Const(Bytes("magic", 4), "data"),
+       ULInt32("length"),
+
+       ULInt32("dwMagnitude"),
+       SLInt32("lOffset"),
+       ULInt32("dwPhase"),
+       ULInt32("dwPeriod"),
+)
+
+dataSpecificSpring = Struct("dataSpecificSpring",
+       Const(Bytes("magic", 4), "data"),
+       ULInt32("length"),
+
+       Array(this._.effectBlock.cAxes, Struct("dataSpecificSpringArray",
+               SLInt32("lOffset"),
+               SLInt32("lPositiveCoefficient"),
+               SLInt32("lNegativeCoefficient"),
+               ULInt32("dwPositiveSaturation"),
+               ULInt32("dwNegativeSaturation"),
+               SLInt32("lDeadBand"),
+               ),
+       ),
+)
+
+dataSpecificCustom = Struct("dataSpecificCustom",
+       Const(Bytes("magic", 4), "data"),
+       ULInt32("length"),
+
+       ULInt32("cChannels"),
+       ULInt32("dwSamplePeriod"),
+       ULInt32("cSamples"),
+
+       Array(this.cSamples, Array(this.cChannels, ULInt16("rglForceData"))),
+)
+
+#---
+# check length of dataAxis/etc - redefine as block refers to higher section
+# new block includes the cAxis count, so overall length is increased by 4 bytes
+
+dataAxisLen = Struct("dataAxisLen",
+       Struct("effectBlock", ULInt32("cAxes"),),
+       LazyBound("dataAxis", lambda: dataAxis),
+)
+
+dataDirectionLen = Struct("dataDirectionLen",
+       Struct("effectBlock", ULInt32("cAxes"),),
+       LazyBound("dataDirection", lambda: dataDirection),
+)
+
+dataSpecificSpringLen = Struct("dataSpecificSpingLen",
+       Struct("effectBlock", ULInt32("cAxes"),),
+       LazyBound("dataSpecific", lambda: dataSpecificSpring),
+)
+
+def fix_block_lengths(effect):
+       effect.length = len(listBlock.build(effect)) - 8
+
+       # fix effectBlock - does this ever change?
+       effect.effectBlock.length = len(effectBlock.build(effect.effectBlock)) - 68     # name is not included in size
+
+       # fix Axis
+       effect.dataAxis.length = len(dataAxisLen.build(effect)) - 12
+
+       # fix Direction
+       effect.dataDirection.length = len(dataDirectionLen.build(effect)) - 12
+
+       if (effect.effectBlock.lpEnvelope):
+               effect.dataEnvelope.length = len(dataEnvelope.build(effect.dataEnvelope)) - 8
+
+       # fix data specific blocks
+       if (effect.Guid == guid_constant):
+               effect.dataSpecific.length = len(dataSpecificConstant.build(effect.dataSpecific)) - 8
+
+       if (effect.Guid == guid_ramp):
+               effect.dataSpecific.length = len(dataSpecificRamp.build(effect.dataSpecific)) - 8
+
+       if (effect.Guid == guid_square or \
+                       effect.Guid == guid_sine or \
+                       effect.Guid == guid_triangle or \
+                       effect.Guid == guid_sawup or \
+                       effect.Guid == guid_sawdown):
+               effect.dataSpecific.length = len(dataSpecificPeriodic.build(effect.dataSpecific)) - 8
+
+       if (effect.Guid == guid_spring or \
+                       effect.Guid == guid_damper or \
+                       effect.Guid == guid_inertia or \
+                       effect.Guid == guid_friction):
+               effect.dataSpecific.length = len(dataSpecificSpringLen.build(effect)) - 12
+               
+#---
+
+haptic = None
+
+def init_sdl2(device):
+       global haptic
+       if haptic != None:
+               return
+
+       sdl2.SDL_Init(sdl2.SDL_INIT_TIMER | sdl2.SDL_INIT_JOYSTICK | sdl2.SDL_INIT_HAPTIC)
+       if (sdl2.SDL_NumHaptics() == 0):
+               print "Error: Unable to initilize SDL"
+               sdl2.SDL_Quit()
+               return
+
+       for index in range(0,sdl2.SDL_NumHaptics()):
+               print "Found", index, ":", sdl2.SDL_HapticName(index)
+
+       if device >= sdl2.SDL_NumHaptics():
+               print "Error: Device not found"
+               sdl2.SDL_Quit()
+               return
+
+       haptic = sdl2.SDL_HapticOpen(device);
+       if haptic == None:
+               print "Unable to open device"
+               sdl2.SDL_Quit()
+               return
+
+def play_effect(ffe, device=0, linux=0):
+       global haptic
+       if haptic == None:
+               init_sdl2(device)
+
+       if haptic == None:
+               # fail out cleanly
+               return
+
+       nefx = 0
+       efx = [0] * 12
+       id = [0] * 12
+       stoptime = 0
+
+       supported = sdl2.SDL_HapticQuery(haptic)
+
+       # assemble effects
+       for item in ffe.listBlock:
+               print "Effect", item.effectBlock.szFriendlyName
+
+               if nefx==12:
+                       print "Sorry too many effects"
+                       break
+
+               # Convert uSec -> mSec
+               timediv = 1000
+
+               # Overall Gain, SDL max force = +/-32767
+               gain = 3.2767 * item.effectBlock.dwGain / 10000
+
+               # Envelope
+               if item.effectBlock.lpEnvelope:
+                       attackLevel = int(item.dataEnvelope.dwAttackLevel * gain)
+                       attackTime = item.dataEnvelope.dwAttackTime / timediv
+                       fadeLevel = int(item.dataEnvelope.dwFadeLevel * gain)
+                       fadeTime = item.dataEnvelope.dwFadeTime / timediv
+               else:
+                       attackLevel = int(10000 * gain)
+                       attackTime = 0
+                       fadeLevel = int(10000 * gain)
+                       fadeTime = 0
+
+               # Direction
+               if sdl2.SDL_HapticNumAxes(haptic) == 1:
+                       print "   Warning: single axis device, forcing Cartesian mode"
+                       direction=sdl2.SDL_HapticDirection(type=sdl2.SDL_HAPTIC_CARTESIAN, dir=(0,0,0))
+               else:
+                       direction=sdl2.SDL_HapticDirection(type=sdl2.SDL_HAPTIC_POLAR, dir=(9000,0,0))
+                       if item.effectBlock.dwFlags.DIEFF_CARTESIAN:
+                               cart = item.dataDirection.direction[:2]
+                               if linux:
+                                       cart[0] *= -1
+                               direction=sdl2.SDL_HapticDirection(type=sdl2.SDL_HAPTIC_CARTESIAN, dir=(tuple(cart)))
+                               print "Cartesian", cart
+
+                       if item.effectBlock.dwFlags.DIEFF_POLAR:
+                               polar = item.dataDirection.direction[:2]
+                               direction=sdl2.SDL_HapticDirection(type=sdl2.SDL_HAPTIC_POLAR, dir=(tuple(polar)))
+                               print "Polar", polar
+
+                       if item.effectBlock.dwFlags.DIEFF_SPHERICAL:
+                               spherical = item.dataDirection.direction[:2]
+                               direction=sdl2.SDL_HapticDirection(type=sdl2.SDL_HAPTIC_SPHERICAL, dir=(tuple(spherical)))
+                               print "Spherical", spherical
+
+
+               if item.Guid == guid_constant and supported & sdl2.SDL_HAPTIC_CONSTANT:
+                       print "   Loading effect", nefx, "Constant Force"
+                       efx[nefx] = sdl2.SDL_HapticEffect(type=sdl2.SDL_HAPTIC_CONSTANT, constant=sdl2.SDL_HapticConstant( \
+                               type=sdl2.SDL_HAPTIC_CONSTANT, \
+                               direction=direction, \
+                               level=int(item.dataSpecific.lMagnitude * gain), \
+                               length=item.effectBlock.dwDuration / timediv, \
+                               attack_length=attackTime, \
+                               attack_level=attackLevel, \
+                               fade_length=fadeTime, \
+                               fade_level=fadeLevel, \
+                               delay=item.effectBlock.dwStartDelay / timediv))
+                       id[nefx] = sdl2.SDL_HapticNewEffect(haptic, efx[nefx])
+                       if id[nefx] < 0:
+                               print "   Error", sdl2.SDL_GetError()
+                       nefx += 1
+
+               if item.Guid == guid_square:
+                       print "   Warning: Square is not currently supported SDL2, using sine instead"
+    
+               if (item.Guid == guid_sine or item.Guid == guid_square) and supported & sdl2.SDL_HAPTIC_SINE:
+                       print "   Loading effect", nefx, "Sine Periodic"
+                       efx[nefx] = sdl2.SDL_HapticEffect(type=sdl2.SDL_HAPTIC_SINE, periodic=sdl2.SDL_HapticPeriodic( \
+                               type=sdl2.SDL_HAPTIC_SINE, \
+                               direction=direction, \
+                               period=item.dataSpecific.dwPeriod / timediv, \
+                               magnitude=int(item.dataSpecific.dwMagnitude * gain), \
+                               offset=item.dataSpecific.lOffset, \
+                               phase=item.dataSpecific.dwPhase, \
+                               length=item.effectBlock.dwDuration, \
+                               attack_length=attackTime, \
+                               attack_level=attackLevel, \
+                               fade_length=fadeTime, \
+                               fade_level=fadeLevel, \
+                               delay=item.effectBlock.dwStartDelay / timediv))
+                       id[nefx] = sdl2.SDL_HapticNewEffect(haptic, efx[nefx])
+                       if id[nefx] < 0:
+                               print "   Error", sdl2.SDL_GetError()
+                       nefx += 1
+
+               if item.Guid == guid_triangle and supported & sdl2.SDL_HAPTIC_TRIANGLE:
+                       print "   Loading effect", nefx, "Triangle Periodic"
+                       efx[nefx] = sdl2.SDL_HapticEffect(type=sdl2.SDL_HAPTIC_TRIANGLE, periodic=sdl2.SDL_HapticPeriodic( \
+                               type=sdl2.SDL_HAPTIC_TRIANGLE, \
+                               direction=direction, \
+                               period=item.dataSpecific.dwPeriod / timediv, \
+                               magnitude=int(item.dataSpecific.dwMagnitude * gain), \
+                               offset=item.dataSpecific.lOffset, \
+                               phase=item.dataSpecific.dwPhase, \
+                               length=item.effectBlock.dwDuration / timediv, \
+                               attack_length=attackTime, \
+                               attack_level=attackLevel, \
+                               fade_length=fadeTime, \
+                               fade_level=fadeLevel, \
+                               delay=item.effectBlock.dwStartDelay / timediv))
+                       id[nefx] = sdl2.SDL_HapticNewEffect(haptic, efx[nefx])
+                       if id[nefx] < 0:
+                               print "   Error", sdl2.SDL_GetError()
+                       nefx += 1
+
+               if item.Guid == guid_sawup and supported & sdl2.SDL_HAPTIC_SAWTOOTHUP:
+                       print "   Loading effect", nefx, "Sawtooth Up"
+                       efx[nefx] = sdl2.SDL_HapticEffect(type=sdl2.SDL_HAPTIC_SAWTOOTHUP, periodic=sdl2.SDL_HapticPeriodic( \
+                               type=sdl2.SDL_HAPTIC_SAWTOOTHUP, \
+                               direction=direction, \
+                               period=item.dataSpecific.dwPeriod / timediv, \
+                               magnitude=int(item.dataSpecific.dwMagnitude * gain), \
+                               offset=item.dataSpecific.lOffset, \
+                               phase=item.dataSpecific.dwPhase, \
+                               length=item.effectBlock.dwDuration / timediv, \
+                               attack_length=attackTime, \
+                               attack_level=attackLevel, \
+                               fade_length=fadeTime, \
+                               fade_level=fadeLevel, \
+                               delay=item.effectBlock.dwStartDelay / timediv))
+                       id[nefx] = sdl2.SDL_HapticNewEffect(haptic, efx[nefx])
+                       if id[nefx] < 0:
+                               print "   Error", sdl2.SDL_GetError()
+                       nefx += 1
+
+               if item.Guid == guid_sawdown and supported & sdl2.SDL_HAPTIC_SAWTOOTHDOWN:
+                       print "   Loading effect", nefx, "Sawtooth Down"
+                       efx[nefx] = sdl2.SDL_HapticEffect(type=sdl2.SDL_HAPTIC_SAWTOOTHDOWN, periodic=sdl2.SDL_HapticPeriodic( \
+                               type=sdl2.SDL_HAPTIC_SAWTOOTHDOWN, \
+                               direction=direction, \
+                               period=item.dataSpecific.dwPeriod / timediv, \
+                               magnitude=int(item.dataSpecific.dwMagnitude * gain), \
+                               offset=item.dataSpecific.lOffset, \
+                               phase=item.dataSpecific.dwPhase, \
+                               length=item.effectBlock.dwDuration, \
+                               attack_length=attackTime, \
+                               attack_level=attackLevel, \
+                               fade_length=fadeTime, \
+                               fade_level=fadeLevel, \
+                               delay=item.effectBlock.dwStartDelay / timediv))
+                       id[nefx] = sdl2.SDL_HapticNewEffect(haptic, efx[nefx])
+                       if id[nefx] < 0:
+                               print "   Error", sdl2.SDL_GetError()
+                       nefx += 1
+
+               if item.Guid == guid_ramp and supported & sdl2.SDL_HAPTIC_RAMP:
+                       print "   Loading effect", nefx, "Ramp"
+                       efx[nefx] = sdl2.SDL_HapticEffect(type=sdl2.SDL_HAPTIC_RAMP, ramp=sdl2.SDL_HapticRamp( \
+                               type=sdl2.SDL_HAPTIC_RAMP, \
+                               direction=direction, \
+                               start=int(item.dataSpecific.lStart * gain), \
+                               end=int(item.dataSpecific.lEnd * gain), \
+                               length=item.effectBlock.dwDuration / timediv, \
+                               attack_length=attackTime, \
+                               attack_level=attackLevel, \
+                               fade_length=fadeTime, \
+                               fade_level=fadeLevel, \
+                               delay=item.effectBlock.dwStartDelay / timediv))
+                       id[nefx] = sdl2.SDL_HapticNewEffect(haptic, efx[nefx])
+                       if id[nefx] < 0:
+                               print "   Error", sdl2.SDL_GetError()
+                       nefx += 1
+
+               time = (item.effectBlock.dwDuration + item.effectBlock.dwStartDelay) / timediv
+               if time > stoptime:
+                       stoptime = time
+
+       # play all simulataneously
+       for i in range(0, nefx):
+               print "Playing effect", i
+               ret = sdl2.SDL_HapticRunEffect(haptic, id[i], 1)
+               if ret < 0:
+                       print "   Error", sdl2.SDL_GetError()
+
+       # wait for all effects to complete
+        sdl2.SDL_Delay(stoptime)
+
+       # clean up
+       for i in range(0, nefx):
+               sdl2.SDL_HapticStopEffect(haptic, id[i]);
+               sdl2.SDL_HapticDestroyEffect(haptic, id[i])
+       
+#---
+
+if __name__ == "__main__":
+       parser = argparse.ArgumentParser(prog="ffe_parse")
+       parser.add_argument("filename", nargs=1)
+       parser.add_argument("-d", "--dump", action='store_true', help="decode file and output to screen")
+       parser.add_argument("-w", "--write", dest="write", help="write data to '.ffe' file")
+
+       # SDW - edit test
+       parser.add_argument("-a", "--axis", action='store_true', help="toggle number of axis")
+       parser.add_argument("-r", "--rotate", dest="rotate", help="rotate polar by Degrees")
+
+       if (_hasPySDL2):
+               parser.add_argument("-p", "--play", action='store_true', help="play effect with SDL2")
+               parser.add_argument("-D", "--device", dest="device", default=0, help="SDL2 device to use (integer 0-)")
+               parser.add_argument("-l", "--linux", action='store_true', help="correct cartesian co-ords for linux")
+
+       options = parser.parse_args()
+
+       datafile = open(options.filename[0])
+       ffe = header.parse(datafile.read(65536))                # Can't imagine these files would be any larger
+       datafile.close()
+
+       if options.rotate:
+               for effect in ffe.listBlock:
+                       if effect.effectBlock.dwFlags.DIEFF_POLAR:
+                               effect.dataDirection.direction[0] += int(options.rotate) * 100
+
+       if options.axis:
+               for effect in ffe.listBlock:
+                       if effect.effectBlock.cAxes == 2:
+                               effect.effectBlock.cAxes = 1
+                       elif effect.effectBlock.cAxes == 1:
+                               effect.effectBlock.cAxes = 2
+
+                       # shorten/lengthen arrays
+                       effect.dataAxis.axis.extend(effect.dataAxis.axis[-1:]*(effect.effectBlock.cAxes-len(effect.dataAxis.axis)))
+                       effect.dataAxis.axis = effect.dataAxis.axis[:effect.effectBlock.cAxes]
+
+                       effect.dataDirection.direction.extend(effect.dataDirection.direction[-1:]*(effect.effectBlock.cAxes-len(effect.dataDirection.direction)))
+                       effect.dataDirection.direction = effect.dataDirection.direction[:effect.effectBlock.cAxes]
+
+                       if (effect.Guid == guid_spring or \
+                                       effect.Guid == guid_damper or \
+                                       effect.Guid == guid_inertia or \
+                                       effect.Guid == guid_friction):
+                               effect.dataSpecific.dataSpecificSpringArray.extend(effect.dataSpecific.dataSpecificSpringArray[-1:]* \
+                                               (effect.effectBlock.cAxes-len(effect.dataSpecific.dataSpecificSpringArray)))
+                               effect.dataSpecific.dataSpecificSpringArray = effect.dataSpecific.dataSpecificSpringArray[:effect.effectBlock.cAxes]
+
+                       # recompute block lengths
+                       fix_block_lengths(effect)
+
+       if options.dump:
+               print "Parsing file:", options.filename[0]
+               print "---"
+               print ffe
+
+       if options.write:
+               datafile = open(options.write, 'w')
+               datafile.write(header.build(ffe))
+               datafile.close()
+
+       if _hasPySDL2:
+               if options.play:
+                       play_effect(ffe, int(options.device), options.linux)
\ No newline at end of file