-
Notifications
You must be signed in to change notification settings - Fork 550
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Alternate Steam Libraries #872
base: master
Are you sure you want to change the base?
Conversation
src/d_iwad.c
Outdated
|
||
static void CheckSteamLibrary(long library_file_size, char *library_file_buffer) | ||
{ | ||
long i, start; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please don't use type long
, since it means very different things in different compilers. Please remove all uses of long
.
src/d_iwad.c
Outdated
{ | ||
long i, start; | ||
boolean key, key_valid, value; | ||
char* buffer; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
char *buffer;
src/d_iwad.c
Outdated
else if (start > -1 && key) | ||
{ | ||
//Keys for libraries are always numeric | ||
if (library_file_buffer[i] < '0' || library_file_buffer[i] > '9') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use isdigit()
.
src/d_iwad.c
Outdated
|
||
if (probe != NULL) | ||
{ | ||
library_file = fopen(probe, "rb"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please factor this whole block out into a separate function.
src/d_iwad.c
Outdated
|
||
for (i=0;i<library_file_size; ++i) | ||
{ | ||
if (library_file_buffer[i] == '"') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The logic in this function is very complicated and hard to follow. It looks like you're trying to do tokenization and higher-level logic in a single function. I think you can probably simplify this greatly by splitting out these into separate concerns.
The main change here is restructuring based on the feedback. Then
Instances of |
I'm going to use the reference from Valve to make some changes to how I parse the Is it worth moving this logic to a new file? (eg |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry that I haven't responded to this sooner.
|
||
if (library_file_size != count) | ||
{ | ||
return NULL; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
free(library_file_buffer);
src/d_iwad.c
Outdated
if (buffer[i] == '"') | ||
{ | ||
//first " is the start of the token | ||
if (token == NULL) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It becomes simpler if you just do two loops: one to find the starting ", another to find the ending ".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, this has helped.
static char *steam_libraries[MAX_STEAM_LIBRARIES]; | ||
static int num_steam_libraries = 0; | ||
|
||
static void AddSteamLibraryDir(char *dir) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you need the steam_libraries[]
array? Can't you just make this a wrapper around AddIWADDir?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that array is not needed.
src/d_iwad.c
Outdated
return NULL; | ||
} | ||
|
||
//file always starts with this line before key/value pairs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So in that case, just put a call before the start of the loop in CheckSteamLibraries()
that calls GetNextSteamLibToken()
and checks it's equal to "LibraryFolders". Then this function should become much simpler.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've changed how I parse the file. I treat the whole VDF file as key value pairs, and I don't bother checking that it starts with "LibraryFolders". The code is a lot simpler now.
I've addressed the reviews, and also made the tokenizer understand VDF more completely. The tokenizer now handles tokens that are not always quoted, as well as ones with escaped characters. To make the parsing simpler I've avoided the issue of subkeys completely. I ignore closing braces and treat the opening ones as part of a regular unquoted token. So
Get tokenized to
Every odd token is a key, every even token is a value. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi,
I hope my feedback here isn't too disheartening, since I have a bunch of comments. I want you to understand that I hold the Chocolate Doom codebase to a very high standard, and I want to work with you to get this into a satisfactory state, because I do really want to get this feature merged.
Every odd token is a key, every even token is a value.
This seems like a strange approach to me, and not how one would usually structure a parser. Doing this essentially ignores the syntactic structure of the file, which is a recipe for fragile code that will potentially break in unexpected ways. For example, if the parser were to read something that begins "ArgleBargle" "ASDFASDF"
instead of "LibraryFolders" {
you'd expect parsing fail, but in the current approach it is silently accepted. Similarly the LibraryFolders block is supposed to end in a } but the current parser doesn't care if it ends in one, none, or a hundred of them.
If you'd like some philosophical background I'd suggest reading the chapter Programming by Coincidence from the book The Pragmatic Programmer, which is a great read in general.
//Read whole of libraryfolders.vdf as string | ||
//silently fail is something goes wrong reading the file | ||
|
||
static char *GetSteamLibText(char *library_path) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you just use M_ReadFile()
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I've got it working with M_ReadFile()
.
The first difference I see between M_ReadFile()
and my code is that I'm adding the \0
to the end of the buffer. I assume I did that to ensure strlen()
is guaranteed to work later. Not sure if that was ever necessary.
The other difference is that I'm using malloc()
, where M_ReadFile()
uses Z_Malloc()
.
There are a few places where malloc()
is used over Z_Malloc()
, are these intentional or should Z_Malloc()
always be used?
{ | ||
if (isspace(c) || c == '}') | ||
{ | ||
success = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just:
return NULL;
Then you can eliminate the success
variable and the if (success)
check below.
{ | ||
c = buffer[i]; | ||
|
||
//{ and } are control characters in VDF used to indicate subkeys. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A tokenizer with baked-in knowledge of syntax is probably a bad design. The usual approach would be to define an enum representing the different token types, then let the higher level parsing take care of things.
continue; | ||
} | ||
|
||
quote = c == '"'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is essentially a loop to skip past leading whitespace. Why is this part of the loop body?
break; | ||
} | ||
|
||
for (; i<buff_size; ++i) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This ends up being complicated because you're trying to process different types of things using the same loop. You'd be better off just treating them separately. For example:
c = ... whatever
if (c == '"') {
return ReadStringToken(.....);
} else if (c == '{') {
return TOKEN_OPEN_BRACE;
} else if (c == '}') {
return TOKEN_CLOSE_BRACE;
} else if (isalpha(c)) {
return ReadKeyword(......);
} else {
return TOKEN_UNKNOWN;
}
Thank you I'm not disheartened, I appreciate the feedback and the time you've spent on it. I'm quite happy to improve this code so it better fits the Chocolate Doom codebase. |
@scfinniss any updates? Otherwise I don't think it's good to keep a nearly 3 year-old PR up. |
Real life got between me and finishing this, and I guess I forgot about it after awhile. I'm not sure how much Chocolate Doom has changed since I worked on this, but I don't mind looking into it again if there's still interest in this feature? |
There have always been efforts to improve WAD-locating, even in recent times. I would say it's still worth it, but it just happens to be a fairly sizable undertaking. |
I noticed while working on the Watcom C stuff that Chocolate Doom was not locating my IWADs.
What I've done here is scrape the alternative library locations from Steam's
libraryfolders.vdf
file and use them to add more search paths. The file itself is mostly key value pairs with a header. Here is the file from my setup for reference.I specifically ignore
LibraryFolders
, thereafter I assume every second string is a path, as long as the preceding key is numeric. I make no real effort to understand the structure of the file beyond this.It's important to note that the unlisted
C:\Program Files (x86)\Steam
is also a library. Presumably Steam always considers its install path a library.I believe this may resolve issue #482 (excluding Mac and Linux support).