Skip to content
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

Implement direct.h functions #636

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/xboxrt/Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
XBOXRT_SRCS := \
$(NXDK_DIR)/lib/xboxrt/libc_extensions/stat.c \
$(NXDK_DIR)/lib/xboxrt/libc_extensions/direct.c \
$(NXDK_DIR)/lib/xboxrt/libc_extensions/strings.c \
$(NXDK_DIR)/lib/xboxrt/libc_extensions/wchar.c \
$(NXDK_DIR)/lib/xboxrt/libc_extensions/wchar_ext_.c \
Expand Down
110 changes: 110 additions & 0 deletions lib/xboxrt/libc_extensions/direct.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// SPDX-License-Identifier: MIT

// SPDX-FileCopyrightText: 2023 ExoSkye

// Part of Microsoft CRT

#include <errno.h>
#include <string.h>
#include <stdbool.h>
#include <windows.h>
#include <assert.h>
#include <hal/debug.h>

// Made referencing https://www.digitalmars.com/rtl/direct.html, retrieved 2023-06-10, copyrighted 1999-2018 by Digital Mars

// Only call this function in the case there actually is an error
static int convert_error(DWORD winerror)
{
switch (winerror) {
case ERROR_FILE_NOT_FOUND:
case ERROR_PATH_NOT_FOUND:
case ERROR_FILENAME_EXCED_RANGE:
case ERROR_BAD_PATHNAME:
return ENOENT;
break;
case ERROR_ALREADY_EXISTS:
case ERROR_FILE_EXISTS:
return EEXIST;
break;
case ERROR_NOT_LOCKED:
case ERROR_ACCESS_DENIED:
return EACCES;
break;
case ERROR_INVALID_PARAMETER:
case ERROR_INVALID_FUNCTION:
return EINVAL;
break;
case ERROR_NOT_ENOUGH_MEMORY:
return ENOMEM;
break;
case ERROR_DIR_NOT_EMPTY:
return ENOTEMPTY;
break;
default:
debugPrint("WARNING: Unknown win32 error code 0x%08lx, returning EINVAL", winerror);
return EINVAL;
}
}

/*
* TODO: Make a working directory system?
*/

int _chdir(char* path) {
Ryzee119 marked this conversation as resolved.
Show resolved Hide resolved
assert(0);
return -1; // Unreachable
Copy link
Member

@JayFoxRox JayFoxRox Jun 18, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is reachable when disabling assertions.
In that case, errno must be set, so I suggest setting it to EACCES?

(Other functions, too - but you need to look up appropriate error codes)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah crap, forgot you could do that, I'll sort it out

}

int _chdrive(int drive) {
assert(0);
return -1; // Unreachable
}

char* _getcwd(char* buffer, size_t length) {
assert(0);
return NULL; // Unreachable
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe we can hard-code this for now, so at least apps which use this to find files will work (sometimes some SDK or API will require absolute paths, and then the application might query the base path through this).

I can see how that would be a lot of additional implementation effort, so it can be done in a follow-up PR.


If this follows the same convention, we might be able to use:

nxdk/lib/nxdk/path.c

Lines 9 to 14 in 500354e

void nxGetCurrentXbeNtPath (char *path)
{
// Retrieve the XBE path
strncpy(path, XeImageFileName->Buffer, XeImageFileName->Length);
path[XeImageFileName->Length] = '\0';
}

Otherwise, we might be able to hardcode it to D:\\ which is either (I don't remember) a kernel requirement or at least a dashboard- or self-imposed XBE controlled symlink?

Eitherway, we'd have to follow conventions of the MS implementation (regarding forward vs backward slash, device names vs driver letters etc.).

Similar reasoning for _getdrive, although I speculate that this is required much less.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

D drive letter mount is handled by XDK on boot when D drive letter isn't set by the kernel. Otherwise, it is handled by the kernel on reboot.

Though, I highly recommended to check LaunchDataPage->Header.szLaunchPath first for semicolon character which is mount as working directory by the kernel itself. Then fallback to XeImageFileName with null terminate append at end of string.

fyi: I provided this type of information in XboxDev discord server based on what I had researched years ago.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer XeImageFileName (which the kernel uses to talk to the app / after launching) over LaunchDataPage (which is used by the app to talk to the kernel / before launching)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, LaunchDataPage is used to talk to the kernel but that's only half correct. It is also used by the app as well hence the LaunchData member variable from LAUNCH_DATA_PAGE structure is used to tell the app, either from same xbe or another xbe, what to do next after reboot occur. The kernel doesn't modify LaunchDataPage created by the app. The kernel read from LaunchDataPage->Header structure to do what the app want the kernel to do on reboot.

Here's a log of how it works on first boot and reboot:

---- first boot ----
LaunchDataPage is NULL.
XeImageFileName: \Device\Harddisk0\Partition1\Games\reboot\default.xbe
\??\D: == \Device\Harddisk0\Partition1\Games\reboot
Relaunching... LaunchDataPage->Header.szLaunchPath: \Device\Harddisk0\Partition1\Games;reboot\default.xbe
============
------ reboot ------
LaunchDataPage->Header.szLaunchPath: \Device\Harddisk0\Partition1\Games;reboot\default.xbe
XeImageFileName: \Device\Harddisk0\Partition1\Games\reboot\default.xbe
\??\D: == \Device\Harddisk0\Partition1\Games
============

As you can see XeImageFileName doesn't include semicolon which break working directory mount from D: symlink pointing to a parent directory than the same directory according to reboot process.
This is the same/similar concept on Windows' command prompt or powershell usage. When you change directory from the terminal itself, i.e. C:\logs> . Then execute D:\build\UnitTest.exe which will create log file into C:\logs instead of D:\build directory.

In summary, this is how demo discs and even xbox live works when they focus on D:\ symlink as a working directory. Otherwise attempting to access the contents wouldn't work engineered by original xbox team. I hope this is more clarified for you and everyone else reading this.

}

char* _getwd(char* path_name) {
assert(0);
return NULL; // Unreachable
Copy link
Member

@JayFoxRox JayFoxRox Jun 18, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The macOS man page says:

The function getwd() is a compatibility routine which calls getcwd() with its buf argument and a size of MAXPATHLEN (as defined in the include file <sys/param.h>). Obviously, buf should be at least MAXPATHLEN bytes in length.

Regarding return values:

In addition, getwd() copies the error message associated with errno into the memory referenced by buf.

Similar wording is in POSIX https://pubs.opengroup.org/onlinepubs/009696699/functions/getwd.html

which should be trivial to implement.

I can't find anything about it in the MSDN docs though. Is it contained in the Windows SDK / MS implementations?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, yea, Wikipedia, and digitalmars state that getwd exists so I'm going on that
Should be trivial to implement though

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't find anything about it in the MSDN docs though. Is it contained in the Windows SDK / MS implementations?

I don't see it in https://github.com/microsoft/win32metadata/blob/main/generation/WinSDK/RecompiledIdlHeaders/ucrt/direct.h either.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like getwd is a legacy function which was discontinued. The question is when did it get removed? As for digitalmars state it exists, I don't see getwd mentioned in the referenced link from the OP post. Heck, even did a search for whole site which also has no mention too.

}

int _getdrive(void) {
assert(0);
return -1; // Unreachable
}

/*
* Below are the only things that can work
*/

int _mkdir(const char* pathname) {
BOOL result = CreateDirectoryA(
pathname,
NULL
);

if (result == true) {
return 0;
} else {
errno = convert_error(GetLastError());

return -1;
}
}

int _rmdir(const char* pathname) {
Ryzee119 marked this conversation as resolved.
Show resolved Hide resolved
BOOL result = RemoveDirectoryA(
pathname
);

if (result == true) {
return 0;
} else {
errno = convert_error(GetLastError());

return -1;
}
}
8 changes: 8 additions & 0 deletions lib/xboxrt/libc_extensions/direct.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,11 @@
// SPDX-FileCopyrightText: 2020 Jannik Vogel

// Part of Microsoft CRT

int _chdir(char* path);
int _chdrive(int drive);
char* _getcwd(char* buffer, size_t length);
char* _getwd(char* path_name);
int _getdrive(void);
int _mkdir(const char* pathname);
int _rmdir(const char* pathname);