aboutsummaryrefslogtreecommitdiff
path: root/srb.py
blob: 5d33efeaa0e54fb7572845551074a9c1ca5d5986 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
#!/usr/bin/env python3
# Startdate: 2024-03-08-6 15:28
# File: srb_lib.py
# Purpose: frontend for srb_lib
# History:
# Usage:
# Reference:
#    blog posts 2024-03
#    delsum from github
#    bgconf.py

# WORKHERE:
# or --reset-level 5 (which zeros out all info about completion of that level)
# --set-level-letters 0,none 0,all
# --set-level 0,done 5,new # sets balloons 10, status general, letters all

import srb_lib, argparse, sys
from srb_lib import ferror, debuglev

HELP_WEAPONS = ""

for w in srb_lib.WEAPONS:
   HELP_WEAPONS += str(w["id"]) + "," + w["name"] + ','
HELP_WEAPONS = HELP_WEAPONS.rstrip(",")

parser = argparse.ArgumentParser(description="Cli tool for manipulating savegame files for srb.exe",
   formatter_class=argparse.RawDescriptionHelpFormatter,
   epilog=f"""LEVELS include {[i['name'] for i in srb_lib.LEVELS]+list(range(0,len(srb_lib.LEVELS)))}
WEAPONS include {HELP_WEAPONS}
NAME_CHARS include "{srb_lib.NAME_CHARS}"
""")
parser.add_argument("-V","--version",action="version",version="%(prog)s " + srb_lib.srb_lib_version)
parser.add_argument("-d","--debug",nargs='?',default=0,type=int,choices=range(0,11),help="Set debug level")
parser.add_argument("--profile",type=int,choices=range(1,4),help="Profile in user menu.")
parser.add_argument("--get-money",action="store_true",help="Print current money for profile.")
parser.add_argument("--set-money",type=int,help="Set money for profile.")
parser.add_argument("--get-weapon",action="store_true",help="Print currently equipped weapon for profile.")
# choices seems to be too strict here for the numbers. We can live with just the
#choices=[i for i in srb_lib.WEAPONS if i != "undefined"]+list(range(0,16))
#parser.add_argument("--set-weapon",choices=[i for i in srb_lib.WEAPONS if i != "undefined"],help="Print currently equipped weapon for profile.")
parser.add_argument("--set-weapon",help="Set currently equipped weapon for profile.")
parser.add_argument("--get-purchased-weapons",action="store_true",help="Print currently purchased weapons for profile.")
parser.add_argument("--add-purchased-weapons",action="append",help="For profile, add these purchased weapons. Can be used multiple times.")
parser.add_argument("--remove-purchased-weapons",action="append",help="For profile, remove (un-buy) these purchased weapons. Can be used multiple times.")
parser.add_argument("--get-purchased-planes",action="store_true",help="Print currently purchased planes for profile.")
parser.add_argument("--add-purchased-planes",action="append",help="For profile, add these purchased planes. Can be used multiple times.")
parser.add_argument("--remove-purchased-planes",action="append",help="For profile, remove (un-buy) these purchased planes. Can be used multiple times.")
parser.add_argument("--get-level",help="Print status for this level for profile.")
parser.add_argument("--set-level-status",action="append",help="Set completion status for this level for profile. Example value to pass: \"0,general\"")
parser.add_argument("--set-level-balloons",action="append",help="Set balloon status for this level for profile. Example value to pass: \"0,all\" or \"0,none\"")
parser.add_argument("--get-levelset",help="Print status for this levelset for profile.")
parser.add_argument("--set-levelset-available-levels",action="append",help="Set number of available levels in this levelset for profile.")
parser.add_argument("--get-name",action="store_true",help="Print name for profile.")
parser.add_argument("--set-name",help="Set name for profile.")
parser.add_argument("--get-profile-in-use",action="store_true",help="Print if profile is in use.")
parser.add_argument("--get-tutorial-completed",action="store_true",help="Print if profile has completed the tutorial.")
parser.add_argument("--set-tutorial-completed",choices=["True","False"],help="Set tutorial-completed for profile.")
parser.add_argument("--get-health",action="store_true",help="Print stat health for profile.")
parser.add_argument("--set-health",type=int,choices=range(1,5),help="Set stat health for profile.")
parser.add_argument("--get-stunt",action="store_true",help="Print stat stunt for profile.")
parser.add_argument("--set-stunt",type=int,choices=range(1,5),help="Set stat stunt for profile.")
parser.add_argument("--get-gun",action="store_true",help="Print stat gun for profile.")
parser.add_argument("--set-gun",type=int,choices=range(1,6),help="Set stat gun for profile.")
parser.add_argument("--checksum",action=argparse.BooleanOptionalAction,default=True,help="Correct checksum. Default is to do this. It happens at the end of everything else.")
parser.add_argument("file",default="Profile 1.sav")
args = parser.parse_args()
debuglevel = 0
if args.debug:
   debuglevel = args.debug

if debuglev(1,debuglevel):
   ferror("debug level", debuglevel)
if debuglev(8,debuglevel):
   ferror(args)

# common parameters
profile_id = args.profile

#print(f"profile_id={profile_id}")

# WORKHERE: new actions that need --profile must be added here.
if not profile_id and (args.get_money or args.set_money or args.get_weapon or args.set_weapon or args.get_level or args.get_name or args.set_name or args.get_profile_in_use or args.get_purchased_weapons or args.get_tutorial_completed or args.add_purchased_weapons or args.remove_purchased_weapons or args.get_health or args.get_stunt or args.get_gun or args.set_health or args.set_stunt or args.set_gun or args.get_levelset or args.get_purchased_planes or args.add_purchased_planes or args.remove_purchased_planes or args.set_level_status or args.set_level_balloons or args.set_levelset_available_levels):
   ferror("Warning: Cannot perform most actions without --profile. Not all tasks may run.")
else:
   if args.get_money:
      money = srb_lib.get_money(args.file, profile_id)
      print(f"Profile {profile_id} has {money} money.")
   if args.set_money:
      if args.set_money > 0xFFFF:
         ferror(f"Warning: Do not set money higher than 65535. While it can technically work, there's no need for that in-game anyways. Continuing...")
      else:
         data = srb_lib.set_money(args.file, profile_id, args.set_money)
         srb_lib.write_file(args.file,0,data)
   if args.get_weapon:
      print(f"Profile {profile_id} has weapon {srb_lib.get_weapon(args.file, profile_id)}")
   if args.set_weapon:
      try:
         args.set_weapon = int(args.set_weapon)
      except:
         pass
      data = srb_lib.set_weapon(args.file, profile_id, args.set_weapon)
      if type(data) == int and data == -1:
         # error is printed in the function
         pass
      else:
         srb_lib.write_file(args.file,0,data)
   if args.get_level:
      print(f"Profile {profile_id} has level {args.get_level} status {srb_lib.get_level_status(args.file,profile_id,args.get_level)}")
   if args.set_level_status:
      for l in args.set_level_status:
         level_num = -1
         try:
            level_num, completion = l.split(",")
         except:
            ferror(f"Warning! Skipping un-parseable level completion spec {l}. Please use \"0,general\" format.")
         if level_num != -1:
            completion_names = [i["name"] for i in srb_lib.LEVEL_STATUSES]
            if completion not in completion_names:
               ferror(f"Warning! Skipping invalid level completion spec {l}; for completion, use one of {completion_names}")
            else:
               # so we have a valid level completion spec.
               data, new_status, message = srb_lib.set_level_status(args.file,profile_id,level_num, completion)
               if (type(data) == int and data == -1) or message != "":
                  ferror(f"Failed to set profile {profile_id} level {level_num} status to {completion} because {message}")
               else:
                  srb_lib.write_file(args.file,0,data)
   if args.set_level_balloons:
      for l in args.set_level_balloons:
         level_num = -1
         try:
            level_num, balloons = l.split(",")
         except:
            ferror(f"Warning! Skipping un-parseable level completion spec {l}. Please use \"0,general\" format.")
         if level_num != -1:
            if balloons not in ["none","all"]:
               ferror(f"Warning! Skipping invalid balloons for level {l}; options are all or none.")
            else:
               data, message = srb_lib.set_level_balloons(args.file,profile_id,level_num,balloons)
               if (type(data) == int and data == -1) or message != "":
                  ferror(f"Failed to set profile {profile_id} level {level_num} balloons to {balloons} because {message}")
               else:
                  srb_lib.write_file(args.file,0,data)
   if args.get_levelset:
      print(f"Profile {profile_id} has levelset {args.get_levelset} status {srb_lib.get_levelset_status(args.file,profile_id,args.get_levelset)}")
   if args.get_name:
      print(f"Profile {profile_id} has name {srb_lib.get_name(args.file,profile_id)}")
   if args.set_name:
      data, message = srb_lib.set_name(args.file, profile_id, args.set_name)
      if (type(data) == int and data == -1) or message != "":
         ferror(f"Failed to set profile {profile_id} name to {args.set_name} because {message}")
      else:
         srb_lib.write_file(args.file,0,data)
   if args.get_profile_in_use:
      print(f"Profile {profile_id} in use is {srb_lib.get_profile_in_use(args.file,profile_id)}")
   if args.get_purchased_weapons:
      print(f"Profile {profile_id} has weapons {srb_lib.get_purchased_weapons(args.file,profile_id)}")
   if args.add_purchased_weapons:
      data, message = srb_lib.set_purchased_weapons(args.file,profile_id,"add",args.add_purchased_weapons)
      if (type(data) == int and data == -1) or message != "":
         ferror(f"Failed to add purchased weapons {args.add_purchased_weapons} because {message}")
      else:
         srb_lib.write_file(args.file,0,data)
   if args.remove_purchased_weapons:
      data, message = srb_lib.set_purchased_weapons(args.file,profile_id,"remove",args.remove_purchased_weapons)
      if (type(data) == int and data == -1) or message != "":
         ferror(f"Failed to add purchased weapons {args.remove_purchased_weapons} because {message}")
      else:
         srb_lib.write_file(args.file,0,data)
   if args.get_tutorial_completed:
      print(f"Profile {profile_id} completed-tutorial is {srb_lib.get_tutorial_completed(args.file,profile_id)}")
   if args.set_tutorial_completed:
      thisbool = False if args.set_tutorial_completed == "False" else True
      data, newstatus = srb_lib.set_tutorial_completed(args.file,profile_id,thisbool)
      srb_lib.write_file(args.file,0,data)
   if args.get_health:
      stat, message = srb_lib.get_plane_stat(args.file,profile_id,"health")
      # error is printed in the function, so if not any error, run
      if not (stat == -1 or message != ""):
         print(f"Profile {profile_id} has health level {stat}")
   if args.set_health:
      data, message = srb_lib.set_plane_stat(args.file,profile_id,"health",args.set_health)
      if data == -1 or message != "":
         print(f"Failed to set health to {args.set_health} because {message}")
      else:
         srb_lib.write_file(args.file,0,data)
   if args.get_stunt:
      stat, message = srb_lib.get_plane_stat(args.file,profile_id,"stunt")
      # error is printed in the function, so if not any error, run
      if not (stat == -1 or message != ""):
         print(f"Profile {profile_id} has stunt level {stat}")
   if args.set_stunt:
      data, message = srb_lib.set_plane_stat(args.file,profile_id,"stunt",args.set_stunt)
      if data == -1 or message != "":
         print(f"Failed to set stunt to {args.set_stunt} because {message}")
      else:
         srb_lib.write_file(args.file,0,data)
   if args.get_gun:
      stat, message = srb_lib.get_plane_stat(args.file,profile_id,"gun")
      # error is printed in the function, so if not any error, run
      if not (stat == -1 or message != ""):
         print(f"Profile {profile_id} has gun level {stat}")
   if args.set_gun:
      data, message = srb_lib.set_plane_stat(args.file,profile_id,"gun",args.set_gun)
      if data == -1 or message != "":
         print(f"Failed to set gun to {args.set_gun} because {message}")
      else:
         srb_lib.write_file(args.file,0,data)
   if args.get_purchased_planes:
      print(f"Profile {profile_id} has planes {srb_lib.get_purchased_planes(args.file,profile_id)}")
   if args.add_purchased_planes:
      data, message = srb_lib.set_purchased_planes(args.file,profile_id,"add",args.add_purchased_planes)
      if (type(data) == int and data == -1) or message != "":
         ferror(f"Failed to add purchased planes {args.add_purchased_planes} because {message}")
      else:
         srb_lib.write_file(args.file,0,data)
   if args.remove_purchased_planes:
      data, message = srb_lib.set_purchased_planes(args.file,profile_id,"remove",args.remove_purchased_planes)
      if (type(data) == int and data == -1) or message != "":
         ferror(f"Failed to add purchased planes {args.remove_purchased_planes} because {message}")
      else:
         srb_lib.write_file(args.file,0,data)
   if args.set_levelset_available_levels:
      for l in args.set_levelset_available_levels:
         levelset_num = -1
         try:
            levelset_num, count = l.split(",")
         except:
            ferror(f"Warning! Skipping un-parseable level completion spec {l}. Please use \"0,general\" format.")
         if levelset_num != -1:
            data, message = srb_lib.set_levelset_available_levels(args.file,profile_id,levelset_num,count)
            if (type(data) == int and data == -1) or message != "":
               ferror(f"Failed to set profile {profile_id} level {levelset_num} available levels to {count} because {message}")
            else:
               srb_lib.write_file(args.file,0,data)

if args.checksum:
   f = args.file
   #for f in args.file:
   if debuglev(1,debuglevel):
      ferror(f"Fixing checksum for file {f}")
   srb_lib.correct_file(f,debuglevel)
bgstack15