Skip to content

Commit

Permalink
Code cleanup and refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
LoveMHz committed Aug 15, 2024
1 parent 7c244d9 commit 3de1bd2
Show file tree
Hide file tree
Showing 5 changed files with 228 additions and 155 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ XBE_XTIMAGE = $(CURDIR)/resources/logo.xpr
#
SRCS += \
$(CURDIR)/source/attach.c \
$(CURDIR)/source/helpers.c \
$(CURDIR)/source/resources.packed.obj

CFLAGS += \
Expand Down
293 changes: 141 additions & 152 deletions source/attach.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,199 +2,188 @@
// SPDX-FileCopyrightText: 2023 Dustin Holden <[email protected]>
// SPDX-License-Identifier: GPL-2.0
// Based on attach.c from driveimageutils by rmenhal
#include <xboxkrnl/xboxkrnl.h>
#include <stdio.h>
#include <string.h>
#include <hal/xbox.h>
#include <xboxkrnl/xboxkrnl.h>
#include <windows.h>
#include "attach.h"
#include "helpers.h"

// Device name
ANSI_STRING VirtualDeviceName = RTL_CONSTANT_STRING("\\Device\\CdRom1");

// Virtual disc image slice data
CHAR VirtualFilePath[8][MAX_PATH] = { 0 };

ATTACH_SLICE_DATA AttachSliceData = {
.NumberOfSlices = 0,
.Files = {
{ .Buffer = VirtualFilePath[0], .MaximumLength = sizeof(VirtualFilePath[0]) },
{ .Buffer = VirtualFilePath[1], .MaximumLength = sizeof(VirtualFilePath[1]) },
{ .Buffer = VirtualFilePath[2], .MaximumLength = sizeof(VirtualFilePath[2]) },
{ .Buffer = VirtualFilePath[3], .MaximumLength = sizeof(VirtualFilePath[3]) },
{ .Buffer = VirtualFilePath[4], .MaximumLength = sizeof(VirtualFilePath[4]) },
{ .Buffer = VirtualFilePath[5], .MaximumLength = sizeof(VirtualFilePath[5]) },
{ .Buffer = VirtualFilePath[6], .MaximumLength = sizeof(VirtualFilePath[6]) },
{ .Buffer = VirtualFilePath[7], .MaximumLength = sizeof(VirtualFilePath[7]) },
}
};

#define MAX_PATHNAME 256

int has_iso_extension(unsigned int len, char *str) {
ANSI_STRING tail;
char extension[] = ".iso";
ANSI_STRING ext_str = { sizeof(extension) - 1, sizeof(extension), extension };

if(len < sizeof(extension) - 1)
return FALSE;

tail.Length = sizeof(extension) - 1;
tail.MaximumLength = tail.Length;
tail.Buffer = str + len - sizeof(extension) + 1;

return (RtlCompareString(&tail, &ext_str, TRUE) == 0);
}

int has_cso_extension(unsigned int len, char *str) {
ANSI_STRING tail;
char extension[] = ".cso";
ANSI_STRING ext_str = { sizeof(extension) - 1, sizeof(extension), extension };

if(len < sizeof(extension) - 1)
return FALSE;

tail.Length = sizeof(extension) - 1;
tail.MaximumLength = tail.Length;
tail.Buffer = str + len - sizeof(extension) + 1;

return (RtlCompareString(&tail, &ext_str, TRUE) == 0);
}

int compare_string_tails(PANSI_STRING str1, PANSI_STRING str2, WORD skip) {
ANSI_STRING n1, n2;

if(str1->Length <= skip)
return(str2->Length <= skip) ? 0 : -1;
else if(str2->Length <= skip)
return 1;

n1.Length = str1->Length - skip;
n1.MaximumLength = n1.Length;
n1.Buffer = str1->Buffer + skip;

n2.Length = str2->Length - skip;
n2.MaximumLength = n2.Length;
n2.Buffer = str2->Buffer + skip;
int main(void) __attribute__((optnone)) {
// Make a copy of the XBE launch path.
ANSI_STRING SearchPath = *XeImageFileName;

return RtlCompareString(&n1, &n2, TRUE);
}
// Determine the last backslash in the search path.
PCHAR LastBackslash = StrRChr(&SearchPath, '\\');

int main(void) {
char info_buf[sizeof(FILE_DIRECTORY_INFORMATION) + MAX_PATHNAME];
// Check if the last backslash was found.
if(LastBackslash == NULL)
goto CleanupAndExit;

char path[MAX_PATHNAME] = { 0 };
strncpy(path, XeImageFileName->Buffer,
XeImageFileName->Length < (MAX_PATHNAME - 1) ? XeImageFileName->Length : (MAX_PATHNAME - 1));
// Update the search path to only include the full device path.
SearchPath.Length = (USHORT)(LastBackslash - SearchPath.Buffer + 1);

int pathlen = strrchr(path, '\\') - path + 1;
path[pathlen] = '\0';
// Sanity check the length of the search path.
if(SearchPath.Length < sizeof("\\Device\\") || SearchPath.Length > (MAX_PATH - 4))
goto CleanupAndExit;

ANSI_STRING dir_name;
RtlInitAnsiString(&dir_name, path);

OBJECT_ATTRIBUTES obj_attr = {
.RootDirectory = NULL,
.ObjectName = &dir_name,
// Open the directory.
OBJECT_ATTRIBUTES ObjectAttributes = {
.ObjectName = &SearchPath,
.Attributes = OBJ_CASE_INSENSITIVE,
};

IO_STATUS_BLOCK io_status;
HANDLE h;
NTSTATUS status = NtOpenFile(&h, GENERIC_READ | SYNCHRONIZE, &obj_attr, &io_status,
FILE_SHARE_READ, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);

if(!NT_SUCCESS(status))
HalReturnToFirmware(HalQuickRebootRoutine);
IO_STATUS_BLOCK IoStatusBlock;
HANDLE Handle;

void *membuf = NULL;
unsigned long membuf_size = 1024 * 1024;
NTSTATUS Status = NtOpenFile(&Handle, FILE_LIST_DIRECTORY | SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock,
FILE_SHARE_READ, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);

status = NtAllocateVirtualMemory(&membuf, 0, &membuf_size, MEM_COMMIT | MEM_NOZERO, PAGE_READWRITE);
if(!NT_SUCCESS(Status))
goto CleanupAndExit;

if(!NT_SUCCESS(status))
HalReturnToFirmware(HalQuickRebootRoutine);
// Query the directory for the first file.
// TODO: Can we use the FileName mask here to filter out only the files we're interested in?
FileInfo FileInformation = { 0 };
Status = NtQueryDirectoryFile(Handle, NULL, NULL, NULL, &IoStatusBlock, &FileInformation, sizeof(FileInformation),
FileDirectoryInformation, NULL, TRUE);

ATTACH_SLICE_DATA *asd = (ATTACH_SLICE_DATA *)membuf;
asd->num_slices = 0;
// Loop through all files in the directory.
while(TRUE) {
// Check if we've reached the end of the directory.
if(Status == STATUS_NO_MORE_FILES)
break;

char *strbuf = (char *)membuf + sizeof(ATTACH_SLICE_DATA);
// Check if an error occurred.
if(!NT_SUCCESS(Status)) {
NtClose(Handle);
goto CleanupAndExit;
}

PFILE_DIRECTORY_INFORMATION dir_info;
BOOLEAN first = TRUE;
for(;;) {
if(first || dir_info->NextEntryOffset == 0) {
status = NtQueryDirectoryFile(h, NULL, NULL, NULL, &io_status,
info_buf, sizeof(info_buf), FileDirectoryInformation, NULL, first);
// Skip directories.
if(FileInformation.DirectoryInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
goto QueryNextFile;

if(status == STATUS_NO_MORE_FILES)
break;

first = FALSE;
dir_info = (PFILE_DIRECTORY_INFORMATION)info_buf;
} else
dir_info = (PFILE_DIRECTORY_INFORMATION)((char *)dir_info + dir_info->NextEntryOffset);
// Null-terminate the file name.
ANSI_STRING FileName = {
.Length = FileInformation.DirectoryInfo.FileNameLength,
.MaximumLength = FileInformation.DirectoryInfo.FileNameLength,
.Buffer = FileInformation.DirectoryInfo.FileName,
};

if(!has_iso_extension(dir_info->FileNameLength, dir_info->FileName) &&
!has_cso_extension(dir_info->FileNameLength, dir_info->FileName))
continue;
// Check if the file is a supported virtual disc image.
if(!IsFileDiscImage(&FileName))
goto QueryNextFile;

ANSI_STRING new_file = {
.Length = pathlen + dir_info->FileNameLength,
.MaximumLength = new_file.Length,
.Buffer = strbuf
// Build the slice path string by concatenating the search path and the file name.
ANSI_STRING SlicePath = {
.Length = 0,
.MaximumLength = sizeof(VirtualFilePath[0]),
.Buffer = VirtualFilePath[AttachSliceData.NumberOfSlices]
};

memcpy(strbuf, path, pathlen);
memcpy(strbuf + pathlen, dir_info->FileName, dir_info->FileNameLength);
strbuf += new_file.Length;
// NOTE: We've already checked that the search path string is < MAX_PATH, so no need to check it here.
RtlCopyString(&SlicePath, &SearchPath);

int i;
for(i = 0; i < asd->num_slices; i++) {
if(compare_string_tails(&new_file, &asd->slice_files[i], pathlen) < 0)
break;
Status = RtlAppendStringToString(&SlicePath, &FileName);
if(!NT_SUCCESS(Status)) {
NtClose(Handle);
goto CleanupAndExit;
}

RtlMoveMemory(&asd->slice_files[i] + 1, &asd->slice_files[i], (asd->num_slices - i) * sizeof(ANSI_STRING));
// Determine the slice index so the slices are stored in alphabetical order.
DWORD Index;
for(Index = 0; Index < AttachSliceData.NumberOfSlices; Index++) {
if(CompareStringTails(&SlicePath, &AttachSliceData.Files[Index], SearchPath.Length) < 0)
break;
}

asd->slice_files[i] = new_file;
// Move the slice data so the new slice can be inserted at the correct position.
RtlMoveMemory(&AttachSliceData.Files[Index] + 1, &AttachSliceData.Files[Index],
(AttachSliceData.NumberOfSlices - Index) * sizeof(ANSI_STRING));

if(++asd->num_slices >= MAX_IMAGE_SLICES)
break;
}
// Store the slice data.
AttachSliceData.Files[Index] = SlicePath;

NtClose(h);
// Increment the number of slices.
AttachSliceData.NumberOfSlices++;

ANSI_STRING dev_name;
RtlInitAnsiString(&dev_name, "\\Device\\CdRom1");
// Check if we've reached the maximum number of slices.
if(AttachSliceData.NumberOfSlices >= MAX_IMAGE_SLICES)
break;

obj_attr.RootDirectory = NULL;
obj_attr.ObjectName = &dev_name;
obj_attr.Attributes = OBJ_CASE_INSENSITIVE;
QueryNextFile:
RtlZeroMemory(&FileInformation, sizeof(FileInformation));
Status = NtQueryDirectoryFile(Handle, NULL, NULL, NULL, &IoStatusBlock, &FileInformation,
sizeof(FileInformation), FileDirectoryInformation, NULL, FALSE);
}

status = NtOpenFile(&h, GENERIC_READ | SYNCHRONIZE, &obj_attr, &io_status,
FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT);
// Close the directory handle.
NtClose(Handle);

if(!NT_SUCCESS(status))
HalReturnToFirmware(HalQuickRebootRoutine);
// Open the virtual device.
OBJECT_ATTRIBUTES DeviceObjectAttributes = {
.ObjectName = &VirtualDeviceName,
.Attributes = OBJ_CASE_INSENSITIVE,
};

status = NtDeviceIoControlFile(h, NULL, NULL, NULL, &io_status, IOCTL_VIRTUAL_CDROM_DETACH, NULL, 0, NULL, 0);
Status = NtOpenFile(&Handle, GENERIC_READ | SYNCHRONIZE, &DeviceObjectAttributes, &IoStatusBlock, FILE_SHARE_READ,
FILE_SYNCHRONOUS_IO_NONALERT);

/* Note that opening the handle will also mount a file system. All access
* via the handle will go through the file system driver. Ideally we should
* first dismount any possibly mounted file system and then detach the virtual
* disc. It would be nice if we could open a direct access to the device driver,
* but this doesn't seem to be possible with the Xbox kernel API (opening with
* limited access rights doesn't yield the result.)
*
* We will just close the handle and dismount the automatically mounted file
* system.
*/
NtClose(h);
status = IoDismountVolumeByName(&dev_name);
if(!NT_SUCCESS(Status))
goto CleanupAndExit;

status = NtOpenFile(&h, GENERIC_READ | SYNCHRONIZE, &obj_attr, &io_status,
FILE_SHARE_READ,
FILE_SYNCHRONOUS_IO_NONALERT);
Status = NtDeviceIoControlFile(Handle, NULL, NULL, NULL, &IoStatusBlock, IOCTL_VIRTUAL_CDROM_DETACH, NULL, 0, NULL, 0);

if(!NT_SUCCESS(status))
HalReturnToFirmware(HalQuickRebootRoutine);
// Note that opening the handle will also mount a file system. All access via the handle will go through the file
// system driver. Ideally we should first dismount any possibly mounted file system and then detach the virtual
// disc. It would be nice if we could open a direct access to the device driver, but this doesn't seem to be possible
// with the Xbox kernel API (opening with limited access rights doesn't yield the result.)
//
// We will just close the handle and dismount the automatically mounted file system.
NtClose(Handle);
Status = IoDismountVolumeByName(&VirtualDeviceName);

status = NtDeviceIoControlFile(h, NULL, NULL, NULL, &io_status,
IOCTL_VIRTUAL_CDROM_ATTACH,
asd, sizeof(ATTACH_SLICE_DATA), NULL, 0);
Status = NtOpenFile(&Handle, GENERIC_READ | SYNCHRONIZE, &DeviceObjectAttributes, &IoStatusBlock, FILE_SHARE_READ,
FILE_SYNCHRONOUS_IO_NONALERT);

NtClose(h);
if(!NT_SUCCESS(Status)) {
NtClose(Handle);
goto CleanupAndExit;
}

/* Also note that access via our second file handle goes through the raw
* file system (there's no other choice since the virtual disc was detached).
* It's still mounted at this point. If we now want to access the attached
* image, we will first need to dismount that file system.
*
* It doesn't matter for this attach.xbe, but do it anyway so we won't forget
* about it in any future application.
*/
status = IoDismountVolumeByName(&dev_name);
Status = NtDeviceIoControlFile(Handle, NULL, NULL, NULL, &IoStatusBlock, IOCTL_VIRTUAL_CDROM_ATTACH,
(PVOID)&AttachSliceData, sizeof(AttachSliceData), NULL, 0);
NtClose(Handle);

HalReturnToFirmware(HalQuickRebootRoutine);
// Also note that access via our second file handle goes through the raw file system (there's no other choice since
// the virtual disc was detached).
// It's still mounted at this point. If we now want to access the attached image, we will first need to dismount
// that file system.
// It doesn't matter for this attach.xbe, but do it anyway so we won't forget about it in any future application.
Status = IoDismountVolumeByName(&VirtualDeviceName);

CleanupAndExit:
return 0;
}
11 changes: 8 additions & 3 deletions source/attach.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
// SPDX-License-Identifier: GPL-2.0
// Based on virtualcdrom.h from driveimageutils by rmenhal
#pragma once

#include <xboxkrnl/xboxkrnl.h>
#include <windows.h>
#include <stdint.h>

#define IOCTL_VIRTUAL_CDROM_ID 0x1EE7CD00
Expand All @@ -14,6 +14,11 @@
#define MAX_IMAGE_SLICES 8

typedef struct _ATTACH_SLICE_DATA {
uint32_t num_slices;
ANSI_STRING slice_files[MAX_IMAGE_SLICES];
DWORD NumberOfSlices;
ANSI_STRING Files[MAX_IMAGE_SLICES];
} ATTACH_SLICE_DATA;

typedef struct _FileInfo {
FILE_DIRECTORY_INFORMATION DirectoryInfo;
CHAR Filename[MAX_PATH];
} FileInfo;
Loading

0 comments on commit 3de1bd2

Please sign in to comment.