(note: I'm not finished writing this. All contact should be sent to artaxerxes2 at iname.com)
If, like me, you were disappointed not to be able to play Serpent Isle in your own mother tongue, especially if the first part, Black Gate, was translated, and if you are not afraid to spend hours at it, you might want to translate Serpent Isle yourself. It took a team of 3-4 people over a year to do a usable translation, doing it during their free time, and about the same for the proofreading. This document will address the biggest questions you might face: where to start? what to do?
Important note: you cannot use Unicode. If your language requires Unicode, you cannot translate Serpent Isle the way I describe here. I do not know of any other way you could translate Serpent Isle. Sorry!
A fully translated Serpent Isle fits those criteria:
This document will help you out with descriptions, conversations and graphics. The rest will be briefly discussed at the end of this document.
You will need some tools for the translation. The Exult Team made available a package which contains some very important tools that you must acquire to translate Serpent Isle, otherwise you'll have to write your own! This package is available on their download page and is called "Exult Tools". You will also need some perl scripts you can download on **** TODO: PUT LINK HERE ****.
This is what you see when you single click on an item, NPC or, weirdly enough, when you are cold or hungry and a few other situations. All of that is found in the file
text.flx, from your
static/ directory. A flex file shouldn't be edited directly. Since it holds text, use the tool
textpack, from the Exult tools you downloaded earlier. Convert the file into a text file using the following command:
textpack -x text.flx text.txt
The first few lines of the resulting
text.txt are as follow:
# Written by Exult Textpack tool 0:water 1:sidewalk 2: 3:icy water 4:floor 5:cavefloor 6:icy water 7:spring 8:water
You are now ready to edit the file
text.txt using a regular text editor. Do not alter the numbers in front of each line! Only change the text. This file is roughly split in three categories, grouped by line numbers:
Once you are finished translating this file (do not use Unicode, it is not supported and won't work!), you need to make it again a flex file. Use the following command:
textpack -c text.flx text.txt
It's time to try out your file! Edit your exult.cfg file and add a <patch> section under the <serpentisle> section if you did not have one already. My configuration file looks like that:
<config> <disk> <game> <serpentisle> <path> /home/aurelien/jeux/u7p2 </path> <gamedat_path> /home/aurelien/.exult/serpentisle/gamedat </gamedat_path> <savegame_path> /home/aurelien/.exult/serpentisle </savegame_path> <keys> (default) </keys> <patch> /home/aurelien/jeux/u7p2/patch </patch> </serpentisle> ... rest of config file here ... </config>Under Windows, it might be:
<config> <disk> <game> <serpentisle> <path> C:\games\ultima\serpentisle\ </path> <gamedat_path> C:\games\ultima\serpentisle\gamedat\ </gamedat_path> <savegame_path> C:\games\ultima\serpentisle\savegame\ </savegame_path> <keys> (default) </keys> <patch> C:\games\ultima\serpentisle\patch\ </patch> </serpentisle> ... rest of config file here ... </config>
Copy your brand new
text.flx to your
patch/ directory as set in your configuration file. Under Linux
cp text.flx /home/aurelien/jeux/u7p2/patchUnder Windows:
copy text.flx C:\games\ultima\serpentisle\patch
Now start Exult, and start playing a bit of Serpent Isle. Click on the gangplank (line #150 in the text.flx file) once and see the name in your language appear! If it doesn't, make sure you have setup a <patch> directory in your exult.cfg file, make sure Exult uses the right exult.cfg file and make sure you read
Patch : /home/aurelien/jeux/u7p2/patch in either
stdout.txt or on your standard output. Also, make sure you copied the translated
text.flx into this directory! If it is still not working, contact me via email (address at the bottom of this document).
It might happen that you used non-ASCII characters in your
text.flx file and while playing, you quickly realised they didn't show in the descriptions. For instance, you translated "light" into "lumière", but when you click on the light in the floor of the cavern (where the bed roll is), it shows "lumire" instead. The reason is simple: each character you typed in the
text.txt file has an character-code. Character-code in the ASCII table have an associated bitmap in the file
fonts.vga but the non-ASCII character codes don't. To show the non-ASCII character codes, you will have to create the bitmaps yourself, or use my patched version of
fonts.vga which contains the (almost complete) iso8859-1 table bitmaps. If you use another character table (like iso8859-2 or others), or if you don't want to use my patched version of
fonts.vga, you will have to alter it yourself.
The simplest and fastest way I have found to alter
fonts.vga is to use yet another tools provided by the Exult Team,
expack. We will concentrate more on the default yellow text, but the same applies to all the fonts used in Serpent Isle (11 fonts in total!). Type the following command:
expack -x fonts.vga
This will create 12 files, named
00.shp and edit it using the Gimp or Photoshop. You will need to install the plugins to edit a .shp file first. The plugins can be obtained from Exult's download page. I do not recommend putting bitmaps in the lower (<32) frames otherwise you will have troubles later on. Simply add layers to the image, each layer showing one bitmap. The layer number must represent the character-code you wish to use. For instance, the letter "é" is character-code #233 (0xE9) in iso8859-1. So it must the 233rd layer in your
00.shp file. You should never have more than 256 characters in your character code. Exult does not support Unicode.
Once you are finished, you need to repack the file into a usable
fonts.vga. Rename the file
00.u7o first, then use the following command:
expack -c fonts.vga ??.u7o
Copy this new
fonts.vga to your
patch/ directory and start Exult. Your new bitmaps should show now! (note: you could use this technique to use a different font if the default Gothic yellow gets on your nerves.)
This is the core of your translation effort. All conversations (when you double-click on most NPCs for instance) are found in the file
usecode from your
static/ directory. The tools you downloaded earlier will help you to translate this file. At this point you have an alternative. Either you translate the content of the
usecode file using:
In either case there is some preparation to do. Put the
usecode file into a directory, say
translation/, as well as the script
le_camomille.pl (don't ask about the name) and the Exult Tools
rip. From this
translation/ directory, type the following command:
perl le_camomille.pl -prepare
After a few minutes, you'll see a large amount of new files and a few new directories. That's OK. The script splitted the
usecode file into a whole lot of individual compiled usecode functions (using
rip), decompiled each function (using
wud) and separated the
data section from the
code section in each of those decompiled usecode functions. For information, the new directories are:
code/: contains the
codepart of each decompiled usecode function. Leave it untouched for now.
data/: contains the
datapart of each decompiled usecode function. The original text is there, but don't touch it.
todo/: contains the
datapart of usecode functions not translated yet. That's the one you want to translate.
french_data/: contains the
datapart of usecode functions already translated.
In either method, you must be very careful about certain pitfalls we encountered in our translation work. Here are some random notes you should get acquainted with.
This method works (we used it for the whole translation) but requires some rigor in your procedures. Also, unless you create a CVS repository, this method shouldn't be used if the translating team is more than 1 person. Pick a file from the
todo/ directory, translate it and when you are done, move it to the
french_data/ directory. Keep doing this until there is no more files in the
An extract of what you would look at would be for instance:
.funcnumber 0401H .data L0000: db '@Dupre...@' L000A: db 00 L000B: db '@Yes, ' L0011: db 00 L0012: db '?@' L0014: db 00 L0015: db '@Bring to me a woman!@' L002B: db 00 L002C: db '@I will slay you all!@' L0042: db 00 L0043: db '@Fulfill thy desires!@' L0059: db 00 L005A: db '@I must have ale!@' L006C: db 00 L006D: db 'leave' L0072: db 00 L0073: db 'join' L0077: db 00 L0078: db '"How good to see thee again, ' L0095: db 00 L0096: db '! Knowing that thou wouldst soon return, I have waited for t' db 'hee at this establishment."' L00ED: db 00 L00EE: db '@And he hath developed quite a bar tab!@' L0116: db 00 L0117: db '"One that I shall pay, worry thou not!"' L013E: db 00 L013F: db '"And I have good news for thee, ' L015F: db 00 L0160: db '."' L0162: db 00 L0163: db 'good news' L016C: db 00 L016D: db 'join' L0171: db 00 L0172: db 'leave' L0177: db 00In this case, this function is from the file
0401.uc.dataand thus is for Dupre (NPC # + 0x400 = usecode function). You must not change the
Lxxxx: dbpart and ignore the lines where the text is only
00(do not delete them though!).
Notice the first seven lines of actual text (L0000, L000B, L0012, L0015, L002C, L0043 and L005A) are barked. Also, the lines you reply are L006D, L0073, L0163, L016D and L0172, the rest is said by the NPC. You could also infer L00EE is said by another NPC given what is being said. This line is not barked however, but the actual NPC's face is shown and this text is displayed, unfortunately no visual clues will inform you of that.
Also, notice L000B and L0012: in this case, you can infer the Avatar's name will be inserted between the two. Same thing between L013F and L0160.
L0096 is split on two lines since it is too big to display on one. This limitation is due to
wud and is only for display purposes. When you translate, you can make a line almost as long as you want. Consequently, you could end up with the following for this line:
L0096: db ' ! Comme je savais que tu allais revenir bientôt, j'ai décidé de t'attendre dans cet établissement."' L00ED: db 00
Very important in this case, L006D and L0172 are identical, same for L0073 and L016D. If you make them different, you won't be able to click on them during the game. Try it once so that you will know what the symptoms are when that mistake is done.
Finally, there is a connection between L013F and L0163. When Dupre said "And I have good news for thee", the answer 'good news' is added. Try to keep it consistent.
Frequently, you want to check out the result of the translation. You must recreate the
usecode file using the newly translated data first. For this, you can again use my perl script
perl le_camomille.pl -creationThis command will use the files found in the
french_data/directory when recombining the
datapart from the
codepart, will recompile each function (using
wuc) and put all usecode functions into one
Simply copy this newly created
usecode file to your
patch/ directory and try it out! I recommend you to get started with simple and short functions first, until you get the hang of it. You also might want to first translate either Iolo, Shamino or Dupre, so that you can see immediate results. For instance, replace every instance of 'bye' from your answers (= what you reply, thus surrounded only with simple quotes) into whatever way you say "bye" in your language. Run the command above and try it out (don't forget to put the translated version in the
If you intend to use the command line method and to share the work with others, you will need to setup a way to have a common repository fron which you will pull files to translate and push translated files. This is already possible is using
le_camomille.pl along with a CVS server. Before every translating sessions, run:
perl le_camomille.pl -synchroniseThis will update your local tree. You can then proceed to translate files in the
todo/directory, or adjust files in the
french_data/directory. When finished, you need to update the CVS tree. Do it by typing:
perl le_camomille.pl -commitHowever, you need to discuss with the other team members how the workload will be shared, otherwise you might unnecessarily overstep each other.
If you have access to a web server that supports PHP and MySQL, you might want to have a look at my package called
tisane. This package is a set of PHP pages that will help you translate Serpent Isle. This is the recommended method, since you will save yourself a lot of time. Once everything is installed, all you will have to do is to point your browser to some web page, select a function to translate, edit the file, press a button and bam! you've just finished a function! You can also search through the original or the translated text for some words, download one or all usecode functions, review changes through a ChangeLog, create a list of recommended words for certain common words.
For instance, you can hint the translator that the recommended word used to translate "MageLord" has been chosen to be "Seigneur Mage". Each time the original text will show the word "MageLord", it will look different and putting the mouse cursor on top will pop the word "Seigneur Mage" in a tooltip.
The page where you edit a usecode function will show the original text on the left and either nothing on the right (if nothing was done), or the currently translated text. Each Lxxxx line to translate or proofread will be in its own textbox and you don't even have to worry anymore about the "Lxxxx", the "db", the "00" and the rest since only the text to translate is alterable.
To help even more, a description can be given to each usecode function, as well as some flags to identify functions, like: isNPC, isProofread, needCorrection, etc.
Finally, the page to edit a usecode function will show at the bottom of the screen a list of quick links in case you need help to translate. You enter a word in the box, select the search type and press the button. Current search types are: Definition (English), Definition (French), Synonymes (French), Conjugaison (French). If you know of a site that can be used to help in your translation, you can also add it there. The only requirement is that it must be possible to access the result page right away, via a GET or a POST method, so if the site requires cookies, tough! If parameters are required, they can be set in advance and are filled on-the-fly when you choose the associated search type.
There is a bit of work to do, but you will have to do it only once. Roughly, the steps are:
Copy the web pages to a directory your web server can serve from. Point your browser to this directory and more specifically to the file
install.php. Follow the instructions. When you are finished, you should end up with a fully prepared database. On to the next step.
As is, you cannot use the usecode file. Fortunately, if you've followed the steps recommended in this documentation, you should have almost everything ready. You ran the command:
perl le_camomille.pl -prepare
This command has done most of the work for you. The
data/ directory contains all the original text and the
french_data/ contains the translated version. Using my perl script
usecode_convert.pl, you can massage the data so that it becomes database friendly. Run it like this:
cat data/*.uc.data | perl usecode_convert.pl > english.txt cat french_data/*.uc.data | perl usecode_convert.pl > french.txt