├── .gitignore
├── LICENSE
├── LifeIsStrangeSaveEditor.Win
├── AboutForm.Designer.cs
├── AboutForm.cs
├── AboutForm.resx
├── AppHelper.cs
├── CheckpointsInfo.cs
├── EpisodesInfo.cs
├── IEnumerableExtensions.cs
├── LifeIsStrangeSaveEditor.Win.csproj
├── MainForm.Designer.cs
├── MainForm.cs
├── MainForm.resx
├── MainForm.yo.resx
├── Model.cs
├── PlayerProgressEditForm.Designer.cs
├── PlayerProgressEditForm.cs
├── PlayerProgressEditForm.resx
├── Program.cs
├── Properties
│ ├── AssemblyInfo.cs
│ ├── Resources.Designer.cs
│ ├── Resources.resx
│ ├── Settings.Designer.cs
│ └── Settings.settings
├── Resources
│ ├── Butterfly.ico
│ ├── Thumbs.db
│ └── butterfly_104x96.gif
├── StreamExtensions.cs
└── SubLevelsInfo.cs
├── LifeIsStrangeSaveEditor.sln
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | x64/
19 | x86/
20 | build/
21 | bld/
22 | [Bb]in/
23 | [Oo]bj/
24 |
25 | # Visual Studo 2015 cache/options directory
26 | .vs/
27 |
28 | # MSTest test Results
29 | [Tt]est[Rr]esult*/
30 | [Bb]uild[Ll]og.*
31 |
32 | # NUNIT
33 | *.VisualState.xml
34 | TestResult.xml
35 |
36 | # Build Results of an ATL Project
37 | [Dd]ebugPS/
38 | [Rr]eleasePS/
39 | dlldata.c
40 |
41 | *_i.c
42 | *_p.c
43 | *_i.h
44 | *.ilk
45 | *.meta
46 | *.obj
47 | *.pch
48 | *.pdb
49 | *.pgc
50 | *.pgd
51 | *.rsp
52 | *.sbr
53 | *.tlb
54 | *.tli
55 | *.tlh
56 | *.tmp
57 | *.tmp_proj
58 | *.log
59 | *.vspscc
60 | *.vssscc
61 | .builds
62 | *.pidb
63 | *.svclog
64 | *.scc
65 |
66 | # Chutzpah Test files
67 | _Chutzpah*
68 |
69 | # Visual C++ cache files
70 | ipch/
71 | *.aps
72 | *.ncb
73 | *.opensdf
74 | *.sdf
75 | *.cachefile
76 |
77 | # Visual Studio profiler
78 | *.psess
79 | *.vsp
80 | *.vspx
81 |
82 | # TFS 2012 Local Workspace
83 | $tf/
84 |
85 | # Guidance Automation Toolkit
86 | *.gpState
87 |
88 | # ReSharper is a .NET coding add-in
89 | _ReSharper*/
90 | *.[Rr]e[Ss]harper
91 | *.DotSettings.user
92 |
93 | # JustCode is a .NET coding addin-in
94 | .JustCode
95 |
96 | # TeamCity is a build add-in
97 | _TeamCity*
98 |
99 | # DotCover is a Code Coverage Tool
100 | *.dotCover
101 |
102 | # NCrunch
103 | _NCrunch_*
104 | .*crunch*.local.xml
105 |
106 | # MightyMoose
107 | *.mm.*
108 | AutoTest.Net/
109 |
110 | # Web workbench (sass)
111 | .sass-cache/
112 |
113 | # Installshield output folder
114 | [Ee]xpress/
115 |
116 | # DocProject is a documentation generator add-in
117 | DocProject/buildhelp/
118 | DocProject/Help/*.HxT
119 | DocProject/Help/*.HxC
120 | DocProject/Help/*.hhc
121 | DocProject/Help/*.hhk
122 | DocProject/Help/*.hhp
123 | DocProject/Help/Html2
124 | DocProject/Help/html
125 |
126 | # Click-Once directory
127 | publish/
128 |
129 | # Publish Web Output
130 | *.[Pp]ublish.xml
131 | *.azurePubxml
132 | # TODO: Comment the next line if you want to checkin your web deploy settings
133 | # but database connection strings (with potential passwords) will be unencrypted
134 | *.pubxml
135 | *.publishproj
136 |
137 | # NuGet Packages
138 | *.nupkg
139 | # The packages folder can be ignored because of Package Restore
140 | **/packages/*
141 | # except build/, which is used as an MSBuild target.
142 | !**/packages/build/
143 | # Uncomment if necessary however generally it will be regenerated when needed
144 | #!**/packages/repositories.config
145 |
146 | # Windows Azure Build Output
147 | csx/
148 | *.build.csdef
149 |
150 | # Windows Store app package directory
151 | AppPackages/
152 |
153 | # Others
154 | *.[Cc]ache
155 | ClientBin/
156 | [Ss]tyle[Cc]op.*
157 | ~$*
158 | *~
159 | *.dbmdl
160 | *.dbproj.schemaview
161 | *.pfx
162 | *.publishsettings
163 | node_modules/
164 | bower_components/
165 |
166 | # RIA/Silverlight projects
167 | Generated_Code/
168 |
169 | # Backup & report files from converting an old project file
170 | # to a newer Visual Studio version. Backup files are not needed,
171 | # because we have git ;-)
172 | _UpgradeReport_Files/
173 | Backup*/
174 | UpgradeLog*.XML
175 | UpgradeLog*.htm
176 |
177 | # SQL Server files
178 | *.mdf
179 | *.ldf
180 |
181 | # Business Intelligence projects
182 | *.rdl.data
183 | *.bim.layout
184 | *.bim_*.settings
185 |
186 | # Microsoft Fakes
187 | FakesAssemblies/
188 |
189 | # Node.js Tools for Visual Studio
190 | .ntvs_analysis.dat
191 |
192 | # Visual Studio 6 build log
193 | *.plg
194 |
195 | # Visual Studio 6 workspace options file
196 | *.opt
197 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 2, June 1991
3 |
4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6 | Everyone is permitted to copy and distribute verbatim copies
7 | of this license document, but changing it is not allowed.
8 |
9 | Preamble
10 |
11 | The licenses for most software are designed to take away your
12 | freedom to share and change it. By contrast, the GNU General Public
13 | License is intended to guarantee your freedom to share and change free
14 | software--to make sure the software is free for all its users. This
15 | General Public License applies to most of the Free Software
16 | Foundation's software and to any other program whose authors commit to
17 | using it. (Some other Free Software Foundation software is covered by
18 | the GNU Lesser General Public License instead.) You can apply it to
19 | your programs, too.
20 |
21 | When we speak of free software, we are referring to freedom, not
22 | price. Our General Public Licenses are designed to make sure that you
23 | have the freedom to distribute copies of free software (and charge for
24 | this service if you wish), that you receive source code or can get it
25 | if you want it, that you can change the software or use pieces of it
26 | in new free programs; and that you know you can do these things.
27 |
28 | To protect your rights, we need to make restrictions that forbid
29 | anyone to deny you these rights or to ask you to surrender the rights.
30 | These restrictions translate to certain responsibilities for you if you
31 | distribute copies of the software, or if you modify it.
32 |
33 | For example, if you distribute copies of such a program, whether
34 | gratis or for a fee, you must give the recipients all the rights that
35 | you have. You must make sure that they, too, receive or can get the
36 | source code. And you must show them these terms so they know their
37 | rights.
38 |
39 | We protect your rights with two steps: (1) copyright the software, and
40 | (2) offer you this license which gives you legal permission to copy,
41 | distribute and/or modify the software.
42 |
43 | Also, for each author's protection and ours, we want to make certain
44 | that everyone understands that there is no warranty for this free
45 | software. If the software is modified by someone else and passed on, we
46 | want its recipients to know that what they have is not the original, so
47 | that any problems introduced by others will not reflect on the original
48 | authors' reputations.
49 |
50 | Finally, any free program is threatened constantly by software
51 | patents. We wish to avoid the danger that redistributors of a free
52 | program will individually obtain patent licenses, in effect making the
53 | program proprietary. To prevent this, we have made it clear that any
54 | patent must be licensed for everyone's free use or not licensed at all.
55 |
56 | The precise terms and conditions for copying, distribution and
57 | modification follow.
58 |
59 | GNU GENERAL PUBLIC LICENSE
60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61 |
62 | 0. This License applies to any program or other work which contains
63 | a notice placed by the copyright holder saying it may be distributed
64 | under the terms of this General Public License. The "Program", below,
65 | refers to any such program or work, and a "work based on the Program"
66 | means either the Program or any derivative work under copyright law:
67 | that is to say, a work containing the Program or a portion of it,
68 | either verbatim or with modifications and/or translated into another
69 | language. (Hereinafter, translation is included without limitation in
70 | the term "modification".) Each licensee is addressed as "you".
71 |
72 | Activities other than copying, distribution and modification are not
73 | covered by this License; they are outside its scope. The act of
74 | running the Program is not restricted, and the output from the Program
75 | is covered only if its contents constitute a work based on the
76 | Program (independent of having been made by running the Program).
77 | Whether that is true depends on what the Program does.
78 |
79 | 1. You may copy and distribute verbatim copies of the Program's
80 | source code as you receive it, in any medium, provided that you
81 | conspicuously and appropriately publish on each copy an appropriate
82 | copyright notice and disclaimer of warranty; keep intact all the
83 | notices that refer to this License and to the absence of any warranty;
84 | and give any other recipients of the Program a copy of this License
85 | along with the Program.
86 |
87 | You may charge a fee for the physical act of transferring a copy, and
88 | you may at your option offer warranty protection in exchange for a fee.
89 |
90 | 2. You may modify your copy or copies of the Program or any portion
91 | of it, thus forming a work based on the Program, and copy and
92 | distribute such modifications or work under the terms of Section 1
93 | above, provided that you also meet all of these conditions:
94 |
95 | a) You must cause the modified files to carry prominent notices
96 | stating that you changed the files and the date of any change.
97 |
98 | b) You must cause any work that you distribute or publish, that in
99 | whole or in part contains or is derived from the Program or any
100 | part thereof, to be licensed as a whole at no charge to all third
101 | parties under the terms of this License.
102 |
103 | c) If the modified program normally reads commands interactively
104 | when run, you must cause it, when started running for such
105 | interactive use in the most ordinary way, to print or display an
106 | announcement including an appropriate copyright notice and a
107 | notice that there is no warranty (or else, saying that you provide
108 | a warranty) and that users may redistribute the program under
109 | these conditions, and telling the user how to view a copy of this
110 | License. (Exception: if the Program itself is interactive but
111 | does not normally print such an announcement, your work based on
112 | the Program is not required to print an announcement.)
113 |
114 | These requirements apply to the modified work as a whole. If
115 | identifiable sections of that work are not derived from the Program,
116 | and can be reasonably considered independent and separate works in
117 | themselves, then this License, and its terms, do not apply to those
118 | sections when you distribute them as separate works. But when you
119 | distribute the same sections as part of a whole which is a work based
120 | on the Program, the distribution of the whole must be on the terms of
121 | this License, whose permissions for other licensees extend to the
122 | entire whole, and thus to each and every part regardless of who wrote it.
123 |
124 | Thus, it is not the intent of this section to claim rights or contest
125 | your rights to work written entirely by you; rather, the intent is to
126 | exercise the right to control the distribution of derivative or
127 | collective works based on the Program.
128 |
129 | In addition, mere aggregation of another work not based on the Program
130 | with the Program (or with a work based on the Program) on a volume of
131 | a storage or distribution medium does not bring the other work under
132 | the scope of this License.
133 |
134 | 3. You may copy and distribute the Program (or a work based on it,
135 | under Section 2) in object code or executable form under the terms of
136 | Sections 1 and 2 above provided that you also do one of the following:
137 |
138 | a) Accompany it with the complete corresponding machine-readable
139 | source code, which must be distributed under the terms of Sections
140 | 1 and 2 above on a medium customarily used for software interchange; or,
141 |
142 | b) Accompany it with a written offer, valid for at least three
143 | years, to give any third party, for a charge no more than your
144 | cost of physically performing source distribution, a complete
145 | machine-readable copy of the corresponding source code, to be
146 | distributed under the terms of Sections 1 and 2 above on a medium
147 | customarily used for software interchange; or,
148 |
149 | c) Accompany it with the information you received as to the offer
150 | to distribute corresponding source code. (This alternative is
151 | allowed only for noncommercial distribution and only if you
152 | received the program in object code or executable form with such
153 | an offer, in accord with Subsection b above.)
154 |
155 | The source code for a work means the preferred form of the work for
156 | making modifications to it. For an executable work, complete source
157 | code means all the source code for all modules it contains, plus any
158 | associated interface definition files, plus the scripts used to
159 | control compilation and installation of the executable. However, as a
160 | special exception, the source code distributed need not include
161 | anything that is normally distributed (in either source or binary
162 | form) with the major components (compiler, kernel, and so on) of the
163 | operating system on which the executable runs, unless that component
164 | itself accompanies the executable.
165 |
166 | If distribution of executable or object code is made by offering
167 | access to copy from a designated place, then offering equivalent
168 | access to copy the source code from the same place counts as
169 | distribution of the source code, even though third parties are not
170 | compelled to copy the source along with the object code.
171 |
172 | 4. You may not copy, modify, sublicense, or distribute the Program
173 | except as expressly provided under this License. Any attempt
174 | otherwise to copy, modify, sublicense or distribute the Program is
175 | void, and will automatically terminate your rights under this License.
176 | However, parties who have received copies, or rights, from you under
177 | this License will not have their licenses terminated so long as such
178 | parties remain in full compliance.
179 |
180 | 5. You are not required to accept this License, since you have not
181 | signed it. However, nothing else grants you permission to modify or
182 | distribute the Program or its derivative works. These actions are
183 | prohibited by law if you do not accept this License. Therefore, by
184 | modifying or distributing the Program (or any work based on the
185 | Program), you indicate your acceptance of this License to do so, and
186 | all its terms and conditions for copying, distributing or modifying
187 | the Program or works based on it.
188 |
189 | 6. Each time you redistribute the Program (or any work based on the
190 | Program), the recipient automatically receives a license from the
191 | original licensor to copy, distribute or modify the Program subject to
192 | these terms and conditions. You may not impose any further
193 | restrictions on the recipients' exercise of the rights granted herein.
194 | You are not responsible for enforcing compliance by third parties to
195 | this License.
196 |
197 | 7. If, as a consequence of a court judgment or allegation of patent
198 | infringement or for any other reason (not limited to patent issues),
199 | conditions are imposed on you (whether by court order, agreement or
200 | otherwise) that contradict the conditions of this License, they do not
201 | excuse you from the conditions of this License. If you cannot
202 | distribute so as to satisfy simultaneously your obligations under this
203 | License and any other pertinent obligations, then as a consequence you
204 | may not distribute the Program at all. For example, if a patent
205 | license would not permit royalty-free redistribution of the Program by
206 | all those who receive copies directly or indirectly through you, then
207 | the only way you could satisfy both it and this License would be to
208 | refrain entirely from distribution of the Program.
209 |
210 | If any portion of this section is held invalid or unenforceable under
211 | any particular circumstance, the balance of the section is intended to
212 | apply and the section as a whole is intended to apply in other
213 | circumstances.
214 |
215 | It is not the purpose of this section to induce you to infringe any
216 | patents or other property right claims or to contest validity of any
217 | such claims; this section has the sole purpose of protecting the
218 | integrity of the free software distribution system, which is
219 | implemented by public license practices. Many people have made
220 | generous contributions to the wide range of software distributed
221 | through that system in reliance on consistent application of that
222 | system; it is up to the author/donor to decide if he or she is willing
223 | to distribute software through any other system and a licensee cannot
224 | impose that choice.
225 |
226 | This section is intended to make thoroughly clear what is believed to
227 | be a consequence of the rest of this License.
228 |
229 | 8. If the distribution and/or use of the Program is restricted in
230 | certain countries either by patents or by copyrighted interfaces, the
231 | original copyright holder who places the Program under this License
232 | may add an explicit geographical distribution limitation excluding
233 | those countries, so that distribution is permitted only in or among
234 | countries not thus excluded. In such case, this License incorporates
235 | the limitation as if written in the body of this License.
236 |
237 | 9. The Free Software Foundation may publish revised and/or new versions
238 | of the General Public License from time to time. Such new versions will
239 | be similar in spirit to the present version, but may differ in detail to
240 | address new problems or concerns.
241 |
242 | Each version is given a distinguishing version number. If the Program
243 | specifies a version number of this License which applies to it and "any
244 | later version", you have the option of following the terms and conditions
245 | either of that version or of any later version published by the Free
246 | Software Foundation. If the Program does not specify a version number of
247 | this License, you may choose any version ever published by the Free Software
248 | Foundation.
249 |
250 | 10. If you wish to incorporate parts of the Program into other free
251 | programs whose distribution conditions are different, write to the author
252 | to ask for permission. For software which is copyrighted by the Free
253 | Software Foundation, write to the Free Software Foundation; we sometimes
254 | make exceptions for this. Our decision will be guided by the two goals
255 | of preserving the free status of all derivatives of our free software and
256 | of promoting the sharing and reuse of software generally.
257 |
258 | NO WARRANTY
259 |
260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268 | REPAIR OR CORRECTION.
269 |
270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278 | POSSIBILITY OF SUCH DAMAGES.
279 |
280 | END OF TERMS AND CONDITIONS
281 |
282 | How to Apply These Terms to Your New Programs
283 |
284 | If you develop a new program, and you want it to be of the greatest
285 | possible use to the public, the best way to achieve this is to make it
286 | free software which everyone can redistribute and change under these terms.
287 |
288 | To do so, attach the following notices to the program. It is safest
289 | to attach them to the start of each source file to most effectively
290 | convey the exclusion of warranty; and each file should have at least
291 | the "copyright" line and a pointer to where the full notice is found.
292 |
293 | {description}
294 | Copyright (C) {year} {fullname}
295 |
296 | This program is free software; you can redistribute it and/or modify
297 | it under the terms of the GNU General Public License as published by
298 | the Free Software Foundation; either version 2 of the License, or
299 | (at your option) any later version.
300 |
301 | This program is distributed in the hope that it will be useful,
302 | but WITHOUT ANY WARRANTY; without even the implied warranty of
303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304 | GNU General Public License for more details.
305 |
306 | You should have received a copy of the GNU General Public License along
307 | with this program; if not, write to the Free Software Foundation, Inc.,
308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309 |
310 | Also add information on how to contact you by electronic and paper mail.
311 |
312 | If the program is interactive, make it output a short notice like this
313 | when it starts in an interactive mode:
314 |
315 | Gnomovision version 69, Copyright (C) year name of author
316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317 | This is free software, and you are welcome to redistribute it
318 | under certain conditions; type `show c' for details.
319 |
320 | The hypothetical commands `show w' and `show c' should show the appropriate
321 | parts of the General Public License. Of course, the commands you use may
322 | be called something other than `show w' and `show c'; they could even be
323 | mouse-clicks or menu items--whatever suits your program.
324 |
325 | You should also get your employer (if you work as a programmer) or your
326 | school, if any, to sign a "copyright disclaimer" for the program, if
327 | necessary. Here is a sample; alter the names:
328 |
329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330 | `Gnomovision' (which makes passes at compilers) written by James Hacker.
331 |
332 | {signature of Ty Coon}, 1 April 1989
333 | Ty Coon, President of Vice
334 |
335 | This General Public License does not permit incorporating your program into
336 | proprietary programs. If your program is a subroutine library, you may
337 | consider it more useful to permit linking proprietary applications with the
338 | library. If this is what you want to do, use the GNU Lesser General
339 | Public License instead of this License.
340 |
341 |
--------------------------------------------------------------------------------
/LifeIsStrangeSaveEditor.Win/AboutForm.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace LifeIsStrangeSaveEditor.Win
2 | {
3 | partial class AboutForm
4 | {
5 | ///
6 | /// Required designer variable.
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// Clean up any resources being used.
12 | ///
13 | /// true if managed resources should be disposed; otherwise, false.
14 | protected override void Dispose(bool disposing)
15 | {
16 | if (disposing && (components != null))
17 | {
18 | components.Dispose();
19 | }
20 | base.Dispose(disposing);
21 | }
22 |
23 | #region Windows Form Designer generated code
24 |
25 | ///
26 | /// Required method for Designer support - do not modify
27 | /// the contents of this method with the code editor.
28 | ///
29 | private void InitializeComponent()
30 | {
31 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(AboutForm));
32 | this.pictureBox1 = new System.Windows.Forms.PictureBox();
33 | this.btnClose = new System.Windows.Forms.Button();
34 | this.lblAppName = new System.Windows.Forms.Label();
35 | this.lblVersion = new System.Windows.Forms.Label();
36 | this.lblThanks = new System.Windows.Forms.Label();
37 | this.lblCopyright = new System.Windows.Forms.Label();
38 | this.lblLine = new System.Windows.Forms.Label();
39 | this.llEmail = new System.Windows.Forms.LinkLabel();
40 | this.label1 = new System.Windows.Forms.Label();
41 | ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
42 | this.SuspendLayout();
43 | //
44 | // pictureBox1
45 | //
46 | this.pictureBox1.Image = ((System.Drawing.Image)(resources.GetObject("pictureBox1.Image")));
47 | this.pictureBox1.Location = new System.Drawing.Point(133, 12);
48 | this.pictureBox1.Name = "pictureBox1";
49 | this.pictureBox1.Size = new System.Drawing.Size(104, 96);
50 | this.pictureBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize;
51 | this.pictureBox1.TabIndex = 0;
52 | this.pictureBox1.TabStop = false;
53 | //
54 | // btnClose
55 | //
56 | this.btnClose.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
57 | this.btnClose.DialogResult = System.Windows.Forms.DialogResult.Cancel;
58 | this.btnClose.Location = new System.Drawing.Point(283, 366);
59 | this.btnClose.Name = "btnClose";
60 | this.btnClose.Size = new System.Drawing.Size(75, 23);
61 | this.btnClose.TabIndex = 1;
62 | this.btnClose.Text = "Close";
63 | this.btnClose.UseVisualStyleBackColor = true;
64 | //
65 | // lblAppName
66 | //
67 | this.lblAppName.AutoSize = true;
68 | this.lblAppName.Font = new System.Drawing.Font("Arial", 14F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(204)));
69 | this.lblAppName.Location = new System.Drawing.Point(68, 123);
70 | this.lblAppName.Name = "lblAppName";
71 | this.lblAppName.Size = new System.Drawing.Size(235, 22);
72 | this.lblAppName.TabIndex = 2;
73 | this.lblAppName.Text = "Life is Strange Save Editor";
74 | //
75 | // lblVersion
76 | //
77 | this.lblVersion.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
78 | this.lblVersion.Location = new System.Drawing.Point(198, 145);
79 | this.lblVersion.Name = "lblVersion";
80 | this.lblVersion.Size = new System.Drawing.Size(105, 13);
81 | this.lblVersion.TabIndex = 3;
82 | this.lblVersion.Text = "Version: 0.0";
83 | this.lblVersion.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
84 | //
85 | // lblThanks
86 | //
87 | this.lblThanks.AutoSize = true;
88 | this.lblThanks.Font = new System.Drawing.Font("Arial", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(204)));
89 | this.lblThanks.Location = new System.Drawing.Point(12, 185);
90 | this.lblThanks.Name = "lblThanks";
91 | this.lblThanks.Size = new System.Drawing.Size(65, 16);
92 | this.lblThanks.TabIndex = 4;
93 | this.lblThanks.Text = "Thanks to";
94 | //
95 | // lblCopyright
96 | //
97 | this.lblCopyright.AutoSize = true;
98 | this.lblCopyright.Font = new System.Drawing.Font("Arial", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(204)));
99 | this.lblCopyright.Location = new System.Drawing.Point(12, 317);
100 | this.lblCopyright.Name = "lblCopyright";
101 | this.lblCopyright.Size = new System.Drawing.Size(141, 16);
102 | this.lblCopyright.TabIndex = 5;
103 | this.lblCopyright.Text = "Vakhtin Andrey © 2015";
104 | //
105 | // lblLine
106 | //
107 | this.lblLine.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
108 | this.lblLine.Location = new System.Drawing.Point(12, 170);
109 | this.lblLine.Name = "lblLine";
110 | this.lblLine.Size = new System.Drawing.Size(346, 2);
111 | this.lblLine.TabIndex = 6;
112 | //
113 | // llEmail
114 | //
115 | this.llEmail.AutoSize = true;
116 | this.llEmail.Font = new System.Drawing.Font("Arial", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(204)));
117 | this.llEmail.Location = new System.Drawing.Point(12, 333);
118 | this.llEmail.Name = "llEmail";
119 | this.llEmail.Size = new System.Drawing.Size(75, 16);
120 | this.llEmail.TabIndex = 7;
121 | this.llEmail.TabStop = true;
122 | this.llEmail.Text = "Contact me";
123 | this.llEmail.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.llEmail_LinkClicked);
124 | //
125 | // label1
126 | //
127 | this.label1.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
128 | this.label1.Location = new System.Drawing.Point(12, 357);
129 | this.label1.Name = "label1";
130 | this.label1.Size = new System.Drawing.Size(346, 2);
131 | this.label1.TabIndex = 8;
132 | //
133 | // AboutForm
134 | //
135 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
136 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
137 | this.CancelButton = this.btnClose;
138 | this.ClientSize = new System.Drawing.Size(370, 401);
139 | this.Controls.Add(this.label1);
140 | this.Controls.Add(this.llEmail);
141 | this.Controls.Add(this.lblLine);
142 | this.Controls.Add(this.lblCopyright);
143 | this.Controls.Add(this.lblThanks);
144 | this.Controls.Add(this.lblVersion);
145 | this.Controls.Add(this.lblAppName);
146 | this.Controls.Add(this.btnClose);
147 | this.Controls.Add(this.pictureBox1);
148 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
149 | this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
150 | this.MaximizeBox = false;
151 | this.MinimizeBox = false;
152 | this.Name = "AboutForm";
153 | this.ShowInTaskbar = false;
154 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
155 | this.Text = "About";
156 | ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit();
157 | this.ResumeLayout(false);
158 | this.PerformLayout();
159 |
160 | }
161 |
162 | #endregion
163 |
164 | private System.Windows.Forms.PictureBox pictureBox1;
165 | private System.Windows.Forms.Button btnClose;
166 | private System.Windows.Forms.Label lblAppName;
167 | private System.Windows.Forms.Label lblVersion;
168 | private System.Windows.Forms.Label lblThanks;
169 | private System.Windows.Forms.Label lblCopyright;
170 | private System.Windows.Forms.Label lblLine;
171 | private System.Windows.Forms.LinkLabel llEmail;
172 | private System.Windows.Forms.Label label1;
173 | }
174 | }
--------------------------------------------------------------------------------
/LifeIsStrangeSaveEditor.Win/AboutForm.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel;
4 | using System.Data;
5 | using System.Drawing;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Windows.Forms;
9 |
10 | namespace LifeIsStrangeSaveEditor.Win
11 | {
12 | public partial class AboutForm : Form
13 | {
14 | public AboutForm()
15 | {
16 | InitializeComponent();
17 |
18 | lblVersion.Text = string.Format("Version: {0}", AppHelper.GetApplicationVersionShortStr());
19 |
20 | var sb = new StringBuilder();
21 | sb.AppendLine("Thanks to:");
22 | sb.AppendLine(" • Eliot van Uytfanghe aka Eliot");
23 | sb.AppendLine(" • Wasteland Ghost aka wghost81");
24 | sb.AppendLine(" • Konstantin Nosov aka Gildor");
25 | sb.AppendLine(" • Kelnor277");
26 | sb.AppendLine(" • And my lovely girlfriend");
27 | sb.AppendLine();
28 | lblThanks.Text = sb.ToString();
29 | }
30 |
31 | private void llEmail_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
32 | {
33 | System.Diagnostics.Process.Start("mailto:smile.voronezh@gmail.com?subject=Life is Strange Save Editor");
34 | }
35 | }
36 | }
--------------------------------------------------------------------------------
/LifeIsStrangeSaveEditor.Win/AppHelper.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 |
3 | namespace LifeIsStrangeSaveEditor.Win
4 | {
5 | public static class AppHelper
6 | {
7 | public static string GetApplicationVersionShortStr()
8 | {
9 | return string.Format("{0}.{1}{2}",
10 | Assembly.GetExecutingAssembly().GetName().Version.Major,
11 | Assembly.GetExecutingAssembly().GetName().Version.Minor,
12 | Assembly.GetExecutingAssembly().GetName().Version.Build != 0
13 | ? "." + Assembly.GetExecutingAssembly().GetName().Version.Build
14 | : string.Empty);
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/LifeIsStrangeSaveEditor.Win/IEnumerableExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 |
4 | namespace LifeIsStrangeSaveEditor.Win
5 | {
6 | public static class IEnumerableExtensions
7 | {
8 | public static IEnumerable DropLast(this IEnumerable enumerable)
9 | {
10 | return enumerable.Reverse().Skip(1).Reverse();
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/LifeIsStrangeSaveEditor.Win/LifeIsStrangeSaveEditor.Win.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {8E39BDD6-980E-4FA9-964E-C18235829C34}
8 | WinExe
9 | Properties
10 | LifeIsStrangeSaveEditor.Win
11 | LifeIsStrangeSaveEditor.Win
12 | v4.0
13 | 512
14 |
15 |
16 | AnyCPU
17 | true
18 | full
19 | false
20 | bin\Debug\
21 | DEBUG;TRACE
22 | prompt
23 | 4
24 |
25 |
26 | AnyCPU
27 | pdbonly
28 | true
29 | bin\Release\
30 | TRACE
31 | prompt
32 | 4
33 |
34 |
35 | Resources\Butterfly.ico
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | Form
55 |
56 |
57 | AboutForm.cs
58 |
59 |
60 |
61 |
62 |
63 |
64 | Form
65 |
66 |
67 | PlayerProgressEditForm.cs
68 |
69 |
70 |
71 | Form
72 |
73 |
74 | MainForm.cs
75 |
76 |
77 |
78 |
79 |
80 |
81 | AboutForm.cs
82 |
83 |
84 | MainForm.cs
85 |
86 |
87 | MainForm.cs
88 |
89 |
90 | PlayerProgressEditForm.cs
91 |
92 |
93 | PublicResXFileCodeGenerator
94 | Resources.Designer.cs
95 | Designer
96 |
97 |
98 | True
99 | Resources.resx
100 | True
101 |
102 |
103 | SettingsSingleFileGenerator
104 | Settings.Designer.cs
105 |
106 |
107 | True
108 | Settings.settings
109 | True
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
126 |
--------------------------------------------------------------------------------
/LifeIsStrangeSaveEditor.Win/MainForm.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Globalization;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Windows.Forms;
7 |
8 | namespace LifeIsStrangeSaveEditor.Win
9 | {
10 | public partial class MainForm : Form
11 | {
12 | private string _saveFilePath;
13 | public string SaveFilePath
14 | {
15 | get { return _saveFilePath; }
16 | set
17 | {
18 | _saveFilePath = value;
19 | tbSaveFilePath.Text = _saveFilePath;
20 | }
21 | }
22 |
23 | public SaveFile SaveFile { get; set; }
24 |
25 | public MainForm()
26 | {
27 | InitializeComponent();
28 |
29 | this.Text = string.Format("Life is Strange Save Editor v{0}", AppHelper.GetApplicationVersionShortStr());
30 |
31 | tpReachedCheckpoints.Tag = tpReachedCheckpoints.Text;
32 | tpAlreadySeenCollectibles.Tag = tpAlreadySeenCollectibles.Text;
33 | tpAlreadySeenCharacters.Tag = tpAlreadySeenCharacters.Text;
34 | tpCheckpointLevelRecords.Tag = tpCheckpointLevelRecords.Text;
35 | tpAchievements.Tag = tpAchievements.Text;
36 | tpVisibleLevels.Tag = tpVisibleLevels.Text;
37 | tpLevelStartStates.Tag = tpLevelStartStates.Text;
38 | tpPlayerProgressActiveFacts.Tag = tpPlayerProgressActiveFacts.Text;
39 | tpPlayerProgressActiveTutorials.Tag = tpPlayerProgressActiveTutorials.Text;
40 | tpPlayerProgressFileSelects.Tag = tpPlayerProgressFileSelects.Text;
41 | tpPlayerProgressInventoryObjects.Tag = tpPlayerProgressInventoryObjects.Text;
42 | tpPlayerProgressCollectibleFoundFactNames.Tag = tpPlayerProgressCollectibleFoundFactNames.Text;
43 |
44 | var checkpoints = CheckpointsInfo.Checkpoints.Select(c => c.CheckPointID).Distinct().ToArray();
45 | cbCheckPointID.Items.AddRange(checkpoints);
46 |
47 | nudRequiredFreeSpace.Minimum = int.MinValue;
48 | nudRequiredFreeSpace.Maximum = int.MaxValue;
49 | nudTotalGameTimePauseExcluded.Maximum = decimal.MaxValue;
50 | nudNbInteractionDoneSum.Maximum = int.MaxValue;
51 | nudNbChoiceMadeSum.Maximum = int.MaxValue;
52 |
53 | nudEpisodePlayTimePauseExcluded0.Maximum = decimal.MaxValue;
54 | nudEpisodePlayTimePauseExcluded1.Maximum = decimal.MaxValue;
55 | nudEpisodePlayTimePauseExcluded2.Maximum = decimal.MaxValue;
56 | nudEpisodePlayTimePauseExcluded3.Maximum = decimal.MaxValue;
57 | nudEpisodePlayTimePauseExcluded4.Maximum = decimal.MaxValue;
58 |
59 | nudEpisodePlayTimePauseIncluded0.Maximum = decimal.MaxValue;
60 | nudEpisodePlayTimePauseIncluded1.Maximum = decimal.MaxValue;
61 | nudEpisodePlayTimePauseIncluded2.Maximum = decimal.MaxValue;
62 | nudEpisodePlayTimePauseIncluded3.Maximum = decimal.MaxValue;
63 | nudEpisodePlayTimePauseIncluded4.Maximum = decimal.MaxValue;
64 |
65 | nudBoundaryStats00Rewinds.Maximum = int.MaxValue;
66 | nudBoundaryStats01Rewinds.Maximum = int.MaxValue;
67 | nudBoundaryStats02Rewinds.Maximum = int.MaxValue;
68 | nudBoundaryStats03Rewinds.Maximum = int.MaxValue;
69 | nudBoundaryStats04Rewinds.Maximum = int.MaxValue;
70 |
71 | nudBoundaryStats10Rewinds.Maximum = int.MaxValue;
72 | nudBoundaryStats11Rewinds.Maximum = int.MaxValue;
73 | nudBoundaryStats12Rewinds.Maximum = int.MaxValue;
74 | nudBoundaryStats13Rewinds.Maximum = int.MaxValue;
75 | nudBoundaryStats14Rewinds.Maximum = int.MaxValue;
76 |
77 | nudBoundaryStats20Rewinds.Maximum = int.MaxValue;
78 | nudBoundaryStats21Rewinds.Maximum = int.MaxValue;
79 | nudBoundaryStats22Rewinds.Maximum = int.MaxValue;
80 | nudBoundaryStats23Rewinds.Maximum = int.MaxValue;
81 | nudBoundaryStats24Rewinds.Maximum = int.MaxValue;
82 |
83 | nudBoundaryStats30Rewinds.Maximum = int.MaxValue;
84 | nudBoundaryStats31Rewinds.Maximum = int.MaxValue;
85 | nudBoundaryStats32Rewinds.Maximum = int.MaxValue;
86 | nudBoundaryStats33Rewinds.Maximum = int.MaxValue;
87 | nudBoundaryStats34Rewinds.Maximum = int.MaxValue;
88 |
89 | nudBoundaryStats40Rewinds.Maximum = int.MaxValue;
90 | nudBoundaryStats41Rewinds.Maximum = int.MaxValue;
91 | nudBoundaryStats42Rewinds.Maximum = int.MaxValue;
92 | nudBoundaryStats43Rewinds.Maximum = int.MaxValue;
93 | nudBoundaryStats44Rewinds.Maximum = int.MaxValue;
94 |
95 | nudBoundaryStats00Hints.Maximum = int.MaxValue;
96 | nudBoundaryStats01Hints.Maximum = int.MaxValue;
97 | nudBoundaryStats02Hints.Maximum = int.MaxValue;
98 | nudBoundaryStats03Hints.Maximum = int.MaxValue;
99 | nudBoundaryStats04Hints.Maximum = int.MaxValue;
100 |
101 | nudBoundaryStats10Hints.Maximum = int.MaxValue;
102 | nudBoundaryStats11Hints.Maximum = int.MaxValue;
103 | nudBoundaryStats12Hints.Maximum = int.MaxValue;
104 | nudBoundaryStats13Hints.Maximum = int.MaxValue;
105 | nudBoundaryStats14Hints.Maximum = int.MaxValue;
106 |
107 | nudBoundaryStats20Hints.Maximum = int.MaxValue;
108 | nudBoundaryStats21Hints.Maximum = int.MaxValue;
109 | nudBoundaryStats22Hints.Maximum = int.MaxValue;
110 | nudBoundaryStats23Hints.Maximum = int.MaxValue;
111 | nudBoundaryStats24Hints.Maximum = int.MaxValue;
112 |
113 | nudBoundaryStats30Hints.Maximum = int.MaxValue;
114 | nudBoundaryStats31Hints.Maximum = int.MaxValue;
115 | nudBoundaryStats32Hints.Maximum = int.MaxValue;
116 | nudBoundaryStats33Hints.Maximum = int.MaxValue;
117 | nudBoundaryStats34Hints.Maximum = int.MaxValue;
118 |
119 | nudBoundaryStats40Hints.Maximum = int.MaxValue;
120 | nudBoundaryStats41Hints.Maximum = int.MaxValue;
121 | nudBoundaryStats42Hints.Maximum = int.MaxValue;
122 | nudBoundaryStats43Hints.Maximum = int.MaxValue;
123 | nudBoundaryStats44Hints.Maximum = int.MaxValue;
124 |
125 | nudLastDiaryPageSeen.Maximum = int.MaxValue;
126 | }
127 |
128 | private void SezializeSaveFile(Stream stream, SaveFile save)
129 | {
130 | var saveDataStream = new MemoryStream();
131 | var nameTableStream = new MemoryStream();
132 |
133 | var newSave = new SaveFile
134 | {
135 | Header =
136 | {
137 | UnkInt1 = save.Header.UnkInt1
138 | },
139 | SaveDataHeader =
140 | {
141 | HeaderSize = save.SaveDataHeader.HeaderSize,
142 | UnkInt2 = save.SaveDataHeader.UnkInt2
143 | }
144 | };
145 |
146 | newSave.SaveData = FillSaveData();
147 |
148 | saveDataStream.SerializeObject(newSave.SaveData, newSave.NameTable.Names);
149 |
150 | nameTableStream.WriteInt32(newSave.NameTable.Names.Count);
151 | for (var i = 0; i < newSave.NameTable.Names.Count; i++)
152 | {
153 | nameTableStream.WriteString(newSave.NameTable.Names[i]);
154 | }
155 |
156 | newSave.SaveDataHeader.SaveDataSize = (int) saveDataStream.Length + 12; // 12 = SaveDataHeader size
157 | newSave.Header.FileSize = (int) saveDataStream.Length + 12 + (int) nameTableStream.Length;
158 |
159 | stream.Seek(0, SeekOrigin.Begin);
160 |
161 | // File header
162 | stream.WriteInt32(newSave.Header.UnkInt1);
163 | stream.WriteInt32(newSave.Header.FileSize);
164 |
165 | // Save data header
166 | stream.WriteInt32(newSave.SaveDataHeader.HeaderSize);
167 | stream.WriteInt32(newSave.SaveDataHeader.SaveDataSize);
168 | stream.WriteInt32(newSave.SaveDataHeader.UnkInt2);
169 |
170 | // Save data
171 | saveDataStream.WriteTo(stream);
172 | //stream.WriteInt32(255);
173 |
174 | // Name table
175 | nameTableStream.WriteTo(stream);
176 | }
177 |
178 | private SaveFile DeserializeSaveFile(Stream stream)
179 | {
180 | var save = new SaveFile();
181 |
182 | // File header
183 | stream.Seek(0, SeekOrigin.Begin);
184 | save.Header = new FileHeader
185 | {
186 | UnkInt1 = stream.ReadInt32(),
187 | FileSize = stream.ReadInt32()
188 | };
189 |
190 | // Save data header
191 | save.SaveDataHeader = new SaveDataHeader
192 | {
193 | HeaderSize = stream.ReadInt32(),
194 | SaveDataSize = stream.ReadInt32(),
195 | UnkInt2 = stream.ReadInt32()
196 | };
197 |
198 | // Name table
199 | stream.Seek(save.SaveDataHeader.SaveDataSize + 8, SeekOrigin.Begin);
200 | var count = stream.ReadInt32();
201 | save.NameTable = new NameTable();
202 | for (var i = 0; i < count; i++)
203 | {
204 | save.NameTable.Names.Add(stream.ReadString());
205 | }
206 |
207 | // Save data
208 | stream.Seek(20, SeekOrigin.Begin);
209 | save.SaveData = new WhatIfSaveData();
210 | var saveDataStream = new byte[save.SaveDataHeader.SaveDataSize - 12];
211 | stream.Read(saveDataStream, 0, save.SaveDataHeader.SaveDataSize - 12);
212 | var ms = new MemoryStream(saveDataStream);
213 | ms.Seek(0, SeekOrigin.Begin);
214 | ms.DeserializeObject(save.SaveData, save.NameTable.Names);
215 |
216 | return save;
217 | }
218 |
219 | private WhatIfSaveData FillSaveData()
220 | {
221 | var now = DateTime.Now;
222 | var today = new TimeSpan(now.Hour, now.Minute, now.Second);
223 |
224 | var saveData = new WhatIfSaveData
225 | {
226 | SaveId = GetNullableFromTextBox(tbSaveId),
227 | SaveFileName = GetNullableFromTextBox(tbSaveFileName),
228 | SaveDetails = GetNullableFromTextBox(tbSaveDetails),
229 | FriendlyName = GetNullableFromTextBox(tbFriendlyName),
230 | SaveTime = new DNESaveTime
231 | {
232 | Year = now.Year,
233 | Month = now.Month,
234 | Day = now.Day,
235 | SecondsSinceMidnight = (int) today.TotalSeconds
236 | },
237 | Status = EOnlineSaveStatus.EOSS_Writing,
238 | bIsOwned = GetNullableFromCheckbox(cbIsOwned),
239 | SlotNumber = (int) nudSlotNumber.Value,
240 |
241 | CheckPointID = GetNullableFromComboBox(cbCheckPointID),
242 | CheckpointLocation = new Vector
243 | {
244 | X = float.Parse(tbCheckpointLocationX.Text, CultureInfo.InvariantCulture),
245 | Y = float.Parse(tbCheckpointLocationY.Text, CultureInfo.InvariantCulture),
246 | Z = float.Parse(tbCheckpointLocationZ.Text, CultureInfo.InvariantCulture)
247 | },
248 | CheckpointRotation = new Rotator
249 | {
250 | Pitch = int.Parse(tbCheckpointRotationPitch.Text),
251 | Yaw = int.Parse(tbCheckpointRotationYaw.Text),
252 | Roll = int.Parse(tbCheckpointRotationRoll.Text)
253 | },
254 | CurrentEpisode = (int) nudCurrentEpisode.Value,
255 | bStartNewGame = GetNullableFromCheckbox(cbStartNewGame),
256 | bGameStarted = GetNullableFromCheckbox(cbGameStarted),
257 | bNeedToSwitchToNextEpisode = GetNullableFromCheckbox(cbNeedToSwitchToNextEpisode),
258 | LastSubLevelPlayedIndex = (int) nudLastSubLevelPlayedIndex.Value,
259 | AlreadySeenLastDiaryPages = new[]
260 | {
261 | (int) nudAlreadySeenLastDiaryPages0.Value,
262 | (int) nudAlreadySeenLastDiaryPages1.Value,
263 | (int) nudAlreadySeenLastDiaryPages2.Value,
264 | (int) nudAlreadySeenLastDiaryPages3.Value,
265 | (int) nudAlreadySeenLastDiaryPages4.Value,
266 | (int) nudAlreadySeenLastDiaryPages5.Value,
267 | },
268 | fTotalGameTimePauseExcluded = (float) nudTotalGameTimePauseExcluded.Value,
269 | NbEpisodeCompletedSum = (int) nudNbEpisodeCompletedSum.Value,
270 | NbInteractionDoneSum = (int) nudNbInteractionDoneSum.Value,
271 | NbChoiceMadeSum = (int) nudNbChoiceMadeSum.Value,
272 | NbPlaythrough = new[]
273 | {
274 | cbNbPlaythrough0.Checked ? 1 : 0,
275 | cbNbPlaythrough1.Checked ? 1 : 0,
276 | cbNbPlaythrough2.Checked ? 1 : 0,
277 | cbNbPlaythrough3.Checked ? 1 : 0,
278 | cbNbPlaythrough4.Checked ? 1 : 0,
279 | },
280 | PercentageCompleted = new[]
281 | {
282 | (float) nudPercentageCompleted0.Value,
283 | (float) nudPercentageCompleted1.Value,
284 | (float) nudPercentageCompleted2.Value,
285 | (float) nudPercentageCompleted3.Value,
286 | (float) nudPercentageCompleted4.Value,
287 | },
288 |
289 | CheckpointReached = SaveFile.SaveData.CheckpointReached,
290 |
291 | AlreadySeenCollectibles = dgvAlreadySeenCollectibles.Rows.OfType()
292 | .DropLast()
293 | .Select(r => new NameProperty
294 | {
295 | Name = r.Cells[0].Value.ToString()
296 | }).ToList(),
297 |
298 | AlreadySeenCharacters = SaveFile.SaveData.AlreadySeenCharacters,
299 |
300 | CheckpointLevelRecords = dgvCheckpointLevelRecords.Rows.OfType()
301 | .DropLast()
302 | .Select(r => new LevelInfos
303 | {
304 | LevelName = new NameProperty {Name = r.Cells[0].Value.ToString()},
305 | bShouldBeLoaded = (bool) r.Cells[1].Value,
306 | bShouldBeVisible = (bool) r.Cells[2].Value,
307 | }).ToList(),
308 |
309 | Achievements = SaveFile.SaveData.Achievements,
310 | VisibleLevels = dgvVisibleLevels.Rows.OfType()
311 | .DropLast()
312 | .Select(r => new NameProperty
313 | {
314 | Name = r.Cells[0].Value.ToString()
315 | }).ToList(),
316 |
317 | PlayerProgress = new PlayerProgress
318 | {
319 | CurrentObjectiveID = string.IsNullOrEmpty(tbCurrentObjectiveID.Text)
320 | ? new NameProperty {Name = tbCurrentObjectiveID.Text}
321 | : null,
322 | CurrentDay = (int) nudCurrentDay.Value,
323 | DiaryAlreadySeen = GetNullableFromCheckbox(cbDiaryAlreadySeen),
324 | LastDiaryDaySeen = (int) nudLastDiaryDaySeen.Value,
325 | LastDiaryPageSeen = (int) nudLastDiaryPageSeen.Value,
326 | MaxReachedContextName = new[]
327 | {
328 | GetNullableFromTextBox(tbMaxReachedContextName0),
329 | GetNullableFromTextBox(tbMaxReachedContextName1),
330 | GetNullableFromTextBox(tbMaxReachedContextName2),
331 | GetNullableFromTextBox(tbMaxReachedContextName3),
332 | GetNullableFromTextBox(tbMaxReachedContextName4)
333 | },
334 | fEpisodePlayTimePauseExcluded = new[]
335 | {
336 | (float) nudEpisodePlayTimePauseExcluded0.Value,
337 | (float) nudEpisodePlayTimePauseExcluded1.Value,
338 | (float) nudEpisodePlayTimePauseExcluded2.Value,
339 | (float) nudEpisodePlayTimePauseExcluded3.Value,
340 | (float) nudEpisodePlayTimePauseExcluded4.Value,
341 | },
342 | fEpisodePlayTimePauseIncluded = new[]
343 | {
344 | (float) nudEpisodePlayTimePauseIncluded0.Value,
345 | (float) nudEpisodePlayTimePauseIncluded1.Value,
346 | (float) nudEpisodePlayTimePauseIncluded2.Value,
347 | (float) nudEpisodePlayTimePauseIncluded3.Value,
348 | (float) nudEpisodePlayTimePauseIncluded4.Value,
349 | },
350 |
351 | ActiveFacts = dgvActiveFacts.Rows.OfType()
352 | .DropLast()
353 | .Select(r => new ActiveFactLevel
354 | {
355 | FactName = new NameProperty {Name = r.Cells[2].Value.ToString()},
356 | FactLevel = int.Parse(r.Cells[3].Value.ToString())
357 | }).ToList(),
358 |
359 | ActiveTutorials = dgvActiveTutorials.Rows.OfType()
360 | .DropLast()
361 | .Select(r => int.Parse(r.Cells[0].Value.ToString())).ToList(),
362 |
363 | FileSelects = dgvFileSelects.Rows.OfType()
364 | .DropLast()
365 | .Select(r => new FileSelect
366 | {
367 | ObjectName = new NameProperty {Name = r.Cells[0].Value.ToString()},
368 | TextureKey = r.Cells[1].Value.ToString(),
369 | Pages = r.Cells[2].Value.ToString().Split(';').Select(int.Parse).ToList()
370 | }).ToList(),
371 |
372 | InventoryObjects = dgvInventoryObjects.Rows.OfType()
373 | .DropLast()
374 | .Select(r => new ObjectInInventory
375 | {
376 | ObjectName = new NameProperty {Name = r.Cells[0].Value.ToString()},
377 | ObjectInstanceCount = int.Parse(r.Cells[1].Value.ToString())
378 | }).ToList(),
379 |
380 | CollectibleFoundFactNames = dgvCollectibleFoundFactNames.Rows.OfType()
381 | .DropLast()
382 | .Select(r => new NameProperty
383 | {
384 | Name = r.Cells[0].Value.ToString()
385 | }).ToList(),
386 |
387 | EpisodeBoundaryStats = SaveFile.SaveData.PlayerProgress.EpisodeBoundaryStats
388 | }
389 | };
390 |
391 | saveData.SLevelStartStates = dgvSubLevelStartStates.Rows.OfType()
392 | .Where(r => !r.IsNewRow)
393 | .Select(r => new SubLevelStartState
394 | {
395 | SubLevelTitle = r.Cells["colSubLevelStartStatesSubLevelName"].Value.ToString(),
396 | PlayerProgress = (PlayerProgress) r.Tag
397 | }).ToList();
398 |
399 | return saveData;
400 | }
401 |
402 | private void LoadFile()
403 | {
404 | Cursor = Cursors.WaitCursor;
405 |
406 | try
407 | {
408 | using (var fs = File.OpenRead(SaveFilePath))
409 | {
410 | SaveFile = DeserializeSaveFile(fs);
411 | }
412 | }
413 | catch (Exception)
414 | {
415 | MessageBox.Show("Error while reading file", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
416 | Cursor = Cursors.Default;
417 | return;
418 | }
419 |
420 | tcSaveData.SuspendLayout();
421 |
422 | tpReachedCheckpoints.Text = GetCaptionWithCountForTab(tpReachedCheckpoints.Tag.ToString(), SaveFile.SaveData.CheckpointReached);
423 | tpAlreadySeenCollectibles.Text = GetCaptionWithCountForTab(tpAlreadySeenCollectibles.Tag.ToString(), SaveFile.SaveData.AlreadySeenCollectibles);
424 | tpAlreadySeenCharacters.Text = GetCaptionWithCountForTab(tpAlreadySeenCharacters.Tag.ToString(), SaveFile.SaveData.AlreadySeenCharacters);
425 | tpCheckpointLevelRecords.Text = GetCaptionWithCountForTab(tpCheckpointLevelRecords.Tag.ToString(), SaveFile.SaveData.CheckpointLevelRecords);
426 | tpAchievements.Text = GetCaptionWithCountForTab(tpAchievements.Tag.ToString(), SaveFile.SaveData.Achievements);
427 | tpVisibleLevels.Text = GetCaptionWithCountForTab(tpVisibleLevels.Tag.ToString(), SaveFile.SaveData.VisibleLevels);
428 | tpLevelStartStates.Text = GetCaptionWithCountForTab(tpLevelStartStates.Tag.ToString(), SaveFile.SaveData.SLevelStartStates);
429 | if (SaveFile.SaveData.PlayerProgress != null)
430 | {
431 | tpPlayerProgressActiveFacts.Text =
432 | GetCaptionWithCountForTab(tpPlayerProgressActiveFacts.Tag.ToString(), SaveFile.SaveData.PlayerProgress.ActiveFacts);
433 | tpPlayerProgressActiveTutorials.Text =
434 | GetCaptionWithCountForTab(tpPlayerProgressActiveTutorials.Tag.ToString(), SaveFile.SaveData.PlayerProgress.ActiveTutorials);
435 | tpPlayerProgressFileSelects.Text =
436 | GetCaptionWithCountForTab(tpPlayerProgressFileSelects.Tag.ToString(), SaveFile.SaveData.PlayerProgress.FileSelects);
437 | tpPlayerProgressInventoryObjects.Text =
438 | GetCaptionWithCountForTab(tpPlayerProgressInventoryObjects.Tag.ToString(), SaveFile.SaveData.PlayerProgress.InventoryObjects);
439 | tpPlayerProgressCollectibleFoundFactNames.Text =
440 | GetCaptionWithCountForTab(tpPlayerProgressCollectibleFoundFactNames.Tag.ToString(), SaveFile.SaveData.PlayerProgress.CollectibleFoundFactNames);
441 | }
442 | else
443 | {
444 | tpPlayerProgressActiveFacts.Text =
445 | GetCaptionWithCountForTab(tpPlayerProgressActiveFacts.Tag.ToString(), null);
446 | tpPlayerProgressActiveTutorials.Text =
447 | GetCaptionWithCountForTab(tpPlayerProgressActiveTutorials.Tag.ToString(), null);
448 | tpPlayerProgressFileSelects.Text =
449 | GetCaptionWithCountForTab(tpPlayerProgressFileSelects.Tag.ToString(), null);
450 | tpPlayerProgressInventoryObjects.Text =
451 | GetCaptionWithCountForTab(tpPlayerProgressInventoryObjects.Tag.ToString(), null);
452 | tpPlayerProgressCollectibleFoundFactNames.Text =
453 | GetCaptionWithCountForTab(tpPlayerProgressCollectibleFoundFactNames.Tag.ToString(), null);
454 | }
455 |
456 | tbSaveId.Text = SaveFile.SaveData.SaveId;
457 | tbSaveFileName.Text = SaveFile.SaveData.SaveFileName;
458 | tbSaveDetails.Text = SaveFile.SaveData.SaveDetails;
459 | tbFriendlyName.Text = SaveFile.SaveData.FriendlyName;
460 | if (SaveFile.SaveData.SaveTime != null)
461 | {
462 | var date = new DateTime(
463 | SaveFile.SaveData.SaveTime.Year ?? 1,
464 | SaveFile.SaveData.SaveTime.Month ?? 1,
465 | SaveFile.SaveData.SaveTime.Day ?? 1);
466 | var dateTime = date.AddSeconds(SaveFile.SaveData.SaveTime.SecondsSinceMidnight ?? 0);
467 | lblSaveTimeValue.Text = dateTime.ToString("G");
468 | }
469 | else
470 | {
471 | lblSaveTimeValue.Text = "NOT SET";
472 | }
473 |
474 | lblStatusValue.Text = SaveFile.SaveData.Status.ToString();
475 |
476 | if (SaveFile.SaveData.bIsOwned != null)
477 | cbIsOwned.CheckState = SaveFile.SaveData.bIsOwned.Value ? CheckState.Checked : CheckState.Unchecked;
478 | else
479 | cbIsOwned.CheckState = CheckState.Indeterminate;
480 |
481 | nudSlotNumber.Value = SaveFile.SaveData.SlotNumber ?? 0;
482 |
483 | nudRequiredFreeSpace.Value = SaveFile.SaveData.RequiredFreeSpace ?? 0;
484 |
485 | if (SaveFile.SaveData.CheckpointLocation != null)
486 | {
487 | tbCheckpointLocationX.Text = SaveFile.SaveData.CheckpointLocation.X.ToString(CultureInfo.InvariantCulture);
488 | tbCheckpointLocationY.Text = SaveFile.SaveData.CheckpointLocation.Y.ToString(CultureInfo.InvariantCulture);
489 | tbCheckpointLocationZ.Text = SaveFile.SaveData.CheckpointLocation.Z.ToString(CultureInfo.InvariantCulture);
490 | }
491 | else
492 | {
493 | tbCheckpointLocationX.Text = string.Empty;
494 | tbCheckpointLocationY.Text = string.Empty;
495 | tbCheckpointLocationZ.Text = string.Empty;
496 | }
497 |
498 | if (SaveFile.SaveData.CheckpointRotation != null)
499 | {
500 | tbCheckpointRotationPitch.Text = SaveFile.SaveData.CheckpointRotation.Pitch.ToString();
501 | tbCheckpointRotationYaw.Text = SaveFile.SaveData.CheckpointRotation.Yaw.ToString();
502 | tbCheckpointRotationRoll.Text = SaveFile.SaveData.CheckpointRotation.Roll.ToString();
503 | }
504 | else
505 | {
506 | tbCheckpointRotationPitch.Text = string.Empty;
507 | tbCheckpointRotationYaw.Text = string.Empty;
508 | tbCheckpointRotationRoll.Text = string.Empty;
509 | }
510 |
511 | cbCheckPointID.Text = SaveFile.SaveData.CheckPointID;
512 |
513 | nudCurrentEpisode.Value = SaveFile.SaveData.CurrentEpisode ?? 0;
514 |
515 | if (SaveFile.SaveData.bStartNewGame != null)
516 | cbStartNewGame.CheckState = SaveFile.SaveData.bStartNewGame.Value ? CheckState.Checked : CheckState.Unchecked;
517 | else
518 | cbStartNewGame.CheckState = CheckState.Indeterminate;
519 |
520 | if (SaveFile.SaveData.bGameStarted != null)
521 | cbGameStarted.CheckState = SaveFile.SaveData.bGameStarted.Value ? CheckState.Checked : CheckState.Unchecked;
522 | else
523 | cbGameStarted.CheckState = CheckState.Indeterminate;
524 |
525 | if (SaveFile.SaveData.bNeedToSwitchToNextEpisode != null)
526 | cbNeedToSwitchToNextEpisode.CheckState = SaveFile.SaveData.bNeedToSwitchToNextEpisode.Value ? CheckState.Checked : CheckState.Unchecked;
527 | else
528 | cbNeedToSwitchToNextEpisode.CheckState = CheckState.Indeterminate;
529 |
530 | nudLastSubLevelPlayedIndex.Value = SaveFile.SaveData.LastSubLevelPlayedIndex ?? 0;
531 |
532 | nudAlreadySeenLastDiaryPages0.Value = SaveFile.SaveData.AlreadySeenLastDiaryPages[0];
533 | nudAlreadySeenLastDiaryPages1.Value = SaveFile.SaveData.AlreadySeenLastDiaryPages[1];
534 | nudAlreadySeenLastDiaryPages2.Value = SaveFile.SaveData.AlreadySeenLastDiaryPages[2];
535 | nudAlreadySeenLastDiaryPages3.Value = SaveFile.SaveData.AlreadySeenLastDiaryPages[3];
536 | nudAlreadySeenLastDiaryPages4.Value = SaveFile.SaveData.AlreadySeenLastDiaryPages[4];
537 | nudAlreadySeenLastDiaryPages5.Value = SaveFile.SaveData.AlreadySeenLastDiaryPages[5];
538 |
539 | nudTotalGameTimePauseExcluded.Value = (decimal?) SaveFile.SaveData.fTotalGameTimePauseExcluded ?? 0;
540 |
541 | cbNbPlaythrough0.Checked = SaveFile.SaveData.NbPlaythrough[0] > 0;
542 | cbNbPlaythrough1.Checked = SaveFile.SaveData.NbPlaythrough[1] > 0;
543 | cbNbPlaythrough2.Checked = SaveFile.SaveData.NbPlaythrough[2] > 0;
544 | cbNbPlaythrough3.Checked = SaveFile.SaveData.NbPlaythrough[3] > 0;
545 | cbNbPlaythrough4.Checked = SaveFile.SaveData.NbPlaythrough[4] > 0;
546 |
547 | nudNbEpisodeCompletedSum.Value = SaveFile.SaveData.NbEpisodeCompletedSum ?? 0;
548 | nudNbInteractionDoneSum.Value = SaveFile.SaveData.NbInteractionDoneSum ?? 0;
549 | nudNbChoiceMadeSum.Value = SaveFile.SaveData.NbChoiceMadeSum ?? 0;
550 |
551 | nudPercentageCompleted0.Value = (decimal) SaveFile.SaveData.PercentageCompleted[0];
552 | nudPercentageCompleted1.Value = (decimal) SaveFile.SaveData.PercentageCompleted[1];
553 | nudPercentageCompleted2.Value = (decimal) SaveFile.SaveData.PercentageCompleted[2];
554 | nudPercentageCompleted3.Value = (decimal) SaveFile.SaveData.PercentageCompleted[3];
555 | nudPercentageCompleted4.Value = (decimal) SaveFile.SaveData.PercentageCompleted[4];
556 |
557 | dgvAlreadySeenCollectibles.Rows.Clear();
558 | if (SaveFile.SaveData.AlreadySeenCollectibles != null)
559 | {
560 | foreach (var collectible in SaveFile.SaveData.AlreadySeenCollectibles.Select(c => c.Name).ToList())
561 | {
562 | dgvAlreadySeenCollectibles.Rows.Add(collectible);
563 | }
564 | }
565 |
566 | dgvAlreadySeenCharacters.Rows.Clear();
567 | if (SaveFile.SaveData.AlreadySeenCharacters != null)
568 | {
569 | foreach (var character in SaveFile.SaveData.AlreadySeenCharacters.Select(c => c.Name).ToList())
570 | {
571 | dgvAlreadySeenCharacters.Rows.Add(character);
572 | }
573 | }
574 |
575 | dgvCheckpointReached.Rows.Clear();
576 | if (SaveFile.SaveData.CheckpointReached != null)
577 | {
578 | foreach (var checkpoint in SaveFile.SaveData.CheckpointReached)
579 | {
580 | dgvCheckpointReached.Rows.Add(checkpoint);
581 | }
582 | }
583 |
584 | dgvCheckpointLevelRecords.Rows.Clear();
585 | if (SaveFile.SaveData.CheckpointLevelRecords != null)
586 | {
587 | foreach (var record in SaveFile.SaveData.CheckpointLevelRecords)
588 | {
589 | dgvCheckpointLevelRecords.Rows.Add(record.LevelName.Name, record.bShouldBeLoaded, record.bShouldBeVisible);
590 | }
591 | }
592 |
593 | dgvAchievements.Rows.Clear();
594 | if (SaveFile.SaveData.Achievements != null)
595 | {
596 | foreach (var achievement in SaveFile.SaveData.Achievements)
597 | {
598 | dgvAchievements.Rows.Add(
599 | achievement.Id,
600 | achievement.Type,
601 | achievement.bHidden,
602 | achievement.PlaystationGrade,
603 | achievement.XboxGamerscore,
604 | achievement.Title,
605 | achievement.Description,
606 | achievement.HowTo,
607 | achievement.CountersLimit,
608 | achievement.bResetCountersWhenPlayerDie,
609 | achievement.CurrentCountersValue);
610 | }
611 | }
612 |
613 | dgvVisibleLevels.Rows.Clear();
614 | if (SaveFile.SaveData.VisibleLevels != null)
615 | {
616 | foreach (var level in SaveFile.SaveData.VisibleLevels.Select(c => c.Name).ToList())
617 | {
618 | dgvVisibleLevels.Rows.Add(level);
619 | }
620 | }
621 |
622 | if (SaveFile.SaveData.PlayerProgress == null)
623 | {
624 | tcPlayerProgress.Visible = false;
625 | }
626 | else
627 | {
628 | tcPlayerProgress.Visible = true;
629 |
630 | tbCurrentObjectiveID.Text = SaveFile.SaveData.PlayerProgress.CurrentObjectiveID != null
631 | ? SaveFile.SaveData.PlayerProgress.CurrentObjectiveID.Name
632 | : string.Empty;
633 |
634 | nudCurrentDay.Value = SaveFile.SaveData.PlayerProgress.CurrentDay ?? 0;
635 |
636 | if (SaveFile.SaveData.PlayerProgress.DiaryAlreadySeen != null)
637 | cbDiaryAlreadySeen.CheckState = SaveFile.SaveData.PlayerProgress.DiaryAlreadySeen.Value ? CheckState.Checked : CheckState.Unchecked;
638 | else
639 | cbDiaryAlreadySeen.CheckState = CheckState.Indeterminate;
640 |
641 | nudLastDiaryDaySeen.Value = SaveFile.SaveData.PlayerProgress.LastDiaryDaySeen ?? 0;
642 |
643 | nudLastDiaryPageSeen.Value = SaveFile.SaveData.PlayerProgress.LastDiaryPageSeen ?? 0;
644 |
645 | tbMaxReachedContextName0.Text = SaveFile.SaveData.PlayerProgress.MaxReachedContextName[0];
646 | tbMaxReachedContextName1.Text = SaveFile.SaveData.PlayerProgress.MaxReachedContextName[1];
647 | tbMaxReachedContextName2.Text = SaveFile.SaveData.PlayerProgress.MaxReachedContextName[2];
648 | tbMaxReachedContextName3.Text = SaveFile.SaveData.PlayerProgress.MaxReachedContextName[3];
649 | tbMaxReachedContextName4.Text = SaveFile.SaveData.PlayerProgress.MaxReachedContextName[4];
650 |
651 | nudEpisodePlayTimePauseExcluded0.Value = (decimal) SaveFile.SaveData.PlayerProgress.fEpisodePlayTimePauseExcluded[0];
652 | nudEpisodePlayTimePauseExcluded1.Value = (decimal) SaveFile.SaveData.PlayerProgress.fEpisodePlayTimePauseExcluded[1];
653 | nudEpisodePlayTimePauseExcluded2.Value = (decimal) SaveFile.SaveData.PlayerProgress.fEpisodePlayTimePauseExcluded[2];
654 | nudEpisodePlayTimePauseExcluded3.Value = (decimal) SaveFile.SaveData.PlayerProgress.fEpisodePlayTimePauseExcluded[3];
655 | nudEpisodePlayTimePauseExcluded4.Value = (decimal) SaveFile.SaveData.PlayerProgress.fEpisodePlayTimePauseExcluded[4];
656 |
657 | nudEpisodePlayTimePauseIncluded0.Value = (decimal) SaveFile.SaveData.PlayerProgress.fEpisodePlayTimePauseIncluded[0];
658 | nudEpisodePlayTimePauseIncluded1.Value = (decimal) SaveFile.SaveData.PlayerProgress.fEpisodePlayTimePauseIncluded[1];
659 | nudEpisodePlayTimePauseIncluded2.Value = (decimal) SaveFile.SaveData.PlayerProgress.fEpisodePlayTimePauseIncluded[2];
660 | nudEpisodePlayTimePauseIncluded3.Value = (decimal) SaveFile.SaveData.PlayerProgress.fEpisodePlayTimePauseIncluded[3];
661 | nudEpisodePlayTimePauseIncluded4.Value = (decimal) SaveFile.SaveData.PlayerProgress.fEpisodePlayTimePauseIncluded[4];
662 |
663 | dgvActiveTutorials.Rows.Clear();
664 | if (SaveFile.SaveData.PlayerProgress.ActiveTutorials != null)
665 | {
666 | foreach (var tutorial in SaveFile.SaveData.PlayerProgress.ActiveTutorials)
667 | {
668 | dgvActiveTutorials.Rows.Add(tutorial.ToString());
669 | }
670 | }
671 |
672 | cbActiveFactsFilterInteractions.Checked = true;
673 | cbActiveFactsFilterInternal.Checked = true;
674 | cbActiveFactsFilterTutorials.Checked = true;
675 | cbActiveFactsFilterDiary.Checked = true;
676 | cbActiveFactsFilterCollectibles.Checked = true;
677 | dgvActiveFacts.Rows.Clear();
678 | if (SaveFile.SaveData.PlayerProgress.ActiveFacts != null)
679 | {
680 | foreach (var fact in SaveFile.SaveData.PlayerProgress.ActiveFacts)
681 | {
682 | dgvActiveFacts.Rows.Add(fact.FactLevel > 0, (EWhatIfFactLevel) Math.Abs(fact.FactLevel.Value), fact.FactName.Name, fact.FactLevel);
683 | }
684 | }
685 |
686 | dgvFileSelects.Rows.Clear();
687 | if (SaveFile.SaveData.PlayerProgress.FileSelects != null)
688 | {
689 | foreach (var file in SaveFile.SaveData.PlayerProgress.FileSelects)
690 | {
691 | dgvFileSelects.Rows.Add(file.ObjectName.Name, file.TextureKey, string.Join(";", file.Pages));
692 | }
693 | }
694 |
695 | dgvInventoryObjects.Rows.Clear();
696 | if (SaveFile.SaveData.PlayerProgress.InventoryObjects != null)
697 | {
698 | foreach (var obj in SaveFile.SaveData.PlayerProgress.InventoryObjects)
699 | {
700 | dgvInventoryObjects.Rows.Add(obj.ObjectName.Name, obj.ObjectInstanceCount);
701 | }
702 | }
703 |
704 | dgvCollectibleFoundFactNames.Rows.Clear();
705 | if (SaveFile.SaveData.PlayerProgress.CollectibleFoundFactNames != null)
706 | {
707 | foreach (var name in SaveFile.SaveData.PlayerProgress.CollectibleFoundFactNames)
708 | {
709 | dgvCollectibleFoundFactNames.Rows.Add(name.Name);
710 | }
711 | }
712 |
713 | var playerProgress = SaveFile.SaveData.PlayerProgress;
714 | nudBoundaryStats00Rewinds.Value = GetBoundaryStat(playerProgress, 0, 0, BoundaryStatType.Rewinds) ?? 0;
715 | nudBoundaryStats01Rewinds.Value = GetBoundaryStat(playerProgress, 0, 1, BoundaryStatType.Rewinds) ?? 0;
716 | nudBoundaryStats02Rewinds.Value = GetBoundaryStat(playerProgress, 0, 2, BoundaryStatType.Rewinds) ?? 0;
717 | nudBoundaryStats03Rewinds.Value = GetBoundaryStat(playerProgress, 0, 3, BoundaryStatType.Rewinds) ?? 0;
718 | nudBoundaryStats04Rewinds.Value = GetBoundaryStat(playerProgress, 0, 4, BoundaryStatType.Rewinds) ?? 0;
719 |
720 | nudBoundaryStats10Rewinds.Value = GetBoundaryStat(playerProgress, 1, 0, BoundaryStatType.Rewinds) ?? 0;
721 | nudBoundaryStats11Rewinds.Value = GetBoundaryStat(playerProgress, 1, 1, BoundaryStatType.Rewinds) ?? 0;
722 | nudBoundaryStats12Rewinds.Value = GetBoundaryStat(playerProgress, 1, 2, BoundaryStatType.Rewinds) ?? 0;
723 | nudBoundaryStats13Rewinds.Value = GetBoundaryStat(playerProgress, 1, 3, BoundaryStatType.Rewinds) ?? 0;
724 | nudBoundaryStats14Rewinds.Value = GetBoundaryStat(playerProgress, 1, 4, BoundaryStatType.Rewinds) ?? 0;
725 |
726 | nudBoundaryStats20Rewinds.Value = GetBoundaryStat(playerProgress, 2, 0, BoundaryStatType.Rewinds) ?? 0;
727 | nudBoundaryStats21Rewinds.Value = GetBoundaryStat(playerProgress, 2, 1, BoundaryStatType.Rewinds) ?? 0;
728 | nudBoundaryStats22Rewinds.Value = GetBoundaryStat(playerProgress, 2, 2, BoundaryStatType.Rewinds) ?? 0;
729 | nudBoundaryStats23Rewinds.Value = GetBoundaryStat(playerProgress, 2, 3, BoundaryStatType.Rewinds) ?? 0;
730 | nudBoundaryStats24Rewinds.Value = GetBoundaryStat(playerProgress, 2, 4, BoundaryStatType.Rewinds) ?? 0;
731 |
732 | nudBoundaryStats30Rewinds.Value = GetBoundaryStat(playerProgress, 3, 0, BoundaryStatType.Rewinds) ?? 0;
733 | nudBoundaryStats31Rewinds.Value = GetBoundaryStat(playerProgress, 3, 1, BoundaryStatType.Rewinds) ?? 0;
734 | nudBoundaryStats32Rewinds.Value = GetBoundaryStat(playerProgress, 3, 2, BoundaryStatType.Rewinds) ?? 0;
735 | nudBoundaryStats33Rewinds.Value = GetBoundaryStat(playerProgress, 3, 3, BoundaryStatType.Rewinds) ?? 0;
736 | nudBoundaryStats34Rewinds.Value = GetBoundaryStat(playerProgress, 3, 4, BoundaryStatType.Rewinds) ?? 0;
737 |
738 | nudBoundaryStats40Rewinds.Value = GetBoundaryStat(playerProgress, 4, 0, BoundaryStatType.Rewinds) ?? 0;
739 | nudBoundaryStats41Rewinds.Value = GetBoundaryStat(playerProgress, 4, 1, BoundaryStatType.Rewinds) ?? 0;
740 | nudBoundaryStats42Rewinds.Value = GetBoundaryStat(playerProgress, 4, 2, BoundaryStatType.Rewinds) ?? 0;
741 | nudBoundaryStats43Rewinds.Value = GetBoundaryStat(playerProgress, 4, 3, BoundaryStatType.Rewinds) ?? 0;
742 | nudBoundaryStats44Rewinds.Value = GetBoundaryStat(playerProgress, 4, 4, BoundaryStatType.Rewinds) ?? 0;
743 |
744 | nudBoundaryStats00Hints.Value = GetBoundaryStat(playerProgress, 0, 0, BoundaryStatType.Hints) ?? 0;
745 | nudBoundaryStats01Hints.Value = GetBoundaryStat(playerProgress, 0, 1, BoundaryStatType.Hints) ?? 0;
746 | nudBoundaryStats02Hints.Value = GetBoundaryStat(playerProgress, 0, 2, BoundaryStatType.Hints) ?? 0;
747 | nudBoundaryStats03Hints.Value = GetBoundaryStat(playerProgress, 0, 3, BoundaryStatType.Hints) ?? 0;
748 | nudBoundaryStats04Hints.Value = GetBoundaryStat(playerProgress, 0, 4, BoundaryStatType.Hints) ?? 0;
749 |
750 | nudBoundaryStats10Hints.Value = GetBoundaryStat(playerProgress, 1, 0, BoundaryStatType.Hints) ?? 0;
751 | nudBoundaryStats11Hints.Value = GetBoundaryStat(playerProgress, 1, 1, BoundaryStatType.Hints) ?? 0;
752 | nudBoundaryStats12Hints.Value = GetBoundaryStat(playerProgress, 1, 2, BoundaryStatType.Hints) ?? 0;
753 | nudBoundaryStats13Hints.Value = GetBoundaryStat(playerProgress, 1, 3, BoundaryStatType.Hints) ?? 0;
754 | nudBoundaryStats14Hints.Value = GetBoundaryStat(playerProgress, 1, 4, BoundaryStatType.Hints) ?? 0;
755 |
756 | nudBoundaryStats20Hints.Value = GetBoundaryStat(playerProgress, 2, 0, BoundaryStatType.Hints) ?? 0;
757 | nudBoundaryStats21Hints.Value = GetBoundaryStat(playerProgress, 2, 1, BoundaryStatType.Hints) ?? 0;
758 | nudBoundaryStats22Hints.Value = GetBoundaryStat(playerProgress, 2, 2, BoundaryStatType.Hints) ?? 0;
759 | nudBoundaryStats23Hints.Value = GetBoundaryStat(playerProgress, 2, 3, BoundaryStatType.Hints) ?? 0;
760 | nudBoundaryStats24Hints.Value = GetBoundaryStat(playerProgress, 2, 4, BoundaryStatType.Hints) ?? 0;
761 |
762 | nudBoundaryStats30Hints.Value = GetBoundaryStat(playerProgress, 3, 0, BoundaryStatType.Hints) ?? 0;
763 | nudBoundaryStats31Hints.Value = GetBoundaryStat(playerProgress, 3, 1, BoundaryStatType.Hints) ?? 0;
764 | nudBoundaryStats32Hints.Value = GetBoundaryStat(playerProgress, 3, 2, BoundaryStatType.Hints) ?? 0;
765 | nudBoundaryStats33Hints.Value = GetBoundaryStat(playerProgress, 3, 3, BoundaryStatType.Hints) ?? 0;
766 | nudBoundaryStats34Hints.Value = GetBoundaryStat(playerProgress, 3, 4, BoundaryStatType.Hints) ?? 0;
767 |
768 | nudBoundaryStats40Hints.Value = GetBoundaryStat(playerProgress, 4, 0, BoundaryStatType.Hints) ?? 0;
769 | nudBoundaryStats41Hints.Value = GetBoundaryStat(playerProgress, 4, 1, BoundaryStatType.Hints) ?? 0;
770 | nudBoundaryStats42Hints.Value = GetBoundaryStat(playerProgress, 4, 2, BoundaryStatType.Hints) ?? 0;
771 | nudBoundaryStats43Hints.Value = GetBoundaryStat(playerProgress, 4, 3, BoundaryStatType.Hints) ?? 0;
772 | nudBoundaryStats44Hints.Value = GetBoundaryStat(playerProgress, 4, 4, BoundaryStatType.Hints) ?? 0;
773 |
774 | Cursor = Cursors.Default;
775 | }
776 |
777 | dgvSubLevelStartStates.Rows.Clear();
778 | if (SaveFile.SaveData.SLevelStartStates != null)
779 | {
780 | foreach (var state in SaveFile.SaveData.SLevelStartStates)
781 | {
782 | var sli = SubLevelsInfo.SubLevels.FirstOrDefault(l => l.Name == state.SubLevelTitle);
783 | var index = dgvSubLevelStartStates.Rows.Add(state.SubLevelTitle, sli == null ? string.Empty : sli.Description);
784 | dgvSubLevelStartStates.Rows[index].Tag = state.PlayerProgress;
785 | }
786 | }
787 |
788 | tcSaveData.ResumeLayout(true);
789 | }
790 |
791 | private void btnOpen_Click(object sender, EventArgs e)
792 | {
793 | if (ofdSaveFile.ShowDialog() == DialogResult.OK)
794 | {
795 | SaveFilePath = ofdSaveFile.FileName;
796 |
797 | LoadFile();
798 | }
799 | }
800 |
801 | private void btnSave_Click(object sender, EventArgs e)
802 | {
803 | if (string.IsNullOrEmpty(SaveFilePath)) return;
804 |
805 | if (MessageBox.Show(string.Format("Overwrite file?{0}{1}", Environment.NewLine, SaveFilePath), "",
806 | MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.No)
807 | return;
808 |
809 | Cursor = Cursors.WaitCursor;
810 |
811 | var ms = new MemoryStream();
812 | SezializeSaveFile(ms, SaveFile);
813 |
814 | using (var fs = File.Create(SaveFilePath))
815 | {
816 | ms.WriteTo(fs);
817 | }
818 |
819 | Cursor = Cursors.Default;
820 | }
821 |
822 | private void btnReload_Click(object sender, EventArgs e)
823 | {
824 | if (!string.IsNullOrEmpty(SaveFilePath))
825 | LoadFile();
826 | }
827 |
828 | private void TimeNUDValueChanged(object sender, EventArgs e)
829 | {
830 | var lblName = ((sender as Control).Name).Substring(3);
831 | (sender as Control).Parent.Controls[string.Format("lbl{0}Formatted", lblName)].Text =
832 | string.Format("= {0}", FloatToTimeSpan((float) ((NumericUpDown) sender).Value).ToString("g"));
833 | }
834 |
835 | private static TimeSpan FloatToTimeSpan(float value)
836 | {
837 | return TimeSpan.FromMilliseconds(value * 1000);
838 | }
839 |
840 | private string GetCaptionWithCountForTab(string caption, IList list)
841 | {
842 | return string.Format("{0} [{1}]", caption, list == null ? "-" : list.Count.ToString());
843 | }
844 |
845 | private string GetNullableFromTextBox(TextBox textBox)
846 | {
847 | return string.IsNullOrEmpty(textBox.Text) ? null : textBox.Text;
848 | }
849 |
850 | private string GetNullableFromComboBox(ComboBox comboBox)
851 | {
852 | return string.IsNullOrEmpty(comboBox.Text) ? null : comboBox.Text;
853 | }
854 |
855 | private bool? GetNullableFromCheckbox(CheckBox checkBox)
856 | {
857 | return checkBox.CheckState != CheckState.Indeterminate
858 | ? checkBox.CheckState == CheckState.Checked
859 | : (bool?) null;
860 | }
861 |
862 | private int? GetBoundaryStat(PlayerProgress playerProgress, int epNum, int boundaryType, BoundaryStatType statType)
863 | {
864 | if (playerProgress.EpisodeBoundaryStats[epNum] == null)
865 | return null;
866 | if (playerProgress.EpisodeBoundaryStats[epNum].BoundaryStats[boundaryType] == null)
867 | return null;
868 | return statType == BoundaryStatType.Rewinds
869 | ? playerProgress.EpisodeBoundaryStats[epNum].BoundaryStats[boundaryType].NbRewinds
870 | : playerProgress.EpisodeBoundaryStats[epNum].BoundaryStats[boundaryType].NbHints;
871 | }
872 |
873 | private void FilterActiveFacts(ActiveFactsFilter filter, bool show)
874 | {
875 | var rows = dgvActiveFacts.Rows.OfType().DropLast().ToList();
876 | switch (filter)
877 | {
878 | case ActiveFactsFilter.Interactions:
879 | rows = rows.Where(r => r.Cells["colActiveFactsName"].Value.ToString().EndsWith("_Triggered")).ToList();
880 | break;
881 | case ActiveFactsFilter.Internal:
882 | rows = rows.Where(r => r.Cells["colActiveFactsName"].Value.ToString().StartsWith("Internal_")).ToList();
883 | break;
884 | case ActiveFactsFilter.Tutorials:
885 | rows = rows.Where(r => r.Cells["colActiveFactsName"].Value.ToString().StartsWith("NOTIFY_Tuto")).ToList();
886 | break;
887 | case ActiveFactsFilter.Diary:
888 | rows = rows.Where(r => r.Cells["colActiveFactsName"].Value.ToString().StartsWith("Diary_")).ToList();
889 | break;
890 | case ActiveFactsFilter.Collectibles:
891 | rows = rows.Where(r => r.Cells["colActiveFactsName"].Value.ToString().EndsWith("_Collectible")).ToList();
892 | break;
893 | case ActiveFactsFilter.Sms:
894 | rows = rows.Where(r => r.Cells["colActiveFactsName"].Value.ToString().Contains("_SMS_")).ToList();
895 | break;
896 | case ActiveFactsFilter.Choices:
897 | /*var choices = EpisodesInfo.Episodes
898 | .SelectMany(e => e.MajorChoices
899 | .SelectMany(c => c.ChoiceResults
900 | .Select(r => new
901 | {
902 | name = r.Id.Name,
903 | fact = r.Fact.Name
904 | })))
905 | .ToList();*/
906 | var choices = EpisodesInfo.Episodes
907 | .SelectMany(e => e.MajorChoices
908 | .SelectMany(c => c.ChoiceResults
909 | .Select(r => r.Fact.Name))).ToList();
910 | rows = rows.Where(r => choices.Contains(r.Cells["colActiveFactsName"].Value.ToString())).ToList();
911 | break;
912 | }
913 | Cursor = Cursors.WaitCursor;
914 | dgvActiveFacts.SuspendLayout();
915 | dgvActiveFacts.Columns.OfType().ToList().ForEach(c => c.AutoSizeMode = DataGridViewAutoSizeColumnMode.None);
916 | rows.ForEach(r => r.Visible = show);
917 | dgvActiveFacts.Columns.OfType().ToList().ForEach(c => c.AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells);
918 | lblActiveFactsShownCount.Text = string.Format("Shown: {0}", dgvActiveFacts.Rows.OfType().Count(r => r.Visible) - 1);
919 | dgvActiveFacts.ResumeLayout(true);
920 | Cursor = Cursors.Default;
921 | }
922 |
923 | private void cbActiveFactsFilterInteractions_CheckedChanged(object sender, EventArgs e)
924 | {
925 | FilterActiveFacts(ActiveFactsFilter.Interactions, cbActiveFactsFilterInteractions.Checked);
926 | }
927 |
928 | private void cbActiveFactsFilterInternal_CheckedChanged(object sender, EventArgs e)
929 | {
930 | FilterActiveFacts(ActiveFactsFilter.Internal, cbActiveFactsFilterInternal.Checked);
931 | }
932 |
933 | private void cbActiveFactsFilterTutorials_CheckedChanged(object sender, EventArgs e)
934 | {
935 | FilterActiveFacts(ActiveFactsFilter.Tutorials, cbActiveFactsFilterTutorials.Checked);
936 | }
937 |
938 | private void cbActiveFactsFilterDiary_CheckedChanged(object sender, EventArgs e)
939 | {
940 | FilterActiveFacts(ActiveFactsFilter.Diary, cbActiveFactsFilterDiary.Checked);
941 | }
942 |
943 | private void cbActiveFactsFilterCollectibles_CheckedChanged(object sender, EventArgs e)
944 | {
945 | FilterActiveFacts(ActiveFactsFilter.Collectibles, cbActiveFactsFilterCollectibles.Checked);
946 | }
947 |
948 | private void cbActiveFactsFilterSms_CheckedChanged(object sender, EventArgs e)
949 | {
950 | FilterActiveFacts(ActiveFactsFilter.Sms, cbActiveFactsFilterSms.Checked);
951 | }
952 |
953 | private void cbActiveFactsFilterChoices_CheckedChanged(object sender, EventArgs e)
954 | {
955 | FilterActiveFacts(ActiveFactsFilter.Choices, cbActiveFactsFilterChoices.Checked);
956 | }
957 |
958 | private void dgvSubLevelStartStates_CellContentClick(object sender, DataGridViewCellEventArgs e)
959 | {
960 | var grid = (DataGridView) sender;
961 | if (e.ColumnIndex == grid.Columns["colSubLevelStartStatesEdit"].Index && e.RowIndex >= 0 && e.RowIndex < grid.Rows.Count-1)
962 | {
963 | var progress = (PlayerProgress) grid.Rows[e.RowIndex].Tag;
964 | var form = new PlayerProgressEditForm(progress, grid.Rows[e.RowIndex].Cells["colSubLevelStartStatesSubLevelName"].Value.ToString());
965 | if (form.ShowDialog() == DialogResult.OK)
966 | {
967 | grid.Rows[e.RowIndex].Tag = form.PlayerProgress;
968 | }
969 | }
970 | }
971 |
972 | private void btnAbout_Click(object sender, EventArgs e)
973 | {
974 | (new AboutForm()).ShowDialog();
975 | }
976 |
977 | private void dgvSubLevelStartStates_UserAddedRow(object sender, DataGridViewRowEventArgs e)
978 | {
979 | e.Row.Tag = null;
980 | }
981 | }
982 | }
983 |
--------------------------------------------------------------------------------
/LifeIsStrangeSaveEditor.Win/MainForm.yo.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
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 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 |
122 | 80
123 |
124 |
--------------------------------------------------------------------------------
/LifeIsStrangeSaveEditor.Win/Model.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace LifeIsStrangeSaveEditor.Win
4 | {
5 | #region Enums
6 | public enum EWhatIfFactLevel
7 | {
8 | EWIFL_None,
9 | EWIFL_Layer,
10 | EWIFL_Episode,
11 | EWIFL_Game,
12 | EWIFL_MAX
13 | };
14 |
15 | public enum EOnlineSaveStatus
16 | {
17 | EOSS_Empty,
18 | EOSS_PendingReading,
19 | EOSS_Reading,
20 | EOSS_ReadComplete,
21 | EOSS_ReadFailed,
22 | EOSS_PendingWriting,
23 | EOSS_Writing,
24 | EOSS_WriteComplete,
25 | EOSS_WriteFailed,
26 | EOSS_WrongVersion,
27 | EOSS_FileCorrupted,
28 | EOSS_NoFile,
29 | EOSS_NotEnoughSpace,
30 | EOSS_MAX
31 | }
32 |
33 | public enum EDNEOptionsId
34 | {
35 | EDNEI_Gamma,
36 | EDNEI_ScreenRatio,
37 | EDNEI_Volume,
38 | EDNEI_MusicVolume,
39 | EDNEI_VoicesVolume,
40 | EDNEI_SFX_Volume,
41 | EDNEI_Subtitle,
42 | EDNEI_InvertXAxis,
43 | EDNEI_InvertYAxis,
44 | EDNEI_Vibration,
45 | EDNEI_AudioLanguage,
46 | EDNEI_Sensitivity,
47 | EDNEI_MAX
48 | }
49 |
50 | public enum EDNEAchievementType
51 | {
52 | EDNEAT_DirectUnlock,
53 | EDNEAT_Counters,
54 | EDNEAT_MAX
55 | }
56 |
57 | public enum EPlaystationGrade
58 | {
59 | EPG_Gold,
60 | EPG_Silver,
61 | EPG_Bronze,
62 | EPG_Platinum,
63 | EPG_MAX
64 | }
65 |
66 | public enum EPlayerBoundaryType
67 | {
68 | PLAYERBOUNDARYTYPE_EPISODE,
69 | PLAYERBOUNDARYTYPE_CONTEXT,
70 | PLAYERBOUNDARYTYPE_OBJECTIVE,
71 | PLAYERBOUNDARYTYPE_CHECKPOINT,
72 | PLAYERBOUNDARYTYPE_PUZZLE,
73 | PLAYERBOUNDARYTYPE_MAX
74 | };
75 |
76 | public enum BoundaryStatType
77 | {
78 | Rewinds,
79 | Hints
80 | }
81 |
82 | public enum ActiveFactsFilter
83 | {
84 | Interactions,
85 | Internal,
86 | Tutorials,
87 | Diary,
88 | Collectibles,
89 | Sms,
90 | Choices
91 | }
92 |
93 | public enum ECheckpointSystemOperation
94 | {
95 | ECSO_UpdateCheckpoint,
96 | ECSO_UpdateAndSaveCheckpoint,
97 | ECSO_LoadCheckpoint,
98 | ECSO_MAX
99 | };
100 | #endregion
101 |
102 | #region Built-in properties
103 | public class BaseProperty
104 | {
105 | public int NameIdx { get; set; }
106 |
107 | public int TypeIdx { get; set; }
108 |
109 | public int PropertySize { get; set; }
110 |
111 | public int ArrayIdx { get; set; }
112 | }
113 |
114 | public class IntProperty
115 | {
116 | public int Value { get; set; }
117 | }
118 |
119 | ///
120 | /// Enum
121 | ///
122 | public class BoolProperty
123 | {
124 | public bool Value { get; set; } // byte
125 | }
126 |
127 | public class ByteProperty
128 | {
129 | public int EnumNameIdx { get; set; }
130 |
131 | public int EnumValueNameIdx { get; set; }
132 | }
133 |
134 | public class FloatProperty
135 | {
136 | public float Value { get; set; }
137 | }
138 |
139 | public class NameProperty
140 | {
141 | public int NameIndex { get; set; }
142 |
143 | public string Name; // Non-existing field, helper
144 |
145 | public override string ToString()
146 | {
147 | return string.Format("NameProperty: {0} [{1}]", Name, NameIndex);
148 | }
149 | }
150 |
151 | public class StrProperty
152 | {
153 | public int Size { get; set; }
154 |
155 | public string Value { get; set; }
156 | }
157 |
158 | public class ArrayProperty
159 | {
160 | public int NumElements { get; set; }
161 | }
162 |
163 | public class Vector
164 | {
165 | public float X { get; set; }
166 | public float Y { get; set; }
167 | public float Z { get; set; }
168 | }
169 |
170 | public class Rotator
171 | {
172 | public int Pitch { get; set; }
173 | public int Yaw { get; set; }
174 | public int Roll { get; set; }
175 | }
176 | #endregion
177 |
178 | public class SaveFile
179 | {
180 | public SaveFile()
181 | {
182 | Header = new FileHeader();
183 | SaveDataHeader = new SaveDataHeader();
184 | SaveData = new WhatIfSaveData();
185 | NameTable = new NameTable();
186 | }
187 |
188 | public FileHeader Header;
189 |
190 | public SaveDataHeader SaveDataHeader;
191 |
192 | public WhatIfSaveData SaveData;
193 |
194 | public NameTable NameTable;
195 | }
196 |
197 | public class FileHeader
198 | {
199 | public int UnkInt1;
200 |
201 | public int FileSize;
202 | }
203 |
204 | public class SaveDataHeader
205 | {
206 | public int HeaderSize;
207 |
208 | public int SaveDataSize;
209 |
210 | public int UnkInt2;
211 | }
212 |
213 | public class NameTable
214 | {
215 | public NameTable()
216 | {
217 | Names = new List();
218 | }
219 |
220 | public List Names;
221 | }
222 |
223 | public class WhatIfSaveData : DNESaveData
224 | {
225 | public WhatIfSaveData()
226 | {
227 | AlreadySeenLastDiaryPages = new int[6] {-1, -1, -1, -1, -1, -1};
228 | NbPlaythrough = new int[5];
229 | PercentageCompleted = new float[6];
230 | }
231 |
232 | public Vector CheckpointLocation;
233 | public Rotator CheckpointRotation;
234 | public string CheckPointID;
235 | public List CheckpointLevelRecords;
236 | public PlayerProgress PlayerProgress;
237 | public List Achievements;
238 | public List VisibleLevels; // NameProperty
239 | public int? CurrentEpisode;
240 | public List SLevelStartStates;
241 | public List CheckpointReached;
242 | public bool? bStartNewGame;
243 | public bool? bGameStarted;
244 | public bool? bNeedToSwitchToNextEpisode;
245 | public int? LastSubLevelPlayedIndex;
246 | public List AlreadySeenCollectibles; // NameProperty
247 | public List AlreadySeenCharacters; // NameProperty
248 | public int[] AlreadySeenLastDiaryPages; // [6]
249 | public float? fTotalGameTimePauseExcluded;
250 | public int[] NbPlaythrough; // [5]
251 | public int? NbEpisodeCompletedSum;
252 | public int? NbInteractionDoneSum;
253 | public int? NbChoiceMadeSum;
254 | public float[] PercentageCompleted; // [5]
255 | }
256 |
257 | public class LevelInfos
258 | {
259 | public NameProperty LevelName; // NameProperty
260 | public bool? bShouldBeLoaded;
261 | public bool? bShouldBeVisible;
262 | }
263 |
264 | public class DNESaveTime
265 | {
266 | public int? SecondsSinceMidnight;
267 | public int? Day;
268 | public int? Month;
269 | public int? Year;
270 | }
271 |
272 | public class SubLevelStartState
273 | {
274 | public string SubLevelTitle;
275 | public PlayerProgress PlayerProgress;
276 | };
277 |
278 | public class PlayerProgress
279 | {
280 | public PlayerProgress()
281 | {
282 | MaxReachedContextName = new string[5];
283 | fEpisodePlayTimePauseExcluded = new float[5];
284 | fEpisodePlayTimePauseIncluded = new float[5];
285 | EpisodeBoundaryStats = new SEpisodeBoundaryStats[5];
286 | }
287 |
288 | public List ActiveFacts;
289 | public List ActiveTutorials;
290 | public List FileSelects;
291 | public List InventoryObjects;
292 | public NameProperty CurrentObjectiveID; // NameProperty
293 | public string[] MaxReachedContextName; // [5]
294 | public List CollectibleFoundFactNames; // NameProperty
295 | public float[] fEpisodePlayTimePauseExcluded; // [5]
296 | public float[] fEpisodePlayTimePauseIncluded; // [5]
297 | public int? CurrentDay;
298 | public bool? DiaryAlreadySeen;
299 | public int? LastDiaryDaySeen;
300 | public int? LastDiaryPageSeen;
301 | public SEpisodeBoundaryStats[] EpisodeBoundaryStats; // [5]
302 | }
303 |
304 | public class ActiveFactLevel
305 | {
306 | public NameProperty FactName; // NameProperty
307 | public int? FactLevel;
308 | }
309 |
310 | public class Achievement
311 | {
312 | public int? Id;
313 | public EDNEAchievementType? Type;
314 | public bool? bHidden;
315 | public EPlaystationGrade? PlaystationGrade;
316 | public int? XboxGamerscore;
317 | public string Title;
318 | public string Description;
319 | public string HowTo;
320 | public int? CountersLimit;
321 | public bool? bResetCountersWhenPlayerDie;
322 | public int? CurrentCountersValue;
323 | }
324 |
325 | public class FileSelect
326 | {
327 | public NameProperty ObjectName; // NameProperty
328 | public string TextureKey;
329 | public List Pages;
330 | }
331 |
332 | public class ObjectInInventory
333 | {
334 | public NameProperty ObjectName; // NameProperty
335 | public int? ObjectInstanceCount;
336 | }
337 |
338 | public class SEpisodeBoundaryStats
339 | {
340 | public SEpisodeBoundaryStats()
341 | {
342 | BoundaryStats = new SBoundaryStats[5];
343 | }
344 |
345 | public SBoundaryStats[] BoundaryStats; //sizeof(EPlayerBoundaryType) = 5
346 | }
347 |
348 | public class SBoundaryStats
349 | {
350 | public int? NbRewinds;
351 | public int? NbHints;
352 | }
353 |
354 | public abstract class DNESaveData
355 | {
356 | public string SaveId;
357 | public string SaveFileName;
358 | public string SaveDetails;
359 | public string FriendlyName;
360 | public DNESaveTime SaveTime;
361 | public EOnlineSaveStatus? Status;
362 | public bool? bIsOwned;
363 | public int? SlotNumber;
364 | public int? RequiredFreeSpace;
365 | }
366 |
367 | public class DebugFact
368 | {
369 | public string Name;
370 |
371 | public EWhatIfFactLevel Level;
372 | }
373 |
374 | public class IA_CheckpointSystem
375 | {
376 | public ECheckpointSystemOperation Operation;
377 |
378 | public string CheckPointID;
379 |
380 | //public Actor SpawnPoint;
381 |
382 | public List LevelsOverride;
383 |
384 | public bool bKeepLevelsStates;
385 |
386 | public List VisibleLevels;
387 |
388 | public List DebugFactToActivate;
389 |
390 | public List DebugInventoryObjectToAdd;
391 |
392 | public List DebugFactToChoose;
393 |
394 | //public ChildList ChildSeqs;
395 | }
396 |
397 | public class SubLevelInfo
398 | {
399 | public string Name;
400 |
401 | public string Description;
402 | }
403 |
404 | public class FactChoice
405 | {
406 | public NameProperty FactName;
407 | public bool bDeactivateFact;
408 | public List DebugFactToActivate;
409 | public List DebugFactToDeactivate;
410 | public List DebugInventoryObjectToAdd;
411 | public List DebugInventoryObjectToRemove;
412 | }
413 |
414 | public class FactList
415 | {
416 | public List Choices;
417 | }
418 |
419 | public class SubEpisodeDescription
420 | {
421 | public NameProperty SubEpisodeName;
422 | public string SubEpisodeTitle;
423 | public string TexturePath;
424 | public string InsertionCheckPointId;
425 | public List FactChoiceList;
426 | public List DebugFactToActivate;
427 | public List DebugFactToDeactivate;
428 | public List DebugInventoryObjectToAdd;
429 | public List DebugInventoryObjectToRemove;
430 | public List LevelsToRemoveAtStartup;
431 | public List Collectibles;
432 | }
433 |
434 | public class EpisodeChoiceResult
435 | {
436 | public NameProperty Id;
437 | public string Description;
438 | public NameProperty Fact;
439 | public bool FactActivated;
440 | }
441 |
442 | public class EpisodeChoiceEntry
443 | {
444 | public string PictureTexture;
445 | public NameProperty ActivationFact;
446 | public List ChoiceResults;
447 | }
448 |
449 | public class EpisodeDescription
450 | {
451 | public NameProperty EpisodeName;
452 | public string EpisodeTitle;
453 | //public EpisodeDispositionData DispositionData;
454 | public List SubEpisodes;
455 | public List MajorChoices;
456 | public List MinorChoices;
457 | public List NextEpisodeTeaserImages;
458 | }
459 | }
460 |
--------------------------------------------------------------------------------
/LifeIsStrangeSaveEditor.Win/PlayerProgressEditForm.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace LifeIsStrangeSaveEditor.Win
2 | {
3 | partial class PlayerProgressEditForm
4 | {
5 | ///
6 | /// Required designer variable.
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// Clean up any resources being used.
12 | ///
13 | /// true if managed resources should be disposed; otherwise, false.
14 | protected override void Dispose(bool disposing)
15 | {
16 | if (disposing && (components != null))
17 | {
18 | components.Dispose();
19 | }
20 | base.Dispose(disposing);
21 | }
22 |
23 | #region Windows Form Designer generated code
24 |
25 | ///
26 | /// Required method for Designer support - do not modify
27 | /// the contents of this method with the code editor.
28 | ///
29 | private void InitializeComponent()
30 | {
31 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(PlayerProgressEditForm));
32 | this.tbProgress = new System.Windows.Forms.TextBox();
33 | this.btnOK = new System.Windows.Forms.Button();
34 | this.btnCancel = new System.Windows.Forms.Button();
35 | this.pnlButtons = new System.Windows.Forms.Panel();
36 | this.btnValidate = new System.Windows.Forms.Button();
37 | this.pnlButtons.SuspendLayout();
38 | this.SuspendLayout();
39 | //
40 | // tbProgress
41 | //
42 | this.tbProgress.Dock = System.Windows.Forms.DockStyle.Fill;
43 | this.tbProgress.Location = new System.Drawing.Point(0, 0);
44 | this.tbProgress.MaxLength = 0;
45 | this.tbProgress.Multiline = true;
46 | this.tbProgress.Name = "tbProgress";
47 | this.tbProgress.ScrollBars = System.Windows.Forms.ScrollBars.Both;
48 | this.tbProgress.Size = new System.Drawing.Size(815, 559);
49 | this.tbProgress.TabIndex = 0;
50 | this.tbProgress.WordWrap = false;
51 | //
52 | // btnOK
53 | //
54 | this.btnOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
55 | this.btnOK.DialogResult = System.Windows.Forms.DialogResult.OK;
56 | this.btnOK.Location = new System.Drawing.Point(647, 15);
57 | this.btnOK.Name = "btnOK";
58 | this.btnOK.Size = new System.Drawing.Size(75, 23);
59 | this.btnOK.TabIndex = 1;
60 | this.btnOK.Text = "OK";
61 | this.btnOK.UseVisualStyleBackColor = true;
62 | this.btnOK.Click += new System.EventHandler(this.btnOK_Click);
63 | //
64 | // btnCancel
65 | //
66 | this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
67 | this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
68 | this.btnCancel.Location = new System.Drawing.Point(728, 15);
69 | this.btnCancel.Name = "btnCancel";
70 | this.btnCancel.Size = new System.Drawing.Size(75, 23);
71 | this.btnCancel.TabIndex = 2;
72 | this.btnCancel.Text = "Cancel";
73 | this.btnCancel.UseVisualStyleBackColor = true;
74 | //
75 | // pnlButtons
76 | //
77 | this.pnlButtons.Controls.Add(this.btnValidate);
78 | this.pnlButtons.Controls.Add(this.btnCancel);
79 | this.pnlButtons.Controls.Add(this.btnOK);
80 | this.pnlButtons.Dock = System.Windows.Forms.DockStyle.Bottom;
81 | this.pnlButtons.Location = new System.Drawing.Point(0, 559);
82 | this.pnlButtons.Name = "pnlButtons";
83 | this.pnlButtons.Size = new System.Drawing.Size(815, 50);
84 | this.pnlButtons.TabIndex = 3;
85 | //
86 | // btnValidate
87 | //
88 | this.btnValidate.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
89 | this.btnValidate.Location = new System.Drawing.Point(12, 15);
90 | this.btnValidate.Name = "btnValidate";
91 | this.btnValidate.Size = new System.Drawing.Size(75, 23);
92 | this.btnValidate.TabIndex = 3;
93 | this.btnValidate.Text = "Validate";
94 | this.btnValidate.UseVisualStyleBackColor = true;
95 | this.btnValidate.Click += new System.EventHandler(this.btnValidate_Click);
96 | //
97 | // PlayerProgressEditForm
98 | //
99 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
100 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
101 | this.CancelButton = this.btnCancel;
102 | this.ClientSize = new System.Drawing.Size(815, 609);
103 | this.Controls.Add(this.tbProgress);
104 | this.Controls.Add(this.pnlButtons);
105 | this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
106 | this.MinimizeBox = false;
107 | this.Name = "PlayerProgressEditForm";
108 | this.ShowInTaskbar = false;
109 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
110 | this.Text = "Player progress";
111 | this.pnlButtons.ResumeLayout(false);
112 | this.ResumeLayout(false);
113 | this.PerformLayout();
114 |
115 | }
116 |
117 | #endregion
118 |
119 | private System.Windows.Forms.TextBox tbProgress;
120 | private System.Windows.Forms.Button btnOK;
121 | private System.Windows.Forms.Button btnCancel;
122 | private System.Windows.Forms.Panel pnlButtons;
123 | private System.Windows.Forms.Button btnValidate;
124 | }
125 | }
--------------------------------------------------------------------------------
/LifeIsStrangeSaveEditor.Win/PlayerProgressEditForm.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Windows.Forms;
4 | using System.Xml.Serialization;
5 |
6 | namespace LifeIsStrangeSaveEditor.Win
7 | {
8 | public partial class PlayerProgressEditForm : Form
9 | {
10 | public PlayerProgress PlayerProgress { get; set; }
11 |
12 | public PlayerProgressEditForm(PlayerProgress playerProgress, string caption = null)
13 | {
14 | InitializeComponent();
15 |
16 | PlayerProgress = playerProgress;
17 | if (!string.IsNullOrEmpty(caption))
18 | this.Text = caption;
19 |
20 | var ms = new MemoryStream();
21 | var ser = new XmlSerializer(typeof (PlayerProgress));
22 | ser.Serialize(ms, PlayerProgress);
23 | ms.Position = 0;
24 | var sr = new StreamReader(ms);
25 | var progressStr = sr.ReadToEnd();
26 | tbProgress.Text = progressStr;
27 |
28 | tbProgress.Select(0, 0);
29 | }
30 |
31 | private void btnOK_Click(object sender, EventArgs e)
32 | {
33 | if (tbProgress.Text != null)
34 | {
35 | try
36 | {
37 | var ser = new XmlSerializer(typeof(PlayerProgress));
38 | using (var sr = new StringReader(tbProgress.Text))
39 | {
40 | PlayerProgress = (PlayerProgress)ser.Deserialize(sr);
41 | }
42 | }
43 | catch (Exception)
44 | {
45 | PlayerProgress = null;
46 | }
47 | }
48 | }
49 |
50 | private void btnValidate_Click(object sender, EventArgs e)
51 | {
52 | if (tbProgress.Text != null)
53 | {
54 | try
55 | {
56 | var ser = new XmlSerializer(typeof(PlayerProgress));
57 | using (var sr = new StringReader(tbProgress.Text))
58 | {
59 | ser.Deserialize(sr);
60 | }
61 | }
62 | catch (Exception ex)
63 | {
64 | PlayerProgress = null;
65 | MessageBox.Show(string.Format("Invalid{0}{0}{1}", Environment.NewLine, ex.InnerException.Message));
66 | return;
67 | }
68 | MessageBox.Show("Valid");
69 | }
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/LifeIsStrangeSaveEditor.Win/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Windows.Forms;
5 |
6 | namespace LifeIsStrangeSaveEditor.Win
7 | {
8 | static class Program
9 | {
10 | ///
11 | /// The main entry point for the application.
12 | ///
13 | [STAThread]
14 | static void Main()
15 | {
16 | Application.EnableVisualStyles();
17 | Application.SetCompatibleTextRenderingDefault(false);
18 | Application.Run(new MainForm());
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/LifeIsStrangeSaveEditor.Win/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 |
4 | // General Information about an assembly is controlled through the following
5 | // set of attributes. Change these attribute values to modify the information
6 | // associated with an assembly.
7 | [assembly: AssemblyTitle("Life is Strange Save Editor")]
8 | [assembly: AssemblyDescription("")]
9 | [assembly: AssemblyConfiguration("")]
10 | [assembly: AssemblyCompany("")]
11 | [assembly: AssemblyProduct("LifeIsStrangeSaveEditor.Win")]
12 | [assembly: AssemblyCopyright("Vakhtin Andrey © 2015")]
13 | [assembly: AssemblyTrademark("")]
14 | [assembly: AssemblyCulture("")]
15 |
16 | // Setting ComVisible to false makes the types in this assembly not visible
17 | // to COM components. If you need to access a type in this assembly from
18 | // COM, set the ComVisible attribute to true on that type.
19 | [assembly: ComVisible(false)]
20 |
21 | // The following GUID is for the ID of the typelib if this project is exposed to COM
22 | [assembly: Guid("1baa0aa4-064e-4e2e-b3b9-e9f3670ea478")]
23 |
24 | // Version information for an assembly consists of the following four values:
25 | //
26 | // Major Version
27 | // Minor Version
28 | // Build Number
29 | // Revision
30 | //
31 | // You can specify all the values or you can default the Build and Revision Numbers
32 | // by using the '*' as shown below:
33 | // [assembly: AssemblyVersion("1.0.*")]
34 | [assembly: AssemblyVersion("0.2.1.0")]
35 | [assembly: AssemblyFileVersion("0.2.1.0")]
36 |
--------------------------------------------------------------------------------
/LifeIsStrangeSaveEditor.Win/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.34209
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace LifeIsStrangeSaveEditor.Win.Properties {
12 | using System;
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | public class Resources {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal Resources() {
33 | }
34 |
35 | ///
36 | /// Returns the cached ResourceManager instance used by this class.
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | public static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("LifeIsStrangeSaveEditor.Win.Properties.Resources", typeof(Resources).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// Overrides the current thread's CurrentUICulture property for all
51 | /// resource lookups using this strongly typed resource class.
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | public static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 |
63 | ///
64 | /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
65 | ///
66 | public static System.Drawing.Icon Butterfly {
67 | get {
68 | object obj = ResourceManager.GetObject("Butterfly", resourceCulture);
69 | return ((System.Drawing.Icon)(obj));
70 | }
71 | }
72 |
73 | ///
74 | /// Looks up a localized resource of type System.Drawing.Bitmap.
75 | ///
76 | public static System.Drawing.Bitmap butterfly_104x96 {
77 | get {
78 | object obj = ResourceManager.GetObject("butterfly_104x96", resourceCulture);
79 | return ((System.Drawing.Bitmap)(obj));
80 | }
81 | }
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/LifeIsStrangeSaveEditor.Win/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
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 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 |
122 | ..\Resources\Butterfly.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
123 |
124 |
125 | ..\Resources\butterfly_104x96.gif;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
126 |
127 |
--------------------------------------------------------------------------------
/LifeIsStrangeSaveEditor.Win/Properties/Settings.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.34209
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace LifeIsStrangeSaveEditor.Win.Properties
12 | {
13 |
14 |
15 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
16 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
17 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
18 | {
19 |
20 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
21 |
22 | public static Settings Default
23 | {
24 | get
25 | {
26 | return defaultInstance;
27 | }
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/LifeIsStrangeSaveEditor.Win/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/LifeIsStrangeSaveEditor.Win/Resources/Butterfly.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VakhtinAndrey/lis-save-editor/b970555f254c389050baad94f5d03d4c6dbdb75b/LifeIsStrangeSaveEditor.Win/Resources/Butterfly.ico
--------------------------------------------------------------------------------
/LifeIsStrangeSaveEditor.Win/Resources/Thumbs.db:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VakhtinAndrey/lis-save-editor/b970555f254c389050baad94f5d03d4c6dbdb75b/LifeIsStrangeSaveEditor.Win/Resources/Thumbs.db
--------------------------------------------------------------------------------
/LifeIsStrangeSaveEditor.Win/Resources/butterfly_104x96.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VakhtinAndrey/lis-save-editor/b970555f254c389050baad94f5d03d4c6dbdb75b/LifeIsStrangeSaveEditor.Win/Resources/butterfly_104x96.gif
--------------------------------------------------------------------------------
/LifeIsStrangeSaveEditor.Win/StreamExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Reflection;
7 | using System.Text;
8 |
9 | namespace LifeIsStrangeSaveEditor.Win
10 | {
11 | public static class StreamExtensions
12 | {
13 | #region Read extensions
14 | public static int ReadInt32(this Stream stream)
15 | {
16 | var buffer = new byte[4];
17 | stream.Read(buffer, 0, 4);
18 | return BitConverter.ToInt32(buffer, 0);
19 | }
20 |
21 | public static float ReadFloat(this Stream stream)
22 | {
23 | var buffer = new byte[4];
24 | stream.Read(buffer, 0, 4);
25 | return BitConverter.ToSingle(buffer, 0);
26 | }
27 |
28 | public static bool ReadBool(this Stream stream)
29 | {
30 | return Convert.ToBoolean(stream.ReadByte());
31 | }
32 |
33 | public static string ReadString(this Stream stream)
34 | {
35 | var length = stream.ReadInt32();
36 | return stream.ReadString(length);
37 | }
38 |
39 | public static string ReadString(this Stream stream, int length)
40 | {
41 | var buffer = new byte[length > 0 ? length : -length*2];
42 | stream.Read(buffer, 0, buffer.Length);
43 | return length > 0
44 | ? Encoding.ASCII.GetString(buffer).TrimEnd('\0')
45 | : Encoding.Unicode.GetString(buffer).TrimEnd('\0');
46 | }
47 |
48 | public static BaseProperty ReadBaseProperty(this Stream stream)
49 | {
50 | return new BaseProperty
51 | {
52 | NameIdx = stream.ReadInt32(),
53 | TypeIdx = stream.ReadInt32(),
54 | PropertySize = stream.ReadInt32(),
55 | ArrayIdx = stream.ReadInt32()
56 | };
57 | }
58 |
59 | public static NameProperty ReadNameProperty(this Stream stream, List names)
60 | {
61 | var nameIdx = stream.ReadInt32();
62 | return new NameProperty
63 | {
64 | NameIndex = nameIdx,
65 | Name = names[nameIdx]
66 | };
67 | }
68 | #endregion
69 |
70 | #region Write extensions
71 | public static void WriteInt32(this Stream stream, int value)
72 | {
73 | stream.Write(BitConverter.GetBytes(value), 0, 4);
74 | }
75 |
76 | public static void WriteBool(this Stream stream, bool value)
77 | {
78 | stream.Write(BitConverter.GetBytes(value), 0, 1);
79 | }
80 |
81 | public static void WriteFloat(this Stream stream, float value)
82 | {
83 | stream.Write(BitConverter.GetBytes(value), 0, 4);
84 | }
85 |
86 | public static void WriteUnicodeString(this Stream stream, string value)
87 | {
88 | var bytes = Encoding.Unicode.GetBytes(value);
89 | stream.Write(BitConverter.GetBytes(-(bytes.Length/2 + 1)), 0, 4);
90 | stream.Write(bytes, 0, bytes.Length);
91 | stream.WriteByte(0); // null-terminator
92 | stream.WriteByte(0); // null-terminator
93 | }
94 |
95 | public static void WriteString(this Stream stream, string value)
96 | {
97 | var bytes = Encoding.ASCII.GetBytes(value);
98 | stream.Write(BitConverter.GetBytes(bytes.Length + 1), 0, 4);
99 | stream.Write(bytes, 0, bytes.Length);
100 | stream.WriteByte(0); // null-terminator
101 | }
102 |
103 | public static void WriteBaseProperty(this Stream stream, BaseProperty value)
104 | {
105 | stream.WriteInt32(value.NameIdx);
106 | stream.WriteInt32(value.TypeIdx);
107 | stream.WriteInt32(value.PropertySize);
108 | stream.WriteInt32(value.ArrayIdx);
109 | }
110 | #endregion
111 |
112 | public static void DeserializeObject(this Stream stream, object obj, List nameTable)
113 | {
114 | while (stream.Position < stream.Length)
115 | {
116 | stream.ReadAndMapProperty(obj, nameTable);
117 | }
118 | }
119 |
120 | public static void ReadAndMapProperty(this Stream stream, object obj, List nameTable)
121 | {
122 | var baseProperty = stream.ReadBaseProperty();
123 |
124 | var pi = obj.GetType().GetField(nameTable[baseProperty.NameIdx], BindingFlags.Public | BindingFlags.Instance);
125 | if (pi == null) return;
126 |
127 | var fieldType = pi.FieldType.IsGenericType && pi.FieldType.GetGenericTypeDefinition() == typeof (Nullable<>)
128 | ? Nullable.GetUnderlyingType(pi.FieldType)
129 | : pi.FieldType;
130 | object value = null;
131 |
132 | switch (nameTable[baseProperty.TypeIdx])
133 | {
134 | case "IntProperty":
135 | value = stream.ReadInt32();
136 | break;
137 | case "BoolProperty":
138 | value = stream.ReadBool();
139 | break;
140 | case "ByteProperty":
141 | stream.ReadInt32(); // EnumNameIdx
142 | var enumValueNameIdx = stream.ReadInt32();
143 | value = Enum.Parse(fieldType, nameTable[enumValueNameIdx]);
144 | break;
145 | case "FloatProperty":
146 | value = stream.ReadFloat();
147 | break;
148 | case "NameProperty":
149 | value = stream.ReadNameProperty(nameTable);
150 | break;
151 | case "StrProperty":
152 | value = stream.ReadString();
153 | break;
154 | case "ArrayProperty":
155 | // У массивов NumElements входит в PropertySize
156 | var numElements = stream.ReadInt32();
157 | // Если элементов нет, то следом идет сразу следующее свойство, массив не заканчивается ничем, нет даже None
158 | if (numElements == 0) return;
159 |
160 | value = Activator.CreateInstance(fieldType);
161 |
162 | var elementType = fieldType.GetGenericArguments()[0];
163 | for (var i = 0; i < numElements; i++)
164 | {
165 | if (elementType == typeof (int)) // array
166 | {
167 | (value as IList).Add(stream.ReadInt32());
168 | }
169 | else if (elementType == typeof (string)) // array
170 | {
171 | (value as IList).Add(stream.ReadString());
172 | }
173 | else if (elementType == typeof (NameProperty)) // array
174 | {
175 | (value as IList).Add(stream.ReadNameProperty(nameTable));
176 | }
177 | else
178 | {
179 | var element = Activator.CreateInstance(elementType);
180 | var nameIdx = stream.ReadInt32();
181 | while (nameTable[nameIdx] != "None")
182 | {
183 | stream.Seek(-4, SeekOrigin.Current);
184 | stream.ReadAndMapProperty(element, nameTable);
185 | nameIdx = stream.ReadInt32();
186 | }
187 | (value as IList).Add(element);
188 | }
189 | }
190 | break;
191 | case "StructProperty":
192 | // У структур StructNameIdx не входит в PropertySize
193 | var structNameIdx = stream.ReadInt32();
194 | switch (nameTable[structNameIdx])
195 | {
196 | case "Vector":
197 | value = new Vector
198 | {
199 | X = stream.ReadFloat(),
200 | Y = stream.ReadFloat(),
201 | Z = stream.ReadFloat()
202 | };
203 | break;
204 | case "Rotator":
205 | value = new Rotator
206 | {
207 | Pitch = stream.ReadInt32(),
208 | Yaw = stream.ReadInt32(),
209 | Roll = stream.ReadInt32()
210 | };
211 | break;
212 | case "Transform":
213 | stream.Seek(baseProperty.PropertySize, SeekOrigin.Current);
214 | break;
215 | default:
216 | // На случай SEpisodeBoundaryStats[]
217 | value = Activator.CreateInstance(fieldType.IsArray ? fieldType.GetElementType() : fieldType);
218 | var nameIdx = stream.ReadInt32();
219 | // None в конце всех структур, кроме стандартных Vector, Rotator, Transform
220 | while (nameTable[nameIdx] != "None")
221 | {
222 | stream.Seek(-4, SeekOrigin.Current);
223 | stream.ReadAndMapProperty(value, nameTable);
224 | nameIdx = stream.ReadInt32();
225 | }
226 | break;
227 | }
228 | break;
229 | }
230 | if (fieldType.IsArray)
231 | {
232 | var array = (Array) pi.GetValue(obj);
233 | array.SetValue(value, baseProperty.ArrayIdx);
234 | pi.SetValue(obj, array);
235 | }
236 | else
237 | {
238 | pi.SetValue(obj, value);
239 | }
240 | }
241 |
242 | public static void SerializeObject(this Stream stream, object obj, List nameTable)
243 | {
244 | var fields = obj.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance);
245 | foreach (var field in fields.Where(f => f.GetValue(obj) != null))
246 | {
247 | var fieldType = field.FieldType.IsGenericType && field.FieldType.GetGenericTypeDefinition() == typeof (Nullable<>)
248 | ? Nullable.GetUnderlyingType(field.FieldType)
249 | : field.FieldType;
250 |
251 | stream.WriteProperty(field.Name, fieldType, field.GetValue(obj), nameTable);
252 | }
253 | stream.WriteInt32(AddToNameTable(nameTable, "None"));
254 | }
255 |
256 | public static void WriteProperty(this Stream stream, string fieldName, Type fieldType,
257 | object fieldValue, List nameTable, int? arrayIndex = null)
258 | {
259 | if (fieldType.IsArray)
260 | {
261 | var array = (Array)fieldValue;
262 | for (var i = 0; i < array.Length; i++)
263 | {
264 | var value = array.GetValue(i);
265 | if (value != null)
266 | stream.WriteProperty(fieldName, fieldType.GetElementType(), array.GetValue(i), nameTable, i);
267 | }
268 | return;
269 | }
270 |
271 | var fieldBodyStream = new MemoryStream();
272 |
273 | var baseProperty = new BaseProperty
274 | {
275 | NameIdx = AddToNameTable(nameTable, fieldName),
276 | ArrayIdx = arrayIndex ?? 0
277 | };
278 |
279 | if (fieldType == typeof (int))
280 | {
281 | baseProperty.TypeIdx = AddToNameTable(nameTable, "IntProperty");
282 | var value = (int) fieldValue;
283 | fieldBodyStream.WriteInt32(value);
284 | }
285 | else if (fieldType == typeof (bool))
286 | {
287 | baseProperty.TypeIdx = AddToNameTable(nameTable, "BoolProperty");
288 | var value = (bool) fieldValue;
289 | fieldBodyStream.WriteBool(value);
290 | }
291 | else if (fieldType.IsEnum)
292 | {
293 | baseProperty.TypeIdx = AddToNameTable(nameTable, "ByteProperty");
294 | var value1 = AddToNameTable(nameTable, fieldType.Name); // Enum type name
295 | var value2 = AddToNameTable(nameTable, fieldValue.ToString()); // Enum value
296 | fieldBodyStream.WriteInt32(value1);
297 | fieldBodyStream.WriteInt32(value2);
298 | }
299 | else if (fieldType == typeof (float))
300 | {
301 | baseProperty.TypeIdx = AddToNameTable(nameTable, "FloatProperty");
302 | var value = (float) fieldValue;
303 | fieldBodyStream.WriteFloat(value);
304 | }
305 | else if (fieldType == typeof (NameProperty))
306 | {
307 | baseProperty.TypeIdx = AddToNameTable(nameTable, "NameProperty");
308 | var nameProp = (NameProperty) fieldValue;
309 | var value = AddToNameTable(nameTable, nameProp.Name);
310 | fieldBodyStream.WriteInt32(value);
311 | }
312 | else if (fieldType == typeof (string))
313 | {
314 | baseProperty.TypeIdx = AddToNameTable(nameTable, "StrProperty");
315 | var value = (string) fieldValue;
316 | switch (fieldName)
317 | {
318 | case "SaveDetails":
319 | case "FriendlyName":
320 | fieldBodyStream.WriteUnicodeString(value);
321 | break;
322 | default:
323 | fieldBodyStream.WriteString(value);
324 | break;
325 | }
326 | //fieldBodyStream.WriteUnicodeString(value); // Save every string as unicode
327 | }
328 | else if (fieldType.IsGenericType && (fieldType.GetGenericTypeDefinition() == typeof(List<>)))
329 | {
330 | baseProperty.TypeIdx = AddToNameTable(nameTable, "ArrayProperty");
331 | var list = fieldValue as IList;
332 | var elementType = fieldType.GetGenericArguments()[0];
333 | fieldBodyStream.WriteInt32(list.Count); // numElements
334 | for (var i = 0; i < list.Count; i++)
335 | {
336 | if (elementType == typeof (int))
337 | {
338 | fieldBodyStream.WriteInt32((int) list[i]);
339 | }
340 | else if (elementType == typeof (string))
341 | {
342 | fieldBodyStream.WriteString((string) list[i]);
343 | }
344 | else if (elementType == typeof (NameProperty))
345 | {
346 | var value = AddToNameTable(nameTable, ((NameProperty) list[i]).Name);
347 | fieldBodyStream.WriteInt32(value);
348 | }
349 | else
350 | {
351 | fieldBodyStream.SerializeObject(list[i], nameTable);
352 | }
353 | }
354 | }
355 | else if (!fieldType.IsGenericType && !fieldType.IsValueType && !fieldType.IsPrimitive && fieldType.IsClass)
356 | {
357 | baseProperty.TypeIdx = AddToNameTable(nameTable, "StructProperty");
358 |
359 | fieldBodyStream.WriteInt32(AddToNameTable(nameTable, fieldType.Name)); // StructNameIdx
360 |
361 | if (fieldType == typeof (Vector))
362 | {
363 | var value = (Vector) fieldValue;
364 | fieldBodyStream.WriteFloat(value.X);
365 | fieldBodyStream.WriteFloat(value.Y);
366 | fieldBodyStream.WriteFloat(value.Z);
367 | }
368 | else if (fieldType == typeof (Rotator))
369 | {
370 | var value = (Rotator) fieldValue;
371 | fieldBodyStream.WriteInt32(value.Pitch);
372 | fieldBodyStream.WriteInt32(value.Yaw);
373 | fieldBodyStream.WriteInt32(value.Roll);
374 | }
375 | else
376 | {
377 | fieldBodyStream.SerializeObject(fieldValue, nameTable);
378 | }
379 | }
380 |
381 | baseProperty.PropertySize = (int) fieldBodyStream.Length;
382 | stream.WriteBaseProperty(baseProperty);
383 | fieldBodyStream.WriteTo(stream);
384 | }
385 |
386 | private static int AddToNameTable(IList nameTable, string name)
387 | {
388 | if (nameTable.All(n => n != name))
389 | nameTable.Add(name);
390 | return nameTable.IndexOf(name);
391 | }
392 | }
393 | }
394 |
--------------------------------------------------------------------------------
/LifeIsStrangeSaveEditor.Win/SubLevelsInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace LifeIsStrangeSaveEditor.Win
4 | {
5 | public class SubLevelsInfo
6 | {
7 | public static List SubLevels = new List
8 | {
9 | #region Episode 1
10 | new SubLevelInfo
11 | {
12 | Name = "Episode1Sub1",
13 | Description = "Nightmare"
14 | },
15 | new SubLevelInfo
16 | {
17 | Name = "Episode1Sub2",
18 | Description = "High School"
19 | },
20 | new SubLevelInfo
21 | {
22 | Name = "Episode1Sub3",
23 | Description = "Main Campus"
24 | },
25 | new SubLevelInfo
26 | {
27 | Name = "Episode1Sub4",
28 | Description = "Dormitories"
29 | },
30 | new SubLevelInfo
31 | {
32 | Name = "Episode1Sub5",
33 | Description = "Girls' Dormitories"
34 | },
35 | new SubLevelInfo
36 | {
37 | Name = "Episode1Sub6",
38 | Description = "Dormitories - With The Flash Drive"
39 | },
40 | new SubLevelInfo
41 | {
42 | Name = "Episode1Sub7",
43 | Description = "Parking Lot"
44 | },
45 | new SubLevelInfo
46 | {
47 | Name = "Episode1Sub8",
48 | Description = "Chloe's Truck"
49 | },
50 | new SubLevelInfo
51 | {
52 | Name = "Episode1Sub9",
53 | Description = "Chloe's House - Upstairs"
54 | },
55 | new SubLevelInfo
56 | {
57 | Name = "Episode1Sub10",
58 | Description = "Chloe's House - Downstairs"
59 | },
60 | new SubLevelInfo
61 | {
62 | Name = "Episode1Sub11",
63 | Description = "Chloe's House - With The Tools"
64 | },
65 | new SubLevelInfo
66 | {
67 | Name = "Episode1Sub12",
68 | Description = "Cliff"
69 | },
70 | new SubLevelInfo
71 | {
72 | Name = "Episode1Sub13",
73 | Description = "Cliff - Nightmare"
74 | },
75 | #endregion
76 |
77 | #region Episode 2
78 | new SubLevelInfo
79 | {
80 | Name = "Episode2Sub1",
81 | Description = "Girls' Dormitories"
82 | },
83 | new SubLevelInfo
84 | {
85 | Name = "Episode2Sub2",
86 | Description = "Dormitories - Morning"
87 | },
88 | new SubLevelInfo
89 | {
90 | Name = "Episode2Sub3",
91 | Description = "Main Campus - Morning"
92 | },
93 | new SubLevelInfo
94 | {
95 | Name = "Episode2Sub4",
96 | Description = "Bus"
97 | },
98 | new SubLevelInfo
99 | {
100 | Name = "Episode2Sub5",
101 | Description = "Diner"
102 | },
103 | new SubLevelInfo
104 | {
105 | Name = "Episode2Sub6",
106 | Description = "Junkyard"
107 | },
108 | new SubLevelInfo
109 | {
110 | Name = "Episode2Sub7",
111 | Description = "Railroad"
112 | },
113 | new SubLevelInfo
114 | {
115 | Name = "Episode2Sub8",
116 | Description = "Main Campus - Before Class"
117 | },
118 | new SubLevelInfo
119 | {
120 | Name = "Episode2Sub9",
121 | Description = "High School"
122 | },
123 | new SubLevelInfo
124 | {
125 | Name = "Episode2Sub10",
126 | Description = "Dormitories"
127 | },
128 | new SubLevelInfo
129 | {
130 | Name = "Episode2Sub11",
131 | Description = "Principal's Office"
132 | },
133 | new SubLevelInfo
134 | {
135 | Name = "Episode2Sub12",
136 | Description = "Main Campus - With Warren"
137 | },
138 | #endregion
139 |
140 | #region Episode 3
141 | new SubLevelInfo
142 | {
143 | Name = "Episode3Sub1",
144 | Description = "Girls' Dormitories"
145 | },
146 | new SubLevelInfo
147 | {
148 | Name = "Episode3Sub2",
149 | Description = "Diner"
150 | },
151 | new SubLevelInfo
152 | {
153 | Name = "Episode3Sub3",
154 | Description = "Chloe's Truck"
155 | },
156 | new SubLevelInfo
157 | {
158 | Name = "Episode3Sub4",
159 | Description = "Main Campus - Noon"
160 | },
161 | new SubLevelInfo
162 | {
163 | Name = "Episode3Sub5",
164 | Description = "Max's Room"
165 | },
166 | new SubLevelInfo
167 | {
168 | Name = "Episode3Sub6",
169 | Description = "Chloe's House - Focus"
170 | },
171 | new SubLevelInfo
172 | {
173 | Name = "Episode3Sub7",
174 | Description = "Alternative Main Campus"
175 | },
176 | new SubLevelInfo
177 | {
178 | Name = "Episode3Sub8",
179 | Description = "Alternative Chloe's House"
180 | },
181 | new SubLevelInfo
182 | {
183 | Name = "Episode3Sub9",
184 | Description = "Dormitories"
185 | },
186 | new SubLevelInfo
187 | {
188 | Name = "Episode3Sub10",
189 | Description = "Main Campus - Evening"
190 | },
191 | new SubLevelInfo
192 | {
193 | Name = "Episode3Sub11",
194 | Description = "High School - Evening"
195 | },
196 | new SubLevelInfo
197 | {
198 | Name = "Episode3Sub12",
199 | Description = "Swimming Pool - Evening"
200 | },
201 | new SubLevelInfo
202 | {
203 | Name = "Episode3Sub13",
204 | Description = "Parking Lot - Evening"
205 | },
206 | new SubLevelInfo
207 | {
208 | Name = "Episode3Sub14",
209 | Description = "Nightmare"
210 | },
211 | new SubLevelInfo
212 | {
213 | Name = "Episode3Sub15",
214 | Description = "Chloe's House - Upstairs"
215 | },
216 | new SubLevelInfo
217 | {
218 | Name = "Episode3Sub16",
219 | Description = "Chloe's House - Downstairs"
220 | },
221 | #endregion
222 |
223 | #region Episode 4
224 | new SubLevelInfo
225 | {
226 | Name = "Episode4Sub1",
227 | Description = "Alternative Beach"
228 | },
229 | new SubLevelInfo
230 | {
231 | Name = "Episode4Sub2",
232 | Description = "Alternative Chloe's Room"
233 | },
234 | new SubLevelInfo
235 | {
236 | Name = "Episode4Sub3",
237 | Description = "Alternative Chloe's House - Downstairs"
238 | },
239 | new SubLevelInfo
240 | {
241 | Name = "Episode4Sub4",
242 | Description = "Alternative Chloe's House - Upstairs"
243 | },
244 | new SubLevelInfo
245 | {
246 | Name = "Episode4Sub5",
247 | Description = "Alternative Chloe's House - Downstairs with the morphine"
248 | },
249 | new SubLevelInfo
250 | {
251 | Name = "Episode4Sub6",
252 | Description = "Chloe's House - Focus"
253 | },
254 | new SubLevelInfo
255 | {
256 | Name = "Episode4Sub7",
257 | Description = "Chloe's House - Upstairs"
258 | },
259 | new SubLevelInfo
260 | {
261 | Name = "Episode4Sub8",
262 | Description = "Chloe's House - Downstairs"
263 | },
264 | new SubLevelInfo
265 | {
266 | Name = "Episode4Sub9",
267 | Description = "Hospital"
268 | },
269 | new SubLevelInfo
270 | {
271 | Name = "Episode4Sub10",
272 | Description = "Dormitories"
273 | },
274 | new SubLevelInfo
275 | {
276 | Name = "Episode4Sub11",
277 | Description = "Boys' Dormitories"
278 | },
279 | new SubLevelInfo
280 | {
281 | Name = "Episode4Sub12",
282 | Description = "Main Campus"
283 | },
284 | new SubLevelInfo
285 | {
286 | Name = "Episode4Sub13",
287 | Description = "Beach"
288 | },
289 | new SubLevelInfo
290 | {
291 | Name = "Episode4Sub14",
292 | Description = "Chloe's House - Investigation"
293 | },
294 | new SubLevelInfo
295 | {
296 | Name = "Episode4Sub15",
297 | Description = "Old Barn"
298 | },
299 | new SubLevelInfo
300 | {
301 | Name = "Episode4Sub16",
302 | Description = "Junkyard"
303 | },
304 | new SubLevelInfo
305 | {
306 | Name = "Episode4Sub17",
307 | Description = "Parking Lot"
308 | },
309 | new SubLevelInfo
310 | {
311 | Name = "Episode4Sub18",
312 | Description = "Swimming Pool - Party"
313 | },
314 | new SubLevelInfo
315 | {
316 | Name = "Episode4Sub19",
317 | Description = "Junkyard - Evening"
318 | },
319 | #endregion
320 |
321 | #region Episode 5
322 |
323 | #endregion
324 | };
325 | }
326 | }
327 |
--------------------------------------------------------------------------------
/LifeIsStrangeSaveEditor.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 2012
4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LifeIsStrangeSaveEditor.Win", "LifeIsStrangeSaveEditor.Win\LifeIsStrangeSaveEditor.Win.csproj", "{8E39BDD6-980E-4FA9-964E-C18235829C34}"
5 | EndProject
6 | Global
7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
8 | Debug|Any CPU = Debug|Any CPU
9 | Release|Any CPU = Release|Any CPU
10 | EndGlobalSection
11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
12 | {8E39BDD6-980E-4FA9-964E-C18235829C34}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
13 | {8E39BDD6-980E-4FA9-964E-C18235829C34}.Debug|Any CPU.Build.0 = Debug|Any CPU
14 | {8E39BDD6-980E-4FA9-964E-C18235829C34}.Release|Any CPU.ActiveCfg = Release|Any CPU
15 | {8E39BDD6-980E-4FA9-964E-C18235829C34}.Release|Any CPU.Build.0 = Release|Any CPU
16 | EndGlobalSection
17 | GlobalSection(SolutionProperties) = preSolution
18 | HideSolutionNode = FALSE
19 | EndGlobalSection
20 | EndGlobal
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | #Life is Strange save editor
2 |
3 | **Warning:** Save editor is still in deep beta and can damage your save files or leads to unpredictable game behavior after load.
4 |
5 | **BACKUP SAVE FILES** before using editor!
6 |
7 | Save files are located in *"\My Games\Life Is Strange\Saves"* (LISSave[1-3].sav).
8 |
9 |
10 |
11 | ####Save data structure is pretty complex and requires additional explanation.
12 |
13 | **Level start states**
14 |
15 | This list stores information about player progress at the beginning of each sublevel. Used when you choose sublevel to play at from main menu. For now this sections supported only as serialized .NET XML objects.
16 |
17 | **Checkpoint level records**
18 |
19 | First record (for example *E1_1A*) is main location package which defines current loading location. Other files are packages with additional models, textures, special effects, shadows, dialogs and so on. Actually, every file from *CookedPCConsoleFinal* folder, which relates to current location, should be in the list.
20 |
21 | **Player progress**
22 |
23 | Information about current player progress. Used when you choose to continue game from main menu.
24 |
25 | **Active Facts**
26 |
27 | Almost every your action in game is stored in save file: interactions with objects (look, read, take etc.), every chosen dialog option, shown tutorials, received SMS, made major and minor episode choices, new diary pages and characters and so on. Negative level means Fact is inactive, positive - Fact is active. You can activate some Fact once, then rewind and choose to not activate it. Thus Fact will have negative level. If there is no Fact in list at all, it means that you has never activated it. You need to try it yourself to understand how it works.
28 |
29 | **File selects**
30 |
31 | Every document you've ever read in game and document's pages you've seen. Data from this list is used in *"files"* diary tab.
32 |
33 |
34 |
35 | Application is not really user-friendly, just raw data editor. Post your feedback and i'll improve some features in further versions.
36 | Hope save editor will helps someone or at least give a small impetus to modding community.
37 |
--------------------------------------------------------------------------------