Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import json
- import os
- from datetime import datetime
- import re
- import struct
- from dataclasses import dataclass
- from enum import Enum
- def find_shallow(base_dir, needle):
- latest_file = None
- latest_time = 0
- for subdir in os.listdir(base_dir):
- file_path = os.path.join(base_dir, subdir, needle)
- if os.path.exists(file_path):
- ts = os.path.getmtime(file_path)
- if ts > latest_time:
- latest_file = file_path
- latest_time = ts
- assert latest_file
- print(f'using {latest_file} modified {datetime.fromtimestamp(latest_time)}')
- return latest_file
- def find_deep(base_dir, needle):
- latest_file = None
- latest_time = 0
- ts = 0
- for root, dirs, files in os.walk(base_dir):
- for file in files:
- if file == needle:
- file_path = os.path.join(root, file)
- ts = os.path.getmtime(file_path)
- if ts > latest_time:
- latest_file = file_path
- latest_time = ts
- return latest_file, ts
- def parse_worker_names(filename):
- with open(filename, "r") as file:
- text = file.read()
- pattern = re.compile(r'workerNo="(.*?)" name="(.*?)"')
- tuples = re.findall(pattern, text)
- wid_names = {int(wid): name for (wid, name) in tuples}
- assert wid_names
- print('parsed', len(wid_names), 'worker names')
- return wid_names
- class JobType(Enum):
- UNKNOWN = -1
- PLANTZONE = 0
- RENTHOUSE = 2
- RENTHOUSELARGE = 6
- HARVESTING = 9
- @dataclass
- class WorkerRecord:
- wid: int
- charkey: int
- tnk: int
- level: int
- mspdLvlup: int # sheet = static * (1 + lvlup / 1000000)
- wspdLvlup: int # sheet = static + lvlup / 1000000
- luckLvlup: int # sheet = static + lvlup / 10000
- skills: tuple[int]
- jobtype: int
- repeats: int
- workplace: int
- label: str
- @classmethod
- def from_file(cls, f, label=''):
- raw = f.read(68)
- tup = struct.unpack("=QHIIIII9HBH", raw[:51])
- fields = (*tup[:7], tup[7:16], *tup[16:], 0, label)
- me = cls(*fields)
- if me.jobtype == 0: # plantzone
- exk, pzk = struct.unpack("HH", raw[51:55])
- me.workplace = pzk
- if me.jobtype == 2: # renthouse
- exk, hk = struct.unpack("HH", raw[51:55])
- me.workplace = hk
- if me.jobtype == 6: # renthouse large
- hk, = struct.unpack("H", raw[51:53])
- me.workplace = hk
- return me
- def to_dict(self):
- d = self.__dict__.copy()
- d.pop('wid')
- d.pop('repeats')
- if d['label'] == '':
- d['label'] = self.wid % 10000000000000000
- d.pop('jobtype')
- d.pop('workplace')
- if self.jobtype == 0: # plantzone
- d['job'] = self.workplace
- elif self.jobtype == 2: # renthouse
- d['job'] = {'kind': 'workshop', 'hk': self.workplace}
- elif self.jobtype == 6: # renthouse large
- d['job'] = {'kind': 'workshop', 'hk': self.workplace}
- elif self.jobtype == 9: # harvesting
- d['job'] = 'farming'
- else:
- d['job'] = None
- return d
- def parse_worker_data(filename, wid_names):
- workers = []
- with open(filename, "rb") as f:
- head, count, version = struct.unpack("4sII", f.read(12))
- assert head == b'PABR'
- assert version == 2
- for i in range(count):
- worker = WorkerRecord.from_file(f)
- if worker.wid in wid_names:
- worker.label = wid_names[worker.wid]
- workers.append(worker)
- print('parsed', len(workers), 'workers')
- return workers
- def to_workerman(workers, filename):
- out = {
- "activateAncado": False,
- "lodgingTaken": {}, # insert lodging data here OR leave empty to
- "lodgingP2W": {}, # keep old lodging data unchanged after import
- 'userWorkers': workers,
- }
- with open(filename, "w") as f:
- json.dump(out, f, default=WorkerRecord.to_dict)
- print('output written to:', os.path.abspath(filename))
- def parse_all(documents_path, verbose=False):
- cache_path = os.path.join(documents_path, "Black Desert\\UserCache\\")
- gamevars = find_shallow(cache_path, "gamevariable.xml")
- wid_names = parse_worker_names(gamevars)
- cache, ts = find_deep(cache_path, 'worker.cache')
- if cache:
- print(f'using {cache} modified {datetime.fromtimestamp(ts)}')
- else:
- while True:
- cache = input(r"Search for worker.cache failed, find it yourself and enter its full pathname (X:\path\to\worker.cache): ")
- if os.path.exists(cache):
- break
- worker_list = parse_worker_data(cache, wid_names)
- worker_list.sort(key=lambda w: w.tnk)
- if verbose:
- for worker in worker_list:
- print(worker)
- to_workerman(worker_list, "to_workerman.json")
- def main():
- paths = [
- os.path.expanduser("~/Documents"),
- os.path.expanduser("~/Documenten"),
- os.path.join(os.environ['USERPROFILE'], "OneDrive/Documents"),
- os.path.join(os.environ['USERPROFILE'], "OneDrive/Documenten"),
- ]
- while True:
- try:
- path = paths.pop(0)
- except IndexError:
- path = input("Enter your User Documents path: ")
- try:
- parse_all(path)
- return
- except FileNotFoundError as e:
- print(e, "- retrying")
- if __name__ == "__main__":
- main()
- input("Press Enter to continue...")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement