duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

generate_cheat_db.py (2764B)


      1 import sys
      2 import re
      3 import xml.etree.ElementTree as ET
      4 from xml.sax.saxutils import unescape
      5 
      6 class GameEntry:
      7     def __init__(self, title, serials):
      8         self.title = title
      9         self.serials = serials
     10 
     11     def __repr__(self):
     12         return self.title + " (" + ",".join(self.serials) + ")"
     13 
     14 
     15 def get_serials(s):
     16     out = []
     17     for it in s.split("/"):
     18         for it2 in it.split(","):
     19             for it3 in it2.split("~"):
     20                 i = it3.find('(')
     21                 if i > 0:
     22                     it3 = it3[:i-1]
     23                 it3 = re.sub("[^A-Za-z0-9-]", "", it3)
     24                 out.append(it3.strip())
     25     print(out)
     26     return out
     27 
     28 
     29 def parse_xml(path):
     30     entries = {}
     31     tree = ET.parse(path)
     32     for child in tree.getroot():
     33         name = child.get("name")
     34         if name is None:
     35             continue
     36 
     37         title = ""
     38         description = child.find("description")
     39         if description is not None:
     40             title = description.text
     41 
     42         serials = []
     43         for grandchild in child.iterfind("info"):
     44             gname = grandchild.get("name")
     45             gvalue = grandchild.get("value")
     46             #print(gname, gvalue)
     47             if gname is not None and gname == "serial" and gvalue is not None:
     48                 serials.extend(get_serials(gvalue))
     49 
     50         if len(serials) > 0:
     51             entries[name] = GameEntry(title, serials)
     52 
     53     return entries
     54 
     55 
     56 def write_codes(entries, fout, name, codes):
     57     if name == "" or len(codes) == 0:
     58         return
     59 
     60     if name not in entries:
     61         print("Unknown game '%s'" % name)
     62         return
     63 
     64     entry = entries[name]
     65     fout.write(";%s\n" % entry.title)
     66     for serial in entry.serials:
     67         fout.write(":%s\n" % serial)
     68     fout.write("\n".join(codes))
     69     fout.write("\n\n")
     70 
     71 
     72 def rewrite_dat(entries, inpath, outpath):
     73     fin = open(inpath, "r", encoding="utf-8")
     74     fout = open(outpath, "w", encoding="utf-8")
     75 
     76     current_name = ""
     77     code_lines = []
     78     
     79     for line in fin.readlines():
     80         if line[0] == ' ' or line[0] == ';' or line[:2] == "##":
     81             continue
     82 
     83         line = line.strip()
     84         if len(line) == 0:
     85             continue
     86 
     87         line = unescape(line)
     88 
     89         if line[0] == ':':
     90             write_codes(entries, fout, current_name, code_lines)
     91             current_name = line[1:].split(':')[0].strip()
     92             code_lines = []
     93         else:
     94             code_lines.append(line)
     95 
     96     write_codes(entries, fout, current_name, code_lines)
     97 
     98     fin.close()
     99     fout.close()
    100 
    101 
    102 if __name__ == "__main__":
    103     if len(sys.argv) < 4:
    104         print("usage: %s <psx.xml path> <cheatpsx.dat> <output file>" % sys.argv[0])
    105         sys.exit(1)
    106 
    107     entries = parse_xml(sys.argv[1])
    108     rewrite_dat(entries, sys.argv[2], sys.argv[3])