SUBSIM: The Web's #1 resource for all submarine & naval simulations since 1997 |
08-18-15, 05:56 PM | #1 |
Difficulties Numbing
|
In-game save date
I have been searching in SH3 Mods for where the date in-game (e.g. 23 January 1940) is placed when saving the game. I have written a utility to extract this information, but wondered, before polishing it too much for release, whether there was an available tool, or something simpler than reading the relevant ISF file.
Have I not used the correct search terms in the forum? Perhaps someone who has been here a long time can tell me if such a tool has been written before. (The utility is handy when writing to the message log as you can ensure that any message WILL appear, if it is dated appropriately). Last edited by ExFishermanBob; 08-18-15 at 05:58 PM. Reason: Clarified! |
08-18-15, 06:01 PM | #2 | |
Sonar Guy
Join Date: Oct 2013
Posts: 377
Downloads: 64
Uploads: 0
|
Quote:
|
|
08-18-15, 06:07 PM | #3 |
Difficulties Numbing
|
Just open it as a binary file in python (or your language of choice).
There are a few parts with the name of your submarine-type (e.g. SSTypeVIIB) - you'll see it with a hex editor. Offset from the first one, at 330 bytes after the end of the submarine-type, is a pair of words with the year (e.g. \x07\x94 which is 1940: see http://coolconversion.com/math/binar...n_hexadecimal_) After that comes the month, day, hour and minute (although the latter can be off by one if the minute has just changed). |
08-18-15, 06:13 PM | #4 |
Difficulties Numbing
|
I suspect that there is lat-long, or distance from 0 and equator somewhere in there too, but I don't need that yet (I have a distance to latlong converter, but not a distance / latlong to grid converter - would be handy for finding out the 6-figure grid reference as the logs only seem to hold 4-figures, such as AM76)
|
08-18-15, 06:19 PM | #5 | |
Sonar Guy
Join Date: Oct 2013
Posts: 377
Downloads: 64
Uploads: 0
|
Quote:
LGN1's Tracking Room mod has a perl function in it that converts to or from the grid system using the meters from equator system that the game uses. |
|
08-18-15, 06:23 PM | #6 |
Difficulties Numbing
|
|
08-18-15, 06:32 PM | #7 | |
Sonar Guy
Join Date: Oct 2013
Posts: 377
Downloads: 64
Uploads: 0
|
Quote:
It takes two params I think. Let me look.... I guess it doesn't take any params, as it is using a global variable called "position_grid" But the function is called "pos_lat_long" and its the last function in his script. You could add parameters to it easily. Info here: http://stackoverflow.com/questions/5...meters-in-perl |
|
08-18-15, 06:33 PM | #8 |
Difficulties Numbing
|
Many thanks - once I work out what is in the ISF regarding latlong...
|
08-19-15, 02:02 PM | #9 |
Difficulties Numbing
|
OK, here is the python3 code for reading the date and time of a save from the relevant ISF file. Explanation in next post. Python3 is available (free) at: https://www.python.org/downloads/
Code:
#! /usr/bin/python3 # -*- coding: utf-8 -*- import sys if sys.version_info < (3,4,0): sys.stderr.write("You need python 3.4.0 or later to run this script\n") exit(1) import os class ISFFile: def __init__(self): # look_for needs to come from the relevant psc file... # MAKE SURE IT IS PASSED AS BYTES self.look_for = b"SSTypeVIIB" self.year_offset = -461 self.extract = None self.year = 0 self.month = 0 self.day = 0 self.hour = None self.minute = None self.file_name = None def dump(self): if self.extract: print(self.file_name, self.year, self.month, self.day, self.hour, self.minute) else: print("No valid result") def extract_bytes(self, file_name): if type(self.look_for) == bytes: with open(file_name, 'rb') as f: all_data = f.read() if self.look_for in all_data: # find the submarine's type-name... start = all_data.index(self.look_for) # ...and snip to just the data that follows first_part = all_data[start+len(self.look_for):] # Now we search for a recurrence of the type-name text... if self.look_for in first_part: end = first_part.index(self.look_for) # Now get just the part that we need to process snip = first_part[:end] # The data we want is 461 bytes (self.year_offset which is negative) BACK from the end... self.extract = snip[self.year_offset:] self.file_name = file_name else: #print("Extract does not contain the second occurence of", self.look_for) self.extract = None else: #print("File does not contain", self.look_for) self.extract = None else: #print("self.look_for must be bytes!") self.extract = None def extract_date_and_time(self): if self.extract: # year starts at self.year_offset and is of a format, for example, like: # \x07\x94\x00\x00\x00\x01\x00\x00\x00\x15\ # 0794 is hex for 1940, s = self.extract[self.year_offset:] year_hex = s[0:2] month_hex = s[4:6] day_hex = s[8:10] self.year = int.from_bytes(year_hex, byteorder='big') self.month = int.from_bytes(month_hex, byteorder='big') self.day = int.from_bytes(day_hex, byteorder='big') # Time field appears to follow hour_hex = s[12:14] min_hex = s[16:18] self.hour = int.from_bytes(hour_hex, byteorder='big') self.minute = int.from_bytes(min_hex, byteorder='big') Last edited by ExFishermanBob; 08-21-15 at 09:57 AM. |
08-19-15, 02:11 PM | #10 |
Difficulties Numbing
|
The ISF file has a section starting with the submarine's type-name (e.g. SSTypeVIIB) and ending with the same.
So, it looks a bit like this (but bigger): ................ .............SST ypeVIIB......... ................ ....007400110004 00200043........ ................ .....SSTypeVIIB. ................ ................ extract_bytes() looks for the first occurrance of the type-name (SSTypeVIIB), and gets all the bytes following, ......... ................ ....007400110004 00200043........ ................ .....SSTypeVIIB. ................ ................ then looks in what it has got and removes the second type-name (SSTypeVIIB), leaving a 'snip' of the required bytes. ......... ................ ....007400110004 00200043........ ................ ..... The year, month, date, hour and minute start at 461 bytes back from the end of the 'snip', so that bit is put into self.extract. 007400110004 00200043 extract_date_and_time() uses the values in self.extract and computes the integer values from the bytes using big-endian conversion. To use it:- Code:
isf = ISFFile() isf.look_for = b"SSTypeVIIB" isf.extract_bytes(r"some_file_path\some_filename.isf") isf.extract_date_and_time() NOTES: The look_for value must be bytes (note the leading b) In Windows, you need to make sure the path is 'raw' or reverse the slashes because the career saves are stored in numbered subfolders: '0', which, of course, with a slash becomes '\0', the 'NUL' character, and '9' which is a tab might cause problems. The code / algorithm should be easily convertable into your favourite programming language. Last edited by ExFishermanBob; 08-21-15 at 09:55 AM. Reason: Notes added |
08-19-15, 03:01 PM | #11 | |
Sonar Guy
Join Date: Oct 2013
Posts: 377
Downloads: 64
Uploads: 0
|
Quote:
I thought career saves are put in the "CareerName"\"Patrol number minus 1" folder? So the 0 sub folder is actually the 1st patrol of the career, 5 subfolder is 6 patrol, etc... I'm excited about the script! |
|
08-21-15, 09:53 AM | #12 |
Difficulties Numbing
|
Yes, I should have stated that the careers are saved in a numbered folder, so '0' will cause problems, as will (probably) 9 (tab) and possibly 7 (bell).
|
08-21-15, 02:23 PM | #13 | |
Sonar Guy
Join Date: Oct 2013
Posts: 377
Downloads: 64
Uploads: 0
|
Quote:
This is an example: "C:\Users\John\Documents\SH3\data\cfg\Careers\Test \5" The above career is called "Test" and the Patrol number is 6. As far as I know you can only save in a career when you are on patrol. If you are not on patrol and in the office you cannot save the game. The game just keeps track of when you are out on patrol or not. The .isf file is in its corresponding patrol number folder. It keeps track when you are on patrol in the Patrols_0.cfg file. This is the file with patrol that hasn't finished. Patrols.cfg holds patrols that have finished only. |
|
08-21-15, 03:49 PM | #14 |
Difficulties Numbing
|
I think you have missed my point. I know all about the saving and so on - what I was pointing out was, that in python, running under Windows a string with only single backslashes for path separators will, for certain values (particularly zero), be evaluated by python as an escape character when passed to file-handling routines.
Thus: "C:\somewhere\anywhere\but\this\0" will be interpreted as "C:\somewhere\anywhere\but<tab>NUL" and an error will occur. \t is tab, \0 is null. You must, therefore, in Windows (when programming in Python): use double backslashes (\\0) or forward slashes (/) or use a (so-called) "raw" string (prefixed by r: r"C:\etc") or decompose and recompose the path using the relevant python functions. The original was easier to read. So yes, I'm talking about the sub-directory of the "Career/name/" - which, for the first patrol, will be zero, for which see above IF you are using python in Windows and need to specify a path in a string. It catches me every single time, hence my note to others who might either encounter it for the first time, or, like me, continually forget. For the full horror: http://www.cisco.com/c/en/us/td/docs...n_r/frf019.pdf |
08-22-15, 03:54 AM | #15 | |
Sonar Guy
Join Date: Oct 2013
Posts: 377
Downloads: 64
Uploads: 0
|
Quote:
|
|
|
|