From The Mana World
(Formatting error in Howto step 4)
(→‎How to create this page: made steps into subchapters)
Line 2: Line 2:
If you just want to add a few images or change the appearance of the page. You can jump directly to step 5.
If you just want to add a few images or change the appearance of the page. You can jump directly to step 5.


'''1.''' First, create individual item images by using the '''tilesplit''' tool. It uses the PIL (Python Image Library), so you possibly have to install that first. The usage is
===Step 1===
First, create individual item images by using the '''tilesplit''' tool. It uses the PIL (Python Image Library), so you possibly have to install that first. The usage is
<pre>
<pre>
pngsplit <startid> <endid> <items.png>.
pngsplit <startid> <endid> <items.png>.
Line 12: Line 13:
This means the IDs used in the client data/items.xml from the client, which begin at 1. The items.png can be found in data/graphics/sprites/ and contains 10 items in a row, which makes it relatively easy to determine the id numbers you want. The program will then copy the files into a subdirectory pics (or pics2, pics3 ..., if that directory already exists).
This means the IDs used in the client data/items.xml from the client, which begin at 1. The items.png can be found in data/graphics/sprites/ and contains 10 items in a row, which makes it relatively easy to determine the id numbers you want. The program will then copy the files into a subdirectory pics (or pics2, pics3 ..., if that directory already exists).


'''2.''' Step two is to upload the images to [http://www.imageshack.us imageshack], or a similiar provider of your choice. I really, with all of my heart, wish I'd know how to write a tool that believably pretends to be a popular webbrowser. But as long as such a thing does not exist, you've got to do it by hand. While uploading, copy the urls as a linebreak-seperated listing into a textfile. Like so:
===Step 2===
Step two is to upload the images to [http://www.imageshack.us imageshack], or a similiar provider of your choice. I really, with all of my heart, wish I'd know how to write a tool that believably pretends to be a popular webbrowser. But as long as such a thing does not exist, you've got to do it by hand. While uploading, copy the urls as a linebreak-seperated listing into a textfile. Like so:
<pre>
<pre>
http://img296.imageshack.us/img296/2363/013nw.png
http://img296.imageshack.us/img296/2363/013nw.png
Line 21: Line 23:
</pre>
</pre>


'''3.''' You can now optionally convert that list to html code with ''' urlstohtml ''' and compare it with the original items.png to figure out whether you did it all right. Like this:
===Step 3===
You can now optionally convert that list to html code with ''' urlstohtml ''' and compare it with the original items.png to figure out whether you did it all right. Like this:
<pre>
<pre>
cat urlist | urlstohtml > foo.html
cat urlist | urlstohtml > foo.html
</pre>
</pre>


'''4.''' Now it's time to modify the imageurl list in the code of '''itemdbtowiki''' . You can autogenerate the updates to the list ''imageurls'' by running '''urlstopython'' like so:
===Step 4===
Now it's time to modify the imageurl list in the code of '''itemdbtowiki''' . You can autogenerate the updates to the list ''imageurls'' by running '''urlstopython'' like so:
<pre>
<pre>
cat urllist | urlstopython <startid>
cat urllist | urlstopython <startid>
Line 42: Line 46:




'''5.''' Now you can run '''itemdbtowiki''' to generate the wiki page. The usage is:
===Step 5===
 
Now you can run '''itemdbtowiki''' to generate the wiki page. The usage is:
<pre>
<pre>
itemdbtowiki <item_db.txt> <items.xml> > wikipage.txt
itemdbtowiki <item_db.txt> <items.xml> > wikipage.txt
</pre>
</pre>
If run without any arguments, it'll look for item_db.txt and items.xml in the current directory.
If run without any arguments, it'll look for item_db.txt and items.xml in the current directory.


==Python-Tools==
==Python-Tools==

Revision as of 20:02, 20 July 2005

How to create this page

If you just want to add a few images or change the appearance of the page. You can jump directly to step 5.

Step 1

First, create individual item images by using the tilesplit tool. It uses the PIL (Python Image Library), so you possibly have to install that first. The usage is

pngsplit <startid> <endid> <items.png>.

for example, like used in the creation of the original page. But obviously you can just generate the new items now:

./tilesplit 1 73 /home/user/_code/tmw/data/graphics/sprites/items.png

This means the IDs used in the client data/items.xml from the client, which begin at 1. The items.png can be found in data/graphics/sprites/ and contains 10 items in a row, which makes it relatively easy to determine the id numbers you want. The program will then copy the files into a subdirectory pics (or pics2, pics3 ..., if that directory already exists).

Step 2

Step two is to upload the images to imageshack, or a similiar provider of your choice. I really, with all of my heart, wish I'd know how to write a tool that believably pretends to be a popular webbrowser. But as long as such a thing does not exist, you've got to do it by hand. While uploading, copy the urls as a linebreak-seperated listing into a textfile. Like so:

http://img296.imageshack.us/img296/2363/013nw.png
http://img167.imageshack.us/img167/6766/025ia.png
http://img179.imageshack.us/img179/9046/031oj.png
http://img301.imageshack.us/img301/6505/041gt.png
http://img207.imageshack.us/img207/2556/059or.png

Step 3

You can now optionally convert that list to html code with urlstohtml and compare it with the original items.png to figure out whether you did it all right. Like this:

cat urlist | urlstohtml > foo.html

Step 4

Now it's time to modify the imageurl list in the code of itemdbtowiki' . You can autogenerate the updates to the list imageurls by running urlstopython like so:

cat urllist | urlstopython <startid>

itemdbtowiki uses the IDs from items.xml, which (as i said before) start with 1. If your urllist, started with, let's say, the image 74. You'd call

> cat urllist | urlstopython 74
    "http://img296.imageshack.us/img296/2363/743nw.png",       #   74
    "http://img167.imageshack.us/img167/6766/75ia.png",        #   75
    "http://img179.imageshack.us/img179/9046/761oj.png",       #   76
    "http://img301.imageshack.us/img301/6505/771gt.png",       #   77
    "http://img207.imageshack.us/img207/2556/789or.png",       #   78
[...]


Step 5

Now you can run itemdbtowiki to generate the wiki page. The usage is:

itemdbtowiki <item_db.txt> <items.xml> > wikipage.txt

If run without any arguments, it'll look for item_db.txt and items.xml in the current directory.

Python-Tools

All these tools are subject to the GNU General Public License.

tilesplit

#!/usr/bin/python

import sys, os, math
import Image
import httplib, urllib,mimetools
import MimeWriter, StringIO

def inttostr_zero(i, count):
    s = "%d" % i
    while len(s) < count:
        s = "0"+s
    return s

def getimgdir():
    if not os.path.exists('pics'):
        os.mkdir('pics')
        return 'pics'
    else:
        i = 2
        while os.path.exists('pics%d' % i):
            i +=1
        os.mkdir('pics%d' % i)
        return 'pics%d' % i


startindex = int(sys.argv[1])
endindex = int(sys.argv[2])
nlength = math.log10(endindex)
img = Image.open(sys.argv[3])
i = startindex-1
imgdir = getimgdir();
while i < endindex:
    cropbox =  ( (i % 10)*32, (i / 10)*32, (i % 10)*32+32, (i / 10)*32+32 )
    region = img.crop(cropbox)
    region.save("%s/%s.png" % (imgdir, inttostr_zero(i+1, nlength) ) )
    i = i + 1

urlstohtml

#!/usr/bin/python
import sys

sys.stdout.write("<html>\n<body>\n")
i = 0
for url in sys.stdin:
    if len(url) > 5: 
        sys.stdout.write('<img src="%s">' % url[:-1])
        if i < 9: 
            i += 1
        else:
            sys.stdout.write('\n')
            i = 0
sys.stdout.write("\n</body>\n</html>\n")

urlstopython

#!/usr/bin/python
import sys

firstskip = 0
space = ""
commentpos = 55

if len(sys.argv) == 1:
    i = 0
    sys.stdout.write('imageurls = [\n')
    sys.stdout.write('# The index represents the imageID from the items.xml of the client.\n')
    sys.stdout.write('    ""')
else:
    i = int(sys.argv[1])-1
    firstskip = 1
    sys.stdout.write('    ')

for url in sys.stdin:
    if len(url) > 5: 
        #if not commentpos: commentpos = len(url) + 5
        if firstskip:
            firstskip = 0
            sys.stdout.write('"%s"' % (url[:-1]))
        else:
            sys.stdout.write(', %s #%5d\n    "%s"' % (space,i,url[:-1]))
        space = " " * (commentpos-(len(url)))
        i += 1

sys.stdout.write('  %s #%5d\n' % (space,i))
sys.stdout.write(']\n')

itemdbtowiki

#!/usr/bin/python
#Licensed under GNU General Public License

import sys, os;
import xml.parsers.expat

imageurls = [
# The index represents the imageID from the items.xml of the client.
    "",  #    0
    "http://img296.imageshack.us/img296/2363/013nw.png",       #    1
    "http://img167.imageshack.us/img167/6766/025ia.png",       #    2
    "http://img179.imageshack.us/img179/9046/031oj.png",       #    3
    "http://img301.imageshack.us/img301/6505/041gt.png",       #    4
    "http://img207.imageshack.us/img207/2556/059or.png",       #    5
    "http://img229.imageshack.us/img229/2586/066zf.png",       #    6
    "http://img236.imageshack.us/img236/2425/077ed.png",       #    7
    "http://img154.imageshack.us/img154/8776/088cu.png",       #    8
    "http://img292.imageshack.us/img292/765/096cx.png",        #    9
    "http://img296.imageshack.us/img296/4291/103ku.png",       #   10
    "http://img265.imageshack.us/img265/4149/119ml.png",       #   11
    "http://img296.imageshack.us/img296/5246/125wt.png",       #   12
    "http://img248.imageshack.us/img248/6633/132dg.png",       #   13
    "http://img254.imageshack.us/img254/6427/149pm.png",       #   14
    "http://img279.imageshack.us/img279/2362/155sr.png",       #   15
    "http://img116.imageshack.us/img116/4408/167sb.png",       #   16
    "http://img139.imageshack.us/img139/6189/173ut.png",       #   17
    "http://img223.imageshack.us/img223/1794/187xp.png",       #   18
    "http://img229.imageshack.us/img229/1659/195yc.png",       #   19
    "http://img239.imageshack.us/img239/622/205at.png",        #   20
    "http://img153.imageshack.us/img153/1839/219lz.png",       #   21
    "http://img296.imageshack.us/img296/2636/228yi.png",       #   22
    "http://img248.imageshack.us/img248/53/239uu.png",         #   23
    "http://img254.imageshack.us/img254/4431/242qj.png",       #   24
    "http://img279.imageshack.us/img279/1489/254er.png",       #   25
    "http://img116.imageshack.us/img116/9810/269ig.png",       #   26
    "http://img139.imageshack.us/img139/5177/270mi.png",       #   27
    "http://img223.imageshack.us/img223/8136/281bl.png",       #   28
    "http://img229.imageshack.us/img229/6661/297cx.png",       #   29
    "http://img239.imageshack.us/img239/5787/304xp.png",       #   30
    "http://img301.imageshack.us/img301/1121/311an.png",       #   31
    "http://img52.imageshack.us/img52/7388/324fk.png",         #   32
    "http://img141.imageshack.us/img141/9527/336nu.png",       #   33
    "http://img100.imageshack.us/img100/7990/349zk.png",       #   34
    "http://img87.imageshack.us/img87/3489/355nw.png",         #   35
    "http://img151.imageshack.us/img151/5561/365gf.png",       #   36
    "http://img91.imageshack.us/img91/955/370ql.png",          #   37
    "http://img157.imageshack.us/img157/3830/387ay.png",       #   38
    "http://img32.imageshack.us/img32/2695/391hf.png",         #   39
    "http://img112.imageshack.us/img112/9297/400la.png",       #   40
    "http://img301.imageshack.us/img301/5387/412uz.png",       #   41
    "http://img52.imageshack.us/img52/3072/423xc.png",         #   42
    "http://img187.imageshack.us/img187/2963/434dc.png",       #   43
    "http://img125.imageshack.us/img125/4331/442fm.png",       #   44
    "http://img260.imageshack.us/img260/6627/456rj.png",       #   45
    "http://img211.imageshack.us/img211/9089/469fy.png",       #   46
    "http://img219.imageshack.us/img219/1188/479yz.png",       #   47
    "http://img232.imageshack.us/img232/3517/489oa.png",       #   48
    "http://img291.imageshack.us/img291/8643/491eg.png",       #   49
    "http://img157.imageshack.us/img157/1425/502hg.png",       #   50
    "http://img293.imageshack.us/img293/4524/514dn.png",       #   51
    "http://img42.imageshack.us/img42/7271/523hc.png",         #   52
    "http://img187.imageshack.us/img187/5241/535fk.png",       #   53
    "http://img125.imageshack.us/img125/8093/543yr.png",       #   54
    "http://img260.imageshack.us/img260/1315/554bt.png",       #   55
    "http://img211.imageshack.us/img211/9687/564wu.png",       #   56
    "http://img219.imageshack.us/img219/6842/571wz.png",       #   57
    "http://img232.imageshack.us/img232/9236/582ue.png",       #   58
    "http://img291.imageshack.us/img291/435/598ni.png",        #   59
    "http://img157.imageshack.us/img157/1323/605pt.png",       #   60
    "http://img293.imageshack.us/img293/1371/618fu.png",       #   61
    "http://img42.imageshack.us/img42/69/625mj.png",           #   62
    "http://img187.imageshack.us/img187/6240/637ju.png",       #   63
    "http://img275.imageshack.us/img275/9440/648zq.png",       #   64
    "http://img169.imageshack.us/img169/8508/655is.png",       #   65
    "http://img195.imageshack.us/img195/9153/660zq.png",       #   66
    "http://img288.imageshack.us/img288/8669/673xe.png",       #   67
    "http://img242.imageshack.us/img242/3228/688wv.png",       #   68
    "http://img214.imageshack.us/img214/8675/693wb.png",       #   69
    "http://img98.imageshack.us/img98/774/703wa.png",          #   70
    "http://img157.imageshack.us/img157/7590/717em.png",       #   71
    "http://img299.imageshack.us/img299/4237/721eu.png",       #   72
    "http://img246.imageshack.us/img246/8151/731cd.png"        #   73
]

imagesused = {}

class whatever: pass

log = []


# parseitems(file)
## Returns list with items from eathena item_db file.

def saveint(string, altval = 0):
    a = 0
    try:
        a = int(string)
    except:
        a = altval
    return a
        
def parsescript(s):
    # Assumes that there's only one call of each method, otherwise it would need to know
    # how to combine those function calls. In practice, the latter call would prevail.
    script = {}
    scriptentry = ""
    parentry = ""
    mode = 0
    for a in s:
        if mode == 0: # looking for method
            if a.isalpha(): 
                mode = 1
                scriptentry += a
            elif a == '}': mode = 9
        elif mode == 1: # reading method name
            if a in " ;}":
                if a == " ": mode = 2
                elif a == ";": mode = 1
                elif a == "}": mode = 9
                parentry = ""
                script[scriptentry] = []
            else: scriptentry += a
        elif mode == 2: #looking for param
            if a == " ": pass
            elif a == ";": 
                mode = 0
                scriptentry = ""
            else:
                mode = 3
                parentry = a
        elif mode == 3: #reading param
            if (a == " ") or (a == ",") or (a == ";"):
                script[scriptentry].append(parentry)
                parentry = ""
                if (a == ';'): 
                    mode = 0
                    scriptentry = ""
                else: mode = 2
            else: 
                parentry += a
        elif mode == 9: #finished
            pass

    # Convert all possible parameters to integers
    for i in script.keys():
        for j in range(len(script[i])):
            try:
                script[i][j] = int(script[i][j])
            except:
                #print script[i][j]
                pass
    return script
    

def parseitems(file):
    objects = []
    for line in file:
        s = line[0:line.find('//')].strip()
        if s:
            #Replace commas inbetween {} with |, so we can use split
            mode = 0
            sout = ""
            for a in s:
                if mode == 0: #Out of {}
                    if a == '{': mode = 1
                    sout += a
                elif mode == 1: #Inside {}
                    if a == ',': sout += '|'
                    else:
                        sout += a
                        if a == '}': mode = 0

            values = sout.split(',')
            if (len(values) != 19):
                log.append("item_db: Warning, item-line with ID %s has %d values instead of 19" % (values[0], len(values)))
                while (len(values) < 19):
                    values.append('')
                while (len(values) > 19):
                    values.pop()
            o = whatever()
            o.id        = int(values[0])
            o.name      = values[1]
            o.jname     = values[2]
            o.type      = saveint(values[3])
            o.price     = saveint(values[4])
            o.sell      = saveint(values[5])
            o.weight    = saveint(values[6])
            o.atk       = saveint(values[7])
            o.defense   = saveint(values[8])
            o.range     = saveint(values[9])
            o.slot      = saveint(values[10],-1)
            o.job       = saveint(values[11],-1)
            o.gender    = saveint(values[12],-1)
            o.loc       = saveint(values[13],-1)
            o.wlv       = saveint(values[14])
            o.elv       = saveint(values[15])
            o.view      = saveint(values[16],-1)
            o.usescript = parsescript(values[17].replace('|',','))
            o.equipscript = parsescript(values[18].replace('|',','))


            objects.append(o)

    return objects


# parsexmlitems(file)
## Creates a dictionary containing the values of a client items.xml
## Yeah, there are XML parsers in the standard python libraries, but they're too object
## oriented and thus don't fit the style of this program.
def parsexmlitems(file):
    items = {}
    pre = "<item "
    term = "/>"
    attrs = ["id", "image", "art", "name", "description", "type", "weight", "slot"]
    intattrs = ["id", "image", "art", "type", "weight", "slot"]
    s = file.read()
    index = 0
    debug = 0
    while s[index:].find(pre) >= 0:
        index += s[index:].find(pre) + len(pre)
        curitem = {}
        termstart = index + s[index:].find(term) + len(term)

        for attr in attrs:
            found = s[index:].find(attr+'="')
            if found >= 0:
                start = index + found + len(attr+'="')
                end= start + s[start:].find('"')
            else:
                start = termstart

            if (start < termstart):
                curitem[attr] = s[start:end]

        for a in intattrs:
            try:
                if curitem.has_key(a): curitem[a] = int(curitem[a])
            except:
                log.append("Item-ID %s: Cannot convert integer attribute %s to an integer. Value: '%s'" % (curitem["id"], a, curitem[a]))

        items[curitem['id']] = curitem


    return items
        

# addclientinformation(items, citems)
## Entends the item data with the data collected from the client items.xml. Adding imageurls,
## client-name and -description

def addclientinformation(items,citems):
    global imageurls
    global imagesused
    for i in items:
        if citems.has_key(i.id):
            i.imgurl = imageurls[ citems[i.id]["image"] ]
            imagesused[ citems[i.id]["image"]] = 1
            i.description = citems[i.id]["description"]
            i.clientname = citems[i.id]["name"]
        else:
            i.imgurl = ''
            i.description = ''
            i.clientname = ''



# gettypedir (items)
## Returns sorted lists of items by itemtype
def gettypedir(items):
    items.sort(lambda x,y: (x.price+x.sell) - (y.price+y.sell))

    typedir = whatever()
    typedir.healthy = []
    typedir.inspiring = []
    typedir.weapons = []
    typedir.combos = []
    typedir.armor = []
    typedir.other = []
    for item in items:
        if (item.imgurl.strip() or item.clientname.strip()):
            #if item.id == 537: log.append('"%s", "%s"' % (item.imgurl, item.name)
            if (item.atk > 0):
                if item.defense == 0: typedir.weapons.append(item)
                else: typedir.combos.append(item)
            elif (item.defense > 0): typedir.armor.append(item)
            elif item.usescript.has_key("itemheal"): 
                if item.usescript["itemheal"][0] > item.usescript["itemheal"][1]: 
                    typedir.healthy.append(item)
                else:
                    typedir.inspiring.append(item)
            else: typedir.other.append(item)

    typedir.weapons.sort(lambda x,y: x.atk - y.atk)
    typedir.armor.sort(lambda x,y: x.defense - y.defense)
    typedir.combos.sort(lambda x,y: (x.defense+x.atk) - (y.defense+y.atk))

    typedir.healthy.sort(lambda x,y: int(x.usescript["itemheal"][0]) - int(y.usescript["itemheal"][0]))
    typedir.inspiring.sort(lambda x,y: int(x.usescript["itemheal"][1]) - int(y.usescript["itemheal"][1]))
    #typedir.other.sort(lambda x,y: (x.price+x.sell) - (y.price+y.sell))

    return typedir
    

            
# printlog()
## Prints the global variable log to stdout
def printlog():
    global log
    if len(log) > 0:
        sys.stdout.write('\n---------------------------------------\n')
    for line in log:
        sys.stdout.write(line+'\n')


# print<>items(items, title)
## Creates the table in wikicode, depending on what kind of item is being printed

def getmoneystring(buy, sell):
    return '| align="right" | %d GP<br>%d gp\n' % (buy,sell)
def getidstring(id):
    return '| align="center" | [%d]\n' % id
    
def printhealitems(items,title):
    sys.stdout.write('==%s==\n' % title)
    sys.stdout.write('{| border="1" cellspacing="0" cellpadding="5" width="100%" align="center"\n')
    sys.stdout.write('! style="background:#efdead;" | Image\n')
    sys.stdout.write('! style="background:#efdead;" | Name\n')
    sys.stdout.write('! style="background:#efdead;" | ID\n')
    sys.stdout.write('! style="background:#efdead;" | HP Bonus\n')
    sys.stdout.write('! style="background:#efdead;" | SP Bonus\n')
    sys.stdout.write('! style="background:#efdead;" | Price<br>BUY/Sell\n')
    sys.stdout.write('! style="background:#efdead;" | Description\n')
    for i in items:
        sys.stdout.write('|-\n')
        sys.stdout.write('| align="center" | %s\n' % i.imgurl)
        #sys.stdout.write('| %s\n' % i.jname.replace('_',' '))
        sys.stdout.write('| %s\n' % i.clientname)
        sys.stdout.write( getidstring(i.id) )
        sys.stdout.write('| align="center" | %d\n' % i.usescript["itemheal"][0])
        sys.stdout.write('| align="center" | %d\n' % i.usescript["itemheal"][1])
        sys.stdout.write( getmoneystring(i.price,i.sell) )
        sys.stdout.write('| %s\n' % i.description)
    sys.stdout.write('|}\n\n')
            

def printweaponitems(items, title):
    sys.stdout.write('==%s==\n' % title)
    sys.stdout.write('{| border="1" cellspacing="0" cellpadding="5" width="100%" align="center"\n')
    sys.stdout.write('! style="background:#efdead;" | Image\n')
    sys.stdout.write('! style="background:#efdead;" | Name\n')
    sys.stdout.write('! style="background:#efdead;" | ID\n')
    sys.stdout.write('! style="background:#efdead;" | Damage<br>(Range)\n')
    sys.stdout.write('! style="background:#efdead;" | Price<br>BUY/Sell\n')
    sys.stdout.write('! style="background:#efdead;" | Description\n')
    for i in items:
        sys.stdout.write('|-\n')
        sys.stdout.write('| align="center" | %s\n' % i.imgurl)
        #sys.stdout.write('| %s\n' % i.jname.replace('_',' '))
        sys.stdout.write('| %s\n' % i.clientname)
        sys.stdout.write( getidstring(i.id) )
        sys.stdout.write('| align="center" | %d (%d)\n' % (i.atk,i.range))
        sys.stdout.write( getmoneystring(i.price,i.sell) )
        sys.stdout.write('| %s\n' % i.description)
    sys.stdout.write('|}\n\n')
    
def printarmoritems(items, title):
    sys.stdout.write('==%s==\n' % title)
    sys.stdout.write('{| border="1" cellspacing="0" cellpadding="5" width="100%" align="center"\n')
    sys.stdout.write('! style="background:#efdead;" | Image\n')
    sys.stdout.write('! style="background:#efdead;" | Name\n')
    sys.stdout.write('! style="background:#efdead;" | ID\n')
    sys.stdout.write('! style="background:#efdead;" | Defense\n')
    sys.stdout.write('! style="background:#efdead;" | Price<br>BUY/Sell\n')
    sys.stdout.write('! style="background:#efdead;" | Description\n')
    for i in items:
        sys.stdout.write('|-\n')
        sys.stdout.write('| align="center" | %s\n' % i.imgurl)
        #sys.stdout.write('| %s\n' % i.jname.replace('_',' '))
        sys.stdout.write('| %s\n' % i.clientname)
        sys.stdout.write( getidstring(i.id) )
        sys.stdout.write('| align="center" | %d\n' % i.defense)
        sys.stdout.write( getmoneystring(i.price,i.sell) )
        sys.stdout.write('| %s\n' % i.description)
    sys.stdout.write('|}\n\n')

def printcomboitems(items, title):
    sys.stdout.write('==%s==\n' % title)
    sys.stdout.write('{| border="1" cellspacing="0" cellpadding="5" width="100%" align="center"\n')
    sys.stdout.write('! style="background:#efdead;" | Image\n')
    sys.stdout.write('! style="background:#efdead;" | Name\n')
    sys.stdout.write('! style="background:#efdead;" | ID\n')
    sys.stdout.write('! style="background:#efdead;" | Damage<br>(Range)\n')
    sys.stdout.write('! style="background:#efdead;" | Defense\n')
    sys.stdout.write('! style="background:#efdead;" | Price<br>BUY/Sell\n')
    sys.stdout.write('! style="background:#efdead;" | Description\n')
    for i in items:
        sys.stdout.write('|-\n')
        sys.stdout.write('| align="center" | %s\n' % i.imgurl)
        #sys.stdout.write('| %s\n' % i.jname.replace('_',' '))
        sys.stdout.write('| %s\n' % i.clientname)
        sys.stdout.write( getidstring(i.id) )
        sys.stdout.write('| align="center" | %d (%d)\n' % (i.atk,i.range))
        sys.stdout.write('| align="center" | %d\n' % i.defense)
        sys.stdout.write( getmoneystring(i.price,i.sell) )
        sys.stdout.write('| %s\n' % i.description)
    sys.stdout.write('|}\n\n')

def getpropertystring(item):
    s = ""
    s += "Weight: %d, " % item.weight
    s += "Slot: %d, " % item.slot
    s += "Job: %d, " % item.job
    s += "Gender: %d, " % item.gender
    s += "Loc: %d, " % item.loc
    s += "wLV: %d, " % item.wlv
    s += "eLV: %d, " % item.wlv
    s += "View: %d " % item.view
    return s


def printotheritems(items, title):
    sys.stdout.write('==%s==\n' % title)
    sys.stdout.write('{| border="1" cellspacing="0" cellpadding="5" width="100%" align="center"\n')
    sys.stdout.write('! style="background:#efdead;" | Image\n')
    sys.stdout.write('! style="background:#efdead;" | Name\n')
    sys.stdout.write('! style="background:#efdead;" | ID\n')
    #sys.stdout.write('! style="background:#efdead;" | Type\n')
    #sys.stdout.write('! style="background:#efdead;" | Properties\n')
    sys.stdout.write('! style="background:#efdead;" | Price<br>BUY/Sell\n')
    sys.stdout.write('! style="background:#efdead;" | Description\n')
    for i in items:
        sys.stdout.write('|-\n')
        sys.stdout.write('| align="center" | %s\n' % i.imgurl)
        #sys.stdout.write('| %s\n' % i.jname.replace('_',' '))
        sys.stdout.write('| %s\n' % i.clientname)
        sys.stdout.write( getidstring(i.id) )
        #sys.stdout.write('| align="center" | %d\n' % i.type)
        #sys.stdout.write('| align="center" | %s\n' % getpropertystring(i))
        sys.stdout.write( getmoneystring(i.price,i.sell) )
        sys.stdout.write('| %s\n' % i.description)
    sys.stdout.write('|}\n\n')



def printunuseditems(title):
    global imageurls
    global imagesused
    ids = []
    for i in range(1,len(imageurls)):
        if not imagesused.has_key(i):
            ids.append(i)
    if len(ids):
        sys.stdout.write('==%s==\n' % title)
        sys.stdout.write('{| border="1" cellspacing="0" cellpadding="5" width="100%" align="center"\n')
        sys.stdout.write('| ')
        for i in ids:
            sys.stdout.write(imageurls[i] + ' ')
        sys.stdout.write('\n|}\n\n')

    

#####################################################################
#  MAIN
#####################################################################

try:
    if (len(sys.argv) == 1):
        item_db = "item_db.txt"
        item_xml = "items.xml"
    elif (len(sys.argv) == 3):
        item_db = sys.argv[1]
        item_xml = sys.argv[2]
    else: 
        item_db = ''
        item_xml = ''
        sys.stdout.write("Wrong number of arguments\n")

    if item_db and (not os.path.isfile(item_db)):
        sys.stdout.write("File does not exist: %s\n" % item_db)
        item_db = ''
    if item_xml and (not os.path.isfile(item_xml)):
        sys.stdout.write("File does not exist: %s\n" % item_db)
        item_db = ''
    
    if not (item_db and item_xml):
        sys.stdout.write("\nUSAGE:\n")
        sys.stdout.write("dbtowiki without any arguments will use item_db.txt and items.xml in the current directory.\n")
        sys.stdout.write("to specify custom files, call: dbtowiki <item_db> <item_xml>\n")
        exit(-1);
    else:
        log.append("Item-list [item_db] = %s" % item_db)
        log.append("Item-list [item_xml] = %s" % item_xml)
        f = open(item_db)
        items = parseitems(f);
        f = open(item_xml)
        citems = parsexmlitems(f);

        addclientinformation(items, citems)

        typedir = gettypedir(items)
        if len(typedir.healthy) > 0: printhealitems(typedir.healthy, "Health")
        if len(typedir.inspiring) > 0: printhealitems(typedir.inspiring, "Mana")
        if len(typedir.weapons) > 0: printweaponitems(typedir.weapons, "Weapons")
        if len(typedir.armor) > 0: printarmoritems(typedir.armor, "Armor")
        if len(typedir.combos) > 0: printcomboitems(typedir.combos, "Combos")
        if len(typedir.other) > 0: printotheritems(typedir.other, "Other")

        printunuseditems("Still unknown")

        sys.stdout.write("\n\n")

finally:
    printlog()

Formatting error in Howto step 4

There's a wiki formatting error in step 4 of "How to create the page". I have no idea where that comes from.