Jump to content
Official BF Editor Forums
Sign in to follow this  
Frankelstner

Ebx File Converter

Recommended Posts

Note: The guid-resolving (and thus much better) version is found here: http://www.bfeditor.org/forums/index.php?showtopic=15531&view=findpost&p=106219

Here's an improved version. Please download http://www.gamefront.com/files/22080360/floattostring_rar and place it in the main Python folder for a better float representation. This version requires the user to provide an output directory (default is C:/bf3 files). This time around the file names will always be read correctly (instead of just 95% of the time) and small numbers will be displayed too instead of just being rounded to zero. Also, the general syntax has been slightly changed (it makes more sense this time around). Drag and drop the folder onto the script file.

Here's my work in progress text/gameplay file converter. It converts all binary text files in the folder of the script. It reads everything without issues, though the layout is a bit cumbersome. A file format analysis can be found here: http://www.bfeditor.org/forums/index.php?showtopic=15531&view=findpost&p=105293

#requires Python 2.7
import string
import sys
from binascii import hexlify
from struct import unpack
import os
from cStringIO import StringIO

#adjust output folder here; you must specify a folder
outputFolder="C:/bf3 files"

try:
   #try to print a number as 0.95
   from ctypes import *
   floatlib = cdll.LoadLibrary("floattostring")
   def formatfloat(num):
       bufType = c_char * 100
       buf = bufType()
       bufpointer = pointer(buf)
       floatlib.convertNum(c_double(num), bufpointer, 100)
       rawstring=(buf.raw)[:buf.raw.find("\x00")]
       if rawstring[:2]=="-.": return "-0."+rawstring[2:]
       elif rawstring[0]==".": return "0."+rawstring[1:]
       elif "e" not in rawstring and "." not in rawstring: return rawstring+".0"
       return rawstring
except:
   #the number will be printed as 0.949999988079
   def formatfloat(num):
       return str(num)


def hasher(keyword): #32bit FNV-1 hash with FNV_offset_basis = 5381 and FNV_prime = 33
   hash = 5381
   for byte in keyword:
       hash = (hash*33) ^ ord(byte)
   return hash & 0xffffffff # use & because Python promotes the num instead of intended overflow



class Header:
   def __init__(self,varList): ##all 4byte unsigned integers
       self.absStringOffset     = varList[0]  ## absolute offset for string section start
       self.lenStringToEOF      = varList[1]  ## length from string section start to EOF
       self.numGUID             = varList[2]  ## number of external GUIDs
       self.null                = varList[3]  ## 00000000
       self.numInstanceRepeater = varList[4]
       self.numComplex          = varList[5]  ## number of complex entries
       self.numField            = varList[6]  ## number of field entries
       self.lenName             = varList[7]  ## length of name section including padding
       self.lenString           = varList[8]  ## length of string section including padding
       self.numArrayRepeater    = varList[9]
       self.lenPayload          = varList[10] ## length of normal payload section; the start of the array payload section is absStringOffset+lenString+lenPayload


class FieldDescriptor:
   #field has 4byte hash, 2byte type, 2byte reference/pointer, 4byte offset , 4byte secondary offset
   #e.g. 4B1F9065  3500  0000  0C000000  1C000000
   #     hash      type  ref   offset    offset2
   # => 'VoiceOverType', 0x0035, 0, 12, 28
   def __init__(self,varList):
       self.name            = keywordDict[varList[0]]
       self.type            = varList[1]
       self.ref             = varList[2] #the field may contain another complex
       self.offset          = varList[3] #offset in payload section; relative to the complex containing it
       self.secondaryOffset = varList[4]


class ComplexDescriptor:
   #complex has 4byte hash, 4byte field index, 1byte number of fields, 1byte alignment size, 2byte type, 2byte payload size, 2byte size2
   #e.g. 39E97F28  52000000    04   04     3500  5000 0000
   #     hash      fieldIndex  num  align  type  size size2
   # => 'EntityVoiceOverInfo', 82, 4, 4, 0x0035, 80, 0
   def __init__(self,varList):
       self.name            = keywordDict[varList[0]]
       self.fieldStartIndex = varList[1] #the index of the first field belonging to the complex
       self.numField        = varList[2] #the total number of fields belonging to the complex
       self.alignment       = varList[3]
       self.type            = varList[4]
       self.size            = varList[5] #total length of the complex in the payload section
       self.secondarySize   = varList[6] #seems deprecated


class InstanceRepeater:
   def __init__(self,varList):
       self.null            = varList[0] #seems to be always null
       self.repetitions     = varList[1] #number of instance repetitions
       self.complexIndex    = varList[2] #index of complex used as the instance

class arrayRepeater:
   def __init__(self,varList):
       self.offset          = varList[0] #offset in array payload section
       self.repetitions     = varList[1] #number of array repetitions
       self.complexIndex    = varList[2] #not necessary for extraction




def read(filename):
   global f1, f2, externalGUIDList, internalGUIDList, fields, complexes, header, trueFilename, arrayRepeaters, isPrimaryInstance, keywordDict

   #check magic
   try: f1=open(filename,"rb")
   except: return
   if f1.read(4)!="\xCE\xD1\xB2\x0F":
       f1.close()
       return

   print filename

   header=Header(unpack("11I",f1.read(44)))
   trueFilename="" #the cas extractor only guesses a filename which may be incorrect; but I do know how to find out the correct one

   #grab the file GUID and its primary instance. Make the hex numbers to string, e.g. 0x4b => "4b"
   fileGUID, primaryInstanceGUID = hexlify(f1.read(16)), hexlify(f1.read(16))


   #add all GUID pairs to a list. These are external GUIDs so the first GUID is the GUID
   #of another file and the second belongs to an instance inside that file (may or may not be primary)
   externalGUIDList=[(hexlify(f1.read(16)),hexlify(f1.read(16))) for i in range(header.numGUID)]

   #make list of names and make a dictionary hash vs name
   keywords=str.split(f1.read(header.lenName),"\x00")
##    while len(keywords[-1])==0:
##        keywords.pop() #remove the last few empty entries which appeared due to null-padding; not necessary because keywordDict does not mind
   keywordDict=dict((hasher(keyword),keyword) for keyword in keywords)


   #read all fields and complexes into lists; replace hashes with names instantly
   fields=[FieldDescriptor(unpack("IHHII",f1.read(16))) for i in xrange(header.numField)]
   complexes=[ComplexDescriptor(unpack("IIBBHHH",f1.read(16))) for i in xrange(header.numComplex)]


   #read instanceRepeater and arrayRepeater, each entry consists of 3 unsigned ints
   instanceRepeaters=[instanceRepeater(unpack("3I",f1.read(12))) for i in range(header.numInstanceRepeater)] 
   while f1.tell()%16!=0: f1.seek(1,1) #padding
   arrayRepeaters=[arrayRepeater(unpack("3I",f1.read(12))) for i in range(header.numArrayRepeater)]

   #ignore string section and read directly only when necessary. The elements are accessed directly via offset instead of index.
   f1.seek(header.absStringOffset+header.lenString)


   #START OF PAYLOAD SECTION


   ##make a list of all internal instance GUID, ignore the actual payload; this way I can instantly replace a payload Guid index with a string
   internalGUIDList=[]
   for instanceRepeater in instanceRepeaters:
       for repetition in xrange(instanceRepeater.repetitions):
           internalGUIDList.append(hexlify(f1.read(16)))
           f1.seek(complexes[instanceRepeater.complexIndex].size,1)

   f1.seek(header.absStringOffset+header.lenString) # go back to start of payload section



   ##do the same as above, but 1) don't make a list and 2) read the payload 
   f2=StringIO() #prepare stream to write the output into memory because filename is not known yet

   for instanceRepeater in instanceRepeaters:
       instance=complexes[instanceRepeater.complexIndex]

       for repetition in xrange(instanceRepeater.repetitions):
           tabLevel=1
           instanceGUID=hexlify(f1.read(16))
           startPos=f1.tell()
           if instanceGUID==primaryInstanceGUID:
               f2.write(instance.name+" "+instanceGUID+" #primary instance\r\n")
               isPrimaryInstance=True
           else:
               f2.write(instance.name+" "+instanceGUID+"\r\n")
               isPrimaryInstance=False
           readComplex(instance,tabLevel)
           f1.seek(startPos+instance.size)

   f1.close() # the source file is read and everything is in the f2 stream

   #create folder, file, etc.
   try:
       outFilename=os.path.join(outputFolder,trueFilename)+" "+fileGUID+".txt"
       if not os.path.isdir(os.path.dirname(outFilename)): os.makedirs(os.path.dirname(outFilename))
       f3=open(outFilename,"wb")
       f3.write(f2.getvalue())
       f3.close()
   except:
       print "Could not write file "+filename
       try: f3.close()
       except: pass
   f2.close()


##field types sorted by the value of their ref:
##ref==0 ("7d40","0dc1","3dc1","4dc1","5dc1","adc0","bdc0","ddc0","edc0","fdc0","3500"):
##        7d40: 4bytes; string, the value is the offset in the string section
##        0dc1: 4bytes; uint32
##        3dc1: 4bytes; single float
##        4dc1: 8bytes; double float
##        5dc1: 16bytes; GUID, referring to chunk files?
##        adc0: 1byte; bool, padded to 4 if no other adc0,bdc0, the same applies to the other <4 bytes values
##        bdc0: 1byte; int8
##        ddc0: 2bytes; uint16
##        edc0: 2bytes; int16
##        fdc0: 4bytes; int32
##        3500: 4bytes; GUID index, read as uint32 and the first bit is the isExternal flag. Do >>31 and afterwards use it as the index for the right GUID table
##                      
##
##ref!=0 ("4100","2900","29d0"):
##        4100: 4bytes; arrayRepeater index
##        2900: 0bytes; complex entry
##        29d0: 0bytes; complex entry
##
##
##ref sometimes 0, sometimes non 0 ("0000","8900"):
##        0000: 0bytes when field within an enum or 8bytes (all nulls) when element of "$" (which indicates inheritance)
##        8900: 4bytes; enum. Find the enum corresponding to the payload value


def readComplex(complex,tabLevel):
   #recursive function to read everything

   #get the fields for the complex
   fieldList=fields[complex.fieldStartIndex : complex.fieldStartIndex+complex.numField]

   startPos=f1.tell()
   if tabLevel!=1: f2.write("::"+complex.name+"\r\n")

   for field in fieldList:
       readField(field,startPos,tabLevel)

   f1.seek(startPos+complex.size)


def readField(field,startPos,tabLevel):
   f1.seek(startPos+field.offset)
##    f2.write("@"+str(f1.tell()))
   if field.type not in (0x0029,0xd029,0x0041,0x0000): #handle the simple stuff
       f2.write(tabLevel*"\t"+field.name+" "+unpackSimpleField(field)+"\r\n")

   elif field.type !=0x0041: #non arrays
       f2.write(tabLevel*"\t"+field.name)
       readComplex(complexes[field.ref],tabLevel+1) #recursion

   else: #arrays
       arrayIndex=unpack("I",f1.read(4))[0]
       if arrayIndex==0: #in contrast to the 0035 type, this time index 0 is reserved for these cases
           f2.write(tabLevel*"\t"+field.name+" *nullArray*"+"\r\n")
           return
       arrayRepeater=arrayRepeaters[arrayIndex] #no arrayIndex-1 necessary
       f1.seek(arrayRepeater.offset+header.absStringOffset+header.lenString+header.lenPayload)
       if arrayRepeater.repetitions==0: f2.write(tabLevel*"\t"+field.name+" *nullArray*"+"\r\n")
       else:
           arrayComplex=complexes[field.ref]
           memberField=fields[arrayComplex.fieldStartIndex]

           f2.write(tabLevel*"\t"+field.name)
           f2.write("::"+arrayComplex.name+"\r\n")
           for arrayRepetition in xrange(arrayRepeater.repetitions):
               position=f1.tell()
               readField(memberField,position,tabLevel+1) #recursion



#make a dictionary for the number/bool types. Mainly to save me from bloating the function below too much. Single floats not included either because I want to display them properly.
numDict={0xc10d:("I",4),0xc14d:("d",8),0xc0ad:("?",1),0xc0fd:("i",4),0xc0bd:("b",1),0xc0ed:("h",2), 0xc0dd:("H",2)}

def unpackSimpleField(field):
   #read everything except 0x0029, 0xd029, 0x0041, 0x0000
   #i.e. all assignments that do not contain another complex (0x0089 being the exception because it is so different)
   global trueFilename
   try:
       #if the entry is number/bool, extract it with the dictionary; else go to except
       (typ,length)=numDict[field.type]
       num=unpack(typ,f1.read(length))[0]
       return str(num)

   except:
       if field.type==0xc13d: return formatfloat(unpack("f",f1.read(4))[0])
       if field.type==0xc15d: return hexlify(f1.read(16)) #GUID, neither external nor internal
       elif field.type==0xc0dd: return hexlify(f1.read(2)) #not sure about this type

       elif field.type==0x0089:                                                                                     
           if field.ref==0: return "*nullEnum*"
           else:
               #The field points at another complex. The fields in this complex then are the choices.
               #Basically I go through the fields one level deeper. These fields do not behave like actual fields, lots of nulls and offset is no offset at all.
               compareValue=unpack("I",f1.read(4))[0] #this value must match fakefield.offset
               fieldList=fields[complexes[field.ref].fieldStartIndex : complexes[field.ref].fieldStartIndex+complexes[field.ref].numField]
               for fakeField in fieldList:
                   if fakeField.offset==compareValue:
                       return fakeField.name

       elif field.type==0x407d: #the string section
           #The 4bytes payload are the offset, so we need to remember where we are, then jump and read
           #a null-terminated string and jump back
           originalPos=f1.tell() 
           f1.seek(header.absStringOffset+unpack("I",f1.read(4))[0])

           string=""
           while 1:
               a=f1.read(1)
               if a=="\x00": break
               else: string+=a
           f1.seek(originalPos+4)

           if len(string)==0: return "*nullString*" #actually the string is ""

           if isPrimaryInstance and trueFilename=="" and field.name=="Name": trueFilename=string
           return string

       elif field.type==0x0035:
           #Write the GUID of another instance. There are two different cases.
           #If the instance is found in another file use externalGUIDList (2*16 bytes).
           #If the instance is found in this file use internalGUIDList (1*16 bytes).
           #The first bit is the isExternal flag. => bitshift by 31 and bitmask
           GUIDIndex=unpack("I",f1.read(4))[0]

           if GUIDIndex>>31: return "-".join(externalGUIDList[GUIDIndex&0x7fffffff])
           elif GUIDIndex==0: return "*nullGuid*"   #this being handled differently by the engine is a bit annoying as internalGUIDList of course has an element at index 0
           else: return internalGUIDList[GUIDIndex-1] #as a result, minus 1 is necessary


def main():
   if not outputFolder:
       return
   for ff in sys.argv[1:]:
       if os.path.isfile(ff):
           read(ff)
       else:
           for dir0,dirs,files in os.walk(ff):
               for f in files:
                   read(dir0+"\\"+f)

try:  
   main()
except Exception, e:
   raw_input(e)

main()

Edited by Frankelstner

Share this post


Link to post
Share on other sites

Amazing work! I've successfully extracted the files and I'm now trying to figure out how to make sense of the data. I get that fdc0 = integer, and adc0 = boolean, but how do I go from "8 10" to 100 (magazine capacity of M60)?

Share this post


Link to post
Share on other sites

Amazing work! I've successfully extracted the files and I'm now trying to figure out how to make sense of the data. I get that fdc0 = integer, and adc0 = boolean, but how do I go from "8 10" to 100 (magazine capacity of M60)?

You don't, at least not directly. The text file contains no values but merely tells us what to expect in the hex file. I'll try to explain the different parts of the text file. The number of assignments is hopefully explained well enough in the picture in my other post.

Then you have a "04/10" line indicated by ------. You can still see the 04/10 in 043500 for example. The last value of a 04/10 specifies where this 04/10 ends (or the next one begins). Thus when looking at a larger section of several 04/10s (a section of the type which starts with ######) you can see the total length of that section by looking at this number at the last 04/10 in that section; i.e. the last value of the last 04/10 in a section gives the length of the section in the hex file. You MUST ignore the 16 byte hashes at the beginning however, all the offsets require you to start counting after a hash.

The "00" lines are everything else. The 4 bytes seem to be the data type. The first number afterwards is the offset in the hex file. This offset is reset to null whenever a new section begins. As for the final number, I've made some guesses and was proven wrong again and again. I don't know its function yet. It's closely tied to the offset though.

As for the thing you were looking for: http://imgur.com/CjrVi

Note however that you can also find the magazine capacity at position 518 (in hex of course). It actually simply says "Ammo" at this very offset. Changing this value changes magazine capacity in SP. I suppose "Ammo" simply marks the beginning of a more detailed ammo section with lots of entries like this (this one appears much later on and in a different ##### section; I am not sure why):

AmmoConfigData			  042900	24    ----------
MagazineCapacity		fdc00000	0	0
NumberOfMagazines		fdc00000	4	4
TraceFrequency			0dc10000	8	8
AmmoPickupMinAmount		0dc10000	c	c
AmmoPickupMaxAmount		0dc10000	10	10
AutoReplenishMagazine		adc00000	14	20
AutoReplenishDelay		3dc10000	18	14
AmmoBagPickupDelayMultiplier	3dc10000	1c	1c
AmmoBagPickupAmount		fdc00000	20	18

Conveniently MagazineCapacity is the first entry and thus appears immediately at the beginning of the "Ammo" section. It might be that the game simply knows what to expect when "Ammo" appears and reads the next xy bytes on its own.

Share this post


Link to post
Share on other sites

Ah.. Now I see that they're actually addresses. However, I still have trouble understanding the part of how you find the specific locations in the hex file based on the values in the text file. I mean, I understand that I can find the ammo count at position 518, but what I don't get is how you arrived at "518" based on the information in the file.

Thanks for your time!

Share this post


Link to post
Share on other sites

Ah.. Now I see that they're actually addresses. However, I still have trouble understanding the part of how you find the specific locations in the hex file based on the values in the text file. I mean, I understand that I can find the ammo count at position 518, but what I don't get is how you arrived at "518" based on the information in the file.

Thanks for your time!

Yup, it's quite difficult still. The issue is that some things seem to behave more or less randomly, otherwise I'd have put the text and hex part together already. Let's go through the weapon A91 d0a2a4d908882da38597a848e9eb46416abb710f (the weapons are very similar; the magazine capacity relative offset is the same for a large number of files, i.e. 0x46c).

In the top there are path strings which I decided to include because they may or may not contain information necessary to understand the things below. You can ignore them for most purposes. Then there is a 16byte hash at 0x30 which belongs to the following section. For offset measurements you ignore this one too. Then you see some nulls, these can finally be found in the text file.

Number of assignments of type below: 1  ####################
############################################################
DataContainer			  043500	8    ----------

CameraRecoilData		  043500	14    ----------
$						00000000	0	0
SpringConstant			3dc10000	8	10
SpringDamping			3dc10000	c	14
SpringMinThresholdAngle	3dc10000	10	18

Hmm, the tabs are really horrible without notepad++. The first eight nulls after the hash belong to $ because $ starts at 0 and SpringConstant starts at 8 (one could argue with data types as well, maybe not so much when it just says $ 00000000). The bytes from 8 to c after the hash belong to SpringConstant, c to 10 SpringDamping. Finally SpringMinThresholdAngle goes from 10 to 14 because the 14 can be seen next to CameraRecoilData and thus defines the end of the section.

The file then has another hash.

Number of assignments of type below: 1  ####################
############################################################
EntityVoiceOverInfo		  043500	28    ----------
$						00000000	0	0
VoiceOverType			35000000	8	10
Labels					41000300	24	14

$ goes from 0 to 8, VoiceOverType goes from 8 to 24, Labels goes from 24 to 28.

Number of assignments of type below: 2  ####################
############################################################
array					  044100	4    ----------
member					35000000	0	0

GameDataContainer		  043500	8    ----------
$						00000000	0	0

FiringFunctionData		  103500	560    ----------
$						00000400	0	0
Dispersion				41000600	8	10
WeaponDispersion		29000800	c	14
FireEffects1p			41000900	50	58
FireEffects3p			41000900	54	5c
Sound					35000000	58	60
Shot					29000c00	90	70
FireLogic				29000d00	3d0	d0
Ammo					29001600	46c	168
OverHeat				29001700	490	190
UsePrimaryAmmo			adc00000	550	1f9
UnlimitedAmmoForAI		adc00000	551	1f8
AmmoCrateReloadDelay	3dc10000	554	1f4
SelfHealTimeWhenDeployed	3dc10000	558	1f0

Another hash again. The following section then has 560 bytes (look at FiringFunctionData). After these 560 bytes there will be another hash and another section with 560bytes again (Number of assignments of type below: 2). Now in this file apparently "member" starts at 0, but so does $ of the two other ---- sections below (I have no explanation for that yet). Additionally the last thing, SelfHealTimeWhenDeployed, starts at 558 and is just another ordinary float. It should end at 55c, but the section has length 560.

I could have saved all these ######## sections as separate files because getting to later parts in the file requires lots of manual work, though you can speed it up by finding things like 20 assignments of the same type to navigate faster. However I feel like they are connected somehow. You can see Ammo 29001600 46c 168 which apparently is an array defined later in the file. The first entry of Ammo happens to be MagazineCapacity.

So you add 0x30 bytes for the path names. Then 0x10 for one hash and 0x14 for its entries. Then 0x10 for another hash and 0x28 for its entries. Then 0x10 for another hash and now the offsets can be used.

It's tedious, but I can't do more until I understand these things better (which requires staring at these files unfortunately). If I were to make these offsets absolute for example I would need to calculate them for every single section, thus if a section has Number of assignments of type below: 5000 things will probably get out of hand.

Two more things. There is another section (probably) unrelated after the hex stuff that has corresponding things in the text file. I'm not perfectly sure that it is unrelated though, so I included it.

Also, it can be quite worthwhile to compare BC2 files with BF3 files, e.g. the materials file of a level in BC2 and a level in BF3:

BC2:

	<instance guid="55E26A49-DB11-4435-9F83-3A5AA936F334" type="GameSharedResources.MaterialPropertySoundData">
	<field name="SourceMaterial">GUIDnull</field>
	<field name="ImpactSound">GUIDnull</field>
	<field name="ScrapeSound">GUIDnull</field>
	<field name="ScrapeLength">1.0</field>
	<field name="SoldierSettings">GUIDa8ee7a57-dd90-4187-bdac-3f474799d307</field>
	<field name="Softness">0</field>
</instance>

BF3:

MaterialPropertySoundData	  043500	98    ----------
$							00000a00	0	0
ImpactSound					35000000	8	18
ScrapeSound					35000000	3c	1c
ScrapeLength				3dc10000	70	20
SoldierSettings				35000000	74	24
Softness					3dc10000	90	28
MaterialSoundId				3dc10000	94	2c

So 35 seems to expect a GUID of some kind.

Edited by Frankelstner

Share this post


Link to post
Share on other sites

Oh thanks! It makes some sense now. (Sort of)

Well, I'm off to take a closer look and send the weapon stats to DenKirson so we can get a quick chart going. Interesting to note is that instead of having damage data inside the weapons files individually, they just defined the weapons' bullets and assigned dmg based on bullet caliber/type.

Share this post


Link to post
Share on other sites

I slightly tweaked the start and end of ##### sections. Hopefully things are bundled together correctly now. At least the length of the entire section can be found right in the beginning instead of somewhere at the end. The entire arrangement makes more sense semantically too (which is how I figured this out; initially I bundled these things merely guided by hashes and got the name-hash function only later).

Hmm... it's still incorrect. I'll need some time to meditate.

Edited by Frankelstner

Share this post


Link to post
Share on other sites

It seems to work correctly now. The next step is figuring out all the different assignment types. Once that is done everything can be put together. The first line now tells how it's called by other lines, e.g. "array 103500 560 -Referenced as: e". If another line has "FireEffects1p 4100 50 58 e array" then this line points at the first one. I've added "array" at the end of the second line for faster identification, but when there happen to be several arrays one still needs to look up the number. The number of whitespace on the left can be adjusted with the variable "padding" at the top of the code.

Edited by Frankelstner

Share this post


Link to post
Share on other sites
I will probably mess around a bit more and see if I can try to understand the explosive damage system (slightly different from BF2 or BC2) and the vehicles.

The explosive system does not look different to me. One thing to keep in mind though, my cas extractor went for a relatively cheap way of figuring out the path structure (I've mentioned that in the thread too). It grabs the first path with two slashes in it. Thus the names are not always correct; so just go for relatively large files. I've discovered just yesterday where the correct paths are saved, might do it some time this week. First I'm going to take a break though.

		EmpTime 0.0
	MaxOcclusionRaycastRadius 0.5
	InnerBlastRadius 0.5
	BlastDamage 112.0
	BlastRadius 7.0
	BlastImpulse 2000.0
	HasStunEffect False
	ShockwaveDamage 1.0
	ShockwaveRadius 10.0
	ShockwaveImpulse 500.0
	ShockwaveTime 0.25
	DisableOcclusion False
	DisableStaticEntityOcclusion True
	CameraShockwaveRadius 0.0
	SpawnDelay 0.0
	TriggerImpairedHearing True
	DetonateOnce True

		<field name="EmpTime">0</field>
	<field name="CoreRadius">0.34</field>
	<field name="BlastDamage">100.0</field>
	<field name="InnerBlastRadius">0.34</field>
	<field name="BlastRadius">6.0</field>
	<field name="BlastImpulse">10000.0</field>
	<field name="ShockwaveDamage">1.0</field>
	<field name="ShockwaveRadius">10.0</field>
	<field name="ShockwaveImpulse">2000.0</field>
	<field name="ShockwaveTime">0.3</field>
	<field name="DisableOcclusion">false</field>
	<field name="DisableStaticEntityOcclusion">true</field>
	<field name="UseAsPreDestuctionEntity">false</field>
	<field name="UnspotsOnExplode">false</field>

Share this post


Link to post
Share on other sites

Yeah, but in some files, the same variables are defined twice, with different values. Not sure what's up with that...

Hey rukqoa,

If it's not too big of a hassle, could you explain how you were able to edit the initfs_win32 file and still be able to get online or even allow the game to start up in the 1st place. I've been trying tons of different things and combinations for the past few hours and I just cant figure it out. It either crashes bf3.exe before it's able to start properly or just hangs at "joining game" or "initializing". I want to up the graphics and animations in the game beyond the ULTRA settings and I would really appreciate it if you could help :D

Share this post


Link to post
Share on other sites

Hey rukqoa,

If it's not too big of a hassle, could you explain how you were able to edit the initfs_win32 file and still be able to get online or even allow the game to start up in the 1st place. I've been trying tons of different things and combinations for the past few hours and I just cant figure it out. It either crashes bf3.exe before it's able to start properly or just hangs at "joining game" or "initializing". I want to up the graphics and animations in the game beyond the ULTRA settings and I would really appreciate it if you could help :D

The end result has to be the exact same filesize as before you edit.

Share this post


Link to post
Share on other sites

The end result has to be the exact same filesize as before you edit.

That's what I'm doing, it's exactly the same size. I havent been changing any true/false values. I've only been changing integer values. Even when changing one value from 0 to 1 causes the game to freeze. Would it be possible for you to change a few settings for me to test out and see?

Share this post


Link to post
Share on other sites

That's what I'm doing, it's exactly the same size. I havent been changing any true/false values. I've only been changing integer values. Even when changing one value from 0 to 1 causes the game to freeze. Would it be possible for you to change a few settings for me to test out and see?

Did you encrypt it back?

Share this post


Link to post
Share on other sites
I want to up the graphics and animations in the game beyond the ULTRA settings and I would really appreciate it if you could help :D

Look, the first thing I'd do is to follow the steps rukqoa has given to turn off shadows to check that everything works correctly and that I understood the concept. Afterwards I'd try my own tweaks. It's not that hard really, just common sense!

And if you did this and it crashed you can easily deduce that the game does simply does not accept the specific value you changed.

Share this post


Link to post
Share on other sites

Look, the first thing I'd do is to follow the steps rukqoa has given to turn off shadows to check that everything works correctly and that I understood the concept. Afterwards I'd try my own tweaks. It's not that hard really, just common sense!

And if you did this and it crashed you can easily deduce that the game does simply does not accept the specific value you changed.

I also tried that. I tried just turning off the shadows and nothing else. Maybe there are multiple ways to disable shadows. He didnt specifically say which cvar/command he changed.

Sorry, I dont mean to be a bother.

Edited by mistan

Share this post


Link to post
Share on other sites

Nevermind! I got it working!!! Wooo, time to take the graphics to a new level!

Is it possible to get banned for this?

You'll most likely get kicked while joining if they decide to lock the file. It would be highly unusual if there were bans for this.

Share this post


Link to post
Share on other sites

You'll most likely get kicked while joining if they decide to lock the file. It would be highly unusual if there were bans for this.

No kick, no ban. PB doesn't even bother to compare the hash.

Share this post


Link to post
Share on other sites

mistan can u explain what u were doing wrong im trying to mod the ps3 version and ur problem is kinda what im having the game freezes on boot

and trial and error isnt is easy for me cuz every time i make a mod to the file i have to wait 9mins for the data transfer from one system to the other to test it

the only thing im changing is the true to false and i take a . but it still craps out iv checked the size and its right so im starting to think the ps3 patch checks it self before boot

if someone has advise it would be nice

sincerely Fox

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×