

Several months ago, @ScottBrooks, a Beamdog developer provided a method to work around the inability to change the text file(s) of the game for iOS (dialog.tlk/dialogF.tlk). A bit later, @WhiteAgnus reported on the BG1 NPC Project forum that the method was also working on Android. That method, which allowed me to offer a partial French version for BG2EE on Android, also opens the gate to using mods on Android, far beyond the currently limited set of override only mods that were already available (because of the restrictions).
You will need a computer pour install mods. However you don't need to have a computer version of the game installed on the computer.
You need an access to the content of your tablet/phone from your computer. To do that, you may need to install a driver or a software provided by the manufacturer of your Android device. For instance, for Sony, I had to install the Sony PC Companion software, which in turn installed the drivers to access the tablet file system.
Finally, you need to download the centralfix.exe software provided by ScottBrooks for Windows. It is not restricted to Windows though. The author also provided the source code, written in Go (a language created by Google and available here), to allow players running another OS to run the software (I'm afraid I can't give any hint on how to proceed for Linux or Mac OS X though).
This procedure was tested on Windows.
WARNINGThis procedure was tested on the beta version of BGEE for Android available from Beamdog for volunteers who register to their google group. I can't confirm that it also works with version 1.3.2053 (latest official patch).
Version 2.5 of BG2EE (and potentially of other EE games) doesn't work properly with the method described below. :( However @Gusinda wrote a new guide for setting up mods for this version.
WeiDU is not available on Android. Therefore we need to build a game environment on a Windows computer that looks like a typical install of the game on that system. Fortunately it's possible to do that starting from the game files installed on your Android tablet or phone.
Note that you could use Linux of Mac OS X instead. In this case, you will need to ensure that you can run the centralfix software from its sources, as it is mandatory for the last step. I'm afraid I can't give any direction for that.
Starting from now, I'll use tablet as a generic way to refer to your tablet or phone.
centralfix Mods.zip
Nota : the step involving centralfix is described only for Windows as I have has no experience with Go and using Mac OS X or Linux.
In the procedure above, files dialog.tlk/dialogF.tlk and the override directory are the bare minimum set of files to include into the mods archive. This should cover most cases.
According to what mods are changing in the game, you might have to add other items into the mods archive:
Nota : the list above is not exhaustive.
As a general rule, use the ability to sort files by date in the various game directories in order to spot the files added or changed by the mods and build the same directory/files structure in the Android_Files directory.
Finally you'll have to add all those files when building the Mods.zip archive.
It is not necessary to transfer onto the tablet the mods directories created when you extract their archives, the game doesn't need them. However you'd better keep the BGEE_Android as it is if you want to be able to update, uninstall or add mods into your game. In case you change your mods installation, you will have to repeat the procedure to create the mods archive, centralfix it and transfer it to your tablet.
Note that the game handles the presence of several archives build using this method. I didn't try to identify if the game had any order when loading them. I suggest avoiding conflicts in the archives content if you want to use this capability.
In my test case, the two archives had non conflicting content: a French voice pack for the game (containing lang/fr_FR/data), and the mods archive (containing override and lang/fr_FR/dialog.tlk).
Using this method, I was able to install the BG1 NPC Project mod (together with its musics), BG1 UB, Find and Thalantyr Item Upgrade for BGEE. So far I didn't get very far enough in the game to check a lot of things, however I can confirm that the mods content actually appears in the game: I had the early meeting with Finch in Candlekeep with a newly created game.
The obb files are in Android/obb/com.beamdog.baldursgateIIenhancededition and are named (with the beta version):
The mods archive shall be copied into Android/data/com.beamdog.baldursgateIIenhancededition/files
I used this method to build a partial French translation package. This is not strictly a mod but at its core, it's similar as it adds a dialog.tlk file to the game. Although I tested it with the beta version, I had confirmation from another player that the package works with the current non beta version (1.3.2064).
Thanks to @Kamigoroshi, who confirmed that this method worked with Icewind Dale Enhanced Edition, here are some more detailed information. The obb files are named:
It is likely that they are located in Android/obb/com.beamdog.icewinddale, if Beamdog followed the same naming convention as for the data (see below).
The mods archive shall be copied into Android/data/com.beamdog.icewinddale/files
; chargen.selectedRace = chargen.races[ currentChargenRace ].id
In menu 'CHARGEN_CLASS', add the following to the end of the DONE_BUTTON action (before the end double quote):
chargen.selectedClass = chargen.class[ currentChargenClass ].id
And finally, replace the all of the menu 'CHARGEN_ABILITIES' code with the following:
BG:EE no SoD:
`
--auto-roller version 2016.04.09.0002
raceHasExceptionalStr = {
true, -- Human
true, -- Elf
true, -- Half-Elf
true, -- Dwarf
false, -- Halfling
true, -- Gnome
true -- Half-Orc
}
classHasExceptionalStr = {
false, -- Mage
true, -- Fighter
false, -- Cleric
false, -- Thief
false, -- Bard
true, -- Paladin
true, -- Fighter / Mage
true, -- Fighter / Cleric
true, -- Fighter / Thief
true, -- Fighter / Mage / Thief
false, -- Druid
true, -- Ranger
false, -- Mage / Thief
false, -- Cleric / Mage
false, -- Cleric / Thief
true, -- Fighter / Druid
true, -- Fighter / Mage / Cleric
true, -- Cleric / Ranger
false, -- Sorcerer
false, -- Monk
false -- Shaman
}
function HasExceptionalStrength( )
return raceHasExceptionalStr[ chargen.selectedRace ] and classHasExceptionalStr[ chargen.selectedClass ]
end
function ShowExceptionalStrength( )
local strength = tonumber( string.sub( chargen.ability[ 1 ].roll, 1, 2 ) )
local abilityToDec = 2
if strength ~= nil then
while ( strength ~= nil ) and ( strength < 18 ) do
createCharScreen:OnAbilityPlusMinusButtonClick( abilityToDec, false )
abilityToDec = abilityToDec + 1
if( abilityToDec == 7 ) then
abilityToDec = 2
end
createCharScreen:OnAbilityPlusMinusButtonClick( 1, true )
strength = tonumber( string.sub( chargen.ability[ 1 ].roll, 1, 2 ) )
end
end
end
RerollFrame = 0
storedTotalRoll = 0
function GetAutoRoll()
if rolling == 1 then
RerollFrame = RerollFrame + 1
if RerollFrame > 1 then
RerollFrame = 0
end
if RerollFrame == 0 then
createCharScreen:OnAbilityReRollButtonClick()
local exceptionalStrength = 0
if( HasExceptionalStrength() ) then
ShowExceptionalStrength( )
exceptionalStrength = tonumber( string.sub( chargen.ability[ 1 ].roll, 4 ) )
if exceptionalStrength ~= nil then
if exceptionalStrength == 0 then
exceptionalStrength = 100
end
else
exceptionalStrength = 0
end
end
if ( ( storedTotalRoll == chargen.totalRoll ) and ( chargen.ability[ 1 ].exceptional < exceptionalStrength ) ) or
( storedTotalRoll < chargen.totalRoll ) then
storedTotalRoll = chargen.totalRoll
chargen.ability[ 1 ].exceptional = exceptionalStrength
chargen.ability[ 1 ].storedRoll = chargen.ability[ 1 ].roll
chargen.ability[ 2 ].storedRoll = chargen.ability[ 2 ].roll
chargen.ability[ 3 ].storedRoll = chargen.ability[ 3 ].roll
chargen.ability[ 4 ].storedRoll = chargen.ability[ 4 ].roll
chargen.ability[ 5 ].storedRoll = chargen.ability[ 5 ].roll
chargen.ability[ 6 ].storedRoll = chargen.ability[ 6 ].roll
createCharScreen:OnAbilityStoreButtonClick()
end
end
end
return
end
`
menu
{
name 'CHARGEN_ABILITIES'
align center center
ignoreesc
onopen "ticksPassed = 0; ticksStarting = 0"
label
{
area 0 0 1024 768
mosaic 'GUICGB'
enabled "CurrentlyInGame()"
}
label
{
area 0 156 1024 612
mosaic GUISMDB
}
label
{
area 20 174 496 48
text "ABILITIES_TITLE"
text style "title"
}
list
{
column
{
width 40
label
{
area 0 0 190 55
text lua "t(chargen.ability[rowNumber].name)"
text style "normal"
align right center
}
}
column
{
width 22
label
{
area 0 0 90 55
text lua "chargen.ability[rowNumber].roll"
text style "normal"
align center center
}
}
column
{
width 9
label
{
area 0 6 45 42
bam GUIOSW
frame lua "currentCellCheck(3)"
sequence 0
}
}
column
{
width 12
label
{
area 0 6 45 42
bam GUIOSW
frame lua "currentCellCheck(4)"
sequence 1
}
}
column
{
width 22
label
{
area 0 0 90 55
text lua "chargen.ability[rowNumber].storedRoll"
text style "normal"
align center center
}
}
action
"
if ticksStarting < 10 then
if cellNumber == 3 then
createCharScreen:OnAbilityPlusMinusButtonClick(currentChargenAbility, true)
elseif cellNumber == 4 then
createCharScreen:OnAbilityPlusMinusButtonClick(currentChargenAbility, false)
end
end
cellNumber = nil
ticksPassed = 0
ticksStarting = 0
"
actionUpdate "
ticksStarting = ticksStarting + 1
if ticksStarting > 10 then
ticksPassed = ticksPassed + 1
if ticksPassed > 7 then
if cellNumber == 3 then
createCharScreen:OnAbilityPlusMinusButtonClick(currentChargenAbility, true)
elseif cellNumber == 4 then
createCharScreen:OnAbilityPlusMinusButtonClick(currentChargenAbility, false)
end
ticksPassed = 0
end
end
"
rowheight 54
hidehighlight
area 32 254 492 322
table "chargen.ability"
var currentChargenAbility
}
label
{
area 32 580 186 42
text "TOTAL_ROLL_NORMAL"
text style "normal"
text align right center
}
label
{
area 248 580 50 42
text lua "chargen.totalRoll"
text style "normal"
text align center center
}
label
{
area 334 580 94 42
text lua "chargen.extraAbilityPoints"
text style "normal"
text align center center
}
label
{
area 458 580 50 42
text lua "storedTotalRoll"
text style "normal"
text align center center
}
text
{
area 582 192 404 406
text lua "abilityOrGeneralHelp()"
text style "normal"
scrollbar 'GUISCRC'
}
button
{
area 568 638 142 40
bam GUIOSTSM
sequence 0
text "AUTO-REROLL"
text style "button"
action "
if rolling == 1 then
rolling = 0
createCharScreen:OnAbilityRecallButtonClick()
else
chargen.ability[ 1 ].exceptional = tonumber( string.sub( chargen.ability[ 1 ].roll, 4 ) )
if chargen.ability[ 1 ].exceptional ~= nil then
if chargen.ability[ 1 ].exceptional == 0 then
chargen.ability[ 1 ].exceptional = 100
end
else
chargen.ability[ 1 ].exceptional = 0
end
storedTotalRoll = chargen.totalRoll
rolling = 1
end"
}
button
{
mosaic lua "GetAutoRoll()"
area 1 1 1 1
}
button
{
area 368 628 142 40
bam GUIOSTSM
sequence 0
text "REROLL_BUTTON"
text style "button"
action "createCharScreen:OnAbilityReRollButtonClick()"
}
button
{
area 60 628 142 40
bam GUIOSTSM
sequence 0
text "STORE_BUTTON"
text style "button"
action "
local exceptionalStrength = tonumber( string.sub( chargen.ability[ 1 ].roll, 4 ) )
if exceptionalStrength ~= nil then
if exceptionalStrength == 0 then
exceptionalStrength = 100
end
else
exceptionalStrength = 0
end
storedTotalRoll = chargen.totalRoll
chargen.ability[ 1 ].exceptional = exceptionalStrength
chargen.ability[ 1 ].storedRoll = chargen.ability[ 1 ].roll
chargen.ability[ 2 ].storedRoll = chargen.ability[ 2 ].roll
chargen.ability[ 3 ].storedRoll = chargen.ability[ 3 ].roll
chargen.ability[ 4 ].storedRoll = chargen.ability[ 4 ].roll
chargen.ability[ 5 ].storedRoll = chargen.ability[ 5 ].roll
chargen.ability[ 6 ].storedRoll = chargen.ability[ 6 ].roll
createCharScreen:OnAbilityStoreButtonClick()
"
}
button
{
area 214 628 142 40
bam GUIOSTSM
sequence 0
text "RECALL_BUTTON"
text style "button"
clickable lua "createCharScreen:IsAbilityRecallButtonClickable()"
action "createCharScreen:OnAbilityRecallButtonClick()"
}
button
{
on '8'
action "createCharScreen:OnCheatyMcCheaterson()"
}
button
{
on escape
area 194 718 232 44
bam GUIOSTUL
text "BACK_BUTTON"
text style "button"
action "Infinity_PopMenu(); createCharScreen:OnCancelButtonClick()"
}
button
{
on return
area 438 718 206 44
bam GUIOSTUM
text "DONE_BUTTON"
text style "button"
clickable lua "createCharScreen:IsDoneButtonClickable()"
action "Infinity_PopMenu(); createCharScreen:OnDoneButtonClick()"
}
}
`
--auto-roller version 2016.04.13.0005
raceHasExceptionalStr = {
true, -- Human
true, -- Elf
true, -- Half-Elf
true, -- Dwarf
false, -- Halfling
true, -- Gnome
true -- Half-Orc
}
classHasExceptionalStr = {
false, -- Mage
true, -- Fighter
false, -- Cleric
false, -- Thief
false, -- Bard
true, -- Paladin
true, -- Fighter / Mage
true, -- Fighter / Cleric
true, -- Fighter / Thief
true, -- Fighter / Mage / Thief
false, -- Druid
true, -- Ranger
false, -- Mage / Thief
false, -- Cleric / Mage
false, -- Cleric / Thief
true, -- Fighter / Druid
true, -- Fighter / Mage / Cleric
true, -- Cleric / Ranger
false, -- Sorcerer
false, -- Monk
false -- Shaman
}
function HasExceptionalStrength( )
return raceHasExceptionalStr[ chargen.selectedRace ] and classHasExceptionalStr[ chargen.selectedClass ]
end
function ShowExceptionalStrength( )
local strength = tonumber( string.sub( chargen.ability[ 1 ].roll, 1, 2 ) )
local abilityToDec = 2
if strength ~= nil then
while ( strength ~= nil ) and ( strength < 18 ) do
createCharScreen:OnAbilityPlusMinusButtonClick( abilityToDec, false )
abilityToDec = abilityToDec + 1
if( abilityToDec == 7 ) then
abilityToDec = 2
end
createCharScreen:OnAbilityPlusMinusButtonClick( 1, true )
strength = tonumber( string.sub( chargen.ability[ 1 ].roll, 1, 2 ) )
end
end
end
function AutoRoll( )
createCharScreen:OnAbilityReRollButtonClick()
local exceptionalStrength = 0
if( HasExceptionalStrength() ) then
ShowExceptionalStrength( )
exceptionalStrength = tonumber( string.sub( chargen.ability[ 1 ].roll, 4 ) )
if exceptionalStrength ~= nil then
if exceptionalStrength == 0 then
exceptionalStrength = 100
end
else
exceptionalStrength = 0
end
end
if ( ( storedTotalRoll == chargen.totalRoll ) and ( chargen.ability[ 1 ].exceptional < exceptionalStrength ) ) or
( storedTotalRoll < chargen.totalRoll ) then
storedTotalRoll = chargen.totalRoll
chargen.ability[ 1 ].exceptional = exceptionalStrength
chargen.ability[ 1 ].storedRoll = chargen.ability[ 1 ].roll
chargen.ability[ 2 ].storedRoll = chargen.ability[ 2 ].roll
chargen.ability[ 3 ].storedRoll = chargen.ability[ 3 ].roll
chargen.ability[ 4 ].storedRoll = chargen.ability[ 4 ].roll
chargen.ability[ 5 ].storedRoll = chargen.ability[ 5 ].roll
chargen.ability[ 6 ].storedRoll = chargen.ability[ 6 ].roll
createCharScreen:OnAbilityStoreButtonClick()
end
end
RerollFrame = 0
storedTotalRoll = 0
function UpdateAutoRoll()
if rolling == 1 then
RerollFrame = RerollFrame + 1
if RerollFrame > 1 then
RerollFrame = 0
end
if RerollFrame == 0 then
local index = 1
for index = 1, 1000, 1 do
AutoRoll( )
end
end
end
end
`
menu
{
name 'CHARGEN_ABILITIES'
modal
align center center
ignoreesc
onopen "ticksPassed = 0; ticksStarting = 0"
label
{
area 0 0 864 710
mosaic GUICGROL
}
label
{
area 220 22 426 44
text "ABILITIES_TITLE"
text style "title"
}
list
{
column
{
width 34
label
{
area 0 0 100 55
text lua "t(chargen.ability[rowNumber].name)"
text style "normal"
align right center
}
}
column
{
width 18
label
{
area 0 0 50 55
text lua "chargen.ability[rowNumber].storedRoll"
text style "normal"
align center center
}
}
column
{
width 13
label
{
area 0 0 50 55
text lua "chargen.ability[rowNumber].roll"
text style "normal"
align center center
}
}
column
{
width 22
label
{
area 36 6 45 42
bam GUIOSW
frame lua "currentCellCheck(4)"
sequence 0
}
}
column
{
width 15
label
{
area 0 6 45 42
bam GUIOSW
frame lua "currentCellCheck(5)"
sequence 1
}
}
action
"
if ticksStarting < 10 then
if cellNumber == 4 then
createCharScreen:OnAbilityPlusMinusButtonClick(currentChargenAbility, true)
elseif cellNumber == 5 then
createCharScreen:OnAbilityPlusMinusButtonClick(currentChargenAbility, false)
end
end
cellNumber = nil
ticksPassed = 0
ticksStarting = 0
"
actionUpdate
"
ticksStarting = ticksStarting + 1
if ticksStarting > 10 then
ticksPassed = ticksPassed + 1
if ticksPassed > 2 then
if cellNumber == 4 then
createCharScreen:OnAbilityPlusMinusButtonClick(currentChargenAbility, true)
elseif cellNumber == 5 then
createCharScreen:OnAbilityPlusMinusButtonClick(currentChargenAbility, false)
end
ticksPassed = 0
end
end
"
rowheight 54
hidehighlight
area 34 85 386 325
table "chargen.ability"
var currentChargenAbility
}
label
{
area 34 410 100 54
text "TOTAL_ROLL_NORMAL"
text style "normal"
text align right center
}
label
{
area 164 410 50 54
text lua "storedTotalRoll"
text style "normal"
text align center center
}
label
{
area 233 410 50 54
text lua "chargen.totalRoll"
text style "normal"
text align center center
}
label
{
area 322 410 94 54
text lua "chargen.extraAbilityPoints"
text style "normal"
text align center center
}
text
{
area 442 91 394 505
text lua "abilityOrGeneralHelp()"
text style "normal"
scrollbar 'GUISCRC'
}
button
{
area 230 480 200 44
bam GUIBUTNT
sequence 0
text "AUTO-REROLL"
text style "button"
action "
if rolling == 1 then
rolling = 0
createCharScreen:OnAbilityRecallButtonClick()
else
chargen.ability[ 1 ].exceptional = tonumber( string.sub( chargen.ability[ 1 ].roll, 4 ) )
if chargen.ability[ 1 ].exceptional ~= nil then
if chargen.ability[ 1 ].exceptional == 0 then
chargen.ability[ 1 ].exceptional = 100
end
else
chargen.ability[ 1 ].exceptional = 0
end
storedTotalRoll = chargen.totalRoll
rolling = 1
end"
}
button
{
mosaic lua "UpdateAutoRoll()"
area 1 1 1 1
}
button
{
area 26 480 200 44
bam GUIBUTNT
sequence 0
text "REROLL_BUTTON"
text style "button"
action "createCharScreen:OnAbilityReRollButtonClick()"
}
button
{
area 26 531 200 44
bam GUIBUTNT
sequence 0
text "STORE_BUTTON"
text style "button"
action "
local exceptionalStrength = tonumber( string.sub( chargen.ability[ 1 ].roll, 4 ) )
if exceptionalStrength ~= nil then
if exceptionalStrength == 0 then
exceptionalStrength = 100
end
else
exceptionalStrength = 0
end
storedTotalRoll = chargen.totalRoll
chargen.ability[ 1 ].exceptional = exceptionalStrength
chargen.ability[ 1 ].storedRoll = chargen.ability[ 1 ].roll
chargen.ability[ 2 ].storedRoll = chargen.ability[ 2 ].roll
chargen.ability[ 3 ].storedRoll = chargen.ability[ 3 ].roll
chargen.ability[ 4 ].storedRoll = chargen.ability[ 4 ].roll
chargen.ability[ 5 ].storedRoll = chargen.ability[ 5 ].roll
chargen.ability[ 6 ].storedRoll = chargen.ability[ 6 ].roll
createCharScreen:OnAbilityStoreButtonClick()
"
}
button
{
area 26 582 200 44
bam GUIBUTNT
sequence 0
text "RECALL_BUTTON"
text style "button"
clickable lua "createCharScreen:IsAbilityRecallButtonClickable()"
action "createCharScreen:OnAbilityRecallButtonClick()"
}
button
{
on '8'
action "createCharScreen:OnCheatyMcCheaterson()"
}
button
{
on escape
area 196 653 230 44
bam GUIBUTMT
text "BACK_BUTTON"
text style "button"
action "Infinity_PopMenu(); createCharScreen:OnCancelButtonClick()"
}
button
{
on return
area 438 653 230 44
bam GUIBUTMT
text "DONE_BUTTON"
text style "button"
clickable lua "createCharScreen:IsDoneButtonClickable()"
action "Infinity_PopMenu(); createCharScreen:OnDoneButtonClick()"
}
}