├── .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 | --------------------------------------------------------------------------------