From 78e9f4de0eabbba23d20037577bdcf1751b8c018 Mon Sep 17 00:00:00 2001 From: Zergling_man Date: Mon, 17 Jun 2024 19:11:52 +1000 Subject: [PATCH] whatever --- codegen.py | 176 ----------------------------------------------------- decode.py | 55 ----------------- encode.py | 76 ----------------------- shared.py | 38 ------------ tables.py | 34 ----------- 5 files changed, 379 deletions(-) delete mode 100644 codegen.py delete mode 100644 decode.py delete mode 100644 encode.py delete mode 100644 shared.py delete mode 100644 tables.py diff --git a/codegen.py b/codegen.py deleted file mode 100644 index a4de8e5..0000000 --- a/codegen.py +++ /dev/null @@ -1,176 +0,0 @@ -import encode, decode, shared -import tables -import os - -conf={} -confp=os.path.expanduser('~/.config/swat.cfg') # This won't work on Windows. You'll need to tweak this to run it on Windows. Not my problem. - - -defaults={'classes':[(1,3),(1,3),(1,3),(1,3),(1,3),(1,3),(1,3),(0,0),(0,0),(1,3),(1,3),(1,3)],'guns':[(1,3),(1,3),(1,3),(1,3),(1,3),(0,0),(0,0),(1,3)],'armour':[(1,3),(1,3),(1,3),(1,3)],'traits':[(1,3),(1,3),(1,3),(1,3),(1,3),(1,3),(1,3),(1,3),(1,3),(1,3),(1,3),(1,3),(1,3),(1,3),(1,3),(1,3)],'specs':[(1,3),(1,3),(1,3),(1,3),(1,3),(1,3),(1,3),(1,3),(1,3)],'talents':[(12,12),(1,3),(1,3),(1,3),(1,3),(1,3),(1,3)],'medals':{}} -def add_user(name): - namehash=encode.hash_name(name) # Store the hash so that if they request a different name that happens to have the same hash, it can just be stored together. - if namehash in conf: conf[namehash]['names'].add(name) - else: conf[namehash]=defaults.copy()|{'names':{name}} - save_conf() - global active_profile - active_profile=namehash - -def parse_conf_line(line): - a=line.split(' ') - out=[] - for n in a: - b=n.split('/') - out.append((int(b[0]),int(b[1]))) - return out - -def load_conf(): - if not os.path.exists(confp): return - a=open(confp).read() - for n in a.split('\n\n'): - lines=n.split('\n') - lines=list(filter(lambda x:x,lines)) - if not lines: continue - header=lines[0].split(' ') - nhash=int(header[0]); names=set(header[1:]) # Makes deduping way easier - medals={} - if ':' in lines[-1]: # Since this isn't used in any other line, it's safe - medals={int(n.split(':')[0]):int(n.split(':')[1]) for n in lines.pop(-1).split(' ')} - stuff={k:parse_conf_line(v) for k,v in zip(tables.displays_str[:-1],lines[1:])} - conf[nhash]={'names':names}|stuff|{'medals':medals} - update_all_talents() - global active_profile - active_profile=nhash - -def unparse_conf_line(line): - return ' '.join([str(n[0])+'/'+str(n[1]) for n in line]) - -def save_conf(): - write='' - for k,v in conf.items(): - write+=' '.join([str(k)]+list(v['names']))+'\n' - for n in map(lambda x:unparse_conf_line(v[x]),tables.displays_str[:-1]): - write+=n+'\n' - write+=' '.join(f'{h}:{m}' for h,m in v['medals'].items())+'\n' - write+='\n' - write=write[:-2] - open(confp,'w').write(write) - -def update_all_talents(): - for profile in conf: - cl=dict(enumerate(conf[profile]['classes'])) - t=defaults['talents'].copy() - conf[profile]['talents']=t - for n in range(1,7): - t[n]=update_talent(n,cl) - -def update_talents(clas,profile=None): - if profile is None: profile=active_profile - cl=dict(enumerate(conf[profile]['classes'])) - for talent in tables.clas[clas][1:]: # Don't care about courage - conf[profile]['talents'][talent]=update_talent(talent,cl) - -def update_talent(talent,classes): - a=enumerate(tables.clas); b=filter(lambda x:talent in x[1],a); c=list(map(lambda x:x[0],b)); d=filter(lambda x:x[0] in c,classes.items()); rel_classes=list(map(lambda x:x[1],d)) - ranks,caps=[],[] - for n in rel_classes: ranks.append(n[0]); caps.append(n[1]) - ranks,caps=sorted(ranks),sorted(caps) - lon=len(ranks)//2+len(ranks)%2 - return (ranks[-lon],caps[-lon]) - -def read_code(code,name=None,save=None): - if name is None: name=active_profile; save2=True - else: save2=False # Don't save spoofed codes unless explicitly told - if save is None: save=save2 - data=list(decode.decode(code,name)) - check,validator=data.pop(-1),data.pop(-1) - talent=data.pop(5) - hero=data[0:6] - show=list(map(lambda x:x[0][x[1]],zip(tables.displays,hero))) - rank,cap=data[6:8] - cap=tables.cap[cap] - if rank==12: cap=12 - data=data[8:] - if rank<9: show[-1]='Hidden' # Don't spoil the talent - print(show) - print(str(rank)+'/'+str(cap)) - print(data) # Extract medals and save them - medals=data[1:-1] - if not check: print('No name given, code validation not performed') - else: - if check!=validator: print('code did not validate:',validator,check); return - else: # Do not save medals unless validation passed - if any(medals): conf[active_profile]['medals'][shared.muxhero(hero[0],hero[3],hero[4])]=shared.muxmedals(*medals) - print('code validated') - for thing in zip(tables.displays_str[:-1],hero): - saved=conf[active_profile][thing[0]][thing[1]] - saved=(max(saved[0],rank),max(saved[1],cap)) - conf[active_profile][thing[0]][thing[1]]=saved - update_talents(hero[0]) - if not save: return - save_conf() - -def mass_add(): - a=input('>') - while a: - try: read_code(a,false) - except: pass - a=input('>') - save_conf() - -def generate(officer=None,name=None): - pro=conf[active_profile] - if name is None: name=active_profile - if officer is None: officer=input('> ') - pieces=officer.split('/') - c=pieces[0].lower() - if 'mav' in c: - gun=c[0] - pieces[0]='mav' - elif 'wm' in c: - gun=c[0:2] - pieces[0]='wm' - else: gun='' - out=[] - if len(pieces)==6: rank=cap=int(pieces.pop(-1)) - else: rank,cap=12,12 - try: - for n in zip(pieces,tables.displays_nogun,tables.displays_nogun_str): - ind=lookup_piece(n[0],n[1],n[2]) - rank,cap=min(rank,pro[n[2]][ind][0]),min(cap,pro[n[2]][ind][1]) - if 0 in [rank,cap]: print(f"{n[0]} is locked, go unlock it first.") - out.append(ind) - if gun: out.insert(1,lookup_piece(gun,tables.guns,'guns',True)) - else: out.insert(1,tables.gunmaps_wrapped[out[0]]) - except LookupException as ex: print(ex); return - rank,cap=min(rank,pro['guns'][out[1]][0]),min(cap,pro['guns'][out[1]][1]) # Derp2, forgot to actually cap on gun - if 0 in [rank,cap]: return # Already warned about locks earlier. - cap=(cap-(cap<10)+(cap>10))//3 # Derp - try: out[-1]=tables.clas[out[0]].index(out[-1]) - except ValueError: print(f"{tables.classes[out[0]]} can't get talent {tables.talent[out[-1]]}, options are {list(map(lambda x:tables.talent[x],tables.clas[out[0]]))}"); return - muxed=shared.muxhero(out[0],out[3],out[4]) - meds=[] - if muxed in pro['medals']: meds=shared.muxmedals(pro['medals'][muxed]) - return encode.encode(name,*out,rank,cap,0,*meds) - -def lookup_piece(piece,table,name,partial=False): - if len(piece)<3: p=piece.upper() - else: p=piece.capitalize() # Bloody Americans - if not partial: - try: return table.index(p) - except ValueError: raise LookupException(f"Couldn't resolve {piece} from {name}") - res=list(filter(lambda x:p in x,table)) - if len(res)!=1: raise LookupException(f"Couldn't resolve {piece} with partial match from {name} (found {len(res)} possibilities)") # Ambiguous lookups not allowed - return table.index(res[0]) -class LookupException(Exception): pass - -def ranks(): - for n,m in zip(tables.displays_str,tables.displays): - a=zip(m,conf[active_profile][n]) - for o in a: - print(f'{o[0]}: {o[1][0]}/{o[1][1]}',end=', ') - print() - -load_conf() - - -if __name__=="__main__": print("Please open an interpreter and use 'import codegen', this script doesn't serve as an entrypoint on its own yet.") diff --git a/decode.py b/decode.py deleted file mode 100644 index 6b1d765..0000000 --- a/decode.py +++ /dev/null @@ -1,55 +0,0 @@ -import shared -import tables -import encode -from functools import reduce - -def decode(code,name=''): # This is going to be a huge function - code=shared.delimit(code) # It didn't end up being that huge - code=shared.scramble(code,True) - code,validator=shared.combine(code) - xp=int(code[-1]); seed=int(code[0]) - code=code[1:-1] - data=unzip(code) - xp+=data.pop(0) - rank,cap,cob,lsa,rem,clas,armour,trait,spec,talent=data - cob,lsa=bool(cob),bool(lsa) - rank,cap,key,moh,pcc=unadjust_rank(rank,cap) - if seed%2: seed-=1; clas+=10 - if clas==6: clas=[8,9,6][armour]; armour=3 - check='' - oldclas=clas # Whoops this all needs to be done before globalising talent - gun=tables.gunmaps[clas] - clas-=(clas>8)+min(4,max(0,clas-10)) # WM and mav adjustments - gtalent=tables.clas[clas][talent] # Globalise it lol - # And do it *before* validation because it needs gtalent passed in now - if name: - check=encode.validate_data(name, oldclas,armour,trait,spec,gtalent, rank,cap,xp, key,moh,pcc,cob,lsa,rem) - rank+=1 # This has to come after extracting medals - return clas,gun,armour,trait,spec,talent,gtalent,rank,cap,xp,key,moh,pcc,cob,lsa,rem,seed,validator,check - -############################ -# SUPPORTING FUNCTIONS BELOW -############################ - -def unzip(code): - code=int(code) - out=[] - weight=reduce(lambda x,y:x*y,tables.weights) - for n in list(zip(tables.weights,['xp//10','rank','cap','cob','lsa','rem','clas','armour','trait','spec','talent']))[::-1]: - shared.trace(code,weight,n,out) - out.append(code//weight) - code%=weight - weight//=n[0] - out[-1]*=10 # This is XP - return out[::-1] - -def unadjust_rank(rank,cap): - key,moh,pcc=False,False,False - if rank>8: - pcc=bool((cap+1)%2) - cap=2+(cap+1)//2 - if rank>11: # Actually need to check this on the way out - key=rank>12 - moh=bool((rank-11)%2) - rank=11 - return rank,cap,key,moh,pcc \ No newline at end of file diff --git a/encode.py b/encode.py deleted file mode 100644 index a63db27..0000000 --- a/encode.py +++ /dev/null @@ -1,76 +0,0 @@ -import shared -import random -import tables - -def encode(name,clas,gun,armour,trait,spec,talent,rank=-1,cap=-1,xp=0, key=False,moh=False,pcc=False,cob=False,lsa=False,rem=False,seed=-1): - if rank==-1: # actually means gun wasn't given, so shift them - gun,armour,trait,spec,talent,rank=-1,gun,armour,trait,spec,talent - gtalent=tables.clas[clas][talent] - if clas>9: clas+=5 # Shift tech/Alice up to their positions - always - # ... And do it before modifying clas below. - if gun!=-1: - #encode gun into class - if clas==9: clas+=gun+1 # Because 9 outside is Mav, and 9 inside is GLWM - if clas==8: clas+=gun-5 - rank-=1 # This actually DOES affect part 2 for some fucking reason - cap=max(cap,(rank)//3+(rank>9)) #Sanity-check - code1=encode_data(clas,armour,trait,spec,talent,rank,cap,xp, key,moh,pcc,cob,lsa,rem,seed) - code2=validate_data(name,clas,armour,trait,spec,gtalent,rank,cap,xp, key,moh,pcc,cob,lsa,rem) - shared.trace(code1,code2) - code=shared.combine(code1,code2) - shared.trace(code) - return shared.delimit(shared.scramble(code),'-') - -def encode_data(clas,armour,trait,spec,talent,rank,cap=-1,xp=0, key=False,moh=False,pcc=False,cob=False,lsa=False,rem=False,seed=-1): - if clas==6: armour=2 # Borg is heavy borg - if clas==8: clas=6; armour=0 # LR WM is light borg - if clas==9: clas=6; armour=1 # GL WM is med borg - seed,clas=rand(seed,clas) - rank,cap=adjust_rank(rank,cap,key,moh,pcc) - code1=0 - weight=1 - for n in list(zip(tables.weights,[xp//10,rank,cap,cob,lsa,rem,clas,armour,trait,spec,talent])): - shared.trace(code1,weight,n[0],n[1]) - weight*=n[0] - code1+=n[1]*weight - return str(seed)+shared.fill0s(code1,9)+str(xp%10) - -def validate_data(name,clas,armour,trait,spec,talent,rank,cap=0,xp=0, key=False,moh=False,pcc=False,cob=False,lsa=False,rem=False): - # gtalent is now looked up outside, before class modification - because I've adjusted that table to be more useful in general. - if isinstance(name,str): name=hash_name(name) - fac1=[171,142,175,157,167,150,149,151,153,165] - fac2=[169,170,166,173,158,177,161,180,186,159] # I don't understand these, but I hope they work. - code2=name*(spec+1) +(trait+4)*(trait+6) +(rank+1)*(xp+1) +(talent+1)*43*fac1[name%10] -(clas+1)*(241+fac2[name%10]) -(rem+1)*50 +(key+1)*4 +(moh+1)*9 +(pcc+1)*19 +(cob+1)*39 +(lsa+1)*79 +(armour+1)*159 - code2+=100*(cap+1)*(code2%1000) # To be honest I don't understand any of this. It's just random nonsense. - while code2>99999: - code2=code2%100000+code2//100000 - return shared.fill0s(code2,5) - -############################ -# SUPPORTING FUNCTIONS BELOW -############################ - -def getval(char): - vals='_9483726150rstlmeaiuonycdpjkhgxwfvqzb(-[.!' # szszss' - #vals='_5698472031aeioyusptndchbrxvzjmlkwgfq-!.([' # meebs' - if char in vals: return vals.index(char)+1 - return 43*len(char.encode('utf-8')) # Apparently multibyte chars do this - -def hash_name(name): - parsed=name.lower().replace(')','(').replace(']','[') - checksum=0 - for i in range(len(parsed)): - checksum+=getval(parsed[i])*((i+1)%3+1) - return checksum - -def adjust_rank(rank,cap=-1, key=False,moh=False,pcc=False): - rank+=key*2+moh # Invalid if rank <11, but whatever - if rank>8: #Can't have PCC below r10, and there are only two possible cap states after that - cap=(cap==4)*2+pcc+1 # I don't know why pcc is encoded like this, I would have thought cap would behave normally if not pcc, but nope. - return rank,cap - -def rand(seed,clas): - if seed>-1: n=seed - else: n=int(random.random()*5)*2 - if clas>9: return n+1,clas-10 - return n,clas \ No newline at end of file diff --git a/shared.py b/shared.py deleted file mode 100644 index 1538502..0000000 --- a/shared.py +++ /dev/null @@ -1,38 +0,0 @@ -tracing=False -def trace(*args,**kwargs): - if tracing: print(*args,**kwargs) - -def delimit(code,spacer=''): - if not spacer: - if len(code)==16: return code - if len(code)!=19: raise Exception('code length should be 19 for despacing, got',len(code),'from',code) - return code[0:4]+code[5:9]+code[10:14]+code[15:19] - if spacer: - if len(code)==19: return code - if len(code)!=16: raise Exception('code length should be 16 for spacing, got',len(code),'from',code) - return code[0:4]+spacer+code[4:8]+spacer+code[8:12]+spacer+code[12:16] - -def scramble(code,unscramble=False): - n=int(code[0]) - if unscramble: n=15-n # Because the 1st digit is left alone this needs to be +1 - return code[0]+code[16-n:16]+code[1:16-n] - -def combine(code,namehash=''): - if namehash: return code[0:2]+namehash[4:5]+code[2:6]+namehash[3:4]+code[6:8]+namehash[1:3]+code[8:11]+namehash[0:1] - charcode=code[0:2]+code[3:7]+code[8:10]+code[12:15] - namehash=code[15]+code[10:12]+code[7]+code[2] - return charcode,namehash - -def fill0s(num,zeroes=0): - if not zeroes: return str(int(num)) - n=str(num) - if len(n)>zeroes: raise Exception(f"can't pad {n} ({len(n)}) with {zeroes} 0s") - return '0'*(zeroes-len(n))+n - -def muxhero(clas,trait,spec): - return clas+trait*12+spec*12*16 # It's actually stored backwards like this, spec is in the most significant bits. Whatever, it doesn't need to be reversable. - -def muxmedals(key,moh=-1,pcc=-1,cob=-1,lsa=-1,rem=-1): - print(key,moh,pcc,cob,lsa,rem) - if moh!=-1: return key+moh*2+pcc*4+cob*8+lsa*16+rem*32 - return bool(key&1),bool(key&2),bool(key&4),bool(key&8),bool(key&16),key//32 \ No newline at end of file diff --git a/tables.py b/tables.py deleted file mode 100644 index 4f44a10..0000000 --- a/tables.py +++ /dev/null @@ -1,34 +0,0 @@ -#Data tables: Mostly used by the codegen itself -clas=[ -[0,1,2,5], # Sniper: Crg, wire, run, tinker -[0,1,4,6], # Medic: Crg, wire, tough, hack -[0,1,2,6], # Tact: Crg, wire, run, hack -[0,1,3,6], # Psy: Crg, wire, spot, hack -[0,2,4,5], # HO: Crg, run, tough, tinker -[0,2,4,3], # Demo: Crg, run, tough, spot -[0,3,2,6], # Borg: Crg, spot, run, hack -[0,3,5,4], # Pyro: Crg, spot, tinker, tough -[0,4,5,3], # WM (LR, GL): Crg, tough, tinker, spot -[0,2,6,5], # Mav (AR, SR, CG, RL, F): Crg, run, hack, tinker -[0,1,5,6], # Tech: Crg, wire, tinker, hack -[0,1,3,4] # Alice: Crg, wire, spot, tough -] -weights=[1,50,15,5,2,2,4,8,3,18,9] - -#Display tables: Mostly used by the rank code manager -classes=['Sniper','Medic','Tact','Psy','HO','Demo','Borg','Pyro','WM','Mav','Tech','Alice'] -guns=['AR','SR','CG','RL','F','LR','GL','PF'] -gunmaps=[1,0,0,0,2,3,2,4,5,6,0,1,2,3,4,1,7] # This is unwrapped mappings so every value matters -gunmaps_wrapped=[1,0,0,0,2,3,2,4,-1,-1,1,7] # Wrapped mappings return -1 for classes that have gun choices. Hopefully you'll never use it with them. -armour='LMHA' -trait=['SK', 'Gift', 'Surv', 'Goon', 'Acro', 'SL', 'Healer', 'FC', 'CR', 'RR', 'Gadg', 'Prowl', 'Gizer', 'PR', 'Engi', 'Reck'] -spec=['Weap', 'PA', 'Cells', 'Cyber', 'Tri', 'Chem', 'Leader', 'Robo', 'Esp'] -talent=['Crg','Wire','Run','Spot','Tough','Tinker','Hack'] -#Convenience -displays=[classes,guns,armour,trait,spec,talent] -displays_str=['classes','guns','armour','traits','specs','talents'] -displays_nogun=[classes,armour,trait,spec,talent] -displays_nogun_str=['classes','armour','traits','specs','talents'] -rank=range(12) -xp=range(2500) -cap=[3, 6, 9, 10, 11] \ No newline at end of file