diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f372971 --- /dev/null +++ b/Makefile @@ -0,0 +1,125 @@ +#By default, the full HDDChecker tool will be compiled when "make" is issued without arguments. +#Enter "make fsck-tool" to build the standalone FSCK tool. +FSCK ?= 0 + +LOG_MESSAGES ?= 1 + +EE_HDDCHECKER_BIN = HDDChecker.elf +EE_FSCK_BIN = fsck.elf +EE_IOP_OBJS = SIO2MAN_irx.o PADMAN_irx.o DEV9_irx.o ATAD_irx.o IOMANX_irx.o FILEXIO_irx.o HDD_irx.o PFS_irx.o FSCK_irx.o +EE_RES_OBJS = background.o buttons.o +EE_OBJS = main.o iop.o pad.o UI.o menu.o system.o graphics.o font.o $(EE_IOP_OBJS) $(EE_RES_OBJS) + +EE_INCS := -I$(PS2SDK)/ports/include -I$(PS2SDK)/ee/include -I$(PS2SDK)/common/include -I. +EE_LDFLAGS := -L$(PS2SDK)/ports/lib -L$(PS2SDK)/ee/lib -s +EE_LIBS := -lpng -lz -lm -lfreetype -lhdd -lpatches -lfileXio -lpadx -lgs -lc -lkernel +EE_GPVAL = -G8192 +EE_CFLAGS += -mgpopt $(EE_GPVAL) + +ifeq ($(FSCK),1) + LOG_MESSAGES = 0 + EE_CFLAGS += -DFSCK=1 + EE_BIN = $(EE_FSCK_BIN) +else + EE_IOP_OBJS += MCMAN_irx.o USBD_irx.o USBHDFSD_irx.o HDST_irx.o HDCK_irx.o HDSK_irx.o FSSK_irx.o + EE_OBJS += hdst.o + EE_BIN = $(EE_HDDCHECKER_BIN) +endif + +ifeq ($(LOG_MESSAGES),1) + EE_CFLAGS += -DLOG_MESSAGES=1 + EE_IOP_OBJS += HDLOG_irx.o +endif + +all: + $(MAKE) $(EE_BIN) + +fsck-tool: + $(MAKE) FSCK=1 all + +%.o : %.c + $(EE_CC) $(EE_CFLAGS) $(EE_INCS) -c $< -o $@ + +%.o : %.s + $(EE_AS) $(EE_ASFLAGS) $< -o $@ + +%.o : %.S + $(EE_CC) $(EE_CFLAGS) $(EE_INCS) -c $< -o $@ + +$(EE_BIN) : $(EE_OBJS) $(PS2SDK)/ee/startup/crt0.o + $(EE_CC) -nostartfiles -Tlinkfile $(EE_LDFLAGS) \ + -o $(EE_BIN) $(PS2SDK)/ee/startup/crt0.o $(EE_OBJS) $(EE_LIBS) + +clean: + rm -f $(EE_BIN) $(EE_OBJS) $(EE_HDDCHECKER_BIN) $(EE_FSCK_BIN) *_irx.c background.c buttons.c hdst.o + make -C hdst clean + make -C hdck clean + make -C fsck clean + make -C hdsk clean + make -C fssk clean + make -C hdlog clean + +background.c: + bin2c resources/background.png background.c background + +buttons.c: + bin2c resources/buttons.png buttons.c buttons + +SIO2MAN_irx.c: $(PS2SDK)/iop/irx/freesio2.irx + bin2c $(PS2SDK)/iop/irx/freesio2.irx SIO2MAN_irx.c SIO2MAN_irx + +MCMAN_irx.c: $(PS2SDK)/iop/irx/mcman.irx + bin2c $(PS2SDK)/iop/irx/mcman.irx MCMAN_irx.c MCMAN_irx + +PADMAN_irx.c: $(PS2SDK)/iop/irx/freepad.irx + bin2c $(PS2SDK)/iop/irx/freepad.irx PADMAN_irx.c PADMAN_irx + +USBD_irx.c: $(PS2SDK)/iop/irx/usbd.irx + bin2c $(PS2SDK)/iop/irx/usbd.irx USBD_irx.c USBD_irx + +USBHDFSD_irx.c: $(PS2SDK)/iop/irx/usbhdfsd.irx + bin2c $(PS2SDK)/iop/irx/usbhdfsd.irx USBHDFSD_irx.c USBHDFSD_irx + +IOMANX_irx.c: $(PS2SDK)/iop/irx/iomanX.irx + bin2c $(PS2SDK)/iop/irx/iomanX.irx IOMANX_irx.c IOMANX_irx + +FILEXIO_irx.c: $(PS2SDK)/iop/irx/fileXio.irx + bin2c $(PS2SDK)/iop/irx/fileXio.irx FILEXIO_irx.c FILEXIO_irx + +DEV9_irx.c: $(PS2SDK)/iop/irx/ps2dev9.irx + bin2c $(PS2SDK)/iop/irx/ps2dev9.irx DEV9_irx.c DEV9_irx + +ATAD_irx.c: $(PS2SDK)/iop/irx/ps2atad.irx + bin2c $(PS2SDK)/iop/irx/ps2atad.irx ATAD_irx.c ATAD_irx + +HDD_irx.c: $(PS2SDK)/iop/irx/ps2hdd-osd.irx + bin2c $(PS2SDK)/iop/irx/ps2hdd-osd.irx HDD_irx.c HDD_irx + +PFS_irx.c: $(PS2SDK)/iop/irx/ps2fs.irx + bin2c $(PS2SDK)/iop/irx/ps2fs.irx PFS_irx.c PFS_irx + +HDST_irx.c: + make -C hdst + bin2c hdst/hdst.irx HDST_irx.c HDST_irx + +HDCK_irx.c: + make -C hdck + bin2c hdck/hdck.irx HDCK_irx.c HDCK_irx + +FSCK_irx.c: + make -C fsck + bin2c fsck/fsck.irx FSCK_irx.c FSCK_irx + +HDSK_irx.c: + make -C hdsk + bin2c hdsk/hdsk.irx HDSK_irx.c HDSK_irx + +FSSK_irx.c: + make -C fssk + bin2c fssk/fssk.irx FSSK_irx.c FSSK_irx + +HDLOG_irx.c: + make -C hdlog + bin2c hdlog/hdlog.irx HDLOG_irx.c HDLOG_irx + +include $(PS2SDK)/Defs.make diff --git a/UI.c b/UI.c new file mode 100644 index 0000000..a047a87 --- /dev/null +++ b/UI.c @@ -0,0 +1,1495 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "main.h" +#include "system.h" +#include "pad.h" +#include "graphics.h" +#include "font.h" +#include "UI.h" + +extern int errno __attribute__((section("data"))); + +struct UIDrawGlobal UIDrawGlobal; +GS_IMAGE BackgroundTexture; +struct ClutImage PadLayoutTexture; + +static void *gFontBuffer = NULL; +static int gFontBufferSize; +unsigned short int SelectButton, CancelButton; + +#define NUM_SUPPORTED_LANGUAGES 8 + +static int language=LANGUAGE_ENGLISH; + +#include "lang.c" + +static char *LangStringTable[SYS_UI_MSG_COUNT]; +static char *LangLblStringTable[SYS_UI_LBL_COUNT]; +static u8 LangStringWrapTable[(SYS_UI_MSG_COUNT + 7) / 8]; + +static void UnloadLanguage(void); + +static void WaitForDevice(void) +{ + nopdelay(); + nopdelay(); + nopdelay(); + nopdelay(); + nopdelay(); + nopdelay(); + nopdelay(); + nopdelay(); +} + +static int FormatLanguageString(const char *in, int len, char *out) +{ + wchar_t wchar1, wchar2; + int ActualLength, CharLen1, CharLen2; + + ActualLength = 0; + CharLen1 = mbtowc(&wchar1, in, len); + while(CharLen1 > 0 && wchar1 != '\0') + { + CharLen2 = mbtowc(&wchar2, in + CharLen1, len - CharLen1); + if((CharLen2 > 0) && (wchar1 == '\\' && wchar2 != '\0')) + { + switch(wchar2) + { //When a translation file is used, escape characters appear like "\n" in the file. + case 'n': + *out = '\n'; + out++; + ActualLength++; + break; + } + + in += (CharLen1 + CharLen2); + len -= (CharLen1 + CharLen2); + CharLen2 = mbtowc(&wchar2, in, len); + } else { + memcpy(out, in, CharLen1); + out += CharLen1; + ActualLength += CharLen1; + in += CharLen1; + len -= CharLen1; + } + + CharLen1 = CharLen2; + wchar1 = wchar2; + } + *out = '\0'; + + return(ActualLength + 1); +} + +static void BreakLongLanguageString(char *str) +{ + wchar_t wchar; + int CharLen, ScreenLineLenPx, LineMaxPx, PxSinceLastSpace, len, width; + char *LastWhitespaceOut; + + LineMaxPx = UIDrawGlobal.width - 2*UI_OFFSET_X; + + // Line lengths are counted from the last newline character (either discovered or inserted). + ScreenLineLenPx = 0; + PxSinceLastSpace = 0; + len = strlen(str) + 1; + CharLen = mbtowc(&wchar, str, len); + LastWhitespaceOut = NULL; + while(CharLen > 0 && wchar != '\0') + { + switch(wchar) + { + case '\n': + ScreenLineLenPx = 0; + LastWhitespaceOut = NULL; + PxSinceLastSpace = 0; + break; + case ' ': //Record where the latest whitespace is. + LastWhitespaceOut = str; + PxSinceLastSpace = 0; + break; + } + + if (wchar != '\n') + { + width = FontGetGlyphWidth(wchar); + + // To wrap long lines, replace the last whitespace with a newline character + if(ScreenLineLenPx + width >= LineMaxPx) + { + if(LastWhitespaceOut != NULL) + { + ScreenLineLenPx = PxSinceLastSpace; + *LastWhitespaceOut = '\n'; + LastWhitespaceOut = NULL; + } + } + + ScreenLineLenPx += width; + if(wchar != ' ') + PxSinceLastSpace += width; + } + + str += CharLen; + len -= CharLen; + + CharLen = mbtowc(&wchar, str, len); + } +} + +static int ParseLanguageFile(char **array, FILE *file, unsigned int ExpectedNumLines) +{ + int result, LinesLoaded, len; + unsigned char BOMTemp[3]; + char line[512]; + + if(fread(BOMTemp, 1, 3, file)!=3 || (BOMTemp[0]!=0xEF || BOMTemp[1]!=0xBB || BOMTemp[2]!=0xBF)) + { //Check for the BOM byte sequence. Skip it, if it exists. + rewind(file); + } + + result=0; + for(LinesLoaded = 0; fgets(line, sizeof(line), file) != NULL; LinesLoaded++) + { + len = strlen(line); + + if(len >= 1 && line[len - 1] == '\n') //Remove the newline character, if it exists. + { + line[len - 1] = '\0'; + len--; + } + + if((array[LinesLoaded] = malloc(len + 1)) != NULL) + { + len = FormatLanguageString(line, len + 1, array[LinesLoaded]); + array[LinesLoaded] = realloc(array[LinesLoaded], len); + } else { + result=-ENOMEM; + break; + } + } + + if(result == 0) + { + if(LinesLoaded != ExpectedNumLines) + { + printf("ParseLanguageFile: Mismatched number of lines (%u/%d)\n", LinesLoaded, ExpectedNumLines); + result = -1; + } + } + + return result; +} + +static int ParseFontListFile(char **array, FILE *file, unsigned int ExpectedNumLines) +{ + int result, LinesLoaded, len; + char line[256]; + + result=0; + for(LinesLoaded = 0; fgets(line, sizeof(line), file) != NULL; LinesLoaded++) + { + len = strlen(line); + + if(len >= 1 && line[len - 1] == '\n') //Remove the newline character, if it exists. + { + line[len - 1] = '\0'; + len--; + } + + if(LinesLoaded < ExpectedNumLines) + { + if((array[LinesLoaded] = malloc(len + 1)) != NULL) + { + len = FormatLanguageString(line, len + 1, array[LinesLoaded]); + array[LinesLoaded] = realloc(array[LinesLoaded], len); + } else { + result = -ENOMEM; + break; + } + } + } + + if(result == 0) + { + if(LinesLoaded!=ExpectedNumLines) + { + printf("ParseFontListFile: Mismatched number of lines (%u/%d)\n", LinesLoaded, ExpectedNumLines); + result = -1; + } + } + + return result; +} + +static const char DefaultFontFilename[]="NotoSans-Bold.ttf"; + +static char *GetDefaultFontFilePath(void) +{ + char *result; + + if((result=malloc(sizeof(DefaultFontFilename)+6+2))!=NULL) + sprintf(result, "lang/%s", DefaultFontFilename); + + return result; +} + +static char *GetFontFilePath(unsigned int language) +{ + char *FontFileArray[NUM_SUPPORTED_LANGUAGES], *result, *pFontFilename; + FILE *file; + int i; + + result=NULL; + memset(FontFileArray, 0, sizeof(FontFileArray)); + + while((file = fopen("lang/fonts.txt", "r")) == NULL) + { + if(errno != ENODEV) + break; + + WaitForDevice(); + } + + if(file != NULL) + { + if(ParseFontListFile(FontFileArray, file, NUM_SUPPORTED_LANGUAGES)==0) + { + pFontFilename=FontFileArray[language]; + + if((result = malloc(strlen(pFontFilename)+6)) != NULL) + sprintf(result, "lang/%s", pFontFilename); + } else + result=GetDefaultFontFilePath(); + + for(i=0; i=NUM_SUPPORTED_LANGUAGES) language=LANGUAGE_ENGLISH; + memset(LangStringWrapTable, 0, sizeof(LangStringWrapTable)); + + DEBUG_PRINTF("InitializeUI: language is: %u\n", language); + + if(GetConsoleRegion() == CONSOLE_REGION_JAPAN) + { + SelectButton = PAD_CIRCLE; + CancelButton = PAD_CROSS; + } else { + SelectButton = PAD_CROSS; + CancelButton = PAD_CIRCLE; + } + + InitGraphics(); + + while((result = LoadLanguageStrings(language)) == -ENODEV) + { + DEBUG_PRINTF("LoadLanguageStrings(%u): %d\n", language, result); + WaitForDevice(); + } + + DEBUG_PRINTF("LoadLanguageStrings(%u) result: %d\n", language, result); + if(result != 0) + { + if((result = LoadDefaultLanguageStrings()) != 0) + { + DEBUG_PRINTF("LoadDefaultLanguageStrings result: %d\n", result); + return result; + } + } + + result = BufferFont ? InitFontWithBuffer() : InitFont(); + + if(result == 0) + { + LoadBackground(&UIDrawGlobal, &BackgroundTexture); + LoadPadGraphics(&UIDrawGlobal, &PadLayoutTexture); + } + + GsClearDrawEnv1(&UIDrawGlobal.draw_env); + + return result; +} + +void DeinitializeUI(void) +{ + UnloadLanguage(); + FontDeinit(); + + if(gFontBuffer != NULL) + { + free(gFontBuffer); + gFontBuffer = NULL; + gFontBufferSize = 0; + } +} + +enum MBOX_SCREEN_ID{ + MBOX_SCREEN_ID_TITLE = 1, + MBOX_SCREEN_ID_MESSAGE, + MBOX_SCREEN_ID_BTN1, + MBOX_SCREEN_ID_BTN2, + MBOX_SCREEN_ID_BTN3, + MBOX_SCREEN_ID_BTN4, +}; + +static struct UIMenuItem MessageBoxItems[]={ + {MITEM_LABEL, MBOX_SCREEN_ID_TITLE}, + {MITEM_SEPERATOR}, + {MITEM_BREAK}, + + {MITEM_STRING, MBOX_SCREEN_ID_MESSAGE, MITEM_FLAG_READONLY}, {MITEM_BREAK}, {MITEM_BREAK}, + + {MITEM_BREAK}, + {MITEM_BREAK}, + {MITEM_BREAK}, + {MITEM_BREAK}, + {MITEM_BREAK}, + {MITEM_BREAK}, + + {MITEM_BUTTON, MBOX_SCREEN_ID_BTN1, MITEM_FLAG_POS_MID, 0, 16}, {MITEM_BREAK}, {MITEM_BREAK}, + {MITEM_BUTTON, MBOX_SCREEN_ID_BTN3, MITEM_FLAG_POS_MID, 0, 16}, {MITEM_BREAK}, {MITEM_BREAK}, + {MITEM_BUTTON, MBOX_SCREEN_ID_BTN2, MITEM_FLAG_POS_MID, 0, 16}, {MITEM_BREAK}, {MITEM_BREAK}, + {MITEM_BUTTON, MBOX_SCREEN_ID_BTN4, MITEM_FLAG_POS_MID, 0, 16}, + + {MITEM_TERMINATOR} +}; + +static struct UIMenu MessageBoxMenu = {NULL, NULL, MessageBoxItems, {{BUTTON_TYPE_SYS_SELECT, SYS_UI_LBL_OK}, {BUTTON_TYPE_SYS_CANCEL, SYS_UI_LBL_CANCEL}}}; + +int ShowMessageBox(int Option1Label, int Option2Label, int Option3Label, int Option4Label, const char *message, int title) +{ + short int numButtons; + + UISetLabel(&MessageBoxMenu, MBOX_SCREEN_ID_TITLE, title); + UISetString(&MessageBoxMenu, MBOX_SCREEN_ID_MESSAGE, message); + + UISetLabel(&MessageBoxMenu, MBOX_SCREEN_ID_BTN1, Option1Label); + UISetVisible(&MessageBoxMenu, MBOX_SCREEN_ID_BTN1, Option1Label != -1); + UISetLabel(&MessageBoxMenu, MBOX_SCREEN_ID_BTN2, Option2Label); + UISetVisible(&MessageBoxMenu, MBOX_SCREEN_ID_BTN2, Option2Label != -1); + UISetLabel(&MessageBoxMenu, MBOX_SCREEN_ID_BTN3, Option3Label); + UISetVisible(&MessageBoxMenu, MBOX_SCREEN_ID_BTN3, Option3Label != -1); + UISetLabel(&MessageBoxMenu, MBOX_SCREEN_ID_BTN4, Option4Label); + UISetVisible(&MessageBoxMenu, MBOX_SCREEN_ID_BTN4, Option4Label != -1); + + numButtons = 0; + if(Option1Label != -1) + numButtons++; + if(Option2Label != -1) + numButtons++; + if(Option3Label != -1) + numButtons++; + if(Option4Label != -1) + numButtons++; + + if(numButtons > 0) + { + MessageBoxMenu.hints[0].button = BUTTON_TYPE_SYS_SELECT; + MessageBoxMenu.hints[1].button = (numButtons == 1) ? -1 : BUTTON_TYPE_SYS_CANCEL; + + switch(UIExecMenu(&MessageBoxMenu, 0, NULL, NULL)) + { + case MBOX_SCREEN_ID_BTN1: + return 1; + case MBOX_SCREEN_ID_BTN2: + return 2; + case MBOX_SCREEN_ID_BTN3: + return 3; + case MBOX_SCREEN_ID_BTN4: + return 4; + default: + return 0; + } + } else { + MessageBoxMenu.hints[0].button = -1; + MessageBoxMenu.hints[1].button = -1; + UIDrawMenu(&MessageBoxMenu, 0, UI_OFFSET_X, UI_OFFSET_Y, -1); + SyncFlipFB(&UIDrawGlobal); + } +} + +void DisplayWarningMessage(unsigned int message) +{ + ShowMessageBox(SYS_UI_LBL_OK, -1, -1, -1, GetUIString(message), SYS_UI_LBL_WARNING); +} + +void DisplayErrorMessage(unsigned int message) +{ + ShowMessageBox(SYS_UI_LBL_OK, -1, -1, -1, GetUIString(message), SYS_UI_LBL_ERROR); +} + +void DisplayInfoMessage(unsigned int message) +{ + ShowMessageBox(SYS_UI_LBL_OK, -1, -1, -1, GetUIString(message), SYS_UI_LBL_INFO); +} + +int DisplayPromptMessage(unsigned int message, unsigned char Button1Label, unsigned char Button2Label) +{ + return ShowMessageBox(Button1Label, Button2Label, -1, -1, GetUIString(message), SYS_UI_LBL_CONFIRM); +} + +void DisplayFlashStatusUpdate(unsigned int message) +{ + ShowMessageBox(-1, -1, -1, -1, GetUIString(message), SYS_UI_LBL_WAIT); +} + +struct UIMenuItem *UIGetItem(struct UIMenu *menu, unsigned char id) +{ + struct UIMenuItem *result; + unsigned int i; + + result = NULL; + for(i=0; menu->items[i].type != MITEM_TERMINATOR; i++) + { + if(menu->items[i].id == id) + { + result = &menu->items[i]; + break; + } + } + + return result; +} + +void UISetVisible(struct UIMenu *menu, unsigned char id, int visible) +{ + struct UIMenuItem *item; + + if((item = UIGetItem(menu, id)) != NULL) + { + if(visible) + item->flags &= ~MITEM_FLAG_HIDDEN; + else + item->flags |= MITEM_FLAG_HIDDEN; + } +} + +void UISetReadonly(struct UIMenu *menu, unsigned char id, int readonly) +{ + struct UIMenuItem *item; + + if((item = UIGetItem(menu, id)) != NULL) + { + if(!readonly) + item->flags &= ~MITEM_FLAG_READONLY; + else + item->flags |= MITEM_FLAG_READONLY; + } +} + +void UISetEnabled(struct UIMenu *menu, unsigned char id, int enabled) +{ + struct UIMenuItem *item; + + if((item = UIGetItem(menu, id)) != NULL) + { + if(enabled) + item->flags &= ~MITEM_FLAG_DISABLED; + else + item->flags |= MITEM_FLAG_DISABLED; + } +} + +void UISetValue(struct UIMenu *menu, unsigned char id, int value) +{ + struct UIMenuItem *item; + + if((item = UIGetItem(menu, id)) != NULL) + { + item->value.value = value; + } +} + +int UIGetValue(struct UIMenu *menu, unsigned char id) +{ + struct UIMenuItem *item; + + if((item = UIGetItem(menu, id)) != NULL) + { + return item->value.value; + } + + return -1; +} + +void UISetLabel(struct UIMenu *menu, unsigned char id, int label) +{ + struct UIMenuItem *item; + + if((item = UIGetItem(menu, id)) != NULL) + { + item->label.id = label; + } +} + +void UISetString(struct UIMenu *menu, unsigned char id, const char *string) +{ + struct UIMenuItem *item; + + if((item = UIGetItem(menu, id)) != NULL) + { + item->string.buffer = string; + } +} + +void UISetType(struct UIMenu *menu, unsigned char id, unsigned char type) +{ + struct UIMenuItem *item; + + if((item = UIGetItem(menu, id)) != NULL) + { + item->type = type; + } +} + +void UISetFormat(struct UIMenu *menu, unsigned char id, unsigned char format, unsigned char width) +{ + struct UIMenuItem *item; + + if((item = UIGetItem(menu, id)) != NULL) + { + item->format = format; + item->width = width; + } +} + +void UISetEnum(struct UIMenu *menu, unsigned char id, const int *labels, int count) +{ + struct UIMenuItem *item; + + if((item = UIGetItem(menu, id)) != NULL) + { + item->enumeration.labels = labels; + item->enumeration.count = count; + item->enumeration.selectedIndex = 0; + } +} + +void UISetEnumSelectedIndex(struct UIMenu *menu, unsigned char id, int selectedIndex) +{ + struct UIMenuItem *item; + + if((item = UIGetItem(menu, id)) != NULL) + { + item->enumeration.selectedIndex = selectedIndex; + } +} + +int UIGetEnumSelectedIndex(struct UIMenu *menu, unsigned char id) +{ + struct UIMenuItem *item; + + if((item = UIGetItem(menu, id)) != NULL) + { + return item->enumeration.selectedIndex; + } + + return -1; +} + +void UIDrawMenu(struct UIMenu *menu, unsigned short int frame, short int StartX, short int StartY, short int selection) +{ + const char *pLabel; + char FormatString[8], *pFormatString, ValueString[32]; + struct UIMenuItem *item, *SelectedItem; + short int x, y, width, height, xRel, yRel, button, i; + GS_RGBAQ colour; + + DrawBackground(&UIDrawGlobal, &BackgroundTexture); + + x = StartX; + y = StartY; + SelectedItem = selection >= 0 ? &menu->items[selection] : NULL; + for(item = menu->items; item->type != MITEM_TERMINATOR; item++) + { + if(item->flags&MITEM_FLAG_HIDDEN) continue; + + if(item->flags&MITEM_FLAG_POS_ABS) + { + x = item->x; + y = item->y; + } else { + x += item->x; + y += item->y; + } + + switch(item->type) + { + case MITEM_SEPERATOR: + x = StartX; + y += UI_FONT_HEIGHT; + DrawLine(&UIDrawGlobal, x, y+UI_FONT_HEIGHT/2, UIDrawGlobal.width - UI_OFFSET_X, y+UI_FONT_HEIGHT/2, 1, GS_WHITE); + //Fall through. + case MITEM_BREAK: + x = StartX; + y += UI_FONT_HEIGHT; + break; + case MITEM_TAB: + x += (UI_TAB_STOPS*UI_FONT_WIDTH) - (unsigned int)x % (unsigned int)(UI_TAB_STOPS*UI_FONT_WIDTH); + break; + case MITEM_SPACE: + x += UI_FONT_WIDTH; + break; + case MITEM_STRING: + if(item->string.buffer != NULL) + { + colour = (item->flags & MITEM_FLAG_DISABLED) ? GS_GREY_FONT : ((item->flags & MITEM_FLAG_READONLY) ? GS_WHITE_FONT : (item == SelectedItem ? GS_YELLOW_FONT : GS_BLUE_FONT)); + FontPrintfWithFeedback(&UIDrawGlobal, x, y, 1, 1.0f, colour, item->string.buffer, &xRel, &yRel); + x += xRel; + y += yRel; + } + break; + case MITEM_BUTTON: + if((pLabel = GetUILabel(item->label.id)) != NULL) + { + width = item->width * UI_FONT_WIDTH; + height = UI_FONT_HEIGHT + UI_FONT_HEIGHT / 2; + if(item == SelectedItem) + { + width *= 1.1f; + height *= 1.1f; + } + + if(item->flags & MITEM_FLAG_POS_MID) + x = StartX + (UIDrawGlobal.width - width) / 2; + + DrawSprite(&UIDrawGlobal, x, y - UI_FONT_HEIGHT / 2, x + width, y + height, 2, item == SelectedItem ? GS_LGREY : GS_GREY); + + colour = (item->flags & MITEM_FLAG_DISABLED) ? GS_GREY_FONT : (item == SelectedItem ? GS_YELLOW_FONT : GS_WHITE_FONT); + + FontPrintfWithFeedback(&UIDrawGlobal, x + (width - item->label.TextWidth) / 2, y, 1, 1.0f, colour, pLabel, &xRel, &yRel); + item->label.TextWidth = xRel; + x += xRel; + y += yRel + UI_FONT_HEIGHT; + } + break; + case MITEM_LABEL: + if((pLabel = GetUILabel(item->value.value)) != NULL) + { + FontPrintfWithFeedback(&UIDrawGlobal, x, y, 1, 1.0f, GS_WHITE_FONT, pLabel, &xRel, &yRel); + x += xRel; + y += yRel; + } + break; + case MITEM_COLON: + FontPrintfWithFeedback(&UIDrawGlobal, x, y, 1, 1.0f, GS_WHITE_FONT, ":", &xRel, NULL); + x += xRel; + break; + case MITEM_DASH: + FontPrintfWithFeedback(&UIDrawGlobal, x, y, 1, 1.0f, GS_WHITE_FONT, "-", &xRel, NULL); + x += xRel; + break; + case MITEM_DOT: + FontPrintfWithFeedback(&UIDrawGlobal, x, y, 1, 1.0f, GS_WHITE_FONT, ".", &xRel, NULL); + x += xRel; + break; + case MITEM_SLASH: + FontPrintfWithFeedback(&UIDrawGlobal, x, y, 1, 1.0f, GS_WHITE_FONT, "/", &xRel, NULL); + x += xRel; + break; + case MITEM_VALUE: + pFormatString = FormatString; + + if(item->flags&MITEM_FLAG_UNIT_PREFIX) + { + switch(item->format) + { + case MITEM_FORMAT_HEX: + pFormatString[0] = '0'; + pFormatString[1] = 'x'; + pFormatString+=2; + break; + } + } + + *pFormatString = '%'; + pFormatString++; + + if(item->width > 0) + { + pFormatString += sprintf(pFormatString, "0%u", item->width); + } + + switch(item->format) + { + case MITEM_FORMAT_DEC: + *pFormatString = 'd'; + pFormatString++; + break; + case MITEM_FORMAT_UDEC: + *pFormatString = 'u'; + pFormatString++; + break; + case MITEM_FORMAT_HEX: + *pFormatString = 'x'; + pFormatString++; + break; + case MITEM_FORMAT_POINTER: + *pFormatString = 'p'; + pFormatString++; + break; + case MITEM_FORMAT_FLOAT: + *pFormatString = 'f'; + pFormatString++; + break; + } + + *pFormatString = '\0'; + sprintf(ValueString, FormatString, item->value.value); + colour = (item->flags & MITEM_FLAG_DISABLED) ? GS_GREY_FONT : ((item->flags & MITEM_FLAG_READONLY) ? GS_WHITE_FONT : (item == SelectedItem ? GS_YELLOW_FONT : GS_BLUE_FONT)); + FontPrintfWithFeedback(&UIDrawGlobal, x, y, 1, 1.0f, colour, ValueString, &xRel, NULL); + x += xRel; + break; + case MITEM_PROGRESS: + DrawProgressBar(&UIDrawGlobal, item->value.value / 100.0f, x + 20, y, 4, UIDrawGlobal.width - (x + 20) - 20, GS_BLUE); + y += UI_FONT_HEIGHT; + break; + case MITEM_TOGGLE: + colour = (item->flags & MITEM_FLAG_DISABLED) ? GS_GREY_FONT : ((item->flags & MITEM_FLAG_READONLY) ? GS_WHITE_FONT : (item == SelectedItem ? GS_YELLOW_FONT : GS_BLUE_FONT)); + FontPrintfWithFeedback(&UIDrawGlobal, x, y, 1, 1.0f, colour, GetUILabel(item->value.value == 0 ? SYS_UI_LBL_DISABLED : SYS_UI_LBL_ENABLED), &xRel, &yRel); + x += xRel; + y += yRel; + break; + case MITEM_ENUM: + if((pLabel = GetUILabel(item->enumeration.labels[item->enumeration.selectedIndex])) != NULL) + { + colour = (item->flags & MITEM_FLAG_DISABLED) ? GS_GREY_FONT : ((item->flags & MITEM_FLAG_READONLY) ? GS_WHITE_FONT : (item == SelectedItem ? GS_YELLOW_FONT : GS_BLUE_FONT)); + FontPrintfWithFeedback(&UIDrawGlobal, x, y, 1, 1.0f, colour, pLabel, &xRel, &yRel); + x += xRel; + y += yRel; + } + } + } + + //Draw legends + if(menu->next != NULL) + { + if(frame % 180 < 30) + DrawButtonLegend(&UIDrawGlobal, &PadLayoutTexture, BUTTON_TYPE_R1, UIDrawGlobal.width - 40 - frame % 30 / 3, 400, 4); + else if(frame % 180 < 60) + DrawButtonLegend(&UIDrawGlobal, &PadLayoutTexture, BUTTON_TYPE_R1, UIDrawGlobal.width - 40 - 10 + (frame % 30 / 3), 400, 4); + else + DrawButtonLegend(&UIDrawGlobal, &PadLayoutTexture, BUTTON_TYPE_R1, UIDrawGlobal.width - 40, 400, 4); + } + if(menu->prev != NULL) + { + if(frame % 180 < 30) + DrawButtonLegend(&UIDrawGlobal, &PadLayoutTexture, BUTTON_TYPE_L1, 20 + frame % 30 / 3, 400, 4); + else if(frame % 180 < 60) + DrawButtonLegend(&UIDrawGlobal, &PadLayoutTexture, BUTTON_TYPE_L1, 20 + 10 - frame % 30 / 3, 400, 4); + else + DrawButtonLegend(&UIDrawGlobal, &PadLayoutTexture, BUTTON_TYPE_L1, 20, 400, 4); + } + + x = 64; + for(i = 0; i < 2; i++) + { + if(menu->hints[i].button >= 0) + { + switch(menu->hints[i].button) + { + case BUTTON_TYPE_SYS_SELECT: + button = SelectButton == PAD_CIRCLE ? BUTTON_TYPE_CIRCLE : BUTTON_TYPE_CROSS; + break; + case BUTTON_TYPE_SYS_CANCEL: + button = CancelButton == PAD_CIRCLE ? BUTTON_TYPE_CIRCLE : BUTTON_TYPE_CROSS; + break; + default: + button = menu->hints[i].button; + } + + DrawButtonLegendWithFeedback(&UIDrawGlobal, &PadLayoutTexture, button, x, 420, 4, &xRel); + x += xRel + 8; + FontPrintfWithFeedback(&UIDrawGlobal, x, 422, 1, 1.0f, GS_WHITE_FONT, GetUILabel(menu->hints[i].label), &xRel, NULL); + x += xRel + 8; + } + } +} + +static void UITransitionSlideRightOut(struct UIMenu *menu, int SelectedOption) +{ + int i; + + for(i=0; i<=15; i++) + { + UIDrawMenu(menu, i, UI_OFFSET_X + i * 48, UI_OFFSET_Y, SelectedOption); + SyncFlipFB(&UIDrawGlobal); + } +} + +static void UITransitionSlideLeftOut(struct UIMenu *menu, int SelectedOption) +{ + int i; + + for(i=0; i<=15; i++) + { + UIDrawMenu(menu, i, UI_OFFSET_X + -i * 48, UI_OFFSET_Y, SelectedOption); + SyncFlipFB(&UIDrawGlobal); + } +} + +static void UITransitionSlideRightIn(struct UIMenu *menu, int SelectedOption) +{ + int i; + + for(i=15; i>0; i--) + { + UIDrawMenu(menu, i, UI_OFFSET_X + i * 48, UI_OFFSET_Y, SelectedOption); + SyncFlipFB(&UIDrawGlobal); + } +} + +static void UITransitionSlideLeftIn(struct UIMenu *menu, int SelectedOption) +{ + int i; + + for(i=15; i>0; i--) + { + UIDrawMenu(menu, i, UI_OFFSET_X + -i * 48, UI_OFFSET_Y, SelectedOption); + SyncFlipFB(&UIDrawGlobal); + } +} + +static void UITransitionFadeIn(struct UIMenu *menu, int SelectedOption) +{ + int i; + GS_RGBAQ rgbaq; + + rgbaq.r = 0; + rgbaq.g = 0; + rgbaq.b = 0; + rgbaq.q = 0; + + for(i=0; i<=15; i++) + { + rgbaq.a = 0x80-(i*8); + UIDrawMenu(menu, i, UI_OFFSET_X, UI_OFFSET_Y, SelectedOption); + SyncFlipFB(&UIDrawGlobal); + } +} + +static void UITransitionFadeOut(struct UIMenu *menu, int SelectedOption) +{ + int i; + GS_RGBAQ rgbaq; + + rgbaq.r = 0; + rgbaq.g = 0; + rgbaq.b = 0; + rgbaq.q = 0; + + for(i=15; i>0; i--) + { + rgbaq.a = 0x80-(i*8); + UIDrawMenu(menu, i, UI_OFFSET_X, UI_OFFSET_Y, SelectedOption); + SyncFlipFB(&UIDrawGlobal); + } +} + +void UITransition(struct UIMenu *menu, int type, int SelectedOption) +{ + switch(type) + { + case UIMT_LEFT_OUT: + UITransitionSlideLeftOut(menu, SelectedOption); + break; + case UIMT_RIGHT_OUT: + UITransitionSlideRightOut(menu, SelectedOption); + break; + case UIMT_LEFT_IN: + UITransitionSlideLeftIn(menu, SelectedOption); + break; + case UIMT_RIGHT_IN: + UITransitionSlideRightIn(menu, SelectedOption); + break; + case UIMT_FADE_IN: + UITransitionFadeIn(menu, SelectedOption); + break; + case UIMT_FADE_OUT: + UITransitionFadeOut(menu, SelectedOption); + break; + } +} + +static short int UIGetSelectableItem(struct UIMenu *menu, short int id) +{ + short int result; + int i; + + result = -1; + for(i = 0; menu->items[i].type != MITEM_TERMINATOR; i++) + { + if(menu->items[i].id == id) + { + if(menu->items[i].type > MITEM_LABEL + && !(menu->items[i].flags & MITEM_FLAG_DISABLED) + && !(menu->items[i].flags & MITEM_FLAG_HIDDEN) + && !(menu->items[i].flags & MITEM_FLAG_READONLY)) + result = i; + + break; + } + } + + return result; +} + +static short int UIGetNextSelectableItem(struct UIMenu *menu, short int index) +{ + short int result; + int i; + + index = (index < 0) ? 0 : index + 1; + result = -1; + for(i = index; menu->items[i].type != MITEM_TERMINATOR; i++) + { + if(menu->items[i].type > MITEM_LABEL + && !(menu->items[i].flags & MITEM_FLAG_DISABLED) + && !(menu->items[i].flags & MITEM_FLAG_HIDDEN) + && !(menu->items[i].flags & MITEM_FLAG_READONLY)) + { + result = i; + break; + } + } + + return result; +} + +static short int UIGetPrevSelectableItem(struct UIMenu *menu, short int index) +{ + short int result; + int i; + + index = (index < 0) ? 0 : index - 1; + result = -1; + for(i = index; i >= 0; i--) + { + if(menu->items[i].type > MITEM_LABEL + && !(menu->items[i].flags & MITEM_FLAG_DISABLED) + && !(menu->items[i].flags & MITEM_FLAG_HIDDEN) + && !(menu->items[i].flags & MITEM_FLAG_READONLY)) + { + result = i; + break; + } + } + + return result; +} + +int UIExecMenu(struct UIMenu *FirstMenu, short int SelectedItem, struct UIMenu **CurrentMenu, int (*callback)(struct UIMenu *menu, unsigned short int frame, int selection, u32 padstatus)) +{ + struct UIMenu *menu; + int result; + u32 PadStatus; + struct UIMenuItem *item; + short int selection, NextSel; + unsigned short int frame; + u32 PadRepeatStatus, PadRepeatStatusOld, PadStatusTemp; + unsigned short int PadRepeatDelayTicks, PadRepeatRateTicks; + + PadStatus = 0; + PadRepeatStatusOld = 0; + PadRepeatDelayTicks = UI_PAD_REPEAT_START_DELAY; + PadRepeatRateTicks = UI_PAD_REPEAT_DELAY; + frame = 0; + menu = FirstMenu; + + if((selection = UIGetSelectableItem(menu, SelectedItem)) >= 0) + { //If the item selected is specified, select it. + item = &menu->items[selection]; + } else + //Find first selectable option. + item = ((selection = UIGetNextSelectableItem(menu, -1)) >= 0) ? &menu->items[selection] : NULL; + + if(callback != NULL) + { + if((result = callback(menu, frame, selection, 0)) != 0) + goto exit_menu; + } + + while(1) + { + PadStatus=ReadCombinedPadStatus(); + + //For the pad repeat delay effect. + PadRepeatStatus = ReadCombinedPadStatus_raw(); + if(PadRepeatStatus == 0 || ((PadRepeatStatusOld != 0) && (PadRepeatStatus != PadRepeatStatusOld))) + { + PadRepeatDelayTicks = UI_PAD_REPEAT_START_DELAY; + PadRepeatRateTicks = UI_PAD_REPEAT_DELAY; + + PadStatusTemp = PadRepeatStatus & ~PadRepeatStatusOld; + PadRepeatStatusOld = PadRepeatStatus; + PadRepeatStatus = PadStatusTemp; + } + else + { + if(PadRepeatDelayTicks == 0) + { + //Allow the pad presses to repeat, but only after the pad repeat delay expires. + if(PadRepeatRateTicks == 0) + { + PadRepeatRateTicks = UI_PAD_REPEAT_DELAY; + } + else + { + PadStatusTemp = PadRepeatStatus & ~PadRepeatStatusOld; + PadRepeatStatusOld = PadRepeatStatus; + PadRepeatStatus = PadStatusTemp; + } + + PadRepeatRateTicks--; + } + else + { + PadStatusTemp = PadRepeatStatus & ~PadRepeatStatusOld; + PadRepeatStatusOld = PadRepeatStatus; + PadRepeatStatus = PadStatusTemp; + + PadRepeatDelayTicks--; + } + } + + if(PadRepeatStatus&PAD_UP) + { + //Try to find the previous selectable option. + if((NextSel = UIGetPrevSelectableItem(menu, selection)) >= 0) + { + selection = NextSel; + item = &menu->items[selection]; + } + } else if(PadRepeatStatus&PAD_DOWN) { + //Try to find the next selectable option. + if((NextSel = UIGetNextSelectableItem(menu, selection)) >= 0) + { + selection = NextSel; + item = &menu->items[selection]; + } + } else if(PadRepeatStatus&PAD_LEFT) { + if(item != NULL && !(item->flags & MITEM_FLAG_READONLY) && !(item->flags & MITEM_FLAG_DISABLED)) + { + switch(item->type) + { + case MITEM_VALUE: + if(item->value.value - 1 >= item->value.min) + item->value.value--; + else + item->value.value = item->value.max; + break; + case MITEM_TOGGLE: + item->value.value = !item->value.value; + break; + case MITEM_ENUM: + if(item->enumeration.selectedIndex > 0) + item->enumeration.selectedIndex--; + else + item->enumeration.selectedIndex = item->enumeration.count; + break; + } + } + } else if(PadRepeatStatus&PAD_RIGHT) { + if(item != NULL && !(item->flags & MITEM_FLAG_READONLY) && !(item->flags & MITEM_FLAG_DISABLED)) + { + switch(item->type) + { + case MITEM_VALUE: + if(item->value.value + 1 <= item->value.max) + item->value.value++; + else + item->value.value = item->value.min; + break; + case MITEM_TOGGLE: + item->value.value = !item->value.value; + break; + case MITEM_ENUM: + if(item->enumeration.selectedIndex + 1 < item->enumeration.count) + item->enumeration.selectedIndex++; + else + item->enumeration.selectedIndex = 0; + break; + } + } + } + + if(PadStatus&SelectButton) + { + if(item != NULL && !(item->flags & MITEM_FLAG_READONLY) && !(item->flags & MITEM_FLAG_DISABLED)) + { + switch(item->type) + { + case MITEM_BUTTON: + result = item->id; + goto exit_menu; + case MITEM_TOGGLE: + item->value.value = !item->value.value; + break; + } + } + } else if(PadStatus&CancelButton) { + //User aborted. + result = 1; + break; + } + + if(PadStatus&PAD_R1) { + if(menu->next != NULL) + { + UITransition(menu, UIMT_RIGHT_OUT, selection); + + menu = menu->next; + //Find first selectable option. + item = ((selection = UIGetNextSelectableItem(menu, -1)) >= 0) ? &menu->items[selection] : NULL; + + UITransition(menu, UIMT_LEFT_IN, selection); + } + } else if(PadStatus&PAD_L1) { + if(menu->prev != NULL) + { + UITransition(menu, UIMT_LEFT_OUT, selection); + + menu = menu->prev; + //Find first selectable option. + item = ((selection = UIGetNextSelectableItem(menu, -1)) >= 0) ? &menu->items[selection] : NULL; + + UITransition(menu, UIMT_RIGHT_IN, selection); + } + } + + UIDrawMenu(menu, frame, UI_OFFSET_X, UI_OFFSET_Y, selection); + + if(callback != NULL) + { + if((result = callback(menu, frame, selection, PadStatus)) != 0) + break; + } + + SyncFlipFB(&UIDrawGlobal); + frame++; + } + +exit_menu: + if (CurrentMenu != NULL) + *CurrentMenu = menu; + + return result; +} diff --git a/UI.h b/UI.h new file mode 100644 index 0000000..bf3882f --- /dev/null +++ b/UI.h @@ -0,0 +1,129 @@ +#include "lang.h" + +#define UI_OFFSET_X 8 +#define UI_OFFSET_Y 8 +#define UI_FONT_WIDTH FNT_CHAR_WIDTH +#define UI_FONT_HEIGHT FNT_CHAR_HEIGHT +#define UI_TAB_STOPS FNT_TAB_STOPS + +//In VSYNC ticks +#define UI_PAD_REPEAT_START_DELAY 20 +#define UI_PAD_REPEAT_DELAY 5 + +enum MENU_ITEM{ + MITEM_SEPERATOR, + MITEM_TAB, + MITEM_SPACE, + MITEM_BREAK, + MITEM_DASH, + MITEM_COLON, + MITEM_DOT, + MITEM_SLASH, + MITEM_PROGRESS, + MITEM_LABEL, //Last non-selectable item. + + MITEM_STRING, + MITEM_VALUE, + MITEM_BUTTON, + MITEM_TOGGLE, + MITEM_ENUM, + + MITEM_TERMINATOR, + + MITEM_COUNT +}; + +enum MENU_ITEM_FORMAT{ + MITEM_FORMAT_DEC = 0, + MITEM_FORMAT_UDEC, + MITEM_FORMAT_HEX, + MITEM_FORMAT_POINTER, + MITEM_FORMAT_FLOAT, + + MITEM_FORMAT_COUNT +}; + +#define MITEM_FLAG_HIDDEN 0x01 +#define MITEM_FLAG_READONLY 0x02 +#define MITEM_FLAG_DISABLED 0x04 +#define MITEM_FLAG_UNIT_PREFIX 0x08 //For formats with a unit prefix, show it. +#define MITEM_FLAG_POS_ABS 0x10 //Coordinates are absolute. +#define MITEM_FLAG_POS_MID 0x20 //Aligned to the middle. Only for buttons. + +struct UIMenuItem{ + unsigned char type; + unsigned char id; //ID 0 is reserved for items that do not need to be tracked (i.e. labels). + unsigned char flags; + unsigned char format; + unsigned char width; + short int x; + short int y; + union{ //Limits do not have to be specified, if the values are not going to be changeable by the user. + struct{ + int value; + int min, max; + }value; + struct{ + const char *buffer; + int maxlen; + }string; + struct{ + const int *labels; + int count; + int selectedIndex; + }enumeration; + struct{ + int id; + int TextWidth; + }label; + }; +}; + +struct UIBtnHint{ + short int button; + short int label; +}; + +struct UIMenu{ + struct UIMenu *next, *prev; + struct UIMenuItem *items; + struct UIBtnHint hints[2]; +}; + +enum UI_MENU_TRANSITION{ + UIMT_LEFT_OUT, + UIMT_RIGHT_OUT, + UIMT_LEFT_IN, + UIMT_RIGHT_IN, + UIMT_FADE_IN, + UIMT_FADE_OUT, +}; + +const char *GetUIString(unsigned int id); +const char *GetUILabel(unsigned int id); +int InitializeUI(int BufferFont); +int ReinitializeUI(void); +void DeinitializeUI(void); +int ShowMessageBox(int Option1Label, int Option2Label, int Option3Label, int Option4Label, const char *message, int title); +void DisplayWarningMessage(unsigned int message); +void DisplayErrorMessage(unsigned int message); +void DisplayInfoMessage(unsigned int message); +int DisplayPromptMessage(unsigned int message, unsigned char Button1Label, unsigned char Button2Label); +void DisplayFlashStatusUpdate(unsigned int message); + +struct UIMenuItem *UIGetItem(struct UIMenu *menu, unsigned char id); +void UISetVisible(struct UIMenu *menu, unsigned char id, int visible); +void UISetReadonly(struct UIMenu *menu, unsigned char id, int readonly); +void UISetEnabled(struct UIMenu *menu, unsigned char id, int enabled); +void UISetValue(struct UIMenu *menu, unsigned char id, int value); +int UIGetValue(struct UIMenu *menu, unsigned char id); +void UISetLabel(struct UIMenu *menu, unsigned char id, int label); +void UISetString(struct UIMenu *menu, unsigned char id, const char *string); +void UISetType(struct UIMenu *menu, unsigned char id, unsigned char type); +void UISetFormat(struct UIMenu *menu, unsigned char id, unsigned char format, unsigned char width); +void UISetEnum(struct UIMenu *menu, unsigned char id, const int *labels, int count); +void UISetEnumSelectedIndex(struct UIMenu *menu, unsigned char id, int selectedIndex); +int UIGetEnumSelectedIndex(struct UIMenu *menu, unsigned char id); +void UIDrawMenu(struct UIMenu *menu, unsigned short int frame, short int StartX, short int StartY, short int selection); +void UITransition(struct UIMenu *menu, int type, int SelectedOption); +int UIExecMenu(struct UIMenu *FirstMenu, short int SelectedItem, struct UIMenu **CurrentMenu, int (*callback)(struct UIMenu *menu, unsigned short int frame, int selection, u32 padstatus)); diff --git a/common/Rules.make b/common/Rules.make new file mode 100644 index 0000000..3024681 --- /dev/null +++ b/common/Rules.make @@ -0,0 +1,56 @@ +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004. +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. + + +IOP_CC_VERSION := $(shell $(IOP_CC) -v 2>&1 | sed -n 's/^.*version //p') + +ASFLAGS_TARGET = -mcpu=r3000 + +ifeq ($(IOP_CC_VERSION),3.2.2) +ASFLAGS_TARGET = -march=r3000 +endif + +ifeq ($(IOP_CC_VERSION),3.2.3) +ASFLAGS_TARGET = -march=r3000 +endif + +IOP_INCS := $(IOP_INCS) -I$(PS2SDK)/iop/include -I$(PS2SDK)/common/include -Iinclude + +IOP_CFLAGS := $(CFLAGS_TARGET) -O2 -G0 -D_IOP -c $(IOP_INCS) $(IOP_CFLAGS) +IOP_ASFLAGS := $(ASFLAGS_TARGET) -EL -G0 $(IOP_ASFLAGS) +IOP_LDFLAGS := $(LDFLAGS_TARGET) -nostdlib $(IOP_LDFLAGS) + +# Externally defined variables: IOP_BIN, IOP_OBJS, IOP_LIB + +%.o : %.c + $(IOP_CC) $(IOP_CFLAGS) $< -o $@ + +%.o : %.s + $(IOP_AS) $(IOP_ASFLAGS) $< -o $@ + +# A rule to build imports.lst. +%.o : %.lst + echo "#include \"irx_imports.h\"" > build-imports.c + cat $< >> build-imports.c + $(IOP_CC) $(IOP_CFLAGS) build-imports.c -o $@ + -rm -f build-imports.c + +# A rule to build exports.tab. +%.o : %.tab + echo "#include \"irx.h\"" > build-exports.c + cat $< >> build-exports.c + $(IOP_CC) $(IOP_CFLAGS) build-exports.c -o $@ + -rm -f build-exports.c + + +$(IOP_BIN) : $(IOP_OBJS) + $(IOP_CC) $(IOP_LDFLAGS) -o $(IOP_BIN) $(IOP_OBJS) $(IOP_LIBS) + +$(IOP_LIB) : $(IOP_OBJS) + $(IOP_AR) cru $(IOP_LIB) $(IOP_OBJS) + diff --git a/common/libapa/include/libapa.h b/common/libapa/include/libapa.h new file mode 100644 index 0000000..e65ab88 --- /dev/null +++ b/common/libapa/include/libapa.h @@ -0,0 +1,179 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +*/ + +#ifndef __LIBAPA_H__ +#define __LIBAPA_H__ + +#include +#include + +// Sectors for this and that ;) +#define APA_SECTOR_MBR 0 +#define APA_SECTOR_SECTOR_ERROR 6// use for last sector that had a error... +#define APA_SECTOR_PART_ERROR 7// use for last partition that had a error... +#define APA_SECTOR_APAL 8 +#define APA_SECTOR_APAL_HEADERS 10 // 10-262 + +// APA Partition +#define APA_MAGIC 0x00415041 // 'APA\0' +#define APA_MBR_VERSION 2 + +#define APA_MODVER ((APA_MODVER_MAJOR << 8) | APA_MODVER_MINOR) + +typedef struct { + u8 unused; + u8 sec; + u8 min; + u8 hour; + u8 day; + u8 month; + u16 year; +} apa_ps2time_t; + +// +// MAIN APA defines/struct +// +typedef struct { + u32 start; // Sector address + u32 length; // Sector count +} apa_sub_t; + +typedef struct +{ + u32 checksum; + u32 magic; // APA_MAGIC + u32 next; + u32 prev; + char id[APA_IDMAX]; + char rpwd[APA_PASSMAX]; + char fpwd[APA_PASSMAX]; + u32 start; + u32 length; + u16 type; + u16 flags; + u32 nsub; + apa_ps2time_t created; + u32 main; + u32 number; + u32 modver; + u32 pading1[7]; + char pading2[128]; + struct { + char magic[32]; + u32 version; + u32 nsector; + apa_ps2time_t created; + u32 osdStart; + u32 osdSize; + char pading3[200]; + } mbr; + apa_sub_t subs[APA_MAXSUB]; +} apa_header_t; + +#define APA_CACHE_FLAG_DIRTY 0x01 +typedef struct sapa_cache +{ + struct sapa_cache *next; + struct sapa_cache *tail; + u16 flags; + u16 nused; + s32 device; + u32 sector; + union + { + apa_header_t *header; + u32 *error_lba; + }; +} apa_cache_t; + +typedef struct +{ + char id[APA_IDMAX]; + char fpwd[APA_PASSMAX]; + char rpwd[APA_PASSMAX]; + u32 size; + u16 type; + u16 flags; + u32 main; + u32 number; +} apa_params_t; + +void apaSaveError(s32 device, void *buffer, u32 lba, u32 err_lba); +void apaSetPartErrorSector(s32 device, u32 lba); +int apaGetPartErrorSector(s32 device, u32 lba, u32 *lba_out); +int apaGetPartErrorName(s32 device, char *name); + +apa_cache_t *apaFillHeader(s32 device, const apa_params_t *params, u32 start, u32 next, u32 prev, u32 length, int *err); +apa_cache_t *apaInsertPartition(s32 device, const apa_params_t *params, u32 sector, int *err); +apa_cache_t *apaFindPartition(s32 device, const char *id, int *err); +void apaAddEmptyBlock(apa_header_t *header, u32 *emptyBlocks); +apa_cache_t *apaRemovePartition(s32 device, u32 start, u32 next, u32 prev, u32 length); +void apaMakeEmpty(apa_cache_t *clink); +apa_cache_t *apaDeleteFixPrev(apa_cache_t *clink1, int *err); +apa_cache_t *apaDeleteFixNext(apa_cache_t *clink, int *err); +int apaDelete(apa_cache_t *clink); +int apaCheckSum(apa_header_t *header); +int apaReadHeader(s32 device, apa_header_t *header, u32 lba); +int apaWriteHeader(s32 device, apa_header_t *header, u32 lba); +int apaGetFormat(s32 device, int *format); +u32 apaGetPartitionMax(u32 totalLBA); +apa_cache_t *apaGetNextHeader(apa_cache_t *clink, int *err); + +/////////////////////////////////////////////////////////////////////////////// + +int apaCacheInit(u32 size); +void apaCacheLink(apa_cache_t *clink, apa_cache_t *cnew); +apa_cache_t *apaCacheUnLink(apa_cache_t *clink); +int apaCacheTransfer(apa_cache_t *clink, int type); +void apaCacheFlushDirty(apa_cache_t *clink); +int apaCacheFlushAllDirty(s32 device); +apa_cache_t *apaCacheGetHeader(s32 device, u32 sector, u32 mode, int *err); +void apaCacheFree(apa_cache_t *clink); +apa_cache_t *apaCacheAlloc(void); + +/////////////////////////////////////////////////////////////////////////////// + +#define APAL_MAGIC 0x4150414C // 'APAL' +typedef struct +{ + u32 magic; // APAL_MAGIC + s32 num; + u32 sectors[126]; +} apa_journal_t; + +#define journalCheckSum(header) apaCheckSum((apa_header_t *)header) +int apaJournalReset(s32 device); +int apaJournalFlush(s32 device); +int apaJournalWrite(apa_cache_t *clink); +int apaJournalRestore(s32 device); + +/////////////////////////////////////////////////////////////////////////////// + +void *apaAllocMem(int size); +void apaFreeMem(void *ptr); +int apaGetTime(apa_ps2time_t *tm); +int apaGetIlinkID(u8 *idbuf); + +/////////////////////////////////////////////////////////////////////////////// +int apaPassCmp(const char *password1, const char *password2); +void apaEncryptPassword(const char *id, char *password_out, const char *password_in); + +/////////////////////////////////////////////////////////////////////////////// +typedef struct +{ + u32 totalLBA; + u32 partitionMaxSize; + int format; + int status; +} apa_device_t; + +int apaGetFreeSectors(s32 device, u32 *free, apa_device_t *deviceinfo); + +#endif /* __LIBAPA_H__ */ diff --git a/common/libapa/src/apa.c b/common/libapa/src/apa.c new file mode 100644 index 0000000..1958ed8 --- /dev/null +++ b/common/libapa/src/apa.c @@ -0,0 +1,499 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +# +# Main APA related routines +*/ + +#include +#include +#ifdef _IOP +#include +#else +#include +#endif +#include +#include +#include + +#include "apa-opt.h" +#include "libapa.h" + +const char apaMBRMagic[]="Sony Computer Entertainment Inc."; + +void apaSaveError(s32 device, void *buffer, u32 lba, u32 err_lba) +{ + memset(buffer, 0, 512); + *(u32 *)buffer=err_lba; + ata_device_sector_io(device, buffer, lba, 1, ATA_DIR_WRITE); + ata_device_flush_cache(device); +} + +void apaSetPartErrorSector(s32 device, u32 lba) +{// used to set the lba of a partition that has a error... + apa_cache_t *clink; + clink=apaCacheAlloc(); + apaSaveError(device, clink->header, APA_SECTOR_PART_ERROR, lba); + apaCacheFree(clink); +} + +int apaGetPartErrorSector(s32 device, u32 lba, u32 *lba_out) +{ + apa_cache_t *clink; + int rv=0; + + if(!(clink=apaCacheAlloc())) + return -ENOMEM; + + if(ata_device_sector_io(device, clink->header, lba, 1, ATA_DIR_READ)) + return -EIO; + + if(lba_out) + *lba_out=*clink->error_lba; + if(*clink->error_lba) + rv=1;// error is set ;) + apaCacheFree(clink); + return rv; +} + +int apaGetPartErrorName(s32 device, char *name) +{ + u32 lba; + int rv=0; + apa_cache_t *clink; + + if((rv=apaGetPartErrorSector(device, APA_SECTOR_PART_ERROR, &lba)) <= 0) + return rv; + if(!(clink=apaCacheGetHeader(device, 0, APA_IO_MODE_READ, &rv))) + return rv; + + while(clink) + { + if(clink->header->type!=APA_TYPE_FREE && + !(clink->header->flags & APA_CACHE_FLAG_DIRTY) && + clink->header->start==lba) + { + if(name) + { + strncpy(name, clink->header->id, APA_IDMAX - 1); + name[APA_IDMAX - 1] = '\0'; + } + apaCacheFree(clink); + return 1; + } + clink=apaGetNextHeader(clink, &rv); + } + + // clear error if no errors and partitions was not found... + if(rv==0) + apaSetPartErrorSector(device, 0); + return rv; +} + +apa_cache_t *apaFillHeader(s32 device, const apa_params_t *params, u32 start, u32 next, + u32 prev, u32 length, int *err) +{ // used for making a new partition + apa_cache_t *clink; + + if(!(clink=apaCacheGetHeader(device, start, APA_IO_MODE_WRITE, err))) + return NULL; + memset(clink->header, 0, sizeof(apa_header_t)); + clink->header->magic=APA_MAGIC; + clink->header->start=start; + clink->header->next=next; + clink->header->prev=prev; + clink->header->length=length; + clink->header->type=params->type; + clink->header->flags=params->flags; + clink->header->modver=APA_MODVER; + memcpy(clink->header->id, params->id, APA_IDMAX); + if(params->flags & APA_FLAG_SUB) + { + clink->header->main=params->main; + clink->header->number=params->number; + } + else + { + if(strncmp(clink->header->id, "_tmp", APA_IDMAX)!=0) + { + memcpy(clink->header->rpwd, params->rpwd, APA_PASSMAX); + memcpy(clink->header->fpwd, params->fpwd, APA_PASSMAX); + } + } + apaGetTime(&clink->header->created); + clink->flags|=APA_CACHE_FLAG_DIRTY; + return clink; +} + +apa_cache_t *apaInsertPartition(s32 device, const apa_params_t *params, u32 sector, int *err) +{ // Adds a new partition using an empty block. + apa_cache_t *clink_empty; + apa_cache_t *clink_this; + apa_cache_t *clink_next; + + if((clink_this=apaCacheGetHeader(device, sector, APA_IO_MODE_READ, err))==0) + return 0; + + while(clink_this->header->length!=params->size) + { + if((clink_next=apaCacheGetHeader(device, clink_this->header->next, APA_IO_MODE_READ, err))==NULL) + { // Get next partition + apaCacheFree(clink_this); + return 0; + } + clink_this->header->length>>=1; + clink_empty=apaRemovePartition(device, (clink_this->header->start+clink_this->header->length), + clink_this->header->next, clink_this->header->start, clink_this->header->length); + clink_this->header->next=clink_empty->header->start; + clink_this->flags|=APA_CACHE_FLAG_DIRTY; + clink_next->header->prev=clink_empty->header->start; + clink_next->flags|=APA_CACHE_FLAG_DIRTY; + + apaCacheFlushAllDirty(device); + apaCacheFree(clink_empty); + apaCacheFree(clink_next); + } + apaCacheFree(clink_this); + clink_this=apaFillHeader(device, params, clink_this->header->start, clink_this->header->next, + clink_this->header->prev, params->size, err); + apaCacheFlushAllDirty(device); + return clink_this; +} + +apa_cache_t *apaFindPartition(s32 device, const char *id, int *err) +{ + apa_cache_t *clink; + + clink=apaCacheGetHeader(device, 0, APA_IO_MODE_READ, err); + while(clink) + { + if(!(clink->header->flags & APA_FLAG_SUB)) { + if(memcmp(clink->header->id, id, APA_IDMAX)==0) + return clink; // found + } + clink=apaGetNextHeader(clink, (int *)err); + } + if(*err==0) { + *err=-ENOENT; + return NULL; + //return (apa_cache_t *)-ENOENT; // <-- BUG code tests for NULL only + } + *err=0; + return NULL; +} + +void apaAddEmptyBlock(apa_header_t *header, u32 *emptyBlocks) +{ // small helper.... to track empty blocks.. + u32 i; + + if(header->type==APA_TYPE_FREE) { + for(i=0;i<32;i++) + { + if(header->length==(1 << i)) { + if(emptyBlocks[i]==APA_TYPE_FREE) { + emptyBlocks[i]=header->start; + return; + } + } + } + } +} + +apa_cache_t *apaRemovePartition(s32 device, u32 start, u32 next, u32 prev, + u32 length) +{ + apa_cache_t *clink; + int err; + + if((clink=apaCacheGetHeader(device, start, APA_IO_MODE_WRITE, &err))==NULL) + return NULL; + memset(clink->header, 0, sizeof(apa_header_t)); + clink->header->magic=APA_MAGIC; + clink->header->start=start; + clink->header->next=next; + clink->header->prev=prev; + clink->header->length=length; + strcpy(clink->header->id, "__empty"); + apaGetTime(&clink->header->created); + clink->flags|=APA_CACHE_FLAG_DIRTY; + return clink; +} + +void apaMakeEmpty(apa_cache_t *clink) +{ + u32 saved_start; + u32 saved_next; + u32 saved_prev; + u32 saved_length; + + saved_start = clink->header->start; + saved_next = clink->header->next; + saved_prev = clink->header->prev; + saved_length = clink->header->length; + memset(clink->header, 0, sizeof(apa_header_t)); + clink->header->magic = APA_MAGIC; + clink->header->start = saved_start; + clink->header->next = saved_next; + clink->header->prev = saved_prev; + clink->header->length = saved_length; + apaGetTime(&clink->header->created); + strcpy(clink->header->id, "__empty"); + clink->flags|=APA_CACHE_FLAG_DIRTY; +} + +apa_cache_t *apaDeleteFixPrev(apa_cache_t *clink, int *err) +{ + apa_cache_t *clink2=clink; + apa_header_t *header=clink2->header; + u32 device=clink->device; + u32 length=clink->header->length; + u32 saved_next=clink->header->next; + u32 saved_length=clink->header->length; + u32 tmp; + + while(header->start) + { + if(!(clink2=apaCacheGetHeader(device, header->prev, APA_IO_MODE_READ, err))) + { + apaCacheFree(clink); + return NULL; + } + header=clink2->header; + tmp=header->length+length; + if(header->type!=0) { + apaCacheFree(clink2); + break; + } + if((header->start%tmp) || (tmp & (tmp-1))) { + apaCacheFree(clink2); + break; + } + length=tmp; + apaCacheFree(clink); + clink=clink2; + } + if(length!=saved_length) + { + if(!(clink2=apaCacheGetHeader(device, saved_next, APA_IO_MODE_READ, err))) + { + apaCacheFree(clink); + return NULL; + } + clink->header->length=length; + clink->header->next=clink->header->start+length; + clink2->header->prev=clink->header->start; + clink2->flags|=APA_CACHE_FLAG_DIRTY; + clink->flags|=APA_CACHE_FLAG_DIRTY; + apaCacheFlushAllDirty(device); + apaCacheFree(clink2); + } + return clink; +} + + +apa_cache_t *apaDeleteFixNext(apa_cache_t *clink, int *err) +{ + apa_header_t *header=clink->header; + u32 length=header->length; + u32 saved_length=header->length; + u32 lnext=header->next; + apa_cache_t *clink1; + apa_cache_t *clink2; + u32 device=clink->device; + u32 tmp; + + while(lnext!=0) + { + if(!(clink1=apaCacheGetHeader(device, lnext, APA_IO_MODE_READ, err))) + { + apaCacheFree(clink); + return 0; + } + header=clink1->header; + tmp=header->length+length; + if(header->type!=0) + { + apaCacheFree(clink1); + break; + } + if((clink->header->start%tmp)!=0 || ((tmp-1) & tmp)) + { + apaCacheFree(clink1); + break; + } + length=tmp; + apaCacheFree(clink1); + lnext=header->next; + } + if(length!=saved_length) + { + if(!(clink2=apaCacheGetHeader(device, lnext, APA_IO_MODE_READ, err))) + { + apaCacheFree(clink); + return NULL; + } + clink->header->length=length; + clink->header->next=lnext; + apaMakeEmpty(clink); + clink2->header->prev=clink->header->start; + clink2->flags|=APA_CACHE_FLAG_DIRTY; + apaCacheFlushAllDirty(device); + apaCacheFree(clink2); + } + return clink; +} + + +int apaDelete(apa_cache_t *clink) +{ + int rv=0; + apa_cache_t *clink_mbr; + u32 device=clink->device; + u32 start=clink->header->start; + int i; + + if(!start) { + apaCacheFree(clink); + return -EACCES; + } + + if(clink->header->next==0) + { + if((clink_mbr=apaCacheGetHeader(device, 0, APA_IO_MODE_READ, &rv))==NULL) + { + apaCacheFree(clink); + return rv; + } + do { + apaCacheFree(clink); + if((clink=apaCacheGetHeader(clink->device, clink->header->prev, APA_IO_MODE_READ, &rv))==NULL) + return 0; + clink->header->next=0; + clink->flags|=APA_CACHE_FLAG_DIRTY; + clink_mbr->header->prev=clink->header->start; + clink_mbr->flags|=APA_CACHE_FLAG_DIRTY; + apaCacheFlushAllDirty(device); + } while(clink->header->type==0); + apaCacheFree(clink_mbr); + } else { + u32 length=clink->header->length; + + for(i=0;i < 2;i++){ + if((clink=apaDeleteFixPrev(clink, &rv))==NULL) + return 0; + if((clink=apaDeleteFixNext(clink, &rv))==NULL) + return 0; + } + if(clink->header->start==start && clink->header->length==length) + { + apaMakeEmpty(clink); + apaCacheFlushAllDirty(clink->device); + } + } + apaCacheFree(clink); + return rv; +} + +int apaCheckSum(apa_header_t *header) +{ + u32 *ptr=(u32 *)header; + u32 sum, i; + + for(sum=0,i=1; i < 256; i++) //sizeof(header)/4 = 256, start at offset +4 to omit the checksum field. + sum+=ptr[i]; + return sum; +} + +int apaReadHeader(s32 device, apa_header_t *header, u32 lba) +{ + if(ata_device_sector_io(device, header, lba, 2, ATA_DIR_READ)!=0) + return -EIO; + if(header->magic!=APA_MAGIC) + return -EIO; + if(apaCheckSum(header)!=header->checksum) + return -EIO; + if(lba==APA_SECTOR_MBR) + { + if(strncmp(header->mbr.magic, apaMBRMagic, sizeof(header->mbr.magic))==0) + return 0; + APA_PRINTF(APA_DRV_NAME": error: invalid partition table or version newer than I know.\n"); + return -EIO; + } + return 0; +} + +int apaWriteHeader(s32 device, apa_header_t *header, u32 lba) +{ + if(ata_device_sector_io(device, header, lba, 2, ATA_DIR_WRITE)) + return -EIO; + return 0; +} + +int apaGetFormat(s32 device, int *format) +{ + apa_cache_t *clink; + int rv=0; + u32 *pDW, i; + + clink=apaCacheAlloc(); + *format=0; + if((rv=apaReadHeader(device, clink->header, 0))==0) + { + *format=clink->header->mbr.version; + if(ata_device_sector_io(device, clink->header, APA_SECTOR_SECTOR_ERROR, 2, ATA_DIR_READ)) + rv=-EIO; // return -EIO; + if(rv==0){ + pDW=(u32 *)clink->header; + for(i=0;i < 256; i++) + { + if((i & 0x7F) && pDW[i]!=0) + rv=1; + } + } + } + apaCacheFree(clink); + return rv==0; +} + +u32 apaGetPartitionMax(u32 totalLBA) +{ + u32 i, size; + + totalLBA>>=6; // totalLBA/64 + size=(1<<0x1F); + for(i=31;i!=0;i--) + { + size=1<header->start; + + apaCacheFree(clink); + if(!clink->header->next) + return NULL; + + if(!(clink=apaCacheGetHeader(clink->device, clink->header->next, APA_IO_MODE_READ, err))) + return NULL; + + if(start!=clink->header->prev) { + APA_PRINTF(APA_DRV_NAME": Warning: Invalid partition information. start != prev\n"); + clink->header->prev=start; + clink->flags|=APA_CACHE_FLAG_DIRTY; + apaCacheFlushAllDirty(clink->device); + } + return clink; + +} diff --git a/common/libapa/src/cache.c b/common/libapa/src/cache.c new file mode 100644 index 0000000..dc986d6 --- /dev/null +++ b/common/libapa/src/cache.c @@ -0,0 +1,199 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +# +# APA cache manipulation routines +*/ + +#include +#include +#ifdef _IOP +#include +#else +#include +#endif +#include +#include + +#include "apa-opt.h" +#include "libapa.h" + +// Globals +static apa_cache_t *cacheBuf; +static int cacheSize; + +int apaCacheInit(u32 size) +{ + apa_header_t *header; + unsigned int i; + + cacheSize=size; // save size ;) + if((header=(apa_header_t *)apaAllocMem(size*sizeof(apa_header_t)))){ + cacheBuf=apaAllocMem((size+1)*sizeof(apa_cache_t)); + if(cacheBuf==NULL) + return -ENOMEM; + } + else + return -ENOMEM; + // setup cache header... + memset(cacheBuf, 0, (size+1)*sizeof(apa_cache_t)); + cacheBuf->next=cacheBuf; + cacheBuf->tail=cacheBuf; + for(i=1; itail, &cacheBuf[i]); + } + return 0; +} + +void apaCacheLink(apa_cache_t *clink, apa_cache_t *cnew) +{ + cnew->tail=clink; + cnew->next=clink->next; + clink->next->tail=cnew; + clink->next=cnew; +} + +apa_cache_t *apaCacheUnLink(apa_cache_t *clink) +{ + clink->tail->next=clink->next; + clink->next->tail=clink->tail; + return clink; +} + +int apaCacheTransfer(apa_cache_t *clink, int type) +{ + int err; + if(type) + err=apaWriteHeader(clink->device, clink->header, clink->sector); + else// 0 + err=apaReadHeader(clink->device, clink->header, clink->sector); + + if(err) + { + APA_PRINTF(APA_DRV_NAME": error: disk err %d on device %ld, sector %ld, type %d\n", + err, clink->device, clink->sector, type); + if(type==0)// save any read error's.. + apaSaveError(clink->device, clink->header, APA_SECTOR_SECTOR_ERROR, clink->sector); + } + clink->flags&=~APA_CACHE_FLAG_DIRTY; + return err; +} + +void apaCacheFlushDirty(apa_cache_t *clink) +{ + if(clink->flags&APA_CACHE_FLAG_DIRTY) + apaCacheTransfer(clink, APA_IO_MODE_WRITE); +} + +int apaCacheFlushAllDirty(s32 device) +{ + int i; + // flush apal + for(i=1;inused==0) + clink=apaCacheUnLink(clink); + clink->nused++; + return clink; + } + if((cacheBuf->tail==cacheBuf) && + (cacheBuf->tail==cacheBuf->tail->next)){ + APA_PRINTF(APA_DRV_NAME": error: free buffer empty\n"); + } + else + { + clink=cacheBuf->next; + if(clink->flags & APA_CACHE_FLAG_DIRTY) + APA_PRINTF(APA_DRV_NAME": error: dirty buffer allocated\n"); + clink->flags=0; + clink->nused=1; + clink->device=device; + clink->sector=sector; + clink=apaCacheUnLink(clink); + } + if(clink==NULL) + { + *result=-ENOMEM; + return NULL; + } + if(!mode) + { + if((*result=apaCacheTransfer(clink, APA_IO_MODE_READ))<0){ + clink->nused=0; + clink->device=-1; + apaCacheLink(cacheBuf, clink); + clink=NULL; + } + } + return clink; +} + +void apaCacheFree(apa_cache_t *clink) +{ + if(clink==NULL){ + APA_PRINTF(APA_DRV_NAME": error: null buffer returned\n"); + return; + } + if(clink->nused==0){ + APA_PRINTF(APA_DRV_NAME": error: unused cache returned\n"); + return; + } + if(clink->flags & APA_CACHE_FLAG_DIRTY) + APA_PRINTF(APA_DRV_NAME": error: dirty buffer returned\n"); + clink->nused--; + if(clink->nused==0) + apaCacheLink(cacheBuf->tail, clink); + return; +} + +apa_cache_t *apaCacheAlloc(void) +{ + apa_cache_t *cnext; + + if((cacheBuf->tail==cacheBuf) && + (cacheBuf->tail==cacheBuf->tail->next)){ + APA_PRINTF(APA_DRV_NAME": error: free buffer empty\n"); + return NULL; + } + cnext=cacheBuf->next; + if(cnext->flags & APA_CACHE_FLAG_DIRTY) + APA_PRINTF(APA_DRV_NAME": error: dirty buffer allocated\n"); + cnext->nused=1; + cnext->flags=0; + cnext->device=-1; + cnext->sector=-1; + return apaCacheUnLink(cnext); +} diff --git a/common/libapa/src/free.c b/common/libapa/src/free.c new file mode 100644 index 0000000..7b9bfcc --- /dev/null +++ b/common/libapa/src/free.c @@ -0,0 +1,85 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +# +# Free space calculation routines +*/ + +#include +#include +#ifdef _IOP +#include +#else +#include +#endif +#include +#include +#include + +#include "apa-opt.h" +#include "libapa.h" + +static void apaCalculateFreeSpace(u32 *free, u32 sectors) +{ + if(0x1FFFFF < sectors) + { + *free += sectors; + return; + } + + if((*free & sectors) == 0) + { + *free |= sectors; + return; + } + + for(sectors /= 2; 0x3FFFF < sectors; sectors /= 2) + *free |= sectors; +} + +int apaGetFreeSectors(s32 device, u32 *free, apa_device_t *deviceinfo) +{ + u32 sectors, partMax; + int rv; + apa_cache_t *clink; + + sectors = 0; + *free = 0; + if((clink = apaCacheGetHeader(device, 0, APA_IO_MODE_READ, &rv)) != NULL) + { + do{ + if(clink->header->type == 0) + apaCalculateFreeSpace(free, clink->header->length); + sectors += clink->header->length; + }while((clink = apaGetNextHeader(clink, &rv)) != NULL); + } + + if(rv == 0) + { + for(partMax = deviceinfo[device].partitionMaxSize; 0x0003FFFF < partMax; partMax = deviceinfo[device].partitionMaxSize) + { //As weird as it looks, this was how it was done in the original HDD.IRX. + for( ; 0x0003FFFF < partMax; partMax /= 2) + { + //Non-SONY: Perform 64-bit arithmetic here to avoid overflows when dealing with large disks. + if((sectors % partMax == 0) && ((u64)sectors + partMax < deviceinfo[device].totalLBA)) + { + apaCalculateFreeSpace(free, partMax); + sectors += partMax; + break; + } + } + + if(0x0003FFFF >= partMax) + break; + } + + APA_PRINTF(APA_DRV_NAME": total = %08lx sectors, installable = %08lx sectors.\n", sectors, *free); + } + + return rv; +} diff --git a/common/libapa/src/journal.c b/common/libapa/src/journal.c new file mode 100644 index 0000000..069412d --- /dev/null +++ b/common/libapa/src/journal.c @@ -0,0 +1,87 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +# +# APA journal related routines +*/ + +#include +#include +#include +#ifdef _IOP +#include +#else +#include +#endif +#include +#include + +#include "apa-opt.h" +#include "libapa.h" + +// Globals +static apa_journal_t journalBuf; + +int apaJournalFlush(s32 device) +{// this write any thing that in are journal buffer :) + if(ata_device_flush_cache(device)) + return -EIO; + if(ata_device_sector_io(device, &journalBuf, APA_SECTOR_APAL, 1, ATA_DIR_WRITE)) + return -EIO; + if(ata_device_flush_cache(device)) + return -EIO; + return 0; +} + +int apaJournalReset(s32 device) +{ + memset(&journalBuf, 0, sizeof(apa_journal_t)); + journalBuf.magic=APAL_MAGIC; + return apaJournalFlush(device); +} + +int apaJournalWrite(apa_cache_t *clink) +{ + clink->header->checksum=journalCheckSum(clink->header); + if(ata_device_sector_io(clink->device, clink->header, + (journalBuf.num << 1)+APA_SECTOR_APAL_HEADERS, 2, ATA_DIR_WRITE)) + return -EIO; + journalBuf.sectors[journalBuf.num]=clink->sector; + journalBuf.num++; + return 0; +} + +int apaJournalRestore(s32 device) +{ // copies the journal from the HDD and erases the original. + int i, ret; + u32 sector; + apa_cache_t *clink; + + APA_PRINTF(APA_DRV_NAME": checking log...\n"); + ret = ata_device_sector_io(device, &journalBuf, APA_SECTOR_APAL, sizeof(apa_journal_t)/512, ATA_DIR_READ) == 0 ? 0 : -EIO; + if((ret == 0) && (journalBuf.magic == APAL_MAGIC)) + { + if(journalBuf.num == 0) + return 0; + + clink=apaCacheAlloc(); + for(i=0, sector=APA_SECTOR_APAL_HEADERS;iheader, sector, 2, ATA_DIR_READ) == 0) ? 0 : -EIO; + if(ret != 0) + break; + + ret = (ata_device_sector_io(device, clink->header, journalBuf.sectors[i], 2, ATA_DIR_WRITE) == 0) ? 0 : -EIO; + if(ret != 0) + break; + } + apaCacheFree(clink); + } + + return apaJournalReset(device); +} diff --git a/common/libapa/src/misc.c b/common/libapa/src/misc.c new file mode 100644 index 0000000..bf5b634 --- /dev/null +++ b/common/libapa/src/misc.c @@ -0,0 +1,138 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +# +# Miscellaneous routines +*/ + +#include +#ifdef _IOP +#include +#include +#include +#include +#include +#else +#include +#include +#include +#endif +#include +#include +#include + +#include "apa-opt.h" +#include "libapa.h" + +void *apaAllocMem(int size) +{ +#ifdef _IOP + int intrStat; + void *mem; + + CpuSuspendIntr(&intrStat); + mem = AllocSysMemory(ALLOC_FIRST, size, NULL); + if(mem == NULL) + APA_PRINTF(APA_DRV_NAME": error: out of memory\n"); + CpuResumeIntr(intrStat); + + return mem; +#else + return malloc(size); +#endif +} + +void apaFreeMem(void *ptr) +{ +#ifdef _IOP + int intrStat; + + CpuSuspendIntr(&intrStat); + FreeSysMemory(ptr); + CpuResumeIntr(intrStat); +#else + free(ptr); +#endif +} + +int apaGetTime(apa_ps2time_t *tm) +{ +#ifdef _IOP + int ret, i; + sceCdCLOCK cdtime; + static apa_ps2time_t timeBuf={ + 0, 7, 6, 5, 4, 3, 2000 // used if can not get time... + }; + + for(i = 0; i < 20; i++) + { + ret = sceCdReadClock(&cdtime); + + if(ret!=0 && cdtime.stat==0) + { + timeBuf.sec=btoi(cdtime.second); + timeBuf.min=btoi(cdtime.minute); + timeBuf.hour=btoi(cdtime.hour); + timeBuf.day=btoi(cdtime.day); + timeBuf.month=btoi(cdtime.month & 0x7F); //The old CDVDMAN sceCdReadClock() function does not automatically file off the highest bit. + timeBuf.year=btoi(cdtime.year) + 2000; + break; + } else { + if(!(cdtime.stat & 0x80)) + break; + } + + DelayThread(100000); + } + + memcpy(tm, &timeBuf, sizeof(apa_ps2time_t)); +#else + time_t rawtime; + struct tm * timeinfo; + time (&rawtime); + timeinfo=localtime (&rawtime); + + tm->sec=timeinfo->tm_sec; + tm->min=timeinfo->tm_min; + tm->hour=timeinfo->tm_hour; + tm->day=timeinfo->tm_mday; + tm->month=timeinfo->tm_mon; + tm->year=timeinfo->tm_year+1900; +#endif + + return 0; +} + +int apaGetIlinkID(u8 *idbuf) +{ +#ifdef _IOP + u32 stat; + int i; + + for(i = 0; ; i++) + { + stat=0; + memset(idbuf, 0, 32); + if((sceCdRI(idbuf, &stat) != 0) && (stat == 0)) + { + return 0; + } + + if(i >= 20) + break; + + DelayThread(100000); + } + + APA_PRINTF(APA_DRV_NAME": Error: cannot get id\n"); + return -EIO; +#else + memset(idbuf, 0, 32); + return 0; +#endif +} diff --git a/common/libapa/src/password.c b/common/libapa/src/password.c new file mode 100644 index 0000000..46b6a90 --- /dev/null +++ b/common/libapa/src/password.c @@ -0,0 +1,522 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +# +# Password-handling routines +*/ + +#include +#include +#include +#ifdef _IOP +#include +#else +#include +#endif +#include +#include + +#include "apa-opt.h" +#include "libapa.h" + +int apaPassCmp(const char *pw1, const char *pw2) +{ +#ifdef APA_ENABLE_PASSWORDS + return memcmp(pw1, pw2, APA_PASSMAX) ? -EACCES : 0; +#else + //Passwords are not supported, hence this check should always pass. + return 0; +#endif +} + +static void DESEncryptPassword(u32 id_lo, u32 id_hi, char *password_out, const char *password); + +void apaEncryptPassword(const char *id, char *password_out, const char *password_in) +{ + char password[APA_PASSMAX]; + memcpy(password, password_in, APA_PASSMAX); + DESEncryptPassword(*(u32*)(id), *(u32*)(id + 4), password_out, password); +} + +struct KeyPair{ + u32 lo, hi; +}; + +//This is a standard DES-ECB implementation. It encrypts the partition ID with the password. +static void DESEncryptPassword(u32 id_lo, u32 id_hi, char *password_out, const char *password) +{ + //Left + static const u8 PC1[]={ 0x39, 0x31, 0x29, 0x21, 0x19, 0x11, 0x09, + 0x01, 0x3a, 0x32, 0x2a, 0x22, 0x1a, 0x12, + 0x0a, 0x02, 0x3b, 0x33, 0x2b, 0x23, 0x1b, + 0x13, 0x0b, 0x03, 0x3c, 0x34, 0x2c, 0x24, + //Right + 0x3f, 0x37, 0x2f, 0x27, 0x1f, 0x17, 0x0f, + 0x07, 0x3e, 0x36, 0x2e, 0x26, 0x1e, 0x16, + 0x0e, 0x06, 0x3d, 0x35, 0x2d, 0x25, 0x1d, + 0x15, 0x0d, 0x05, 0x1c, 0x14, 0x0c, 0x04 }; + static const u8 Rotations[]={ 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 }; + static const u8 PC2[]={ 0x0e, 0x11, 0x0b, 0x18, 0x01, 0x05, + 0x03, 0x1c, 0x0f, 0x06, 0x15, 0x0a, + 0x17, 0x13, 0x0c, 0x04, 0x1a, 0x08, + 0x10, 0x07, 0x1b, 0x14, 0x0d, 0x02, + 0x29, 0x34, 0x1f, 0x25, 0x2f, 0x37, + 0x1e, 0x28, 0x33, 0x2d, 0x21, 0x30, + 0x2c, 0x31, 0x27, 0x38, 0x22, 0x35, + 0x2e, 0x2a, 0x32, 0x24, 0x1d, 0x20 }; + static const u8 IP[]={ 0x3a, 0x32, 0x2a, 0x22, 0x1a, 0x12, 0x0a, 0x02, 0x3c, 0x34, 0x2c, 0x24, 0x1c, 0x14, 0x0c, 0x04, + 0x3e, 0x36, 0x2e, 0x26, 0x1e, 0x16, 0x0e, 0x06, 0x40, 0x38, 0x30, 0x28, 0x20, 0x18, 0x10, 0x08, + + 0x39, 0x31, 0x29, 0x21, 0x19, 0x11, 0x09, 0x01, 0x3b, 0x33, 0x2b, 0x23, 0x1b, 0x13, 0x0b, 0x03, + 0x3d, 0x35, 0x2d, 0x25, 0x1d, 0x15, 0x0d, 0x05, 0x3f, 0x37, 0x2f, 0x27, 0x1f, 0x17, 0x0f, 0x07 }; + static const u8 Expansion[]={ 0x20, 0x01, 0x02, 0x03, 0x04, 0x05, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x08, 0x09, 0x0a, 0x0b, + 0x0c, 0x0d, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x14, 0x15, + 0x16, 0x17, 0x18, 0x19, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x01 }; + static const u8 sbox[][64]={ { 0x0e, 0x04, 0x0d, 0x01, 0x02, 0x0f, 0x0b, 0x08, 0x03, 0x0a, 0x06, 0x0c, 0x05, 0x09, 0x00, 0x07, + 0x00, 0x0f, 0x07, 0x04, 0x0e, 0x02, 0x0d, 0x01, 0x0a, 0x06, 0x0c, 0x0b, 0x09, 0x05, 0x03, 0x08, + 0x04, 0x01, 0x0e, 0x08, 0x0d, 0x06, 0x02, 0x0b, 0x0f, 0x0c, 0x09, 0x07, 0x03, 0x0a, 0x05, 0x00, + 0x0f, 0x0c, 0x08, 0x02, 0x04, 0x09, 0x01, 0x07, 0x05, 0x0b, 0x03, 0x0e, 0x0a, 0x00, 0x06, 0x0d }, + { 0x0f, 0x01, 0x08, 0x0e, 0x06, 0x0b, 0x03, 0x04, 0x09, 0x07, 0x02, 0x0d, 0x0c, 0x00, 0x05, 0x0a, + 0x03, 0x0d, 0x04, 0x07, 0x0f, 0x02, 0x08, 0x0e, 0x0c, 0x00, 0x01, 0x0a, 0x06, 0x09, 0x0b, 0x05, + 0x00, 0x0e, 0x07, 0x0b, 0x0a, 0x04, 0x0d, 0x01, 0x05, 0x08, 0x0c, 0x06, 0x09, 0x03, 0x02, 0x0f, + 0x0d, 0x08, 0x0a, 0x01, 0x03, 0x0f, 0x04, 0x02, 0x0b, 0x06, 0x07, 0x0c, 0x00, 0x05, 0x0e, 0x09 }, + { 0x0a, 0x00, 0x09, 0x0e, 0x06, 0x03, 0x0f, 0x05, 0x01, 0x0d, 0x0c, 0x07, 0x0b, 0x04, 0x02, 0x08, + 0x0d, 0x07, 0x00, 0x09, 0x03, 0x04, 0x06, 0x0a, 0x02, 0x08, 0x05, 0x0e, 0x0c, 0x0b, 0x0f, 0x01, + 0x0d, 0x06, 0x04, 0x09, 0x08, 0x0f, 0x03, 0x00, 0x0b, 0x01, 0x02, 0x0c, 0x05, 0x0a, 0x0e, 0x07, + 0x01, 0x0a, 0x0d, 0x00, 0x06, 0x09, 0x08, 0x07, 0x04, 0x0f, 0x0e, 0x03, 0x0b, 0x05, 0x02, 0x0c }, + { 0x07, 0x0d, 0x0e, 0x03, 0x00, 0x06, 0x09, 0x0a, 0x01, 0x02, 0x08, 0x05, 0x0b, 0x0c, 0x04, 0x0f, + 0x0d, 0x08, 0x0b, 0x05, 0x06, 0x0f, 0x00, 0x03, 0x04, 0x07, 0x02, 0x0c, 0x01, 0x0a, 0x0e, 0x09, + 0x0a, 0x06, 0x09, 0x00, 0x0c, 0x0b, 0x07, 0x0d, 0x0f, 0x01, 0x03, 0x0e, 0x05, 0x02, 0x08, 0x04, + 0x03, 0x0f, 0x00, 0x06, 0x0a, 0x01, 0x0d, 0x08, 0x09, 0x04, 0x05, 0x0b, 0x0c, 0x07, 0x02, 0x0e }, + { 0x02, 0x0c, 0x04, 0x01, 0x07, 0x0a, 0x0b, 0x06, 0x08, 0x05, 0x03, 0x0f, 0x0d, 0x00, 0x0e, 0x09, + 0x0e, 0x0b, 0x02, 0x0c, 0x04, 0x07, 0x0d, 0x01, 0x05, 0x00, 0x0f, 0x0a, 0x03, 0x09, 0x08, 0x06, + 0x04, 0x02, 0x01, 0x0b, 0x0a, 0x0d, 0x07, 0x08, 0x0f, 0x09, 0x0c, 0x05, 0x06, 0x03, 0x00, 0x0e, + 0x0b, 0x08, 0x0c, 0x07, 0x01, 0x0e, 0x02, 0x0d, 0x06, 0x0f, 0x00, 0x09, 0x0a, 0x04, 0x05, 0x03 }, + { 0x0c, 0x01, 0x0a, 0x0f, 0x09, 0x02, 0x06, 0x08, 0x00, 0x0d, 0x03, 0x04, 0x0e, 0x07, 0x05, 0x0b, + 0x0a, 0x0f, 0x04, 0x02, 0x07, 0x0c, 0x09, 0x05, 0x06, 0x01, 0x0d, 0x0e, 0x00, 0x0b, 0x03, 0x08, + 0x09, 0x0e, 0x0f, 0x05, 0x02, 0x08, 0x0c, 0x03, 0x07, 0x00, 0x04, 0x0a, 0x01, 0x0d, 0x0b, 0x06, + 0x04, 0x03, 0x02, 0x0c, 0x09, 0x05, 0x0f, 0x0a, 0x0b, 0x0e, 0x01, 0x07, 0x06, 0x00, 0x08, 0x0d }, + { 0x04, 0x0b, 0x02, 0x0e, 0x0f, 0x00, 0x08, 0x0d, 0x03, 0x0c, 0x09, 0x07, 0x05, 0x0a, 0x06, 0x01, + 0x0d, 0x00, 0x0b, 0x07, 0x04, 0x09, 0x01, 0x0a, 0x0e, 0x03, 0x05, 0x0c, 0x02, 0x0f, 0x08, 0x06, + 0x01, 0x04, 0x0b, 0x0d, 0x0c, 0x03, 0x07, 0x0e, 0x0a, 0x0f, 0x06, 0x08, 0x00, 0x05, 0x09, 0x02, + 0x06, 0x0b, 0x0d, 0x08, 0x01, 0x04, 0x0a, 0x07, 0x09, 0x05, 0x00, 0x0f, 0x0e, 0x02, 0x03, 0x0c }, + { 0x0d, 0x02, 0x08, 0x04, 0x06, 0x0f, 0x0b, 0x01, 0x0a, 0x09, 0x03, 0x0e, 0x05, 0x00, 0x0c, 0x07, + 0x01, 0x0f, 0x0d, 0x08, 0x0a, 0x03, 0x07, 0x04, 0x0c, 0x05, 0x06, 0x0b, 0x00, 0x0e, 0x09, 0x02, + 0x07, 0x0b, 0x04, 0x01, 0x09, 0x0c, 0x0e, 0x02, 0x00, 0x06, 0x0a, 0x0d, 0x0f, 0x03, 0x05, 0x08, + 0x02, 0x01, 0x0e, 0x07, 0x04, 0x0a, 0x08, 0x0d, 0x0f, 0x0c, 0x09, 0x00, 0x03, 0x05, 0x06, 0x0b }}; + static const u8 Permutation[]={ 0x10, 0x07, 0x14, 0x15, 0x1d, 0x0c, 0x1c, 0x11, 0x01, 0x0f, 0x17, 0x1a, 0x05, 0x12, 0x1f, 0x0a, + 0x02, 0x08, 0x18, 0x0e, 0x20, 0x1b, 0x03, 0x09, 0x13, 0x0d, 0x1e, 0x06, 0x16, 0x0b, 0x04, 0x19 }; + static const u8 FP[]={ 0x28, 0x08, 0x30, 0x10, 0x38, 0x18, 0x40, 0x20, 0x27, 0x07, 0x2f, 0x0f, 0x37, 0x17, 0x3f, 0x1f, + 0x26, 0x06, 0x2e, 0x0e, 0x36, 0x16, 0x3e, 0x1e, 0x25, 0x05, 0x2d, 0x0d, 0x35, 0x15, 0x3d, 0x1d, + 0x24, 0x04, 0x2c, 0x0c, 0x34, 0x14, 0x3c, 0x1c, 0x23, 0x03, 0x2b, 0x0b, 0x33, 0x13, 0x3b, 0x1b, + 0x22, 0x02, 0x2a, 0x0a, 0x32, 0x12, 0x3a, 0x1a, 0x21, 0x01, 0x29, 0x09, 0x31, 0x11, 0x39, 0x19 }; + u32 BitMask_hi, BitMask_lo; + u32 PermutedKeyMask_lo, PermutedKeyMask_hi, Rot2Mask_lo, Rot1Mask_lo, Rot2Mask_hi, Rot1Mask_hi; + u32 BitPerm_lo, BitPerm_hi; + u32 password_lo, password_hi; + u32 key1, key2, key3, key4, input_lo, input_hi; + u32 PermutedKey_lo, PermutedKey_hi, kVal_lo, kVal_hi, PermutedInput_lo, PermutedInput_hi, eVal_lo, eVal_hi, sVal_lo, sVal_hi, pVal_lo, pVal_hi, output_lo, output_hi; + struct KeyPair pairC[17], pairD[17], pairK[17], pairL[17], pairR[17]; + struct KeyPair *pPairC, *pPairD, *pPairK, *pPairL, *pPairR; + unsigned int i, j, k; + int shift; + + //Phase 1 (Permute KEY with Permuted Choice 1) + PermutedKey_lo = 0; + PermutedKey_hi = 0; + BitMask_lo = 0; + BitMask_hi = 0x80000000; + BitPerm_lo = 0; + BitPerm_hi = 0x80000000; + + password_lo = *(const u32*)password; + password_hi = *(const u32*)(password + 4); + + for(i = 0; i < 56; i++) + { + shift = PC1[i] - 1; + + if((shift << 26) >= 0) + { //0 to 31-bit shift + if((shift << 26) > 0) //Shift all bits left by shift (hi,lo >> shift) + key1 = (BitMask_lo >> shift) | (BitMask_hi << (-shift)); + else //0-bit shift, which should not happen. + key1 = BitMask_lo >> shift; + + key2 = BitMask_hi >> shift; + } else { //>31-bit shift + key2 = 0; + key1 = BitMask_hi >> shift; + } + + //If bit (shift) is set, set the current bit. + if(((password_lo & key1) | (password_hi & key2)) != 0) + { + PermutedKey_lo |= BitPerm_lo; + PermutedKey_hi |= BitPerm_hi; + } + + //Shift all bits left by 1 (hi,lo >> 1) + BitPerm_lo = (BitPerm_lo >> 1) | (BitPerm_hi << 31); + BitPerm_hi >>= 1; + } + + //Phase 2 (Key Schedule Calculation) + //This mask is used to extract the lower 28-bits of a key. + PermutedKeyMask_lo = 0x0FFFFFFF; + PermutedKeyMask_hi = 0x00000000; + + Rot2Mask_lo = 3; + Rot2Mask_hi = 0x00000000; + Rot1Mask_lo = 1; + Rot1Mask_hi = 0x00000000; + + //C-bits, upper 28-bits of Permuted Key + pairC[0].lo = PermutedKey_hi >> 4; + pairC[0].hi = 0; + + //D-bits, lower 28-bits of Permuted Key + pairD[0].lo = ((PermutedKey_lo >> 8) | (PermutedKey_hi << 24)) & PermutedKeyMask_lo; + pairD[0].hi = ((PermutedKey_hi >> 8) & PermutedKeyMask_hi); + + //Calculate all Cn and Dn. + for(i = 0; i < 16; i++) + { + if(Rotations[i] != 1) + { //Rotate left twice + //hi 26:0 | lo 31:26 + key1 = ((pairC[i].hi << 6) | (pairC[i].lo >> 26)) & Rot2Mask_lo; + //lo 29:0 + //key1|key 4 results in: LLLLLLLLLLLLLLLLLLLLLLLLLLLLLXX, where X is from key1 + key4 = pairC[i].lo << 2; + //hi 31:26 + //This part is discarded. + key2 = (pairC[i].hi >> 26) & Rot2Mask_hi; + //hi 29:2 | lo 31:30 + //key2|key3 results in: HHHHHHHHHHHHHHHHHHHHHHHHHHHHHLL + key3 = (pairC[i].hi << 2) | (pairC[i].lo >> 30); + } else { //Rotate left once + //hi 27:0 | lo 31:27 + key1 = ((pairC[i].hi << 5) | (pairC[i].lo >> 27)) & Rot1Mask_lo; + //lo 30:0 + //key1|key4 results in: LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLX, where X is from key1 + key4 = pairC[i].lo << 1; + //hi 31:27 + //This part is discarded. + key2 = (pairC[i].hi >> 27) & Rot1Mask_hi; + //hi 30:0 | lo 31:31 + //key2:key3 results in: HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHL + key3 = (pairC[i].hi << 1) | (pairC[i].lo >> 31); + } + + //Merge the two rotated parts together, for the hi and low pair. + //Note: hi contains nothing. + pairC[i + 1].lo = (key1 | key4) & PermutedKeyMask_lo; + pairC[i + 1].hi = (key2 | key3) & PermutedKeyMask_hi; + + if(Rotations[i] != 1) + { //Rotate left twice + //hi 26:0 | lo 31:26 + key1 = ((pairD[i].hi << 6) | (pairD[i].lo >> 26)) & Rot2Mask_lo; + //lo 29:0 + //key1|key 4 results in: LLLLLLLLLLLLLLLLLLLLLLLLLLLLLXX, where X is from key1 + key4 = pairD[i].lo << 2; + //hi 31:26 + //This part is discarded. + key2 = (pairD[i].hi >> 26) & Rot2Mask_hi; + //hi 29:2 | lo 31:30 + //key2|key3 results in: HHHHHHHHHHHHHHHHHHHHHHHHHHHHHLL + key3 = (pairD[i].hi << 2) | (pairD[i].lo >> 30); + } else { //Rotate left once + //hi 27:0 | lo 31:27 + key1 = ((pairD[i].hi << 5) | (pairD[i].lo >> 27)) & Rot1Mask_lo; + //lo 30:0 + //key1|key4 results in: LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLX, where X is from key1 + key4 = pairD[i].lo << 1; + //hi 31:27 + //This part is discarded. + key2 = (pairD[i].hi >> 27) & Rot1Mask_hi; + //hi 30:0 | lo 31:31 + //key2:key3 results in: HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHL + key3 = (pairD[i].hi << 1) | (pairD[i].lo >> 31); + } + + //Merge the two rotated parts together, for the hi and low pair. + //Note: hi contains nothing. + pairD[i + 1].lo = (key1 | key4) & PermutedKeyMask_lo; + pairD[i + 1].hi = (key2 | key3) & PermutedKeyMask_hi; + } + + //Phase 3 + BitMask_lo = 0x00000000; + BitMask_hi = 0x80000000; + + // Determine all K, through the Permutation of CnDn by PC-2 + for(pPairC = &pairC[1],pPairD = &pairD[1],pPairK = &pairK[1]; pPairK < &pairK[17]; pPairC++,pPairD++,pPairK++) + { + kVal_lo = 0; + kVal_hi = 0; + BitPerm_lo = 0x00000000; + BitPerm_hi = 0x80000000; + + /* Calculate CnDn: + Note: hi of both Cn and Dn are assumed to, and actually contain nothing. + lo: (D lo 23:0) | (0 7:0) + D lo 23:0 LLLLLLLLLLLLLLLLLLLLLLLL00000000 + 0 7:0 00000000000000000000000000000000 + hi: (C hi 27:0) | (D hi 23:0) | (D lo 31:24) + C lo 27:0 llllllllllllllllllllllllllll0000 + D hi 23:0 HHHHHHHHHHHHHHHHHHHHHHHH00000000 All zero + D lo 31:24 000000000000000000000000XXXXLLLL + + l = C lo L = D lo + h = C hi H = D hi X = unused bits (28/32-bit value) */ + input_lo = 0 | (pPairD->lo << 8); + input_hi = (pPairC->lo << 4) | (pPairD->hi << 8) | (pPairD->lo >> 24); + + for(i = 0; i < 48; i++) + { + shift = PC2[i] - 1; + + if((shift << 26) >= 0) + { //0 to 31-bit shift + if((shift << 26) > 0) //Shift all bits left by shift (hi,lo >> shift) + key1 = (BitMask_lo >> shift) | (BitMask_hi << (-shift)); + else //0-bit shift, which should not happen. + key1 = BitMask_lo >> shift; + + key2 = BitMask_hi >> shift; + } else { //>31-bit shift + key2 = 0; + key1 = BitMask_hi >> shift; + } + + //If bit (shift) is set, set the current bit. + if(((input_lo & key1) | (input_hi & key2)) != 0) + { + kVal_lo |= BitPerm_lo; + kVal_hi |= BitPerm_hi; + } + + //Shift all bits left by 1 (hi,lo >> 1) + BitPerm_lo = (BitPerm_hi << 31) | (BitPerm_lo >> 1); + BitPerm_hi >>= 1; + } + + pPairK->lo = kVal_lo; + pPairK->hi = kVal_hi; + } + + //Phase 4 (Enciphering) + BitMask_lo = 0x00000000; + BitMask_hi = 0x80000000; + BitPerm_lo = 0x00000000; + BitPerm_hi = 0x80000000; + PermutedInput_lo = 0; + PermutedInput_hi = 0; + + //Initial Permutation (IP) + for(i = 0; i < 64; i++) + { + shift = IP[i] - 1; + + if((shift << 26) >= 0) + { //0 to 31-bit shift + if((shift << 26) > 0) //Shift all bits left by shift (hi,lo >> shift) + key1 = (BitMask_lo >> shift) | (BitMask_hi << (-shift)); + else //0-bit shift, which should not happen. + key1 = BitMask_lo >> shift; + + key2 = BitMask_hi >> shift; + } else { //>31-bit shift + key2 = 0; + key1 = BitMask_hi >> shift; + } + + //If bit (shift) is set, set the current bit. + if(((id_lo & key1) | (id_hi & key2)) != 0) + { + PermutedInput_lo |= BitPerm_lo; + PermutedInput_hi |= BitPerm_hi; + } + + //Shift all bits left by 1 (hi,lo >> 1) + BitPerm_lo = (BitPerm_lo >> 1) | (BitPerm_hi << 31); + BitPerm_hi >>= 1; + } + + //Phase 5 (Key-dependent Computation) + BitMask_lo = 0x00000000; + BitMask_hi = 0x80000000; + + //L0 and R0 make up the permuted input block. + //The lo values are not used. + pairL[0].lo = PermutedInput_lo & 0x00000000; + pairL[0].hi = PermutedInput_hi & 0xFFFFFFFF; + pairR[0].lo = 0x00000000; + pairR[0].hi = PermutedInput_lo << 0; + + for (j = 0, k = 1, pPairL = &pairL[1], pPairR = &pairR[1]; pPairR < &pairR[17]; j++, k++, pPairR++, pPairL++) + { + eVal_lo = 0; + eVal_hi = 0; + BitPerm_lo = 0x00000000; + BitPerm_hi = 0x80000000; + + //Ln' = Rn + pPairL->lo = input_lo = pairR[j].lo; + pPairL->hi = input_hi = pairR[j].hi; + + //Calculate E(Rn) + for(i = 0; i < 48; i++) + { + shift = Expansion[i] - 1; + if((shift << 26) >= 0) + { //0 to 31-bit shift + if((shift << 26) > 0) //Shift all bits left by shift (hi,lo >> shift) + key1 = (BitMask_lo >> shift) | (BitMask_hi << (-shift)); + else //0-bit shift, which should not happen. + key1 = BitMask_lo >> shift; + + key2 = BitMask_hi >> shift; + } else { //>31-bit shift + key2 = 0; + key1 = BitMask_hi >> shift; + } + + //If bit (shift) is set, set the current bit. + if(((input_lo & key1) | (input_hi & key2)) != 0) + { + eVal_lo |= BitPerm_lo; + eVal_hi |= BitPerm_hi; + } + + //Shift all bits left by 1 (hi,lo >> 1) + BitPerm_lo = (BitPerm_hi << 31) | (BitPerm_lo >> 1); + BitPerm_hi >>= 1; + } + + //Phase 6 (Substitution Boxes) + sVal_lo = 0; + sVal_hi = 0; + //Calculate Bn = Kn ^ E(Rn) + //Also, shift the 48-bit Expansion value forward towards position 0. + eVal_lo = (eVal_lo ^ pairK[k].lo) >> 16; + eVal_hi = eVal_hi ^ pairK[k].hi; + eVal_lo |= (eVal_hi << 16); + eVal_hi >>= 16; + + //Calculate Sn(Bn), which is stored in the upper 32-bits of the result. + for(i = 0, shift = 32; i < 8; i++, shift += 4) + { + input_lo = sbox[7 - i][((eVal_lo & 0x20) | ((eVal_lo & 0x3F) >> 1 & 0xF) | ((eVal_lo & 0x3F) << 4 & 0x10))]; + //Shift to the next 6-bit B-block + eVal_lo = (eVal_lo >> 6) | (eVal_hi << 26); + eVal_hi >>= 6; + + input_hi = 0; + if((shift << 26) >= 0) + { //0 to 31-bit shift + if((shift << 26) > 0) //Shift all bits right by shift (hi,lo >> shift) + key3 = (input_hi << shift) | (input_lo >> (-shift)); + else //0-bit shift, which should not happen. + key3 = input_hi << shift; + + key4 = input_lo << shift; + } else { //>31-bit shift + key3 = input_lo << shift; + key4 = 0; + } + + sVal_lo |= key4; + sVal_hi |= key3; + } + + //Phase 7 (Permutation with function P) + pVal_lo = 0; + pVal_hi = 0; + BitPerm_lo = 0x00000000; + BitPerm_hi = 0x80000000; + + //Calculate P(Sn(K ^ E(Rn))) + for(i = 0; i < 32; i++) + { + shift = Permutation[i] - 1; + if((shift << 26) >= 0) + { //0 to 31-bit shift + if((shift << 26) > 0) //Shift all bits left by shift (hi,lo >> shift) + key1 = (BitMask_lo >> shift) | (BitMask_hi << (-shift)); + else //0-bit shift, which should not happen. + key1 = BitMask_lo >> shift; + + key2 = BitMask_hi >> shift; + } else { //>31-bit shift + key1 = BitMask_hi >> shift; + key2 = 0; + } + + //If bit (shift) is set, set the current bit. + if(((sVal_lo & key1) | (sVal_hi & key2)) != 0) + { + pVal_lo |= BitPerm_lo; + pVal_hi |= BitPerm_hi; + } + + //Shift all bits left by 1 (hi,lo >> 1) + BitPerm_lo = (BitPerm_lo >> 1) | (BitPerm_hi << 31); + BitPerm_hi >>= 1; + } + + //0x00003bc4 - Rn' = Ln ^ f(Rn,Kn) + pPairR->lo = pairL[j].lo ^ pVal_lo; + pPairR->hi = pairL[j].hi ^ pVal_hi; + } + + //Phase 8 (Final Permutation) + output_lo = 0; + output_hi = 0; + BitMask_lo = BitPerm_lo = 0x00000000; + BitMask_hi = BitPerm_hi = 0x80000000; + //Retrieve preoutput blocks + //Ln and Rn lo do not contain anything, hence they are discarded. + input_lo = pairR[16].lo | (pairL[16].hi >> 0); + input_hi = pairR[16].hi | 0; + + //Subject the preoutput to the Final Permutation + for(i = 0; i < 64; i++) + { + shift = FP[i] - 1; + if((shift << 26) >= 0) + { //0 to 31-bit shift + if((shift << 26) > 0) //Shift all bits left by shift (hi,lo >> shift) + key1 = (BitMask_lo >> shift) | (BitMask_hi << (-shift)); + else //0-bit shift, which should not happen. + key1 = BitMask_lo >> shift; + + key2 = BitMask_hi >> shift; + } else { //>31-bit shift + key1 = BitMask_hi >> shift; + key2 = 0; + } + + //If bit (shift) is set, set the current bit. + if(((input_lo & key1) | (input_hi & key2)) != 0) + { + output_lo |= BitPerm_lo; + output_hi |= BitPerm_hi; + } + + //Shift all bits left by 1 (hi,lo >> 1) + BitPerm_lo = (BitPerm_lo >> 1) | (BitPerm_hi << 31); + BitPerm_hi >>= 1; + } + + //Encrypted output + *(u32*)password_out = output_lo; + *(u32*)(password_out + 4) = output_hi; +} diff --git a/common/libpfs/include/libpfs.h b/common/libpfs/include/libpfs.h new file mode 100644 index 0000000..d224929 --- /dev/null +++ b/common/libpfs/include/libpfs.h @@ -0,0 +1,343 @@ + +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +*/ + +#ifndef __LIBPFS_H__ +#define __LIBPFS_H__ + +#include + +// General constants +#define PFS_BLOCKSIZE 0x2000 +#define PFS_SUPER_MAGIC 0x50465300 // "PFS\0" aka Playstation Filesystem +#define PFS_JOUNRNAL_MAGIC 0x5046534C // "PFSL" aka PFS Log +#define PFS_SEGD_MAGIC 0x53454744 // "SEGD" aka segment descriptor direct +#define PFS_SEGI_MAGIC 0x53454749 // "SEGI" aka segment descriptor indirect +#define PFS_MAX_SUBPARTS 64 +#define PFS_NAME_LEN 255 +#define PFS_FORMAT_VERSION 3 +#define PFS_INODE_MAX_BLOCKS 114 + +// attribute flags +#define PFS_FIO_ATTR_READABLE 0x0001 +#define PFS_FIO_ATTR_WRITEABLE 0x0002 +#define PFS_FIO_ATTR_EXECUTABLE 0x0004 +#define PFS_FIO_ATTR_COPYPROTECT 0x0008 +#define PFS_FIO_ATTR_UNK0010 0x0010 +#define PFS_FIO_ATTR_SUBDIR 0x0020 +#define PFS_FIO_ATTR_UNK0040 0x0040 +#define PFS_FIO_ATTR_CLOSED 0x0080 +#define PFS_FIO_ATTR_UNK0100 0x0100 +#define PFS_FIO_ATTR_UNK0200 0x0200 +#define PFS_FIO_ATTR_UNK0400 0x0400 +#define PFS_FIO_ATTR_PDA 0x0800 +#define PFS_FIO_ATTR_PSX 0x1000 +#define PFS_FIO_ATTR_UNK2000 0x2000 +#define PFS_FIO_ATTR_HIDDEN 0x4000 + +// cache flags (status) +#define PFS_CACHE_FLAG_DIRTY 0x01 +#define PFS_CACHE_FLAG_NOLOAD 0x02 +#define PFS_CACHE_FLAG_MASKSTATUS 0x0F + +// cache flags (types) +#define PFS_CACHE_FLAG_NOTHING 0x00 +#define PFS_CACHE_FLAG_SEGD 0x10 +#define PFS_CACHE_FLAG_SEGI 0x20 +#define PFS_CACHE_FLAG_BITMAP 0x40 +#define PFS_CACHE_FLAG_MASKTYPE 0xF0 + +// fsck stats +#define PFS_FSCK_STAT_OK 0x00 +#define PFS_FSCK_STAT_WRITE_ERROR 0x01 +#define PFS_FSCK_STAT_ERRORS_FIXED 0x02 + +// odd and end +#define PFS_MODE_SET_FLAG 0x00 +#define PFS_MODE_REMOVE_FLAG 0x01 +#define PFS_MODE_CHECK_FLAG 0x02 + +// UID and GID +/* UID and GID are fixed with constants. + Files (and directories) created by the system have UID and GID set to 0, + as they were made with an older library. + + As of release v2.3, the UID and GID were changed to 0xFFFF. + The HDD Browser probably changes the UID and GID to 0xFFFF with chstat, + although I have not verified that. + + If the UID and GID are not set to 0xFFFF, then the software may ignore the entry. */ +#define PFS_UID 0xFFFF +#define PFS_GID 0xFFFF + +// journal/log +typedef struct { + u32 magic; // =PFS_JOUNRNAL_MAGIC + u16 num; // + u16 checksum; // + struct{ + u32 sector; // block/sector for partition + u16 sub; // main(0)/sub(+1) partition + u16 logSector; // block/sector offset in journal area + } log[127]; +} pfs_journal_t; + +// Attribute Entry +typedef struct{ + u8 kLen; // key len/used for offset in to str for value + u8 vLen; // value len + u16 aLen; // allocated length == ((kLen+vLen+7) & ~3) + char str[3]; // size = 3 so sizeof pfs_aentry_t=7 :P +} pfs_aentry_t; + +// Directory Entry +typedef struct { + u32 inode; + u8 sub; + u8 pLen; // path length + u16 aLen; // allocated length == ((pLen+8+3) & ~3) + char path[512-8]; +} pfs_dentry_t; + +// Block number/count pair (used in inodes) +typedef struct { + u32 number; // + u16 subpart; // + u16 count; // +} pfs_blockinfo_t; + +// Date/time descriptor +typedef struct { + u8 unused; // + u8 sec; // + u8 min; // + u8 hour; // + u8 day; // + u8 month; // + u16 year; // +} pfs_datetime_t; + +// Superblock structure +typedef struct { + u32 magic; // + u32 version; // + u32 modver; // + u32 pfsFsckStat; // + u32 zone_size; // + u32 num_subs; // number of subs attached to filesystem + pfs_blockinfo_t log; // block info for metadata log + pfs_blockinfo_t root; // block info for root directory +} pfs_super_block_t; + +// Inode structure +typedef struct { + u32 checksum; // Sum of all other words in the inode + u32 magic; // + pfs_blockinfo_t inode_block; // start block of inode + pfs_blockinfo_t next_segment; // next segment descriptor inode + pfs_blockinfo_t last_segment; // last segment descriptor inode + pfs_blockinfo_t unused; // + pfs_blockinfo_t data[PFS_INODE_MAX_BLOCKS]; // + u16 mode; // file mode + u16 attr; // file attributes + u16 uid; // + u16 gid; // + pfs_datetime_t atime; // + pfs_datetime_t ctime; // + pfs_datetime_t mtime; // + u64 size; // + u32 number_blocks; // number of blocks/zones used by file + u32 number_data; // number of used entries in data array + u32 number_segdesg; // number of "indirect blocks"/next segment descriptor's + u32 subpart; // subpart of inode + u32 reserved[4]; // +} pfs_inode_t; + +typedef struct { + char *devName; + int (*transfer)(int fd, void *buffer, /*u16*/u32 sub, u32 sector, u32 size, u32 mode); + u32 (*getSubNumber)(int fd); + u32 (*getSize)(int fd, /*u16*/u32 sub/*0=main 1+=subs*/); + void (*setPartitionError)(int fd); // set open partition as having an error + int (*flushCache)(int fd); +} pfs_block_device_t; + +typedef struct { + pfs_block_device_t *blockDev; // call table for hdd(hddCallTable) + int fd; // + u32 flags; // rename to attr ones checked + u32 total_zones; // number of zones in the filesystem + u32 zfree; // zone free + u32 sector_scale; // Number of sectors within a zone + u32 inode_scale; // Number of inodes within a zone + u32 zsize; // zone size + u32 num_subs; // number of sub partitions in the filesystem + pfs_blockinfo_t root_dir; // block info for root directory + pfs_blockinfo_t log; // block info for the log + pfs_blockinfo_t current_dir; // block info for current directory + u32 lastError; // 0 if no error :) + u32 free_zone[65]; // free zones in each partition (1 main + 64 possible subs) +} pfs_mount_t; + +typedef struct pfs_cache_s { + struct pfs_cache_s *next; // + struct pfs_cache_s *prev; // + u16 flags; // + u16 nused; // + pfs_mount_t *pfsMount; // + u32 sub; // main(0)/sub(+1) partition + u32 block; // block within for partition + union{ // + void *data; + pfs_inode_t *inode; + pfs_aentry_t *aentry; + pfs_dentry_t *dentry; + pfs_super_block_t *superblock; + u32 *bitmap; + } u; +} pfs_cache_t; + +typedef struct +{ + pfs_cache_t *inode; + u32 block_segment; // index into data array in inode structure for current zone segment + u32 block_offset; // block offset from start of current zone segment + u32 byte_offset; // byte offset into current zone +} pfs_blockpos_t; + +/////////////////////////////////////////////////////////////////////////////// +// Super Block functions + +#define PFS_SUPER_SECTOR 8192 +#define PFS_SUPER_BACKUP_SECTOR 8193 + +int pfsCheckZoneSize(u32 zone_size); +u32 pfsGetBitmapSizeSectors(int zoneScale, u32 partSize); +u32 pfsGetBitmapSizeBlocks(int scale, u32 mainsize); +int pfsFormatSub(pfs_block_device_t *blockDev, int fd, u32 sub, u32 reserved, u32 scale, u32 fragment); +int pfsFormat(pfs_block_device_t *blockDev, int fd, int zonesize, int fragment); +int pfsUpdateSuperBlock(pfs_mount_t *pfsMount, pfs_super_block_t *superblock, u32 sub); +int pfsMountSuperBlock(pfs_mount_t *pfsMount); + +/////////////////////////////////////////////////////////////////////////////// +// Cache functions + +void pfsCacheFree(pfs_cache_t *clink); +void pfsCacheLink(pfs_cache_t *clink, pfs_cache_t *cnew); +pfs_cache_t *pfsCacheUnLink(pfs_cache_t *clink); +pfs_cache_t *pfsCacheUsedAdd(pfs_cache_t *clink); +int pfsCacheTransfer(pfs_cache_t* clink, int mode); +void pfsCacheFlushAllDirty(pfs_mount_t *pfsMount); +pfs_cache_t *pfsCacheAlloc(pfs_mount_t *pfsMount, u16 sub, u32 block, int flags, int *result); +pfs_cache_t *pfsCacheGetData(pfs_mount_t *pfsMount, u16 sub, u32 block, int flags, int *result); +pfs_cache_t *pfsCacheAllocClean(int *result); +int pfsCacheIsFull(void); +int pfsCacheInit(u32 numBuf, u32 bufSize); +void pfsCacheClose(pfs_mount_t *pfsMount); +void pfsCacheMarkClean(pfs_mount_t *pfsMount, u32 subpart, u32 blockStart, u32 blockEnd); + +/////////////////////////////////////////////////////////////////////////////// +// Bitmap functions + +#define PFS_BITMAP_ALLOC 0 +#define PFS_BITMAP_FREE 1 + +typedef struct +{ + u32 chunk; + u32 index; + u32 bit; + u32 partitionChunks; + u32 partitionRemainder; +} pfs_bitmapInfo_t; + +void pfsBitmapSetupInfo(pfs_mount_t *pfsMount, pfs_bitmapInfo_t *info, u32 subpart, u32 number); +void pfsBitmapAllocFree(pfs_cache_t *clink, u32 operation, u32 subpart, u32 chunk, u32 index, u32 _bit, u32 count); +int pfsBitmapAllocateAdditionalZones(pfs_mount_t *pfsMount, pfs_blockinfo_t *bi, u32 count); +int pfsBitmapAllocZones(pfs_mount_t *pfsMount, pfs_blockinfo_t *bi, u32 amount); +int pfsBitmapSearchFreeZone(pfs_mount_t *pfsMount, pfs_blockinfo_t *bi, u32 max_count); +void pfsBitmapFreeBlockSegment(pfs_mount_t *pfsMount, pfs_blockinfo_t *bi); +int pfsBitmapCalcFreeZones(pfs_mount_t *pfsMount, int sub); +void pfsBitmapShow(pfs_mount_t *pfsMount); +void pfsBitmapFreeInodeBlocks(pfs_cache_t *clink); + +/////////////////////////////////////////////////////////////////////////////// +// Block functions + +int pfsBlockSeekNextSegment(pfs_cache_t *clink, pfs_blockpos_t *blockpos); +u32 pfsBlockSyncPos(pfs_blockpos_t *blockpos, u64 size); +int pfsBlockInitPos(pfs_cache_t *clink, pfs_blockpos_t *blockpos, u64 position); +int pfsBlockExpandSegment(pfs_cache_t *clink, pfs_blockpos_t *blockpos, u32 count); +int pfsBlockAllocNewSegment(pfs_cache_t *clink, pfs_blockpos_t *blockpos, u32 blocks); +pfs_blockinfo_t* pfsBlockGetCurrent(pfs_blockpos_t *blockpos); +pfs_cache_t *pfsBlockGetNextSegment(pfs_cache_t *clink, int *result); +pfs_cache_t *pfsBlockGetLastSegmentDescriptorInode(pfs_cache_t *clink, int *result); + +/////////////////////////////////////////////////////////////////////////////// +// Directory-Entry (DEntry) inode functions + +pfs_cache_t *pfsGetDentry(pfs_cache_t *clink, char *path, pfs_dentry_t **dentry, u32 *size, int option); +int pfsGetNextDentry(pfs_cache_t *clink, pfs_blockpos_t *blockpos, u32 *position, char *name, pfs_blockinfo_t *bi); +pfs_cache_t *pfsFillDentry(pfs_cache_t *clink, pfs_dentry_t *dentry, char *path1, pfs_blockinfo_t *bi, u32 len, u16 mode); +pfs_cache_t *pfsDirAddEntry(pfs_cache_t *dir, char *filename, pfs_blockinfo_t *bi, u16 mode, int *result); +pfs_cache_t *pfsDirRemoveEntry(pfs_cache_t *clink, char *path); +int pfsCheckDirForFiles(pfs_cache_t *clink); +void pfsFillSelfAndParentDentries(pfs_cache_t *clink, pfs_blockinfo_t *self, pfs_blockinfo_t *parent); +pfs_cache_t* pfsSetDentryParent(pfs_cache_t *clink, pfs_blockinfo_t *bi, int *result); +pfs_cache_t *pfsInodeGetFileInDir(pfs_cache_t *dirInode, char *path, int *result); +pfs_cache_t *pfsInodeGetFile(pfs_mount_t *pfsMount, pfs_cache_t *clink, const char *name, int *result); +void pfsInodeFill(pfs_cache_t *ci, pfs_blockinfo_t *bi, u16 mode, u16 uid, u16 gid); +int pfsInodeRemove(pfs_cache_t *parent, pfs_cache_t *inode, char *path); +pfs_cache_t *pfsInodeGetParent(pfs_mount_t *pfsMount, pfs_cache_t *clink, const char *filename, char *path, int *result); +pfs_cache_t *pfsInodeCreate(pfs_cache_t *clink, u16 mode, u16 uid, u16 gid, int *result); +int pfsCheckAccess(pfs_cache_t *clink, int flags); +char* pfsSplitPath(char *filename, char *path, int *result); +u16 pfsGetMaxIndex(pfs_mount_t *pfsMount); + +int pfsAllocZones(pfs_cache_t *clink, int msize, int mode); +void pfsFreeZones(pfs_cache_t *pfree); + +/////////////////////////////////////////////////////////////////////////////// +// Inode functions + +void pfsInodePrint(pfs_inode_t *inode); +int pfsInodeCheckSum(pfs_inode_t *inode); +void pfsInodeSetTime(pfs_cache_t *clink); +void pfsInodeSetTimeParent(pfs_cache_t *parent, pfs_cache_t *self); +pfs_cache_t *pfsInodeGetData(pfs_mount_t *pfsMount, u16 sub, u32 inode, int *result); +int pfsInodeSync(pfs_blockpos_t *blockpos, u64 size, u32 used_segments); +pfs_cache_t *pfsGetDentriesChunk(pfs_blockpos_t *position, int *result); +pfs_cache_t *pfsGetDentriesAtPos(pfs_cache_t *clink, u64 position, int *offset, int *result); + +/////////////////////////////////////////////////////////////////////////////// +// Journal functions + +int pfsJournalChecksum(void *header); +void pfsJournalWrite(pfs_mount_t *pfsMount, pfs_cache_t *clink, u32 pfsCacheNumBuffers); +int pfsJournalReset(pfs_mount_t *pfsMount); +int pfsJournalFlush(pfs_mount_t *pfsMount); +int pfsJournalRestore(pfs_mount_t *pfsMount); +int pfsJournalResetThis(pfs_block_device_t *blockDev, int fd, u32 sector); + +/////////////////////////////////////////////////////////////////////////////// +// Function declerations + +int pfsFsckStat(pfs_mount_t *pfsMount, pfs_super_block_t *superblock, u32 stat, int mode); + +void *pfsAllocMem(int size); +void pfsFreeMem(void *buffer); +int pfsGetTime(pfs_datetime_t *tm); +void pfsPrintBitmap(const u32 *bitmap); + +pfs_block_device_t *pfsGetBlockDeviceTable(const char *name); +u32 pfsGetScale(u32 num, u32 size); +u32 pfsFixIndex(u32 index); + +#endif /* _LIBPFS_H */ diff --git a/common/libpfs/src/bitmap.c b/common/libpfs/src/bitmap.c new file mode 100644 index 0000000..aed0144 --- /dev/null +++ b/common/libpfs/src/bitmap.c @@ -0,0 +1,385 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +# +# PFS bitmap manipulation routines +*/ + +#include +#include +#include + +#include "pfs-opt.h" +#include "libpfs.h" + +extern u32 pfsMetaSize; +extern u32 pfsBlockSize; + +u32 pfsBitsPerBitmapChunk = 8192; // number of bitmap bits in each bitmap data chunk (1024 bytes) + +void pfsBitmapSetupInfo(pfs_mount_t *pfsMount, pfs_bitmapInfo_t *info, u32 subpart, u32 number) +{ + u32 size; + + size = pfsMount->blockDev->getSize(pfsMount->fd, subpart) >> pfsMount->sector_scale; + + info->chunk = number / pfsBitsPerBitmapChunk; + info->bit = number & 31; + info->index = (number % pfsBitsPerBitmapChunk) / 32; + info->partitionChunks = size / pfsBitsPerBitmapChunk; + info->partitionRemainder = size % pfsBitsPerBitmapChunk; +} + +// Allocates or frees (depending on operation) the bitmap area starting at chunk/index/bit, of size count +void pfsBitmapAllocFree(pfs_cache_t *clink, u32 operation, u32 subpart, u32 chunk, u32 index, u32 _bit, u32 count) +{ + int result; + u32 sector, bit; + u32 *bitmapWord, *bitmapEnd; + + while (clink) + { + bitmapEnd = (u32*)&((u8*)clink->u.bitmap)[pfsMetaSize]; + for (bitmapWord = &clink->u.bitmap[index]; (bitmapWord < bitmapEnd) && count; bitmapWord++, _bit = 0) + { + for (bit = _bit; bit < 32 && count; bit++, count--) + { + if(operation == PFS_BITMAP_ALLOC) + { + if (*bitmapWord & (1 << bit)) + PFS_PRINTF(PFS_DRV_NAME": Error: Tried to allocate used block!\n"); + + *bitmapWord |= (1 << bit); + } + else + { + if ((*bitmapWord & (1 << bit))==0) + PFS_PRINTF(PFS_DRV_NAME": Error: Tried to free unused block!\n"); + + *bitmapWord &= ~(1 << bit); + } + } + } + + index = 0; + clink->flags |= PFS_CACHE_FLAG_DIRTY; + pfsCacheFree(clink); + + if (count==0) + break; + + chunk++; + + sector = (1 << clink->pfsMount->inode_scale) + chunk; + if(subpart==0) + sector += 0x2000 >> pfsBlockSize; + + clink = pfsCacheGetData(clink->pfsMount, subpart, sector, PFS_CACHE_FLAG_BITMAP, &result); + } +} + +// Attempts to allocate 'count' more continuous zones for 'bi' +int pfsBitmapAllocateAdditionalZones(pfs_mount_t *pfsMount, pfs_blockinfo_t *bi, u32 count) +{ + int result; + pfs_bitmapInfo_t info; + pfs_cache_t *c; + int res=0; + u32 bitmapMax; + u32 *bitmapWord, *bitmapEnd; + + pfsBitmapSetupInfo(pfsMount, &info, bi->subpart, bi->number+bi->count); + + // Make sure we're not trying to allocate more than is possible + if ((u32)USHRT_MAX - bi->count < count) + count = USHRT_MAX - bi->count; + + // Loop over each bitmap chunk (each is 1024 bytes in size) until either we have allocated + // the requested amount of blocks, or until we have run out of space on the current partition + while ((((info.partitionRemainder==0) && (info.chunk < info.partitionChunks )) || + ((info.partitionRemainder!=0) && (info.chunk < info.partitionChunks+1))) && count) + { + u32 sector=(1<inode_scale) + info.chunk; + + // if main partition, add offset (in units of blocks) + if (bi->subpart==0) + sector += 0x2000 >> pfsBlockSize; + + // Read the bitmap chunk from the hdd + c=pfsCacheGetData(pfsMount, bi->subpart, sector, PFS_CACHE_FLAG_BITMAP, &result); + if (c==NULL)break; + + // Loop over each 32-bit word in the current bitmap chunk until + // we find a used zone or we've allocated all the zones we need + bitmapMax = info.chunk==info.partitionChunks ? info.partitionRemainder / 8 : pfsMetaSize; + bitmapEnd = (u32*)&((u8*)c->u.bitmap)[bitmapMax]; + for (bitmapWord=&c->u.bitmap[info.index]; (bitmapWord < bitmapEnd) && count; bitmapWord++, info.bit=0) + { + // Loop over each of the 32 bits in the current word from the current bitmap chunk, + // trying to allocate the requested number of zones + for ( ; (info.bit < 32) && count; count--) + { + // We only want to allocate a continuous area, so if we come + // accross a used zone bail + if (*bitmapWord & (1<flags |= PFS_CACHE_FLAG_DIRTY; + } + } + pfsCacheFree(c); + info.index=0; + info.chunk++; + } +exit: + + // Adjust global free zone counts + pfsMount->free_zone[bi->subpart]-=res; + pfsMount->zfree-=res; + return res; +} + +// Searches for 'amount' free zones, starting from the position specified in 'bi'. +// Returns 1 if allocation was successful, otherwise 0. +int pfsBitmapAllocZones(pfs_mount_t *pfsMount, pfs_blockinfo_t *bi, u32 amount) +{ + pfs_bitmapInfo_t info; + int result; + u32 startBit = 0, startPos = 0, startChunk = 0, count = 0; + u32 sector; + pfs_cache_t *bitmap; + u32 *bitmapWord, *bitmapEnd; + u32 i, bitmapMax; + + pfsBitmapSetupInfo(pfsMount, &info, bi->subpart, bi->number); + + for ( ; ((info.partitionRemainder==0) && (info.chunk < info.partitionChunks))|| + ((info.partitionRemainder!=0) && (info.chunk < info.partitionChunks+1)); info.chunk++){ + + sector = info.chunk + (1 << pfsMount->inode_scale); + if(bi->subpart==0) + sector += 0x2000 >> pfsBlockSize; + + // read in the bitmap chunk + bitmap = pfsCacheGetData(pfsMount, bi->subpart, sector, PFS_CACHE_FLAG_BITMAP, &result); + if(bitmap==0) + return 0; + + bitmapMax = info.chunk == info.partitionChunks ? info.partitionRemainder / 8 : pfsMetaSize; + bitmapEnd = (u32*)&((u8*)bitmap->u.bitmap)[bitmapMax]; + for (bitmapWord=&bitmap->u.bitmap[info.index]; bitmapWord < bitmapEnd; info.bit=0, bitmapWord++) + { + for (i=info.bit; i < 32; i++) + { + // if this bit is marked as free.. + if (((*bitmapWord >> i) & 1)==0) + { + if (count==0) + { + startBit = i; + startChunk = info.chunk; + startPos = bitmapWord - bitmap->u.bitmap; + } + if (++count == amount) + { + bi->number = (startPos * 32) + (startChunk * pfsBitsPerBitmapChunk) + startBit; + if (count < bi->count) + bi->count=count; + + if (bitmap->block != (startChunk + (1 << pfsMount->inode_scale))) + { + pfsCacheFree(bitmap); + sector = (1 << pfsMount->inode_scale) + startChunk; + if(bi->subpart==0) + sector += 0x2000 >> pfsBlockSize; + + bitmap = pfsCacheGetData(pfsMount, bi->subpart, sector, PFS_CACHE_FLAG_BITMAP, &result); + } + + pfsBitmapAllocFree(bitmap, PFS_BITMAP_ALLOC, bi->subpart, startChunk, startPos, startBit, bi->count); + return 1; + } + } + else + count=0; + } + } + pfsCacheFree(bitmap); + info.index=0; + } + return 0; +} + +// Searches for 'max_count' free zones over all the partitions, and +// allocates them. Returns 0 on success, -ENOSPC if the zones could +// not be allocated. +int pfsBitmapSearchFreeZone(pfs_mount_t *pfsMount, pfs_blockinfo_t *bi, u32 max_count) +{ + u32 num, count, n; + + num = pfsMount->num_subs + 1; + + if (bi->subpart >= num) + bi->subpart = 0; + if (bi->number) + num = pfsMount->num_subs + 2; + + count = max_count < 33 ? max_count : 32; //min(max_count, 32) + if(count < bi->count) + count = bi->count; //max(count, bi->count) + // => count = bound(bi->count, 32); + for(--num; num >= 0; num--) + { + for (n = count; n; n /= 2) + { + if ((pfsMount->free_zone[bi->subpart] >= n) && + pfsBitmapAllocZones(pfsMount, bi, n)) + { + pfsMount->free_zone[bi->subpart] -= bi->count; + pfsMount->zfree -= bi->count; + return 0; // the good exit ;) + } + } + + bi->number=0; + bi->subpart++; + + if(bi->subpart == pfsMount->num_subs + 1) + bi->subpart=0; + } + return -ENOSPC; +} + +// De-allocates the block segment 'bi' in the bitmaps +void pfsBitmapFreeBlockSegment(pfs_mount_t *pfsMount, pfs_blockinfo_t *bi) +{ + pfs_bitmapInfo_t info; + pfs_cache_t *clink; + u32 sector; + int rv; + + pfsBitmapSetupInfo(pfsMount, &info, bi->subpart, bi->number); + + sector = (1 << pfsMount->inode_scale) + info.chunk; + if(bi->subpart==0) + sector += 0x2000 >> pfsBlockSize; + + if((clink=pfsCacheGetData(pfsMount, (u16)bi->subpart, sector, PFS_CACHE_FLAG_BITMAP, &rv)) != NULL) + { + pfsBitmapAllocFree(clink, PFS_BITMAP_FREE, bi->subpart, info.chunk, info.index, info.bit, bi->count); + pfsMount->free_zone[(u16)bi->subpart]+=bi->count; + pfsMount->zfree+=bi->count; + } +} + +// Returns the number of free zones for the partition 'sub' +int pfsBitmapCalcFreeZones(pfs_mount_t *pfsMount, int sub) +{ + // "Free zone" map. Used to get number of free zone in bitmap, 4-bits at a time + u32 pfsFreeZoneBitmap[16]={4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 0}; + int result; + pfs_bitmapInfo_t info; + pfs_cache_t *clink; + u32 i, bitmapSize, zoneFree=0, sector; + + pfsBitmapSetupInfo(pfsMount, &info, sub, 0); + + while (((info.partitionRemainder!=0) && (info.chunkinode_scale) + info.chunk; + if (sub==0) + sector +=0x2000>>pfsBlockSize; + + if ((clink=pfsCacheGetData(pfsMount, sub, sector, PFS_CACHE_FLAG_BITMAP, &result))) + { + for (i=0; iu.bitmap)[i] & 0xF] + +pfsFreeZoneBitmap[((u8*)clink->u.bitmap)[i] >> 4]; + } + + pfsCacheFree(clink); + } + info.chunk++; + } + + return zoneFree; +} + +// Debugging function, prints bitmap information +void pfsBitmapShow(pfs_mount_t *pfsMount) +{ + int result; + pfs_bitmapInfo_t info; + u32 pn, bitcnt; + + for (pn=0; pn < pfsMount->num_subs+1; pn++) + { + bitcnt=pfsBitsPerBitmapChunk; + pfsBitmapSetupInfo(pfsMount, &info, pn, 0); + + while (((info.partitionRemainder!=0) && (info.chunkinode_scale) + info.chunk; + u32 i; + + if(pn==0) + sector += 0x2000 >> pfsBlockSize; + clink=pfsCacheGetData(pfsMount, pn, sector, PFS_CACHE_FLAG_BITMAP, &result); + + if (info.chunk == info.partitionChunks) + bitcnt=info.partitionRemainder; + + PFS_PRINTF(PFS_DRV_NAME": Zone show: pn %ld, bn %ld, bitcnt %ld\n", pn, info.chunk, bitcnt); + + for(i=0; (i < (u32)(1<u.bitmap+128*i); + + pfsCacheFree(clink); + info.chunk++; + } + } +} + +// Free's all blocks allocated to an inode +void pfsBitmapFreeInodeBlocks(pfs_cache_t *clink) +{ + pfs_mount_t *pfsMount=clink->pfsMount; + u32 i; + + pfsCacheUsedAdd(clink); + for(i=0;i < clink->u.inode->number_data; i++) + { + u32 index = pfsFixIndex(i); + pfs_blockinfo_t *bi=&clink->u.inode->data[index]; + int err; + + if(i!=0) { + if(index==0) + if((clink = pfsBlockGetNextSegment(clink, &err))==NULL) + return; + } + + pfsBitmapFreeBlockSegment(pfsMount, bi); + pfsCacheMarkClean(pfsMount, (u16)bi->subpart, bi->number << pfsMount->inode_scale, + (bi->number + bi->count) << pfsMount->inode_scale); + } + pfsCacheFree(clink); +} diff --git a/common/libpfs/src/block.c b/common/libpfs/src/block.c new file mode 100644 index 0000000..d1fbe4b --- /dev/null +++ b/common/libpfs/src/block.c @@ -0,0 +1,99 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +# +# PFS block/zone related routines +*/ + +#include +#include +#ifdef _IOP +#include +#else +#include +#endif + +#include "pfs-opt.h" +#include "libpfs.h" + +// Returns the next block descriptor inode +pfs_cache_t *pfsBlockGetNextSegment(pfs_cache_t *clink, int *result) +{ + pfsCacheFree(clink); + + if (clink->u.inode->next_segment.number) + return pfsCacheGetData(clink->pfsMount, + clink->u.inode->next_segment.subpart, + clink->u.inode->next_segment.number << clink->pfsMount->inode_scale, + PFS_CACHE_FLAG_SEGI, result); + + PFS_PRINTF(PFS_DRV_NAME": Error: There is no next segment descriptor\n"); + *result=-EINVAL; + return NULL; +} + +// Sets 'blockpos' to point to the next block segment for the inode (and moves onto the +// next block descriptor inode if necessary) +int pfsBlockSeekNextSegment(pfs_cache_t *clink, pfs_blockpos_t *blockpos) +{ + pfs_cache_t *nextSegment; + int result=0; + + if (blockpos->byte_offset) + { + PFS_PRINTF(PFS_DRV_NAME": Panic: This is a bug!\n"); + return -1; + } + + // If we're at the end of the block descriptor array for the current + // inode, then move onto the next inode + if (pfsFixIndex(blockpos->block_segment+1)==0) + { + if ((nextSegment=pfsBlockGetNextSegment(pfsCacheUsedAdd(blockpos->inode), &result)) == NULL) + return result; + pfsCacheFree(blockpos->inode); + blockpos->inode=nextSegment; + if (clink->u.inode->number_data-1 == ++blockpos->block_segment) + return -EINVAL; + } + + blockpos->block_offset=0; + blockpos->block_segment++; + return result; +} + +u32 pfsBlockSyncPos(pfs_blockpos_t *blockpos, u64 size) +{ + u32 i; + + i = (u32)(size / blockpos->inode->pfsMount->zsize); + blockpos->byte_offset += size % blockpos->inode->pfsMount->zsize; + + if (blockpos->byte_offset >= blockpos->inode->pfsMount->zsize) + { + blockpos->byte_offset -= blockpos->inode->pfsMount->zsize; + i++; + } + return i; +} + +int pfsBlockInitPos(pfs_cache_t *clink, pfs_blockpos_t *blockpos, u64 position) +{ + blockpos->inode=pfsCacheUsedAdd(clink); + blockpos->byte_offset=0; + + if (clink->u.inode->size) + { + blockpos->block_segment=1; + blockpos->block_offset=0; + }else{ + blockpos->block_segment=0; + blockpos->block_offset=1; + } + return pfsInodeSync(blockpos, position, clink->u.inode->number_data); +} diff --git a/common/libpfs/src/blockWrite.c b/common/libpfs/src/blockWrite.c new file mode 100644 index 0000000..37e8eaa --- /dev/null +++ b/common/libpfs/src/blockWrite.c @@ -0,0 +1,146 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +# +# PFS block/zone (write) related routines +*/ + +#include +#include +#ifdef _IOP +#include +#else +#include +#endif + +#include "pfs-opt.h" +#include "libpfs.h" + +// Attempt to expand the block segment 'blockpos' by 'count' blocks +int pfsBlockExpandSegment(pfs_cache_t *clink, pfs_blockpos_t *blockpos, u32 count) +{ + int ret; + pfs_blockinfo_t *bi; + + if(pfsFixIndex(blockpos->block_segment)==0) + return 0; + + bi = &blockpos->inode->u.inode->data[pfsFixIndex(blockpos->block_segment)]; + + if ((ret = pfsBitmapAllocateAdditionalZones(clink->pfsMount, bi, count))) + { + bi->count+=ret; + clink->u.inode->number_blocks+=ret; + blockpos->inode->flags |= PFS_CACHE_FLAG_DIRTY; + clink->flags |= PFS_CACHE_FLAG_DIRTY; + } + + return ret; +} + +// Attempts to allocate 'blocks' new blocks for an inode +int pfsBlockAllocNewSegment(pfs_cache_t *clink, pfs_blockpos_t *blockpos, u32 blocks) +{ + pfs_blockinfo_t bi, *bi2; + int result=0; + pfs_mount_t *pfsMount=clink->pfsMount; + u32 i, old_blocks = blocks; + + if (pfsCacheIsFull()) + return -ENOMEM; + + // create "indirect segment descriptor" if necessary + if (pfsFixIndex(clink->u.inode->number_data) == 0) + { + pfs_cache_t *clink2; + + bi2 = &blockpos->inode->u.inode->data[pfsFixIndex(blockpos->block_segment)]; + bi.subpart=bi2->subpart; + bi.count=1; + bi.number=bi2->number+bi2->count; + result=pfsBitmapSearchFreeZone(pfsMount, &bi, clink->u.inode->number_blocks); + if (result<0) + { + PFS_PRINTF(PFS_DRV_NAME": Error: Couldnt allocate zone! (1)\n"); + return result; + } + + clink2=pfsCacheGetData(pfsMount, bi.subpart, bi.number << pfsMount->inode_scale, + PFS_CACHE_FLAG_SEGI | PFS_CACHE_FLAG_NOLOAD, &result); + memset(clink2->u.inode, 0, sizeof(pfs_inode_t)); + clink2->u.inode->magic=PFS_SEGI_MAGIC; + + memcpy(&clink2->u.inode->inode_block, &clink->u.inode->inode_block, sizeof(pfs_blockinfo_t)); + memcpy(&clink2->u.inode->last_segment, &blockpos->inode->u.inode->data[0], sizeof(pfs_blockinfo_t)); + memcpy(&clink2->u.inode->data[0], &bi, sizeof(pfs_blockinfo_t)); + + clink2->flags |= PFS_CACHE_FLAG_DIRTY; + + clink->u.inode->number_blocks+=bi.count; + clink->u.inode->number_data++; + + memcpy(&clink->u.inode->last_segment, &bi, sizeof(pfs_blockinfo_t)); + + clink->u.inode->number_segdesg++; + + clink->flags |= PFS_CACHE_FLAG_DIRTY; + blockpos->block_segment++; + blockpos->block_offset=0; + + memcpy(&blockpos->inode->u.inode->next_segment, &bi, sizeof(pfs_blockinfo_t)); + + blockpos->inode->flags |= PFS_CACHE_FLAG_DIRTY; + pfsCacheFree(blockpos->inode); + blockpos->inode=clink2; + } + + bi2 = &blockpos->inode->u.inode->data[pfsFixIndex(blockpos->block_segment)]; + bi.subpart = bi2->subpart; + bi.count = blocks; + bi.number = bi2->number + bi2->count; + + result = pfsBitmapSearchFreeZone(pfsMount, &bi, clink->u.inode->number_blocks); + if(result < 0) + { + PFS_PRINTF(PFS_DRV_NAME": Error: Couldnt allocate zone! (2)\n"); + return result; + } + + clink->u.inode->number_blocks += bi.count; + clink->u.inode->number_data++; + clink->flags |= PFS_CACHE_FLAG_DIRTY; + blockpos->block_offset=0; + blockpos->block_segment++; + + i = pfsFixIndex(clink->u.inode->number_data-1); + memcpy(&blockpos->inode->u.inode->data[i], &bi, sizeof(pfs_blockinfo_t)); + + blockpos->inode->flags |= PFS_CACHE_FLAG_DIRTY; + blocks -= bi.count; + if (blocks) + blocks -= pfsBlockExpandSegment(clink, blockpos, blocks); + + return old_blocks - blocks; +} + +// Returns the block info for the block segment corresponding to the +// files current position. +pfs_blockinfo_t* pfsBlockGetCurrent(pfs_blockpos_t *blockpos) +{ + return &blockpos->inode->u.inode->data[pfsFixIndex(blockpos->block_segment)]; +} + +pfs_cache_t *pfsBlockGetLastSegmentDescriptorInode(pfs_cache_t *clink, int *result) +{ + return pfsCacheGetData(clink->pfsMount, clink->u.inode->last_segment.subpart, + clink->u.inode->last_segment.number<pfsMount->inode_scale, + (clink->u.inode->last_segment.subpart== + clink->u.inode->inode_block.subpart)&& + (clink->u.inode->last_segment.number== + clink->u.inode->inode_block.number) ? 16 : 32, result); +} diff --git a/common/libpfs/src/cache.c b/common/libpfs/src/cache.c new file mode 100644 index 0000000..dc321b9 --- /dev/null +++ b/common/libpfs/src/cache.c @@ -0,0 +1,256 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +# +# PFS metadata cache manipulation routines +*/ + +#include +#include +#ifdef _IOP +#include +#else +#include +#endif +#include + +#include "pfs-opt.h" +#include "libpfs.h" + +extern u32 pfsBlockSize; + +pfs_cache_t *pfsCacheBuf; +u32 pfsCacheNumBuffers; + +void pfsCacheFree(pfs_cache_t *clink) +{ + if(clink==NULL) { + PFS_PRINTF(PFS_DRV_NAME": Warning: NULL buffer returned\n"); + return; + } + + if(clink->nused==0){ + PFS_PRINTF(PFS_DRV_NAME": Error: Unused cache returned\n"); + return; + } + + clink->nused--; + if(clink->pfsMount!=NULL) { + if(clink->nused!=0) + return; + pfsCacheLink(pfsCacheBuf->prev, clink); + return; + } + if(clink->nused!=0) { + PFS_PRINTF(PFS_DRV_NAME": Warning: Invalidated buffer is in use\n"); + return; + } + pfsCacheLink(pfsCacheBuf, clink); +} + +void pfsCacheLink(pfs_cache_t *clink, pfs_cache_t *cnew) +{ + cnew->prev=clink; + cnew->next=clink->next; + clink->next->prev=cnew; + clink->next=cnew; +} + +pfs_cache_t *pfsCacheUnLink(pfs_cache_t *clink) +{ + clink->prev->next=clink->next; + clink->next->prev=clink->prev; + return clink; +} + +pfs_cache_t *pfsCacheUsedAdd(pfs_cache_t *clink) +{ + clink->nused++; + return clink; +} + +int pfsCacheTransfer(pfs_cache_t* clink, int mode) +{ + pfs_mount_t *pfsMount=clink->pfsMount; + int err; + + if(pfsMount->lastError == 0) { // no error + if((err=pfsMount->blockDev->transfer(pfsMount->fd, clink->u.data, clink->sub, + clink->block << pfsBlockSize, 1 << pfsBlockSize, mode))==0) { + if(mode==PFS_IO_MODE_READ) { + if(clink->flags & PFS_CACHE_FLAG_SEGD && ((pfs_inode_t *)clink->u.inode)->magic!=PFS_SEGD_MAGIC) + err=-EIO; + if(clink->flags & PFS_CACHE_FLAG_SEGI && ((pfs_inode_t *)clink->u.inode)->magic!=PFS_SEGI_MAGIC) + err=-EIO; + if(clink->flags & (PFS_CACHE_FLAG_SEGD|PFS_CACHE_FLAG_SEGI)) { + if(((pfs_inode_t *)clink->u.inode)->checksum!=pfsInodeCheckSum(clink->u.inode)) + err=-EIO; + } + } + } + if(err!=0) { + PFS_PRINTF(PFS_DRV_NAME": Error: Disk error partition %ld, block %ld, err %d\n", + clink->sub, clink->block, err); +#ifndef PFS_NO_WRITE_ERROR_STAT + pfsMount->blockDev->setPartitionError(pfsMount->fd); + pfsFsckStat(pfsMount, clink->u.superblock, PFS_FSCK_STAT_WRITE_ERROR, PFS_MODE_SET_FLAG); + pfsMount->lastError=err; +#endif + } + } + clink->flags&=~PFS_CACHE_FLAG_DIRTY; // clear dirty :) + return pfsMount->lastError; +} + +void pfsCacheFlushAllDirty(pfs_mount_t *pfsMount) +{ + u32 i; + int found=0; + + for(i=1;iprev==pfsCacheBuf && pfsCacheBuf->prev->next==pfsCacheBuf->prev) { + PFS_PRINTF(PFS_DRV_NAME": Error: Free buffer list is empty\n"); + *result=-ENOMEM; + return NULL; + } + allocated=pfsCacheBuf->next; + if (pfsCacheBuf->next==NULL) + PFS_PRINTF(PFS_DRV_NAME": Panic: Null pointer allocated\n"); + if (allocated->pfsMount && (allocated->flags & PFS_CACHE_FLAG_DIRTY)) + pfsCacheFlushAllDirty(allocated->pfsMount); + allocated->flags = flags & PFS_CACHE_FLAG_MASKTYPE; + allocated->pfsMount = pfsMount; + allocated->sub = sub; + allocated->block = block; + allocated->nused = 1; + return pfsCacheUnLink(allocated); +} + +pfs_cache_t *pfsCacheGetData(pfs_mount_t *pfsMount, u16 sub, u32 block, + int flags, int *result) +{ + u32 i; + pfs_cache_t *clink; + + *result=0; + + for (i=1; i < pfsCacheNumBuffers + 1; i++) + if ( pfsCacheBuf[i].pfsMount && + (pfsCacheBuf[i].pfsMount==pfsMount) && + (pfsCacheBuf[i].block == block)) + if (pfsCacheBuf[i].sub==sub){ + pfsCacheBuf[i].flags &= PFS_CACHE_FLAG_MASKSTATUS; + pfsCacheBuf[i].flags |= flags & PFS_CACHE_FLAG_MASKTYPE; + if (pfsCacheBuf[i].nused == 0) + pfsCacheUnLink(&pfsCacheBuf[i]); + pfsCacheBuf[i].nused++; + return &pfsCacheBuf[i]; + } + + clink=pfsCacheAlloc(pfsMount, sub, block, flags, result); + + if (clink){ + if (flags & PFS_CACHE_FLAG_NOLOAD) + return clink; + + if ((*result=pfsCacheTransfer(clink, PFS_IO_MODE_READ))>=0) + return clink; + + clink->pfsMount=NULL; + pfsCacheFree(clink); + } + return NULL; +} + +pfs_cache_t *pfsCacheAllocClean(int *result) +{ + *result = 0; + return pfsCacheAlloc(NULL, 0, 0, 0, result); +} + +// checks if the pfsCacheBuf list has some room +int pfsCacheIsFull(void) +{ + if (pfsCacheBuf->prev != pfsCacheBuf) return 0; + return pfsCacheBuf->prev->next == pfsCacheBuf->prev; +} + +int pfsCacheInit(u32 numBuf, u32 bufSize) +{ + char *cacheData; + u32 i; + + if(numBuf > 127) { + PFS_PRINTF(PFS_DRV_NAME": Error: Number of buffers larger than 127.\n"); + return -EINVAL; + } + + cacheData = pfsAllocMem(numBuf * bufSize); + + if(!cacheData || !(pfsCacheBuf = pfsAllocMem((numBuf + 1) * sizeof(pfs_cache_t)))) + return -ENOMEM; + + pfsCacheNumBuffers = numBuf; + memset(pfsCacheBuf, 0, (numBuf + 1) * sizeof(pfs_cache_t)); + + pfsCacheBuf->next = pfsCacheBuf; + pfsCacheBuf->prev = pfsCacheBuf; + + for(i = 1; i < numBuf + 1; i++) + { + pfsCacheBuf[i].u.data = cacheData; + pfsCacheLink(pfsCacheBuf->prev, &pfsCacheBuf[i]); + cacheData += bufSize; + } + + return 0; +} + +void pfsCacheClose(pfs_mount_t *pfsMount) +{ + unsigned int i; + + pfsCacheFlushAllDirty(pfsMount); + for(i=1; i < pfsCacheNumBuffers+1;i++){ + if(pfsCacheBuf[i].pfsMount==pfsMount) + pfsCacheBuf[i].pfsMount=NULL; + } +} + +void pfsCacheMarkClean(pfs_mount_t *pfsMount, u32 subpart, u32 blockStart, u32 blockEnd) +{ + u32 i; + + for(i=1; i< pfsCacheNumBuffers+1;i++){ + if(pfsCacheBuf[i].pfsMount==pfsMount && pfsCacheBuf[i].sub==subpart) { + if(pfsCacheBuf[i].block >= blockStart && pfsCacheBuf[i].block < blockEnd) + pfsCacheBuf[i].flags&=~PFS_CACHE_FLAG_DIRTY; + } + } +} diff --git a/common/libpfs/src/dir.c b/common/libpfs/src/dir.c new file mode 100644 index 0000000..2f92107 --- /dev/null +++ b/common/libpfs/src/dir.c @@ -0,0 +1,718 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +# +# PFS directory parsing routines +*/ + +#include +#include +#ifdef _IOP +#include +#else +#include +#endif +#include + +#include "pfs-opt.h" +#include "libpfs.h" + +extern u32 pfsMetaSize; + +// Gets a dir entry from the inode specified by clink +pfs_cache_t *pfsGetDentry(pfs_cache_t *clink, char *path, pfs_dentry_t **dentry, u32 *size, int option) +{ + pfs_blockpos_t block_pos; + pfs_dentry_t *d; + u16 aLen; + pfs_dentry_t *d2; + pfs_cache_t *dentCache; + u32 new_dentryLen=0,dentryLen; + int len=0, result; + + if (path) + { + len = strlen(path); + new_dentryLen = (len+8+3) &~3; + } + *size = 0; + + block_pos.inode = pfsCacheUsedAdd(clink); + block_pos.block_segment = 1; + block_pos.block_offset = 0; + block_pos.byte_offset = 0; + dentCache = pfsGetDentriesChunk(&block_pos, &result); + + if (dentCache != NULL) + { + d2=d=dentCache->u.dentry; + while(*size < clink->u.inode->size) + { + // Read another dentry chunk if we need to + if (d2 >= (pfs_dentry_t*)((u8*)dentCache->u.inode + pfsMetaSize)) + { + if (pfsInodeSync(&block_pos, pfsMetaSize, clink->u.inode->number_data)) + break; + pfsCacheFree(dentCache); + + if ((dentCache=pfsGetDentriesChunk(&block_pos, &result)) == NULL) + break; + d=dentCache->u.dentry; + } + + // Scan through the sector. + for (d2=(pfs_dentry_t*)((u8*)d+512); d < d2; d=(pfs_dentry_t *)((u8*)d + aLen)) + { + aLen=(d->aLen & 0xFFF); + + if (aLen & 3){ + PFS_PRINTF(PFS_DRV_NAME": Error: dentry allocated length/4 != 0\n"); + goto _exit; + } + dentryLen = (d->pLen + 8 + 3) & 0x1FC; + if (aLen < dentryLen){ + PFS_PRINTF(PFS_DRV_NAME": Error: dentry is too small\n"); + goto _exit; + } + if (d2 < (pfs_dentry_t*)((u8*)d + aLen)){ + PFS_PRINTF(PFS_DRV_NAME": Error: dentry across sectors\n"); + goto _exit; + } + + // decide if the current dentry meets the required criteria, based on 'option' + switch(option) + { + case 0: // result = 1 when paths are equal + result = (len==d->pLen) && (memcmp(path, d->path, d->pLen)==0); + break; + case 1: // result = 1 when either there is no inode or if there is enough space remaining in the inode. + result = ((d->inode) || (aLen < new_dentryLen)) ? (aLen - dentryLen + >= new_dentryLen) : 1; + break; + case 2: // result = 1 when dir path is not empty, "." or ".." + result = d->pLen && strcmp(d->path, ".") && strcmp(d->path, ".."); + break; + default: + goto _exit; + } + + if (result) + { + *dentry=d; + pfsCacheFree(block_pos.inode); + return dentCache; + } + *size+=aLen; + } + } +_exit: pfsCacheFree(dentCache); + } + pfsCacheFree(block_pos.inode); + return NULL; +} + +int pfsGetNextDentry(pfs_cache_t *clink, pfs_blockpos_t *blockpos, u32 *position, char *name, pfs_blockinfo_t *bi) +{ + int result; + pfs_cache_t *dcache; + pfs_dentry_t *dentry; + u32 len=0; + + // loop until a non-empty entry is found or until the end of the dentry chunk is reached + while((len == 0) && (*position < clink->u.inode->size)) + { + + if (!(dcache=pfsGetDentriesChunk(blockpos, &result))) + { + PFS_PRINTF(PFS_DRV_NAME": couldnt get dentries chunk for dread!\n"); + break; + } + + dentry = (pfs_dentry_t*)((u8*)dcache->u.data + (blockpos->byte_offset % pfsMetaSize)); + + len = dentry->pLen; + memcpy(name, dentry->path, len); + name[len] = '\0'; + + bi->subpart = dentry->sub; + bi->number = dentry->inode; + pfsCacheFree(dcache); + + *position += dentry->aLen & 0xFFF; + + // update blockpos + if (pfsInodeSync(blockpos, dentry->aLen & 0xFFF, clink->u.inode->number_data)) + break; + } + + return len; +} + +pfs_cache_t *pfsFillDentry(pfs_cache_t *clink, pfs_dentry_t *dentry, char *path1, pfs_blockinfo_t *bi, u32 len, u16 mode) +{ + dentry->inode=bi->number; + dentry->sub=(u8)bi->subpart; + dentry->pLen=strlen(path1); + dentry->aLen=len | (mode & FIO_S_IFMT); + memcpy(dentry->path, path1, dentry->pLen & 0xFF); + + return clink; +} + +pfs_cache_t *pfsDirAddEntry(pfs_cache_t *dir, char *filename, pfs_blockinfo_t *bi, u16 mode, int *result) +{ + pfs_dentry_t *dentry; + u32 size; + u32 len; + pfs_cache_t *dcache; + + dcache=pfsGetDentry(dir, filename, &dentry, &size, 1); + if (dcache != NULL){ + len=dentry->aLen & 0xFFF; + if (dentry->pLen) + len-=(dentry->pLen + 11) & 0x1FC; + dentry->aLen=(dentry->aLen & FIO_S_IFMT) | ((dentry->aLen & 0xFFF) - len); + dentry=(pfs_dentry_t *)((u8*)dentry + (dentry->aLen & 0xFFF)); + }else{ + int offset; + + if ((*result=pfsAllocZones(dir, sizeof(pfs_dentry_t), 0))<0) + return NULL; + dcache=pfsGetDentriesAtPos(dir, dir->u.inode->size, &offset, result); + if (dcache==NULL) + return NULL; + + dir->u.inode->size += sizeof(pfs_dentry_t); + + dentry=(pfs_dentry_t*)((u8*)dcache->u.dentry+offset); + len=sizeof(pfs_dentry_t); + } + return pfsFillDentry(dcache, dentry, filename, bi, len, mode); +} + +pfs_cache_t *pfsDirRemoveEntry(pfs_cache_t *clink, char *path) +{ + pfs_dentry_t *dentry; + u32 size; + u16 aLen; + int i=0, val; + pfs_dentry_t *dlast=NULL, *dnext; + pfs_cache_t *c; + + if ((c=pfsGetDentry(clink, path, &dentry, &size, 0)) != NULL){ + val=(int)dentry-(int)c->u.dentry; + if (val<0) val +=511; + val /=512; + val *=512; + + dnext=(pfs_dentry_t*)((u8*)c->u.dentry+val); + do{ + aLen = dnext->aLen & 0xFFF; + + if (dnext==dentry){ + if (dlast) + dlast->aLen=(dlast->aLen & FIO_S_IFMT) | ((dlast->aLen & 0xFFF) + aLen); + else{ + dnext->inode=0; + dnext->pLen=0; + + if (size+aLen >= clink->u.inode->size) + clink->u.inode->size -= aLen; + } + return c; + } + i+=aLen; + dlast=dnext; + dnext=(pfs_dentry_t *)((u8*)dnext + aLen); + }while (i<512); + } + return NULL; +} + +int pfsCheckDirForFiles(pfs_cache_t *clink) +{ + pfs_dentry_t *dentry; + pfs_cache_t *dcache; + u32 size; + + if((dcache=pfsGetDentry(clink, NULL, &dentry, &size, 2))){ + pfsCacheFree(dcache); + return 0; + } + return 1; +} + +void pfsFillSelfAndParentDentries(pfs_cache_t *clink, pfs_blockinfo_t *self, pfs_blockinfo_t *parent) +{ + pfs_dentry_t *dentry=clink->u.dentry; + + memset(dentry, 0, pfsMetaSize); + dentry->inode=self->number; + dentry->path[0]='.'; + dentry->path[1]='\0'; + dentry->sub=(u8)self->subpart; + dentry->pLen=1; + dentry->aLen=12 | FIO_S_IFDIR; + + dentry=(pfs_dentry_t *)((u8*)dentry + 12); + + dentry->inode=parent->number; + dentry->path[0]='.'; + dentry->path[1]='.'; + dentry->path[2]='\0'; + dentry->sub=(u8)parent->subpart; + dentry->pLen=2; + dentry->aLen=500 | FIO_S_IFDIR; +} + +pfs_cache_t* pfsSetDentryParent(pfs_cache_t *clink, pfs_blockinfo_t *bi, int *result) +{ + int offset; + pfs_cache_t *dcache; + + dcache=pfsGetDentriesAtPos(clink, 0, &offset, result); + if (dcache){ + pfs_dentry_t *d=(pfs_dentry_t*)(12+(u8*)dcache->u.data); + d->inode=bi->number; + d->sub =(u8)bi->subpart; + } + return dcache; +} + +// Returns the cached inode for the file (dir) in the directory pointed +// to by the dir inode. +pfs_cache_t *pfsInodeGetFileInDir(pfs_cache_t *dirInode, char *path, int *result) +{ + pfs_dentry_t *dentry; + u32 size; + pfs_cache_t *clink; + + if (path[0]==0) + return pfsCacheUsedAdd(dirInode); + + // If we're in the root dir, don't do anything for ".." + if ((dirInode->block == + dirInode->pfsMount->root_dir.number << dirInode->pfsMount->inode_scale) && + (dirInode->sub == dirInode->pfsMount->root_dir.subpart) && + (strcmp(path, "..") == 0)) + return pfsCacheUsedAdd(dirInode); + + if ((*result=pfsCheckAccess(dirInode, 1)) < 0) + return NULL; + + // Get dentry of file/dir specified by path from the dir pointed to + // by the inode (dirInode). Then return the cached inode for that dentry. + if ((clink=pfsGetDentry(dirInode, path, &dentry, &size, 0))){ + pfsCacheFree(clink); + return pfsInodeGetData(dirInode->pfsMount, + dentry->sub, dentry->inode, result); + } + + *result=-ENOENT; + return NULL; +} + +pfs_cache_t *pfsInodeGetFile(pfs_mount_t *pfsMount, pfs_cache_t *clink, + const char *name, int *result) { + char path[256]; + pfs_cache_t *c,*fileInode; + + c=pfsInodeGetParent(pfsMount, clink, name, path, result); + if (c){ + fileInode=pfsInodeGetFileInDir(c, path, result); + pfsCacheFree(c); + return fileInode; + } + return NULL; +} + +void pfsInodeFill(pfs_cache_t *ci, pfs_blockinfo_t *bi, u16 mode, u16 uid, u16 gid) +{ + u32 val; + + memset(ci->u.inode, 0, pfsMetaSize); + + ci->u.inode->magic=PFS_SEGD_MAGIC; + + ci->u.inode->inode_block.number=bi->number; + ci->u.inode->inode_block.subpart=bi->subpart; + ci->u.inode->inode_block.count=bi->count; + + ci->u.inode->last_segment.number=bi->number; + ci->u.inode->last_segment.subpart=bi->subpart; + ci->u.inode->last_segment.count=bi->count; + + ci->u.inode->mode=mode; + ci->u.inode->uid=uid; + ci->u.inode->gid=gid; + + if ((mode & FIO_S_IFMT) == FIO_S_IFDIR){ + ci->u.inode->attr=0xA0; + ci->u.inode->size=sizeof(pfs_dentry_t); + val=2; + }else{ + ci->u.inode->size=0; + val=1; + } + ci->u.inode->number_data=val; + ci->u.inode->number_blocks=val; + + pfsGetTime(&ci->u.inode->ctime); + memcpy(&ci->u.inode->atime, &ci->u.inode->ctime, sizeof(pfs_datetime_t)); + memcpy(&ci->u.inode->mtime, &ci->u.inode->ctime, sizeof(pfs_datetime_t)); + + ci->u.inode->number_segdesg=1; + ci->u.inode->data[0].number =bi->number; + ci->u.inode->data[0].subpart=bi->subpart; + ci->u.inode->data[0].count =bi->count; + + ci->u.inode->subpart=bi->subpart; + + ci->flags |= PFS_CACHE_FLAG_DIRTY; +} + + +// Given a path, this function will return the inode for the directory which holds +// the file (dir) specified by the filename. +// +// ie: if filename = /usr/local/ps2/games then it will return the inode for /usr/local/ps2 +pfs_cache_t* pfsInodeGetParent(pfs_mount_t *pfsMount, pfs_cache_t *clink, const char *filename, + char *path, int *result) +{ + static int pfsSymbolicLinks = 0; + pfs_cache_t *link, *inode; + char *filename2=(char*)filename; + + if (filename2[0]==0) + { + *result=-ENOENT; + if (clink) pfsCacheFree(clink); + return NULL; + } + + if (filename2[0] == '/') + { + + // Get inode for root dir + + if (clink) pfsCacheFree(clink); + if ((clink=pfsInodeGetData(pfsMount, pfsMount->root_dir.subpart, + pfsMount->root_dir.number, result))==0) + return NULL; + } + else if (clink==NULL) + { + + // Otherwise if relative path, get inode for current dir + + if ((clink=pfsInodeGetData(pfsMount, pfsMount->current_dir.subpart, + pfsMount->current_dir.number, result))==0) + return NULL; + } + + do + { + filename2=pfsSplitPath(filename2, path, result); + if (filename2==NULL) return NULL; + + // If we've reached the end of the path, then we want to return + // the cached inode for the directory which holds the file/dir in path + if (filename2[0]==0) + { + if ((clink->u.inode->mode & FIO_S_IFMT) == FIO_S_IFDIR) + return clink; + + pfsCacheFree(clink); + *result=-ENOTDIR; // not a directory + return NULL; + } + + inode=pfsInodeGetFileInDir(clink, path, result); + + if (inode && ((inode->u.inode->mode & FIO_S_IFMT) == FIO_S_IFLNK)) + { + if (pfsSymbolicLinks >= 4) + { + *result=-ELOOP; // too many symbolic links + pfsCacheFree(clink); + pfsCacheFree(inode); + return NULL; + } + + pfsSymbolicLinks++; + link=pfsInodeGetFile(pfsMount, clink, (char*)&inode->u.inode->data[1], result); + pfsSymbolicLinks--; + + clink=inode; + if (link==0) + { + pfsCacheFree(inode); + return NULL; + } + inode=link; + } + pfsCacheFree(clink); + clink=inode; + + } while (inode); + + return NULL; +} + +int pfsInodeRemove(pfs_cache_t *parent, pfs_cache_t *inode, char *path) +{ + pfs_cache_t *entry; + int rv=0; + + if((entry=pfsDirRemoveEntry(parent, path))!=NULL) + { + pfsInodeSetTimeParent(parent, entry); + pfsCacheFree(entry); + } + else + rv=-ENOENT; + + pfsCacheFree(parent); + if(rv==0) + { + inode->flags&=~PFS_CACHE_FLAG_DIRTY; + pfsBitmapFreeInodeBlocks(inode); + //if(parent->pfsMount->flags & PFS_FIO_ATTR_WRITEABLE) //Not checked for in late versions of PFS. + pfsCacheFlushAllDirty(parent->pfsMount); + } + + pfsCacheFree(inode); + return rv; +} + +pfs_cache_t *pfsInodeCreate(pfs_cache_t *clink, u16 mode, u16 uid, u16 gid, int *result) +{ + pfs_blockinfo_t a, b; + + pfs_mount_t *pfsMount=clink->pfsMount; + u32 j; + u32 i; + pfs_cache_t *inode; + + if ((mode & FIO_S_IFMT) == FIO_S_IFDIR) + { + if (pfsMount->num_subs > clink->u.inode->subpart) + clink->u.inode->subpart++; + else + clink->u.inode->subpart=0; + a.number =0; + a.subpart=clink->u.inode->subpart; + j= (pfsMount->zfree * (u64)100) / pfsMount->total_zones; + i= (pfsMount->free_zone[a.subpart] * (u64)100) / + (pfsMount->blockDev->getSize(pfsMount->fd, a.subpart) >> pfsMount->sector_scale); + if ((i < j) && ((j-i) >= 11)) + a.subpart=pfsGetMaxIndex(pfsMount); + }else{ + a.number=clink->u.inode->inode_block.number; + a.subpart=clink->u.inode->inode_block.subpart; + a.count=clink->u.inode->inode_block.count; + } + a.count=1; + + // Search for a free zone, starting from parent dir inode block + *result=pfsBitmapSearchFreeZone(pfsMount, &a, 2); + if (*result<0) return 0; + inode=pfsCacheGetData(pfsMount, a.subpart, a.number << pfsMount->inode_scale, + PFS_CACHE_FLAG_SEGD | PFS_CACHE_FLAG_NOLOAD, result); + if (inode == NULL) + return NULL; + + // Initialise the inode (which has been allocate blocks specified by a) + pfsInodeFill(inode, (pfs_blockinfo_t*)&a, mode, uid, gid); + if ((mode & FIO_S_IFMT) != FIO_S_IFDIR) + return inode; + + b.number=a.number; + b.subpart=a.subpart; + b.count=a.count; + + *result=pfsBitmapSearchFreeZone(pfsMount, (pfs_blockinfo_t*)&a, 0); + if (*result<0){ + pfsCacheFree(inode); + pfsBitmapFreeBlockSegment(pfsMount, (pfs_blockinfo_t*)&b); + return NULL; + } + + inode->u.inode->data[1].number=a.number; + inode->u.inode->data[1].subpart=a.subpart; + inode->u.inode->data[1].count =a.count; + + return inode; +} + +int pfsCheckAccess(pfs_cache_t *clink, int flags) +{ + int mode; + + // Bail if trying to write to read-only mount + if ((clink->pfsMount->flags & FIO_MT_RDONLY) && (flags & O_WRONLY)) + return -EROFS; + + if (((clink->u.inode->mode & FIO_S_IFMT) != FIO_S_IFDIR) && + ((clink->u.inode->mode & 0111) == 0)) + mode=6; + else + mode=7; + return ((mode & flags) & 7) == flags ? 0 : -EACCES; +} + +char* pfsSplitPath(char *filename, char *path, int *result) +{ + int i=0; + int j=0; + + for (i=0; filename[i] == '/'; i++) + ; + + for (; i<1024 && filename[i] != '/'; i++){ + if (filename[i] == 0) break; + if (j < 255) + path[j++] = filename[i]; + } + + if (j<256) + path[j]=0; + + while (filename[i] == '/') + if (i<1024) + i++; + else + break; + if (i<1024) + return &filename[i]; + + *result=-ENAMETOOLONG; + return 0; +} + +u16 pfsGetMaxIndex(pfs_mount_t *pfsMount) +{ + u32 max=0, maxI=0, i, v; + + for (i=0; i < pfsMount->num_subs + 1; i++) //enumerate all subs + { + v = (pfsMount->free_zone[i] * (u64)100) / + (pfsMount->blockDev->getSize(pfsMount->fd, i) >> pfsMount->sector_scale); + if (max < v) + { + max=v; + maxI=i; + } + } + return maxI; +} + +int pfsAllocZones(pfs_cache_t *clink, int msize, int mode) +{ + pfs_blockpos_t blockpos; + int result=0; + u32 val; + int zsize; + + zsize=clink->pfsMount->zsize; + val=((msize-1 + zsize) & (-zsize)) / zsize; + + if(mode==0) + if (((clink->u.inode->number_blocks-clink->u.inode->number_segdesg) *(u64)zsize) + >= (clink->u.inode->size + msize)) + return 0; + + if((blockpos.inode = pfsBlockGetLastSegmentDescriptorInode(clink, &result))) + { + blockpos.block_offset=blockpos.byte_offset=0; + blockpos.block_segment=clink->u.inode->number_data-1; + val-=pfsBlockExpandSegment(clink, &blockpos, val); + while (val && ((result=pfsBlockAllocNewSegment(clink, &blockpos, val))>=0)){ + val-=result; + result=0; + } + pfsCacheFree(blockpos.inode); + } + return result; +} + +void pfsFreeZones(pfs_cache_t *pfree) +{ + pfs_blockinfo_t b; + int result; + pfs_mount_t *pfsMount = pfree->pfsMount; + pfs_inode_t *inode = pfree->u.inode; + u32 nextsegdesc = 1, limit = inode->number_data, i, j = 0, zones; + pfs_blockinfo_t *bi; + pfs_cache_t *clink; + + zones = (u32)(inode->size / pfsMount->zsize); + if(inode->size % pfsMount->zsize) + zones++; + if(inode->number_segdesg + zones == inode->number_blocks) + return; + + j = zones; + b.number = 0; + clink = pfsCacheUsedAdd(pfree); + + // iterate through each of the block segments used by the inode + for (i = 1; i < limit && j; i++) + { + if(pfsFixIndex(i) == 0) + { + if ((clink = pfsBlockGetNextSegment(clink, &result)) == 0) + return; + + nextsegdesc++; + } + else + { + bi = &clink->u.inode->data[pfsFixIndex(i)]; + + if(j < bi->count) + { + bi->count -= j; + b.subpart = bi->subpart; + b.count = j; + b.number = bi->number + + bi->count; + j = 0; + clink->flags |= PFS_CACHE_FLAG_DIRTY; + } + else + j -= bi->count; + } + } + + pfree->u.inode->number_data = i; + pfree->u.inode->number_blocks = zones + nextsegdesc; + pfree->u.inode->number_segdesg = nextsegdesc; + pfree->u.inode->last_segment.number = clink->u.inode->data[0].number; + pfree->u.inode->last_segment.subpart= clink->u.inode->data[0].subpart; + pfree->u.inode->last_segment.count = clink->u.inode->data[0].count; + pfree->flags |= PFS_CACHE_FLAG_DIRTY; + + if (b.number) + pfsBitmapFreeBlockSegment(pfsMount, &b); + + for( ; i < limit; i++) + { + if (pfsFixIndex(i) == 0) + { + if((clink = pfsBlockGetNextSegment(clink, &result)) == 0) + return; + } + bi = &clink->u.inode->data[pfsFixIndex(i)]; + pfsBitmapFreeBlockSegment(pfsMount, bi); + pfsCacheMarkClean(pfsMount, bi->subpart, bi->number<inode_scale, + (bi->number+bi->count)<inode_scale); + } + + pfsCacheFree(clink); +} diff --git a/common/libpfs/src/inode.c b/common/libpfs/src/inode.c new file mode 100644 index 0000000..1d217c5 --- /dev/null +++ b/common/libpfs/src/inode.c @@ -0,0 +1,135 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +# +# PFS low-level inode manipulation routines +*/ + +#include +#include +#ifdef _IOP +#include +#else +#include +#endif + +#include "pfs-opt.h" +#include "libpfs.h" + +extern u32 pfsMetaSize; + +/////////////////////////////////////////////////////////////////////////////// +// Function definitions + +void pfsInodePrint(pfs_inode_t *inode) +{ + PFS_PRINTF(PFS_DRV_NAME": pfsInodePrint: Checksum = 0x%lX, Magic = 0x%lX\n", inode->checksum, inode->magic); + PFS_PRINTF(PFS_DRV_NAME": Mode = 0x%X, attr = 0x%X\n", inode->mode, inode->attr); + PFS_PRINTF(PFS_DRV_NAME": size = 0x%08lX%08lX\n", (u32)(inode->size >> 32), (u32)(inode->size)); +} + +int pfsInodeCheckSum(pfs_inode_t *inode) +{ + u32 *ptr=(u32 *)inode; + u32 sum=0; + int i; + + for(i=1; i < 256; i++) + sum+=ptr[i]; + return sum; +} + +pfs_cache_t *pfsInodeGetData(pfs_mount_t *pfsMount, u16 sub, u32 inode, int *result) +{ + return pfsCacheGetData(pfsMount, sub, inode << pfsMount->inode_scale, + PFS_CACHE_FLAG_SEGD, result); +} + +void pfsInodeSetTime(pfs_cache_t *clink) +{ // set the inode time's in cache + pfsGetTime(&clink->u.inode->mtime); + memcpy(&clink->u.inode->ctime, &clink->u.inode->mtime, sizeof(pfs_datetime_t)); + memcpy(&clink->u.inode->atime, &clink->u.inode->mtime, sizeof(pfs_datetime_t)); + clink->flags|=PFS_CACHE_FLAG_DIRTY; +} + +void pfsInodeSetTimeParent(pfs_cache_t *parent, pfs_cache_t *self) +{ // set the inode time's in cache + pfsInodeSetTime(parent); + self->flags|=PFS_CACHE_FLAG_DIRTY; +} + +int pfsInodeSync(pfs_blockpos_t *blockpos, u64 size, u32 used_segments) +{ + int result=0; + u32 i; + u16 count; + + for(i=pfsBlockSyncPos(blockpos, size); i; ) + { + count=blockpos->inode->u.inode->data[pfsFixIndex(blockpos->block_segment)].count; + + i+=blockpos->block_offset; + + if (i < count){ + blockpos->block_offset=i; + break; + } + + i-=count; + + if (blockpos->block_segment + 1 == used_segments) + { + blockpos->block_offset=count; + if (i || blockpos->byte_offset){ + PFS_PRINTF(PFS_DRV_NAME": panic: fp exceeds file.\n"); + return -EINVAL; + } + }else{ + blockpos->block_offset=0; + blockpos->block_segment++; + } + + if (pfsFixIndex(blockpos->block_segment)) + continue; + + if ((blockpos->inode = pfsBlockGetNextSegment(blockpos->inode, &result)) == 0) + break; + + i++; + } + + return result; +} + +pfs_cache_t *pfsGetDentriesChunk(pfs_blockpos_t *position, int *result) +{ + pfs_blockinfo_t *bi; + pfs_mount_t *pfsMount=position->inode->pfsMount; + + bi = &position->inode->u.inode->data[pfsFixIndex(position->block_segment)]; + + return pfsCacheGetData(pfsMount, bi->subpart, + ((bi->number + position->block_offset) << pfsMount->inode_scale) + + position->byte_offset / pfsMetaSize, PFS_CACHE_FLAG_NOTHING, result); +} + +pfs_cache_t *pfsGetDentriesAtPos(pfs_cache_t *clink, u64 position, int *offset, int *result) +{ + pfs_blockpos_t blockpos; + pfs_cache_t *r; + *result=pfsBlockInitPos(clink, &blockpos, position); + if (*result) return 0; + + *offset=blockpos.byte_offset % pfsMetaSize; + + r=pfsGetDentriesChunk(&blockpos, result); + pfsCacheFree(blockpos.inode); + + return r; +} diff --git a/common/libpfs/src/journal.c b/common/libpfs/src/journal.c new file mode 100644 index 0000000..1568139 --- /dev/null +++ b/common/libpfs/src/journal.c @@ -0,0 +1,150 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +# +# PFS metadata journal related routines +*/ + +#include +#ifdef _IOP +#include +#else +#include +#endif +#include + +#include "pfs-opt.h" +#include "libpfs.h" + +extern u32 pfsBlockSize; + +/////////////////////////////////////////////////////////////////////////////// +// Globals + +pfs_journal_t pfsJournalBuf; + +/////////////////////////////////////////////////////////////////////////////// +// Function defenitions + +int pfsJournalChecksum(void *header) +{ + u32 *ptr=(u32 *)header; + u32 sum=0; + int i; + for(i=2; i < 256; i++) + sum+=ptr[i]; + return sum & 0xFFFF; +} + +void pfsJournalWrite(pfs_mount_t *pfsMount, pfs_cache_t *clink, u32 pfsCacheNumBuffers) +{ + u32 i=0; + u32 logSector=2; + + for(i=0; i checksum=pfsInodeCheckSum(clink[i].u.inode); + pfsJournalBuf.log[pfsJournalBuf.num].sector = clink[i].block << pfsBlockSize; + pfsJournalBuf.log[pfsJournalBuf.num].sub = clink[i].sub; + pfsJournalBuf.log[pfsJournalBuf.num].logSector = logSector; + pfsJournalBuf.num+=1; + } + logSector+=2; + } + + if(pfsMount->blockDev->transfer(pfsMount->fd, clink->u.inode, 0, + (pfsMount->log.number << pfsMount->sector_scale) + 2, pfsCacheNumBuffers*2, + PFS_IO_MODE_WRITE)>=0) + pfsJournalFlush(pfsMount); +} + +int pfsJournalReset(pfs_mount_t *pfsMount) +{ + int rv; + + memset(&pfsJournalBuf, 0, sizeof(pfs_journal_t)); + pfsJournalBuf.magic=PFS_JOUNRNAL_MAGIC; + + pfsMount->blockDev->flushCache(pfsMount->fd); + + rv = pfsMount->blockDev->transfer(pfsMount->fd, &pfsJournalBuf, 0, + (pfsMount->log.number << pfsMount->sector_scale), 2, PFS_IO_MODE_WRITE); + + pfsMount->blockDev->flushCache(pfsMount->fd); + return rv; +} + +int pfsJournalResetThis(pfs_block_device_t *blockDev, int fd, u32 sector) +{ + memset(&pfsJournalBuf, 0, sizeof(pfs_journal_t)); + pfsJournalBuf.magic=PFS_JOUNRNAL_MAGIC; + return blockDev->transfer(fd, &pfsJournalBuf, 0, sector, 2, 1); +} + +int pfsJournalFlush(pfs_mount_t *pfsMount) +{// this write any thing that in are journal buffer :) + int rv; + + pfsMount->blockDev->flushCache(pfsMount->fd); + + pfsJournalBuf.checksum=pfsJournalChecksum(&pfsJournalBuf); + + rv=pfsMount->blockDev->transfer(pfsMount->fd, &pfsJournalBuf, 0, + (pfsMount->log.number << pfsMount->sector_scale), 2, PFS_IO_MODE_WRITE); + + pfsMount->blockDev->flushCache(pfsMount->fd); + return rv; +} + +int pfsJournalRestore(pfs_mount_t *pfsMount) +{ + int rv; + int result; + pfs_cache_t *clink; + u32 i; + + // Read journal buffer from disk + rv = pfsMount->blockDev->transfer(pfsMount->fd, &pfsJournalBuf, 0, + (pfsMount->log.number << pfsMount->sector_scale), 2, PFS_IO_MODE_READ); + + if(rv || (pfsJournalBuf.magic != PFS_JOUNRNAL_MAGIC) || + (pfsJournalBuf.checksum != (u16)pfsJournalChecksum(&pfsJournalBuf))) + { + PFS_PRINTF(PFS_DRV_NAME": Error: cannot read log/invalid log\n"); + return pfsJournalReset(pfsMount); + } + + if(pfsJournalBuf.num == 0) + { + return pfsJournalReset(pfsMount); + } + + clink = pfsCacheAllocClean(&result); + if(!clink) + return result; + + for(i = 0; i < pfsJournalBuf.num; i++) + { + PFS_PRINTF(PFS_DRV_NAME": Log overwrite %d:%08lx\n", pfsJournalBuf.log[i].sub, pfsJournalBuf.log[i].sector); + + // Read data in from log section on disk into cache buffer + rv = pfsMount->blockDev->transfer(pfsMount->fd, clink->u.data, 0, + (pfsMount->log.number << pfsMount->sector_scale) + pfsJournalBuf.log[i].logSector, 2, + PFS_IO_MODE_READ); + + // Write from cache buffer into destination location on disk + if(!rv) + pfsMount->blockDev->transfer(pfsMount->fd, clink->u.data, pfsJournalBuf.log[i].sub, + pfsJournalBuf.log[i].sector, 2, PFS_IO_MODE_WRITE); + } + + pfsCacheFree(clink); + return pfsJournalReset(pfsMount); +} diff --git a/common/libpfs/src/misc.c b/common/libpfs/src/misc.c new file mode 100644 index 0000000..dc66096 --- /dev/null +++ b/common/libpfs/src/misc.c @@ -0,0 +1,263 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +# +# Miscellaneous routines +*/ + +#include +#include +#include +#ifdef _IOP +#include +#include +#include +#include +#include +#else +#include +#include +#include +#endif +#include +#include + +#include "pfs-opt.h" +#include "libpfs.h" + +/////////////////////////////////////////////////////////////////////////////// +// Function definitions + +void *pfsAllocMem(int size) +{ +#ifdef _IOP + int intrStat; + void *mem; + + CpuSuspendIntr(&intrStat); + mem = AllocSysMemory(ALLOC_FIRST, size, NULL); + CpuResumeIntr(intrStat); + + return mem; +#else + return malloc(size); +#endif +} + +void pfsFreeMem(void *buffer) +{ +#ifdef _IOP + int OldState; + + CpuSuspendIntr(&OldState); + FreeSysMemory(buffer); + CpuResumeIntr(OldState); +#else + free(buffer); +#endif +} + +int pfsGetTime(pfs_datetime_t *tm) +{ +#ifdef _IOP + int ret, i; + sceCdCLOCK cdtime; + static pfs_datetime_t timeBuf={ + 0, 7, 6, 5, 4, 3, 2000 // used if can not get time... + }; + + for(i = 0; i < 20; i++) + { + ret = sceCdReadClock(&cdtime); + + if(ret!=0 && cdtime.stat==0) + { + timeBuf.sec=btoi(cdtime.second); + timeBuf.min=btoi(cdtime.minute); + timeBuf.hour=btoi(cdtime.hour); + timeBuf.day=btoi(cdtime.day); + timeBuf.month=btoi(cdtime.month & 0x7F); //The old CDVDMAN sceCdReadClock() function does not automatically file off the highest bit. + timeBuf.year=btoi(cdtime.year) + 2000; + break; + } else { + if(!(cdtime.stat & 0x80)) + break; + } + + DelayThread(100000); + } + memcpy(tm, &timeBuf, sizeof(pfs_datetime_t)); +#else + time_t rawtime; + struct tm * timeinfo; + time (&rawtime); + timeinfo=localtime (&rawtime); + + tm->sec=timeinfo->tm_sec; + tm->min=timeinfo->tm_min; + tm->hour=timeinfo->tm_hour; + tm->day=timeinfo->tm_mday; + tm->month=timeinfo->tm_mon; + tm->year=timeinfo->tm_year+1900; +#endif + + return 0; +} + +int pfsFsckStat(pfs_mount_t *pfsMount, pfs_super_block_t *superblock, + u32 stat, int mode) +{ // mode 0=set flag, 1=remove flag, else check stat + + if(pfsMount->blockDev->transfer(pfsMount->fd, superblock, 0, PFS_SUPER_SECTOR, 1, + PFS_IO_MODE_READ)==0) + { + switch(mode) + { + case PFS_MODE_SET_FLAG: + superblock->pfsFsckStat|=stat; + break; + case PFS_MODE_REMOVE_FLAG: + superblock->pfsFsckStat&=~stat; + break; + default/*PFS_MODE_CHECK_FLAG*/: + return 0 < (superblock->pfsFsckStat & stat); + } + pfsMount->blockDev->transfer(pfsMount->fd, superblock, 0, PFS_SUPER_SECTOR, 1, + PFS_IO_MODE_WRITE); + pfsMount->blockDev->flushCache(pfsMount->fd); + } + return 0; +} + +void pfsPrintBitmap(const u32 *bitmap) { + u32 i, j; + char a[48+1], b[16+1]; + + b[16]=0; + for (i=0; i < 32; i++){ + memset(a, 0, 49); + for (j=0; j < 16; j++){ + const char *c=(const char*)bitmap+j+i*16; + + sprintf(a+j*3, "%02x ", *c); + b[j] = ((*c>=0) && (isgraph(*c))) ? + *c : '.'; + } + PFS_PRINTF("%s%s\n", a, b); + } +} + +u32 pfsGetScale(u32 num, u32 size) +{ + u32 scale = 0; + + while((size << scale) != num) + scale++; + + return scale; +} + +u32 pfsFixIndex(u32 index) +{ + if(index < PFS_INODE_MAX_BLOCKS) + return index; + + index -= PFS_INODE_MAX_BLOCKS; + return index % 123; +} + +/////////////////////////////////////////////////////////////////////////////// +// Functions to work with hdd.irx + +static int pfsHddTransfer(int fd, void *buffer, u32 sub/*0=main 1+=subs*/, u32 sector, + u32 size/* in sectors*/, u32 mode); +static u32 pfsHddGetSubCount(int fd); +static u32 pfsHddGetPartSize(int fd, u32 sub/*0=main 1+=subs*/); +static void pfsHddSetPartError(int fd); +static int pfsHddFlushCache(int fd); + +#define NUM_SUPPORTED_DEVICES 1 +pfs_block_device_t pfsBlockDeviceCallTable[NUM_SUPPORTED_DEVICES] = { + { + "hdd", + &pfsHddTransfer, + &pfsHddGetSubCount, + &pfsHddGetPartSize, + &pfsHddSetPartError, + &pfsHddFlushCache, + } +}; + +pfs_block_device_t *pfsGetBlockDeviceTable(const char *name) +{ + char *end; + char devname[32]; + char *tmp; + u32 len; + int i; + + while(name[0] == ' ') + name++; + + end = strchr(name, ':'); + if(!end) { + PFS_PRINTF(PFS_DRV_NAME": Error: Unknown block device '%s'\n", name); + return NULL; + } + + len = (u32)end - (u32)name; + strncpy(devname, name, len); + devname[len] = '\0'; + + // Loop until digit is found, then terminate string at that digit. + // Should then have just the device name left, minus any front spaces or trailing digits. + tmp = devname; + while(!(isdigit(tmp[0]))) + tmp++; + tmp[0] = '\0'; + + for(i = 0; i < NUM_SUPPORTED_DEVICES; i++) + if(!strcmp(pfsBlockDeviceCallTable[i].devName, devname)) + return &pfsBlockDeviceCallTable[i]; + + return NULL; +} + +static int pfsHddTransfer(int fd, void *buffer, u32 sub/*0=main 1+=subs*/, u32 sector, + u32 size/* in sectors*/, u32 mode) +{ + hddIoctl2Transfer_t t; + + t.sub=sub; + t.sector=sector; + t.size=size; + t.mode=mode; + t.buffer=buffer; + + return ioctl2(fd, HIOCTRANSFER, &t, 0, NULL, 0); +} + +static u32 pfsHddGetSubCount(int fd) +{ + return ioctl2(fd, HIOCNSUB, NULL, 0, NULL, 0); +} + +static u32 pfsHddGetPartSize(int fd, u32 sub/*0=main 1+=subs*/) +{ // of a partition + return ioctl2(fd, HIOCGETSIZE, &sub, 0, NULL, 0); +} + +static void pfsHddSetPartError(int fd) +{ + ioctl2(fd, HIOCSETPARTERROR, NULL, 0, NULL, 0); +} + +static int pfsHddFlushCache(int fd) +{ + return ioctl2(fd,HIOCFLUSH, NULL, 0, NULL, 0); +} diff --git a/common/libpfs/src/super.c b/common/libpfs/src/super.c new file mode 100644 index 0000000..9e900db --- /dev/null +++ b/common/libpfs/src/super.c @@ -0,0 +1,57 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +# +# PFS superblock manipulation routines +*/ + +#include +#include +#ifdef _IOP +#include +#else +#include +#endif +#include + +#include "pfs-opt.h" +#include "libpfs.h" + +u32 pfsBlockSize = 1;// block size scale in sectors (512). Note that 0 = 1x +u32 pfsMetaSize = 1024; // size of each metadata structure + +int pfsCheckZoneSize(u32 zone_size) +{ + if((zone_size & (zone_size - 1)) || (zone_size < (2 * 1024)) || (zone_size > (128 * 1024))) + { + PFS_PRINTF(PFS_DRV_NAME": Error: Invalid zone size\n"); + return 0; + } + + return 1; +} + +// Returns the number of sectors (512 byte units) which will be used +// for bitmaps, given the zone size and partition size +u32 pfsGetBitmapSizeSectors(int zoneScale, u32 partSize) +{ + int w, zones = partSize / (1 << zoneScale); + + w = (zones & 7); + zones = zones / 8 + w; + + w = (zones & 511); + return zones / 512 + w; +} + +// Returns the number of blocks/zones which will be used for bitmaps +u32 pfsGetBitmapSizeBlocks(int scale, u32 mainsize) +{ + u32 a=pfsGetBitmapSizeSectors(scale, mainsize); + return a / (1<0); +} diff --git a/common/libpfs/src/superWrite.c b/common/libpfs/src/superWrite.c new file mode 100644 index 0000000..d76c27c --- /dev/null +++ b/common/libpfs/src/superWrite.c @@ -0,0 +1,254 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +# +# PFS superblock (write) manipulation routines +*/ + +#include +#include +#ifdef _IOP +#include +#else +#include +#endif +#include + +#include "pfs-opt.h" +#include "libpfs.h" + +extern u32 pfsBlockSize; +extern u32 pfsMetaSize; + +// Formats a partition (main or sub) by filling with fragment pattern and setting the bitmap accordingly +int pfsFormatSub(pfs_block_device_t *blockDev, int fd, u32 sub, u32 reserved, u32 scale, u32 fragment) +{ + pfs_cache_t *cache; + int i; + unsigned int j; + u32 sector, count, size, *b; + int result = 0; + + size = blockDev->getSize(fd, sub); + sector = 1 << scale; + count = pfsGetBitmapSizeSectors(scale, size); + if (reserved>=2) + sector+=0x2000; + reserved += pfsGetBitmapSizeBlocks(scale, size); + + if((cache = pfsCacheAllocClean(&result))) + { + // fill with fragment pattern + for (i=127; i>=0; i--) + cache->u.bitmap[i]=fragment; + + // set as allocated the sectors up to reserved, for the first part of the bitmap + // this will mark the area the bitmaps themselves occupy as used + for (j=0, b=cache->u.bitmap; jtransfer(fd, cache->u.bitmap, sub, sector++, 1, 1))>=0)) + for (i=127; i>=0; i--) + cache->u.bitmap[i]=fragment; + + PFS_PRINTF("sector end = %ld\n", sector - 1); + + pfsCacheFree(cache); + } + return result; +} + +// Formats a partition and all sub-partitions with PFS +int pfsFormat(pfs_block_device_t *blockDev, int fd, int zonesize, int fragment) +{ + int result, result2; + pfs_cache_t *clink, *cache; + pfs_super_block_t *sb; + int scale; + u32 i, mainsize, subnumber = blockDev->getSubNumber(fd); + + mainsize=blockDev->getSize(fd, 0); + if(pfsCheckZoneSize(zonesize) == 0) + return -EINVAL; + + scale = pfsGetScale(zonesize, 512); + + if((clink=pfsCacheAllocClean(&result))) + { + sb = clink->u.superblock; + memset(sb, 0, pfsMetaSize); + sb->magic = PFS_SUPER_MAGIC; + sb->version = PFS_FORMAT_VERSION; + sb->modver = ((PFS_MAJOR << 8) | PFS_MINOR); + sb->zone_size = zonesize; + sb->num_subs = subnumber; + sb->log.number = pfsGetBitmapSizeBlocks(scale, mainsize) + (0x2000 >> scale) + 1; + sb->log.count = 0x20000 / zonesize ? 0x20000 / zonesize : 1; + + PFS_PRINTF(PFS_DRV_NAME": Format: log.number = %ld, log.count = %d\n", sb->log.number << scale, sb->log.count); + + sb->root.count = 1; + sb->root.number = sb->log.number + sb->log.count; + if((result = pfsJournalResetThis(blockDev, fd, sb->log.number<= 0) + { + if((cache = pfsCacheAllocClean(&result2))) + { + pfsFillSelfAndParentDentries(cache, &sb->root, &sb->root); + result2 = blockDev->transfer(fd, cache->u.dentry, 0, (sb->root.number+1) << scale, + 1 << pfsBlockSize, 1); + if(result2 == 0) + { + // setup root directory + pfsInodeFill(cache, &sb->root, 0x11FF, 0, 0); + cache->u.inode->data[1].subpart = 0; + cache->u.inode->data[1].number = sb->root.number + 1; + cache->u.inode->data[1].count = 1; + cache->u.inode->checksum = pfsInodeCheckSum(cache->u.inode); + + result2=blockDev->transfer(fd, cache->u.inode, 0, sb->root.number << scale, + 1 << pfsBlockSize, 1); + } + + pfsCacheFree(cache); + } + + if((result = result2) >= 0) + { + for (i=0; i < subnumber+1; i++) + if((result=pfsFormatSub(blockDev, fd, i, i ? 1 : (0x2000 >> scale) + + sb->log.count + 3, scale, fragment))<0) + break; + + if((result == 0) && ((result = blockDev->transfer(fd, sb, 0, PFS_SUPER_BACKUP_SECTOR, 1, 1))==0)) + result = blockDev->transfer(fd, sb, 0, PFS_SUPER_SECTOR, 1, 1); + } + } + + pfsCacheFree(clink); + blockDev->flushCache(fd); + } + return result; +} + +// Formats sub partitions which are added to the main partition after it is initially +// formatted with PFS +int pfsUpdateSuperBlock(pfs_mount_t *pfsMount, pfs_super_block_t *superblock, u32 sub) +{ + u32 scale; + u32 i; + int rv; + + scale = pfsGetScale(superblock->zone_size, 512); + + for(i = superblock->num_subs + 1; i < sub + 1; i++) + { + rv = pfsFormatSub(pfsMount->blockDev, pfsMount->fd, i, 1, scale, 0); + if(rv < 0) + return rv; + } + + superblock->num_subs = sub; + + // Write superblock, then write backup + rv = pfsMount->blockDev->transfer(pfsMount->fd, superblock, 0, PFS_SUPER_SECTOR, 1, PFS_IO_MODE_WRITE); + if(!rv) + rv = pfsMount->blockDev->transfer(pfsMount->fd, superblock, 0, PFS_SUPER_BACKUP_SECTOR, 1, PFS_IO_MODE_WRITE); + + pfsMount->blockDev->flushCache(pfsMount->fd); + + return rv; +} + +int pfsMountSuperBlock(pfs_mount_t *pfsMount) +{ + int result; + pfs_cache_t *clink; + pfs_super_block_t *superblock; + u32 sub; + u32 i; + + + // Get number of sub partitions attached to the main partition + sub = pfsMount->blockDev->getSubNumber(pfsMount->fd); + + // Allocate a cache entry for the superblock + clink = pfsCacheAllocClean(&result); + if(!clink) + return result; + + superblock = clink->u.superblock; + + // Read the suprerblock from the main partition + result = pfsMount->blockDev->transfer(pfsMount->fd, superblock, 0, PFS_SUPER_SECTOR, 1, PFS_IO_MODE_READ); + if(result) goto error; + + if((superblock->magic != PFS_SUPER_MAGIC) || (superblock->version > PFS_FORMAT_VERSION)) + { + PFS_PRINTF(PFS_DRV_NAME": Error: Invalid magic/version\n"); + pfsCacheFree(clink); + return -EIO; + } + + if(!pfsCheckZoneSize(superblock->zone_size)) + result = -EIO; + + if((superblock->pfsFsckStat & PFS_FSCK_STAT_WRITE_ERROR) && (pfsMount->flags & PFS_FIO_ATTR_EXECUTABLE)) + result = -EIO; + + if(sub < superblock->num_subs) + { + PFS_PRINTF(PFS_DRV_NAME": Error: Filesystem larger than partition\n"); + result = -EIO; + } + + if(result) goto error; + + // If new subs have been added, update filesystem + if(superblock->num_subs < sub) + { + PFS_PRINTF(PFS_DRV_NAME": New subs added, updating filesystem..\n"); + result = pfsUpdateSuperBlock(pfsMount, superblock, sub); + } + + if(result) goto error; + + pfsMount->zsize = superblock->zone_size; + pfsMount->sector_scale = pfsGetScale(pfsMount->zsize, 512); + pfsMount->inode_scale = pfsGetScale(pfsMount->zsize, pfsMetaSize); + pfsMount->num_subs = superblock->num_subs; + memcpy(&pfsMount->root_dir, &superblock->root, sizeof(pfs_blockinfo_t)); + memcpy(&pfsMount->log, &superblock->log, sizeof(pfs_blockinfo_t)); + memcpy(&pfsMount->current_dir, &superblock->root, sizeof(pfs_blockinfo_t)); + pfsMount->total_zones = 0; + + // Do a journal restore (in case of un-clean unmount) + pfsJournalRestore(pfsMount); + + // Calculate free space and total size + for(i = 0; i < (pfsMount->num_subs + 1); i++) + { + int free; + + pfsMount->total_zones += pfsMount->blockDev->getSize(pfsMount->fd, i) >> pfsMount->sector_scale; + + free = pfsBitmapCalcFreeZones(pfsMount, i); + pfsMount->free_zone[i] = free; + pfsMount->zfree += free; + } + +error: + pfsCacheFree(clink); + return result; +} diff --git a/docs/README-fsck.txt b/docs/README-fsck.txt new file mode 100644 index 0000000..c1acbf6 --- /dev/null +++ b/docs/README-fsck.txt @@ -0,0 +1,64 @@ +PlayStation FileSystem (PFS) FileSystem ChecKer (FSCK) - 27/05/2015 +-------------------------------------------------------------------- + +The FileSystem ChecKer (FSCK) module is an IOP module for ensuring the integrity of PlayStation FileSystem (PFS) partitions. + +Syntax: + fsck + Options: + -n - specifies the number of buffers that FSCK will use, which affects performance. + +After successful initialization, FSCK will create a fsck device, which will have the following I/O functions: + open: + Opens the partition that will be checked, with a path like "fsck:hdd0:__system". + The mode parameter has the following bit definition: VVVV00PW + W = Write enabled (0 = read-only). + P = Prompt user before performing each fix. + V = Verbosity level (0-15). + 0 = unused. Must be set to 0. + + When this function is invoked FSCK will attempt to: + 1. Get information on the partition with getstat(). + 2. Identify the block device (only hdd: is supported). + 3. Open the partition/block device. + 4. Check super block. + 5. Initialize bitmap. + 6. Check all zones. + close + Used to close the partition that will be checked. + ioctl2 + Used for controlling the FSCK module. + + Codes: + FSCK_IOCTL2_CMD_GET_ESTIMATE (0) = Gets an estimated time remaining for the checking operation to complete. + FSCK_IOCTL2_CMD_START (1) = Starts the checking operation. + FSCK_IOCTL2_CMD_WAIT (2) = Waits for the checking operation to end. + FSCK_IOCTL2_CMD_POLL (3) = Polls for whether the checking operation has ended (1 = in progress, 0 = done). + FSCK_IOCTL2_CMD_GET_STATUS (4) = Gets runtime data (struct fsckStatus, 28 bytes). + FSCK_IOCTL2_CMD_STOP (5) = Aborts the checking operation. Termination must be confirmed with either codes #2 or #3. + +FSCK will check the following: + 1. (For FSCK v1.10 only) extended attribute information. + 2. Root directory - integrity of inodes. + 3. Check all files - integrity of directory entries and inodes. + 4. Compare the bitmap (checks against the bitmap that it generates, as it checks through all files). + 5. Clears the PFS error status bit (1). + 6. If errors were fixed, bit 2 of the super block is set. + +Known bugs: + While a directory entry is checked through, a NULL pointer appears to be dereferenced when an inode cannot be read (wrong method of handling the error?). + +The different FSCK versions: + HDD Utility Disc v1.00 (Japan), FSCK v1.00: FSCK.IRX v1.04 + HDD Utility Disc v1.00 (USA), FSCK v1.00: FSCK.IRX v1.04 + HDD Utility Disc v1.10 (USA), FSCK v1.10: FSCK.IRX v1.04 + PSBBN v0.32, FSCK v1.10: FSCK.IRX v2.01 + + The FSCK module from the US Utility Discs are exactly the same. However, compared to the original Japanese version (despite the similar version number): + 1. The partition's extended attribute area is now checked (read) and will be zero-filled if it cannot be read properly. + 2. Bitmap mismatched errors are no longer recorded as errors, although opting to not fix them will still cause FSCK to terminate. + 3. The __mbr partition is now used for storing the generated bitmap, instead of __tmp. + 4. The PFS library that FSCK is compiled with, appears to be newer (the pfsGetTime function has a newer design). + + There doesn't seem to be a difference in code between the PSBBN FSCK v1.10 module (v2.01) and the modules that come from the US Utility Discs. + The version number is different. And the code has some slight differences in generation, as if it was built with a different compiler. diff --git a/docs/README-fssk.txt b/docs/README-fssk.txt new file mode 100644 index 0000000..c36750d --- /dev/null +++ b/docs/README-fssk.txt @@ -0,0 +1,37 @@ +PlayStation FileSystem (PFS) FileSystem trimmer (FSSK) - 01/01/2016 +-------------------------------------------------------------------- + +The FileSystem trimmer (FSSK) module is an IOP module for reducing internal fragmentation of PlayStation FileSystem (PFS) partitions. + +Syntax: + fssk + Options: + -n - specifies the number of buffers that FSSK will use, which affects performance. + +After successful initialization, FSSK will create a fssk device, which will have the following I/O functions: + open: + Opens the partition that will be checked, with a path like "fssk:hdd0:__system". + The mode parameter has the following bit definition: VVVV0000 + V = Verbosity level (0-15). + 0 = unused. Must be set to 0. + close + Used to close the partition that will be checked. + ioctl2 + Used for controlling the FSSK module. + + Codes: + FSSK_IOCTL2_CMD_GET_ESTIMATE (0) = Gets an estimated time remaining for the checking operation to complete. + FSSK_IOCTL2_CMD_START (1) = Starts the checking operation. + FSSK_IOCTL2_CMD_WAIT (2) = Waits for the checking operation to end. + FSSK_IOCTL2_CMD_POLL (3) = Polls for whether the checking operation has ended (1 = in progress, 0 = done). + FSSK_IOCTL2_CMD_GET_STATUS (4) = Gets runtime data (struct fsskStatus, 28 bytes). + FSSK_IOCTL2_CMD_STOP (5) = Aborts the checking operation. Termination must be confirmed with either codes #2 or #3. + FSSK_IOCTL2_CMD_SET_MINFREE (6) = Sets the minimum free threshold in percentage. The default is 3, but the minimum that can be specified is 4. + FSSK_IOCTL2_CMD_SIM (7) = Runs a simulation. The partsDeleted status will indicate the number of partitions to deleted and the size of the partitions deleted will be returned. + +The different FSSK versions: + HDD Utility Disc v1.00 (Japan): FSSK.IRX v1.04 + HDD Utility Disc v1.10 (USA): FSSK.IRX v1.04 + + The FSSK module from the US Utility Discs are exactly the same as its counterpart from the Japanese Utility Discs, + other than being built with a newer SDK. diff --git a/docs/README-hdck.txt b/docs/README-hdck.txt new file mode 100644 index 0000000..fac7672 --- /dev/null +++ b/docs/README-hdck.txt @@ -0,0 +1,50 @@ +HardDisk ChecKer (HDCK) - 24/05/2015 +------------------------------------ + +The HardDisk ChecKer (HDCK) module is IOP module for ensuring the integrity of the APA scheme, which SONY uses to organize space on the PlayStation 2 HDD unit. + +Syntax: + hdck + +After successful initialization, HDCK will create a hdck device, which will have the following I/O functions: + devctl + Used for starting off the checking process of the HDCK module. + +HDCK will do the following as checks: + 1. Check that the specified HDD unit exists. + 2. Attempt to read the __mbr partition. + 3. Check partition links for integrity issues, both forward and backwards: + a) Forward direction: + i) If the partition has an invalid previous partition address, correct it. + ii) Check that the final partition address is the last partition in the chain. + b) If errors were found in a(i), then scan the disk in reverse: + i) If the partition has an invalid next partition address, correct it. + ii) Check that the first partition address is the first partition in the chain. + + Errors in the links will be fixed on the spot and are not considered errors. + Only I/O errors will be treated as errors for the module to fix. + 4. If I/O errors are found, determine if the partition is a sub-partition of another partition. Attempt to recover it. + 5. If the I/O error still persists (either because it cannot be fixed or if the partition is not a sub-partition of another partition), + delete the affected partitions: + a) If the partition(s) is/are not the last partition(s), mark them as empty. + b) Otherwise, delete them. + 6. If the number of bad partitions exceeds 8, abort the scanning process. No further error handling takes place. + 7. The checks on the partition links ends here. Erase the error sector record. + 8. Delete all free/empty partitions. + 9. Check the relationship between all main and sub partitions: + a) Generate lists of main and sub partitions. + b) For each sub-partition: check through the list of sub-partitions, to see if a the sub-partition belongs to a main partition. + i) If the sub-partition does not belong to anything (orphaned, aka is a "missing sub"), the main partition's sub-partition list is truncated. + ii) All further sub-partitions after the orphan, are truncated. + c) For each sub-partition: check through the list of main partitions, to see if the sub-partition belongs to a main partition. + i) All orphaned sub-partitions are deleted. + +Known bugs/issues/limitations: + 1. if partitions are crosslinked, HDCK will get stuck in an infinite loop as it checks the links between partitions. + +The different FSCK versions: + HDD Utility Disc v1.00 (Japan): HDCK.IRX v1.04 + PSBBN v0.32, FSCK v1.10: HDCK.IRX v2.02 + + There doesn't seem to be a difference in code between the different versions. The version number is different. + And the code has some slight differences in generation, as if it was built with a different compiler and different library versions. diff --git a/docs/README-hdsk.txt b/docs/README-hdsk.txt new file mode 100644 index 0000000..5042134 --- /dev/null +++ b/docs/README-hdsk.txt @@ -0,0 +1,28 @@ +HDD (APA) defragmenter (HDSK) - 01/01/2016 +-------------------------------------------- + +The HDD defragmenter (HDSK) module is an IOP module for reducing external fragmentation of a HDD unit. + +Syntax: + hdsk + +After successful initialization, HDSK will create a hdsk device, which will have the following I/O functions: + devctl + Used for controlling the HDSK module. + + Codes: + HDSK_DEVCTL_GET_FREE (0) = Gets an estimated time remaining for the checking operation to complete. + HDSK_DEVCTL_GET_HDD_STAT (1) = Gets (struct hdskStat) the amount of free space and the amount of data (in sectors) that has to be moved. + HDSK_DEVCTL_START (2) = Starts the defragmentation process. + HDSK_DEVCTL_WAIT (3) = Waits for the defragmentation operation to end. + HDSK_DEVCTL_POLL (4) = Polls for whether the defragmentation operation has ended (1 = in progress, 0 = done). + HDSK_DEVCTL_GET_STATUS (5) = Gets runtime status (0 = OK, non-zero = error). + HDSK_DEVCTL_STOP (6) = Aborts the defragmentation operation. Termination must be confirmed with either codes #2 or #3. + HDSK_DEVCTL_GET_PROGRESS (7) = Gets the progress of the defragmentation operation (returns the amount of sectors moved so far). + +The different HDSK versions: + HDD Utility Disc v1.00 (Japan): HDSK.IRX v1.04 + HDD Utility Disc v1.10 (USA): HDSK.IRX v1.04 + + The HDSK module from the US Utility Discs are exactly the same as its counterpart from the Japanese Utility Discs, + other than being built with a newer SDK. diff --git a/font.c b/font.c new file mode 100644 index 0000000..0e6d47b --- /dev/null +++ b/font.c @@ -0,0 +1,530 @@ +/* Font-drawing engine + Version: 1.20 + Last updated: 2018/06/08 */ + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include FT_FREETYPE_H +#include FT_ADVANCES_H + +#include "graphics.h" +#include "font.h" + +/* Draw the glyphs as close as possible to each other, to save VRAM. + + C: character + X: Horizontal frontier + Y: Vertical frontier + I: Initial state, no frontier. + + Initial state: + IIII + IIII + IIII + IIII + + After one character is added to an empty atlas, initialize the X and Y frontier. + CXXX + YXXX + YXXX + YXXX + + After one more character is added: + CCXX + YYXX + YYXX + YYXX + + After one more character is added: + CCCX + YYYX + YYYX + YYYX + + After one more character is added: + CCCC + YYYY + YYYY + YYYY + + After one more character is added: + CCCC + CXXX + YXXX + YXXX */ + +struct FontGlyphSlot{ + wint_t character; + unsigned short int VramPageX, VramPageY; + short int top, left; //Offsets to place the glyph at. Refer to the FreeType documentation. + short int advance_x, advance_y; + unsigned short int width, height; +}; + +struct FontFrontier{ + short int x, y; + short int width, height; +}; + +struct FontAtlas{ + u32 vram; //Address of the buffer in VRAM + void *buffer; //Address of the buffer in local memory + + unsigned int NumGlyphs; + struct FontGlyphSlot *GlyphSlot; + struct FontFrontier frontier[2]; +}; + +typedef struct Font{ + FT_Face FTFace; + GS_IMAGE Texture; + GS_IMAGE Clut; + void *ClutMem; + unsigned int IsLoaded; + struct FontAtlas atlas[FNT_MAX_ATLASES]; +} Font_t; + +static Font_t GS_FTFont; + +int FontReset(struct UIDrawGlobal *gsGlobal) +{ + struct FontAtlas *atlas; + unsigned short int i; + int result, vram; + + if(GS_FTFont.IsLoaded) + { + result=0; + + GS_FTFont.Clut.x = 0; + GS_FTFont.Clut.y = 0; + GS_FTFont.Clut.width = 16; + GS_FTFont.Clut.height = 16; + GS_FTFont.Clut.vram_width = 1; //width (16) / 64 = 1 + + vram = GsVramAllocTextureBuffer(GS_FTFont.Clut.width, GS_FTFont.Clut.height, GS_FTFont.Clut.psm); + + if (vram >= 0) + { + GS_FTFont.Clut.vram_addr = (u16)vram; + + UploadClut(gsGlobal, &GS_FTFont.Clut, GS_FTFont.ClutMem); + + GS_FTFont.Texture.x = 0; + GS_FTFont.Texture.y = 0; + for(i=0,atlas=GS_FTFont.atlas; ifrontier[0].width = 0; + atlas->frontier[0].height = 0; + atlas->frontier[1].width = 0; + atlas->frontier[1].height = 0; + atlas->vram = -1; + if(atlas->GlyphSlot != NULL) + { + free(atlas->GlyphSlot); + atlas->GlyphSlot = NULL; + } + if(atlas->buffer != NULL) + { + free(atlas->buffer); + atlas->buffer = NULL; + } + atlas->NumGlyphs = 0; + } + } else { + printf("Font: error - unable to allocate VRAM for CLUT.\n"); + result = -1; + } + } + else + result = 0; + + return result; +} + +static int InitFontSupportCommon(struct UIDrawGlobal *gsGlobal, Font_t *GS_FTFont) +{ + int result; + unsigned short int i; + + if((result=FT_Set_Pixel_Sizes(GS_FTFont->FTFace, FNT_CHAR_WIDTH, FNT_CHAR_HEIGHT))==0) + { + GS_FTFont->Texture.width = FNT_ATLAS_WIDTH; + GS_FTFont->Texture.height = FNT_ATLAS_HEIGHT; + GS_FTFont->Texture.psm = GS_TEX_8; + GS_FTFont->Clut.psm = GS_CLUT_32; + + // generate the clut table + u32 *clut = memalign(64, 256 * 4); + GS_FTFont->ClutMem=clut; + for (i = 0; i < 256; ++i) clut[i] = (i > 0x80 ? 0x80 : i) << 24 | i << 16 | i << 8 | i; + SyncDCache(clut, (void*)((unsigned int)clut+256*4)); + + GS_FTFont->IsLoaded=1; + FontReset(gsGlobal); + } + + return result; +} + +int FontInit(struct UIDrawGlobal *gsGlobal, const char *FontFile) +{ + FT_Library FTLibrary; + int result; + + memset(&GS_FTFont, 0, sizeof(Font_t)); + + if((result=FT_Init_FreeType(&FTLibrary))==0) + { + if((result=FT_New_Face(FTLibrary, FontFile, 0, &GS_FTFont.FTFace))==0) + result=InitFontSupportCommon(gsGlobal, &GS_FTFont); + + if(result!=0) + FontDeinit(); + } + + return result; +} + +int FontInitWithBuffer(struct UIDrawGlobal *gsGlobal, void *buffer, unsigned int size) +{ + FT_Library FTLibrary; + int result; + + memset(&GS_FTFont, 0, sizeof(Font_t)); + + if((result=FT_Init_FreeType(&FTLibrary))==0) + { + if((result=FT_New_Memory_Face(FTLibrary, buffer, size, 0, &GS_FTFont.FTFace))==0) + result=InitFontSupportCommon(gsGlobal, &GS_FTFont); + + if(result!=0) + FontDeinit(); + } + + return result; +} + +void FontDeinit(void) +{ + struct FontAtlas *atlas; + unsigned int i; + + if(GS_FTFont.IsLoaded) + FT_Done_Face(GS_FTFont.FTFace); + + for(i=0,atlas=GS_FTFont.atlas; ibuffer!=NULL) + free(atlas->buffer); + if(atlas->GlyphSlot!=NULL) + free(atlas->GlyphSlot); + } + + if(GS_FTFont.ClutMem!=NULL) + free(GS_FTFont.ClutMem); + + memset(&GS_FTFont, 0, sizeof(Font_t)); +} + +static int AtlasInit(Font_t *font, struct FontAtlas *atlas) +{ + unsigned int TextureSizeEE, i; + short int width_aligned, height_aligned; + int result; + + result = 0; + width_aligned = (font->Texture.width+127)&~127; + height_aligned = (font->Texture.height+127)&~127; + font->Texture.vram_width = width_aligned / 64; + if((atlas->vram = GsVramAllocTextureBuffer(width_aligned, height_aligned, font->Texture.psm)) >= 0) + { + TextureSizeEE = width_aligned * height_aligned; + if((atlas->buffer = memalign(64, TextureSizeEE)) == NULL) + { + printf("Font: error - unable to allocate memory for atlas.\n"); + result = -ENOMEM; + } + memset(atlas->buffer, 0, TextureSizeEE); + SyncDCache(atlas->buffer, atlas->buffer+TextureSizeEE); + } + else + { + printf("Font: error - unable to allocate VRAM for atlas.\n"); + result = -ENOMEM; + } + + return result; +} + +static struct FontGlyphSlot *AtlasAlloc(Font_t *font, struct FontAtlas *atlas, short int width, short int height) +{ + struct FontGlyphSlot *GlyphSlot; + + GlyphSlot = NULL; + if(atlas->buffer == NULL) + { //No frontier (empty atlas) + if(AtlasInit(font, atlas) != 0) + return NULL; + + //Give the glyph 1px more, for texel rendering. + atlas->frontier[0].width = FNT_ATLAS_WIDTH - (width + 1); + atlas->frontier[0].height = FNT_ATLAS_HEIGHT; + atlas->frontier[0].x = width + 1; + atlas->frontier[0].y = 0; + atlas->frontier[1].width = FNT_ATLAS_WIDTH; + atlas->frontier[1].height = FNT_ATLAS_HEIGHT - (height + 1); + atlas->frontier[1].x = 0; + atlas->frontier[1].y = height + 1; + + atlas->NumGlyphs++; + + if((atlas->GlyphSlot = realloc(atlas->GlyphSlot, atlas->NumGlyphs * sizeof(struct FontGlyphSlot))) != NULL) + { + GlyphSlot = &atlas->GlyphSlot[atlas->NumGlyphs - 1]; + + GlyphSlot->VramPageX = 0; + GlyphSlot->VramPageY = 0; + } else { + printf("Font: error - unable to allocate a new glyph slot.\n"); + atlas->NumGlyphs = 0; + } + + return GlyphSlot; + } else { //We have the frontiers + //Try to allocate from the horizontal frontier first. + if((atlas->frontier[0].width >= width + 1) + && (atlas->frontier[0].height >= height + 1)) + { + atlas->NumGlyphs++; + + if((atlas->GlyphSlot = realloc(atlas->GlyphSlot, atlas->NumGlyphs * sizeof(struct FontGlyphSlot))) != NULL) + { + GlyphSlot = &atlas->GlyphSlot[atlas->NumGlyphs - 1]; + + GlyphSlot->VramPageX = atlas->frontier[0].x; + GlyphSlot->VramPageY = atlas->frontier[0].y; + + //Give the glyph 1px more, for texel rendering. + //Update frontier. + atlas->frontier[0].width -= width + 1; + atlas->frontier[0].x += width + 1; + + //If the new glyph is a little taller than the glyphs under the horizontal frontier, move the vertical frontier. + if(atlas->frontier[0].y + height + 1 > atlas->frontier[1].y) + atlas->frontier[1].y = atlas->frontier[0].y + height + 1; + } else { + printf("Font: error - unable to allocate a new glyph slot.\n"); + atlas->NumGlyphs = 0; + } + //Now try the vertical frontier. + } else if((atlas->frontier[1].width >= width + 1) + && (atlas->frontier[1].height >= height + 1)) + { + atlas->NumGlyphs++; + + if((atlas->GlyphSlot = realloc(atlas->GlyphSlot, atlas->NumGlyphs * sizeof(struct FontGlyphSlot))) != NULL) + { + GlyphSlot = &atlas->GlyphSlot[atlas->NumGlyphs - 1]; + + GlyphSlot->VramPageX = atlas->frontier[1].x; + GlyphSlot->VramPageY = atlas->frontier[1].y; + + //Give the glyph 1px more, for texel rendering. + /* Update frontier. + If we got here, it means that the horizontal frontier is very close the edge of VRAM. + Give a large portion of the space recorded under this frontier to the horizontal frontier. + + Before: After one more character is added: + CCCC CCCC + YYYY CXXX + YYYY YXXX + YYYY YXXX */ + atlas->frontier[0].x = width + 1; + atlas->frontier[0].y = atlas->frontier[1].y; + atlas->frontier[0].width = FNT_ATLAS_WIDTH - (width + 1); + atlas->frontier[0].height = atlas->frontier[1].height; + atlas->frontier[1].height -= height + 1; + atlas->frontier[1].y += height + 1; + } else { + printf("Font: error - unable to allocate a new glyph slot.\n"); + atlas->NumGlyphs = 0; + } + } + } + + return GlyphSlot; +} + +static void AtlasCopyFT(Font_t *font, struct FontAtlas *atlas, struct FontGlyphSlot *GlyphSlot, FT_GlyphSlot FT_GlyphSlot) +{ + short int yOffset; + unsigned char *FTCharRow; + + for(yOffset=0; yOffsetbitmap.rows; yOffset++) + { + FTCharRow=(void*)UNCACHED_SEG(((unsigned int)atlas->buffer+GlyphSlot->VramPageX+(GlyphSlot->VramPageY+yOffset)*font->Texture.width)); + memcpy(FTCharRow, &((unsigned char*)FT_GlyphSlot->bitmap.buffer)[yOffset*FT_GlyphSlot->bitmap.width], FT_GlyphSlot->bitmap.width); + } +} + +static struct FontGlyphSlot *UploadGlyph(struct UIDrawGlobal *gsGlobal, Font_t *font, wint_t character, FT_GlyphSlot FT_GlyphSlot, struct FontAtlas **AtlasOut) +{ + struct FontAtlas *atlas; + struct FontGlyphSlot *GlyphSlot; + unsigned short int i; + + *AtlasOut=NULL; + GlyphSlot = NULL; + for(i = 0,atlas=font->atlas; i < FNT_MAX_ATLASES; i++,atlas++) + { + if((GlyphSlot = AtlasAlloc(font, atlas, FT_GlyphSlot->bitmap.width, FT_GlyphSlot->bitmap.rows)) != NULL) + break; + } + + if(GlyphSlot != NULL) + { + GlyphSlot->character = character; + GlyphSlot->left = FT_GlyphSlot->bitmap_left; + GlyphSlot->top = FT_GlyphSlot->bitmap_top; + GlyphSlot->width = FT_GlyphSlot->bitmap.width; + GlyphSlot->height = FT_GlyphSlot->bitmap.rows; + GlyphSlot->advance_x = FT_GlyphSlot->advance.x >> 6; + GlyphSlot->advance_y = FT_GlyphSlot->advance.y >> 6; + + *AtlasOut = atlas; + + //Initiate a texture flush before reusing the VRAM page, if the slot was just used earlier. + GsTextureFlush(); + + AtlasCopyFT(font, atlas, GlyphSlot, FT_GlyphSlot); + font->Texture.vram_addr = atlas->vram; + GsLoadImage(atlas->buffer, &font->Texture); + } else + printf("Font: error - all atlas are full.\n"); + + return GlyphSlot; +} + +static void DrawGlyph(struct UIDrawGlobal *gsGlobal, Font_t *font, wint_t character, short int x, short int y, short int z, float scale, GS_RGBAQ colour, short int *width) +{ + FT_GlyphSlot FT_GlyphSlot; + struct FontGlyphSlot *GlyphSlot; + struct FontAtlas *atlas; + unsigned short int i, slot; + short int XCoordinates, YCoordinates; + + if(font->IsLoaded) + { + FT_GlyphSlot = font->FTFace->glyph; + + //Scan through all uploaded glyph slots. + for(i=0,atlas=font->atlas,GlyphSlot=NULL; iNumGlyphs; slot++) + { + if(atlas->GlyphSlot[slot].character==character) + { + GlyphSlot=&atlas->GlyphSlot[slot]; + goto atlas_selected; + } + } + } + + if(GlyphSlot == NULL) + { //Not in VRAM? Upload it. + if(FT_Load_Char(font->FTFace, character, FT_LOAD_RENDER)) + return; + if((GlyphSlot = UploadGlyph(gsGlobal, font, character, FT_GlyphSlot, &atlas)) == NULL) + return; + // printf("Uploading %c, %u, %u\n", character, GlyphSlot->VramPageX, GlyphSlot->VramPageY); + } + +atlas_selected: + font->Texture.vram_addr=atlas->vram; + + YCoordinates=y+FNT_CHAR_HEIGHT*scale-GlyphSlot->top*scale; + XCoordinates=x+GlyphSlot->left*scale; + DrawSpriteTexturedClut(gsGlobal, &font->Texture, &font->Clut, + XCoordinates, YCoordinates, //x1, y1 + GlyphSlot->VramPageX, GlyphSlot->VramPageY, //u1, v1 + XCoordinates+GlyphSlot->width*scale, YCoordinates+GlyphSlot->height*scale, //x2, y2 + GlyphSlot->VramPageX+GlyphSlot->width, GlyphSlot->VramPageY+GlyphSlot->height, //u2, v2 + z, colour); + + *width = GlyphSlot->advance_x * scale; + } +} + +void FontPrintfWithFeedback(struct UIDrawGlobal *gsGlobal, short x, short int y, short int z, float scale, GS_RGBAQ colour, const char *string, short int *xRel, short int *yRel) +{ + wchar_t wchar; + short int StartX, StartY, width; + int charsize, bufmax; + + StartX = x; + StartY = y; + + for(bufmax = strlen(string) + 1; *string != '\0'; string += charsize, bufmax -= charsize) + { + //Up to MB_CUR_MAX + charsize = mbtowc(&wchar, string, bufmax); + + switch(wchar) + { + case '\r': + x=StartX; + break; + case '\n': + y+=(short int)(FNT_CHAR_HEIGHT*scale); + x=StartX; + break; + case '\t': + x+=(short int)(FNT_TAB_STOPS*FNT_CHAR_WIDTH*scale)-(unsigned int)x%(unsigned int)(FNT_TAB_STOPS*FNT_CHAR_WIDTH*scale); + break; + default: + width = 0; + DrawGlyph(gsGlobal, &GS_FTFont, wchar, x, y, z, scale, colour, &width); + x += width; + } + } + + if(xRel != NULL) + *xRel = x - StartX; + if(yRel != NULL) + *yRel = y - StartY; +} + +void FontPrintf(struct UIDrawGlobal *gsGlobal, short int x, short int y, short int z, float scale, GS_RGBAQ colour, const char *string) +{ + return FontPrintfWithFeedback(gsGlobal, x, y, z, scale, colour, string, NULL, NULL); +} + +static int GetGlyphWidth(Font_t *font, wint_t character) +{ + FT_UInt index; + FT_Fixed advance; + + if(font->IsLoaded) + { + index = FT_Get_Char_Index(font->FTFace, character); + + if(FT_Get_Advance(font->FTFace, index, FT_LOAD_DEFAULT, &advance) == 0) + return(advance >> 16); + } + + return 0; +} + +int FontGetGlyphWidth(wint_t character) +{ + return GetGlyphWidth(&GS_FTFont, character); +} diff --git a/font.h b/font.h new file mode 100644 index 0000000..707a5f8 --- /dev/null +++ b/font.h @@ -0,0 +1,14 @@ +#define FNT_ATLAS_WIDTH 128 +#define FNT_ATLAS_HEIGHT 128 +#define FNT_MAX_ATLASES 4 +#define FNT_CHAR_WIDTH 16 +#define FNT_CHAR_HEIGHT 16 +#define FNT_TAB_STOPS 4 + +int FontInit(struct UIDrawGlobal *gsGlobal, const char *FontFile); +int FontInitWithBuffer(struct UIDrawGlobal *gsGlobal, void *buffer, unsigned int size); +void FontDeinit(void); +int FontReset(struct UIDrawGlobal *gsGlobal); //Performs a partial re-initialization of the Font library and re-allocates VRAM. Used when VRAM has been cleared. +void FontPrintfWithFeedback(struct UIDrawGlobal *gsGlobal, short int x, short int y, short int z, float scale, GS_RGBAQ colour, const char *string, short int *xRel, short int *yRel); +void FontPrintf(struct UIDrawGlobal *gsGlobal, short int x, short int y, short int z, float scale, GS_RGBAQ colour, const char *string); +int FontGetGlyphWidth(wint_t character); diff --git a/fsck/Makefile b/fsck/Makefile new file mode 100644 index 0000000..dad9925 --- /dev/null +++ b/fsck/Makefile @@ -0,0 +1,19 @@ + +LIBPFS_PATH = ../common/libpfs + +IOP_BIN = fsck.irx +PFS_OBJS = $(LIBPFS_PATH)/src/bitmap.o $(LIBPFS_PATH)/src/inode.o $(LIBPFS_PATH)/src/journal.o $(LIBPFS_PATH)/src/misc.o $(LIBPFS_PATH)/src/super.o $(LIBPFS_PATH)/src/cache.o $(LIBPFS_PATH)/src/block.o +IOP_OBJS = fsck.o misc.o bitmap.o $(PFS_OBJS) imports.o + +IOP_INCS += -I$(CURDIR) -I$(LIBPFS_PATH)/include +IOP_CFLAGS += -Wall -fno-builtin -DPFS_OSD_VER +IOP_LDFLAGS += -s +IOP_LIBS += -lgcc + +all: $(IOP_BIN) + +clean: + rm -f $(IOP_BIN) $(IOP_OBJS) + +include $(PS2SDK)/Defs.make +include ../common/Rules.make diff --git a/fsck/bitmap.c b/fsck/bitmap.c new file mode 100644 index 0000000..d0c6d0f --- /dev/null +++ b/fsck/bitmap.c @@ -0,0 +1,202 @@ +#include +#include +#include +#include +#include + +#include "pfs-opt.h" +#include "libpfs.h" +#include "bitmap.h" + +#define NUM_BITMAP_ENTRIES 5 +#define BITMAP_BUFFER_SIZE 256 +#define BITMAP_BUFFER_SIZE_BYTES (BITMAP_BUFFER_SIZE * 512) + +extern u32 pfsMetaSize; +extern u32 pfsBlockSize; + +static pfs_bitmap_t *pfsBitmapData; +static void *pfsBitmapBuffer; +static int PfsTempBitmapFD; + +//0x00004ce8 +pfs_cache_t *pfsBitmapReadPartition(pfs_mount_t *mount, u16 subpart, u32 chunk) +{ + int result; + return pfsCacheGetData(mount, subpart, chunk + (1 << mount->inode_scale) + (0x2000 >> pfsBlockSize), PFS_CACHE_FLAG_BITMAP, &result); +} + +//0x000052c0 +static int pfsBitmapTransfer(pfs_bitmap_t *bitmap, int mode) +{ + hddIoctl2Transfer_t xferParams; + int result; + + xferParams.sub = 0; + xferParams.sector = (bitmap->index << 8); + xferParams.size = BITMAP_BUFFER_SIZE; + xferParams.mode = mode; + xferParams.buffer = bitmap->bitmap; + + if((result = ioctl2(PfsTempBitmapFD, HIOCTRANSFER, &xferParams, 0, NULL, 0)) < 0) + printf("fsck: error: could not read/write bitmap.\n"); + else + bitmap->isDirty = 0; + + return result; +} + +//0x00005380 +pfs_bitmap_t *pfsBitmapRead(u32 index) +{ + unsigned int i; + pfs_bitmap_t *pBitmap; + + for(i = 1, pBitmap = NULL; i < NUM_BITMAP_ENTRIES; i++) + { + if(pfsBitmapData[i].index == index) + { + pBitmap = &pfsBitmapData[i]; + break; + } + } + + if(pBitmap != NULL) + { + if(pBitmap->nused == 0) + pBitmap = (pfs_bitmap_t*)pfsCacheUnLink((pfs_cache_t*)pBitmap); + + pBitmap->nused++; + }else{ + pBitmap = pfsBitmapData->next; + if(pBitmap->isDirty != 0) + { + if(pfsBitmapTransfer(pBitmap, PFS_IO_MODE_WRITE) < 0) + return NULL; + } + + pBitmap->index = index; + pBitmap->isDirty = 0; + pBitmap->nused = 1; + if(pfsBitmapTransfer(pBitmap, PFS_IO_MODE_READ) < 0) + return NULL; + pBitmap = (pfs_bitmap_t*)pfsCacheUnLink((pfs_cache_t*)pBitmap); + } + + return pBitmap; +} + +//0x00005484 +void pfsBitmapFree(pfs_bitmap_t *bitmap) +{ + if(bitmap->nused == 0) + { + printf("fsck: error: unused cache returned\n"); + }else{ + bitmap->nused--; + if(bitmap->nused == 0) + pfsCacheLink((pfs_cache_t*)pfsBitmapData->prev, (pfs_cache_t*)bitmap); + } +} + +//0x000054f0 +u32 *pfsGetBitmapEntry(u32 index) +{ + pfs_bitmap_t *bitmap; + u32 *result; + + if((bitmap = pfsBitmapRead(index >> 20)) != NULL) + { + pfsBitmapFree(bitmap); + result = &bitmap->bitmap[(index >> 5) & 0x7FFF]; + }else{ + result = NULL; + } + + return result; +} + +//0x0000567c +int pfsBitmapPartInit(u32 size) +{ + int i, result; + unsigned int bitmapCount; + + bitmapCount = (size >> 20) + (0 < ((size >> 3) & 0x0001FFFF)); + + for(i = 1 ; i < NUM_BITMAP_ENTRIES; i++) + { + pfsBitmapData[i].isDirty = 0; + pfsBitmapData[i].nused = 0; + pfsBitmapData[i].index = i - 1; + memset(pfsBitmapData[i].bitmap, 0, BITMAP_BUFFER_SIZE_BYTES); + } + + if(bitmapCount >= NUM_BITMAP_ENTRIES) + { + for(i = 1 ; i < NUM_BITMAP_ENTRIES; i++) + { + if((result = pfsBitmapTransfer(&pfsBitmapData[i], PFS_IO_MODE_WRITE)) < 0) + { + printf("fsck: error: could not initialize bitmap.\n"); + return result; + } + } + + for(i = NUM_BITMAP_ENTRIES-1 ; i < bitmapCount; i++) + { + pfsBitmapData[NUM_BITMAP_ENTRIES-1].index = i; + if((result = pfsBitmapTransfer(&pfsBitmapData[NUM_BITMAP_ENTRIES-1], PFS_IO_MODE_WRITE)) < 0) + { + printf("fsck: error: could not initialize bitmap.\n"); + return result; + } + } + + result = 0; + }else{ + result = 0; + } + + return result; +} + +//0x000057bc +int pfsBitmapInit(void) +{ + u32 *bitmap; + int i; + +#ifdef FSCK100 + remove("hdd0:_tmp"); + + if((PfsTempBitmapFD = open("hdd0:_tmp,,,128M,PFS", O_CREAT|O_RDWR)) < 0) + printf("fsck: error: could not create temporary partition.\n"); +#else + if((PfsTempBitmapFD = open("hdd0:__mbr", O_RDWR)) < 0) + { + printf("fsck: error: could not open mbr partition.\n"); + return PfsTempBitmapFD; + } +#endif + + if((pfsBitmapBuffer = pfsAllocMem((NUM_BITMAP_ENTRIES-1) * BITMAP_BUFFER_SIZE_BYTES)) == NULL || (pfsBitmapData = pfsAllocMem(NUM_BITMAP_ENTRIES * sizeof(pfs_bitmap_t))) == NULL) + { + return -ENOMEM; + } + + memset(pfsBitmapData, 0, NUM_BITMAP_ENTRIES * sizeof(pfs_bitmap_t)); + + pfsBitmapData[0].next = pfsBitmapData; + pfsBitmapData[0].prev = pfsBitmapData; + + bitmap = pfsBitmapBuffer; + for(i = 1 ; i < NUM_BITMAP_ENTRIES; i++) + { + pfsBitmapData[i].bitmap = bitmap; + bitmap = (u32*)((u8*)bitmap + BITMAP_BUFFER_SIZE_BYTES); + pfsCacheLink((pfs_cache_t*)pfsBitmapData[0].prev, (pfs_cache_t*)&pfsBitmapData[i]); + } + + return 0; +} diff --git a/fsck/bitmap.h b/fsck/bitmap.h new file mode 100644 index 0000000..e864479 --- /dev/null +++ b/fsck/bitmap.h @@ -0,0 +1,15 @@ +typedef struct pfs_bitmap{ + struct pfs_bitmap *next; //0x00 + struct pfs_bitmap *prev; //0x04 + u16 isDirty; //0x08 + u16 nused; //0x0A + u32 index; //0x0C + u32 *bitmap; //0x10 +} pfs_bitmap_t; + +pfs_cache_t *pfsBitmapReadPartition(pfs_mount_t *mount, u16 subpart, u32 chunk); +pfs_bitmap_t *pfsBitmapRead(u32 index); +void pfsBitmapFree(pfs_bitmap_t *bitmap); +u32 *pfsGetBitmapEntry(u32 index); +int pfsBitmapPartInit(u32 size); +int pfsBitmapInit(void); diff --git a/fsck/fsck-ioctl.h b/fsck/fsck-ioctl.h new file mode 100644 index 0000000..9f30f40 --- /dev/null +++ b/fsck/fsck-ioctl.h @@ -0,0 +1,23 @@ +struct fsckStatus{ + u32 zoneUsed; //0x00 + u32 inodeBlockCount; //0x04 + u32 files; //0x08 + u32 directories; //0x0C + u32 PWDLevel; //0x10 + u32 errorCount; //0x14 + u32 fixedErrorCount; //0x18 +}; + +//IOCTL2 codes - none of these commands have any inputs or outputs, unless otherwise specified. +enum FSCK_IOCTL2_CMD{ + FSCK_IOCTL2_CMD_GET_ESTIMATE = 0, //Output = u32 time + FSCK_IOCTL2_CMD_START, + FSCK_IOCTL2_CMD_WAIT, + FSCK_IOCTL2_CMD_POLL, + FSCK_IOCTL2_CMD_GET_STATUS, //Output = struct fsckStatus + FSCK_IOCTL2_CMD_STOP +}; + +#define FSCK_MODE_WRITE 1 +#define FSCK_MODE_AUTO 2 +#define FSCK_MODE_VERBOSITY(x) (((x)&0xF)<<4) diff --git a/fsck/fsck.c b/fsck/fsck.c new file mode 100644 index 0000000..f3dde6a --- /dev/null +++ b/fsck/fsck.c @@ -0,0 +1,1332 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pfs-opt.h" +#include "libpfs.h" +#include "bitmap.h" +#include "misc.h" + +#include "fsck-ioctl.h" + +IRX_ID("fsck", PFS_MAJOR, PFS_MINOR); + +struct fsckRuntimeData{ + struct fsckStatus status; //0x00 + int hasError; //0x1C + int stopFlag; //0x20 +}; + +static int fsckWriteEnabled; +static int fsckAutoMode; +static int fsckVerbosityLevel; +extern u32 pfsMetaSize; +extern u32 pfsBlockSize; +static pfs_mount_t MainPFSMount = {0}; //FIXME: if not explicitly initialized to 0, the generated IRX would somehow have garbage in this structure. + +#define IO_BUFFER_SIZE 256 +#define IO_BUFFER_SIZE_BYTES (IO_BUFFER_SIZE * 512) + +static struct fsckRuntimeData fsckRuntimeData; +static u8 IOBuffer[IO_BUFFER_SIZE_BYTES]; + +#define FSCK_NUM_SUPPORTED_DEVICES 1 +#define FSCK_MAX_PATH_LEVELS 64 +#define FSCK_MAX_PATH_SEG_LENGTH 256 + +static int fsckEventFlagID; +static int fsckThreadID; +static char fsckPathBuffer[FSCK_MAX_PATH_LEVELS][FSCK_MAX_PATH_SEG_LENGTH]; + +static u32 ZoneSizes[0x41]; //Contains the sizes of all zones, in units of 512-byte sectors. +static u32 ZoneMap[0x41]; //Contains the starting addresses of all zones, in units of blocks. + +//0x00000000 +const char *fsckGetChar(void) +{ + static char buffer[80]; + const char *pChar; + + if(gets(buffer) != NULL) + { + for(pChar = buffer; *pChar != '\0'; pChar++) + { + if(look_ctype_table(*(const unsigned char*)pChar) & 0x17) + break; + } + }else{ + pChar = NULL; + } + + return pChar; +} + +//0x00000068 +static int fsckPromptUserAction(const char *description, int mode) +{ + int result; + unsigned char choice; + + fsckRuntimeData.status.errorCount++; + + if(fsckWriteEnabled != 0) + { + if(fsckAutoMode != 0) + { + printf("\n"); + if(mode) + fsckRuntimeData.status.fixedErrorCount++; + else + fsckRuntimeData.hasError = 1; + + result = mode; + }else{ + printf("%s (%s)? ", description, mode == 0 ? "n/y" : "y/n"); + do + { + choice = *fsckGetChar(); + }while(choice != 'n' && choice != 'y'); + + if(choice == 'y') + { + fsckRuntimeData.status.fixedErrorCount++; + result = 1; + }else{ + fsckRuntimeData.hasError = 1; + result = 0; + } + } + }else{ + printf("\n"); + fsckRuntimeData.hasError = 1; + result = 0; + } + + return result; +} + +#ifndef FSCK100 +static int fsckPromptUserAction2(const char *description, int mode) +{ + int result; + unsigned char choice; + + if(fsckWriteEnabled != 0) + { + if(fsckAutoMode != 0) + { + printf("\n"); + if(mode == 0) + fsckRuntimeData.hasError = 1; + + result = mode; + }else{ + printf("%s (%s)? ", description, mode == 0 ? "n/y" : "y/n"); + do + { + choice = *fsckGetChar(); + }while(choice != 'n' && choice != 'y'); + + if(choice == 'y') + { + result = 1; + }else{ + fsckRuntimeData.hasError = 1; + result = 0; + } + } + }else{ + printf("\n"); + fsckRuntimeData.hasError = 1; + result = 0; + } + + return result; +} +#endif + +//0x00002654 +static int DisplayUsageHelp(void) +{ + printf("fsck: error: Usage: fsck [-n ]\n"); + return MODULE_NO_RESIDENT_END; +} + +#ifndef FSCK100 +static int fsckCheckExtendedAttribute(pfs_mount_t *mount) +{ + int remaining, size, result; + + lseek(mount->fd, 0, SEEK_SET); + for(result = 0,remaining = 0x1FF8; remaining != 0; remaining -= size) + { + size = (remaining > IO_BUFFER_SIZE) ? IO_BUFFER_SIZE : remaining; + + if(read(mount->fd, IOBuffer, size * 512) == -EIO) + { + printf("fsck: cannot read extended attribute %d\n", -EIO); + if(fsckPromptUserAction(" nullify extended attribute", 1) == 0) + break; + + memset(IOBuffer, 0, IO_BUFFER_SIZE_BYTES); + lseek(mount->fd, 0, SEEK_SET); + for(remaining = 0x1FF8; remaining != 0; remaining -= size) + { + size = (remaining > IO_BUFFER_SIZE) ? IO_BUFFER_SIZE : remaining; + + if((result = write(mount->fd, IOBuffer, size * 512)) < 0) + { + printf("fsck: error: could not nullify extended attribute.\n"); + break; + } + } + + break; + } + } + + return result; +} +#endif + +static int fsckCheckDirentryInode(pfs_cache_t *clink); + +//0x0000183c +static pfs_cache_t *CheckRootDirectory(pfs_mount_t *mount) +{ + pfs_cache_t *pResultClink, *clink; + int result; + + if((clink = pfsInodeGetData(mount, mount->root_dir.subpart, mount->root_dir.number, &result)) != NULL) + { + if(fsckVerbosityLevel >= 2) + printf("/: "); + if(fsckCheckDirentryInode(clink) == 0) + pResultClink = clink; + else{ + pfsCacheFree(clink); + pResultClink = NULL; + } + }else{ + pResultClink = NULL; + } + + return pResultClink; +} + +//0x000007a8 +static void pfsPrintPWD(void) +{ + int i; + char *pName; + + for(i = 0, pName = fsckPathBuffer[0]; i < fsckRuntimeData.status.PWDLevel; i++, pName += FSCK_MAX_PATH_SEG_LENGTH) + printf("/%s", pName); + + if(i == 0) + printf("/"); +} + +//0x000008c8 +static int pfsInitDirEnt(pfs_mount_t *mount, u16 subpart, u32 inodeNumber, u32 number, int isDir) +{ + pfs_cache_t *clink; + pfs_dentry_t *pDentry; + int result; + + result = -EIO; + if(fsckPromptUserAction(" initialize directory entry", 1) != 0) + { + if((clink = pfsCacheGetData(mount, subpart, number, PFS_CACHE_FLAG_NOLOAD, &result)) != NULL) + { + memset(clink->u.dentry, 0, pfsMetaSize); + pDentry = clink->u.dentry; + + if(isDir) + { //Similar to pfsFillSelfAndParentDentries(). + //Self entry + pDentry->inode = inodeNumber; + pDentry->sub = (u8)subpart; + pDentry->pLen = 1; + pDentry->aLen = FIO_S_IFDIR | 12; + pDentry->path[0] = '.'; + pDentry->path[1] = '\0'; + + //Parent entry + pDentry = (pfs_dentry_t *)((u8*)pDentry + 12); + + pDentry->inode = inodeNumber; + pDentry->sub = (u8)subpart; + pDentry->pLen = 2; + pDentry->aLen = FIO_S_IFDIR | 500; + pDentry->path[0] = '.'; + pDentry->path[1] = '.'; + pDentry->path[2] = '\0'; + } else { + pDentry->aLen = sizeof(pfs_dentry_t); + } + + pDentry = clink->u.dentry + 1; + pDentry->aLen = sizeof(pfs_dentry_t); + + clink->flags |= PFS_CACHE_FLAG_DIRTY; + pfsCacheFree(clink); + } + } + + return result; +} + +//0x000001c0 +static int fsckCheckFileBitmap(pfs_mount_t *mount, pfs_blockinfo_t *blockinfo) +{ + pfs_bitmapInfo_t bitmapinfo; + pfs_cache_t *clink; + u32 chunk, bit, count; + u32 *pBitmap; + + pfsBitmapSetupInfo(mount, &bitmapinfo, blockinfo->subpart, blockinfo->number); + for(count = blockinfo->count; count != 0; ) + { + chunk = bitmapinfo.chunk; + bitmapinfo.chunk++; + //bitmapinfo.index will contain the first index in the bitmap array to start from. + if((clink = pfsBitmapReadPartition(mount, blockinfo->subpart, chunk)) != NULL) + { //bitmapinfo.bit will contain the first bit to start checking from. + for(pBitmap = &clink->u.bitmap[bitmapinfo.index]; (pBitmap < clink->u.bitmap + 0x100) && count != 0; pBitmap++,bitmapinfo.bit = 0) + { + for(bit = bitmapinfo.bit; (bit < 32) && (count != 0); bit++,count--) + { + if((*pBitmap & (1 << bit)) == 0) + { + printf("fsck: not marked as used.\n"); + if(fsckPromptUserAction(" Mark in use", 1) != 0) + { + *pBitmap |= (1 << bit); + clink->flags |= PFS_CACHE_FLAG_DIRTY; + }else + return -EINVAL; + } + } + } + + bitmapinfo.index = 0; + pfsCacheFree(clink); + }else{ + break; + } + } + + return 0; +} + +//0x00005550 +static int fsckCheckZones(u32 number, u32 size) +{ + u32 index, zone, remaining, bit, startBit; + u32 *pZone; + pfs_bitmap_t *pBitmap; + + index = number >> 20; + zone = (number >> 5) & 0x7FFF; //Contains the index of the first zone to start checking from. + startBit = number & 31; + for(remaining = size; remaining != 0; ) + { + pBitmap = pfsBitmapRead(index); + index++; + + if(pBitmap != NULL) + { //startBit contains the first bit to start checking from. + for(pZone = &pBitmap->bitmap[zone]; (pZone < pBitmap->bitmap + 0x8000) && remaining != 0; pZone++,startBit = 0) + { + for(bit = startBit; bit < 32 && remaining != 0; bit++,remaining--) + { + if((*pZone & (1 << bit)) != 0) + { + printf("fsck: error: overlapped zone found.\n"); + return 1; + } + + *pZone |= (1 << bit); + pBitmap->isDirty = 1; + } + } + + pfsBitmapFree(pBitmap); + zone = 0; + }else{ + return -EIO; + } + } + + return 0; +} + +//0x000009dc - BUGBUG - if there's no next segment (blockClink == NULL), this function will end up dereferencing a NULL pointer. +static void fsckFillInode(pfs_cache_t *inodeClink, pfs_cache_t *blockClink, u32 blocks, u32 entries, u32 segdesg) +{ + memset(&blockClink->u.inode->next_segment, 0, sizeof(blockClink->u.inode->next_segment)); + + inodeClink->u.inode->number_segdesg = (blocks - segdesg) * inodeClink->pfsMount->zsize; + inodeClink->u.inode->subpart = 0; + if(!FIO_S_ISDIR(inodeClink->u.inode->mode)) + inodeClink->u.inode->attr &= ~PFS_FIO_ATTR_CLOSED; + + inodeClink->u.inode->number_blocks = blocks; + inodeClink->u.inode->number_data = entries; + inodeClink->u.inode->number_segdesg = segdesg; + inodeClink->u.inode->last_segment.subpart = blockClink->sub; + inodeClink->u.inode->last_segment.number = blockClink->block >> blockClink->pfsMount->inode_scale; + blockClink->flags |= PFS_CACHE_FLAG_DIRTY; + inodeClink->flags |= PFS_CACHE_FLAG_DIRTY; +} + +//0x00000b04 - I hate this function and it hates me. +static int fsckCheckDirentryInode(pfs_cache_t *direntInodeClink) +{ + int i, result; + pfs_cache_t *blockClink, *childDirentBlockClink; + pfs_blockinfo_t *pInodeInfo; + u32 index, new_index, blocks, segdesg, inodeStart, sector; + u32 inodeOffset; //inodeOffset doesn't seem to be 64-bit, even though the inode size field is 64-bits wide. + + inodeOffset = 0; + blocks = 0; + segdesg = 1; + result = 0; + blockClink = pfsCacheUsedAdd(direntInodeClink); + + //Iterate through the whole directory entry and check that every part of it can be read. + for(index = 0; (index < direntInodeClink->u.inode->number_data) && (result == 0); index++) //While within bounds and no error occurs. + { + new_index = pfsFixIndex(index); + + if(index != 0 && new_index == 0) + { //Read the next inode + pInodeInfo = &blockClink->u.inode->next_segment; + pfsCacheFree(blockClink); + if((blockClink = pfsCacheGetData(direntInodeClink->pfsMount, pInodeInfo->subpart, pInodeInfo->number << direntInodeClink->pfsMount->inode_scale, PFS_CACHE_FLAG_SEGI, &result)) == NULL) + { + if((result == -EIO) && (fsckPromptUserAction(" Remove rest of file", 1) != 0)) + fsckFillInode(direntInodeClink, NULL, blocks, index, segdesg); //bug?! This will cause a NULL-pointer to be dereferenced! + break; + } + + segdesg++; + } + + //0x00000c18 + //Check that the block is valid. + pInodeInfo = &blockClink->u.inode->data[new_index]; + if((direntInodeClink->pfsMount->num_subs < pInodeInfo->subpart) + || (pInodeInfo->count == 0) + || (pInodeInfo->number < 2) + || (ZoneSizes[pInodeInfo->subpart] < ((pInodeInfo->number + pInodeInfo->count) << direntInodeClink->pfsMount->sector_scale))) + { + putchar('\n'); + pfsPrintPWD(); + printf(" contains a bad zone."); + if(fsckPromptUserAction(" Remove rest of file", 1) != 0) + { + fsckFillInode(direntInodeClink, blockClink, blocks, index, segdesg); + break; + } + + result = -EINVAL; + break; + } + + //0x00000ccc + if(new_index != 0) + { + if(FIO_S_ISDIR(direntInodeClink->u.inode->mode)) + { //If the inode is a directory, then all its blocks contain directory entries. Ensure that all of them can be read. + //0x00000cfc + for(i = 0; i < pInodeInfo->count; i++) + { + inodeStart = (pInodeInfo->number + i) << direntInodeClink->pfsMount->inode_scale; + + //0x00000dbc + for(sector = 0; (sector < (1 << direntInodeClink->pfsMount->inode_scale)) && (inodeOffset < direntInodeClink->u.inode->size); sector++) + { + inodeOffset += pfsMetaSize; + if((childDirentBlockClink = pfsCacheGetData(direntInodeClink->pfsMount, pInodeInfo->subpart, inodeStart + sector, PFS_CACHE_FLAG_NOTHING, &result)) != NULL) + pfsCacheFree(childDirentBlockClink); + else{ + if(result == -ENOMEM) + goto end; + + printf("fsck: could not read directory block.\n"); + if((result = pfsInitDirEnt(direntInodeClink->pfsMount, pInodeInfo->subpart, inodeStart + sector, direntInodeClink->u.inode->inode_block.number, (index == 1 && i == 0) ? sector < 1 : 0)) < 0) + goto end2; + } + } + + //0x00000e0c + if(fsckVerbosityLevel >= 10) + putchar('.'); + } + } else { + //The Inode contains a file. Ensure that the whole file can be read. + //0x00000e50 + for(i = 0; i < pInodeInfo->count; i++) + { + if(direntInodeClink->pfsMount->blockDev->transfer(direntInodeClink->pfsMount->fd, IOBuffer, pInodeInfo->subpart, (pInodeInfo->number + i) << direntInodeClink->pfsMount->sector_scale, 1 << direntInodeClink->pfsMount->sector_scale, PFS_IO_MODE_READ) == 0) + { + if(fsckVerbosityLevel >= 10) + putchar('.'); + + if(fsckRuntimeData.stopFlag != 0) + goto end; + }else{ + printf("fsck: could not read zone.\n"); + if(fsckPromptUserAction(" Remove rest of file", 1) != 0) + fsckFillInode(direntInodeClink, blockClink, blocks, index, segdesg); + + goto end; + } + } + } + } + + //0x00000ef8 + //Check the free space bitmap. + if((result = fsckCheckFileBitmap(direntInodeClink->pfsMount, pInodeInfo)) >= 0) + { + result = fsckCheckZones(pInodeInfo->number + ZoneMap[pInodeInfo->subpart], pInodeInfo->count); + if(result > 0) + { //0x00000f44 + if(fsckPromptUserAction(" Remove rest of file", 1) != 0) + fsckFillInode(direntInodeClink, blockClink, blocks, index, segdesg); + break; + } + else if (result < 0) // result < 0 + goto end2; + }else + goto end2; + + //0x00000f7c - Final part of the loop. + fsckRuntimeData.status.inodeBlockCount += pInodeInfo->count; + blocks += pInodeInfo->count; + if(fsckRuntimeData.stopFlag != 0) + break; + } + +end: + if(result < 0) + { +end2: + fsckRuntimeData.hasError = 1; + } + + //0x00000ff0 + if(fsckVerbosityLevel >= 2) + printf("\n"); + + pfsCacheFree(blockClink); + + return result; +} + +//0x00000828 +static void fsckFixDEntry(pfs_cache_t *clink, pfs_dentry_t *dentry) +{ + u32 dEntrySize, offset; + u16 aLen; + pfs_dentry_t *pDEntryNew, *pDEntry; + + dEntrySize = (u32)((u8*)dentry - (u8*)clink->u.dentry); + //if((s32)dEntrySize < 0) + if(clink->u.dentry > dentry) + dEntrySize += 0x1FF; + + dEntrySize = dEntrySize >> 9 << 9; //Round off + pDEntryNew = (pfs_dentry_t*)((u8*)clink->u.dentry + dEntrySize); + for(pDEntry = NULL,offset = 0; offset < sizeof(pfs_dentry_t); offset += aLen,pDEntry = pDEntryNew,pDEntryNew = (pfs_dentry_t*)((u8*)pDEntryNew + aLen)) + { + aLen = pDEntryNew->aLen & 0x0FFF; + + if(pDEntryNew == dentry) + { + if(pDEntry != NULL) + { + pDEntry->aLen = (pDEntry->aLen & FIO_S_IFMT) | ((pDEntry->aLen & 0x0FFF) + aLen); + }else{ + pDEntryNew->inode = 0; + pDEntryNew->pLen = 0; + } + + clink->flags |= PFS_CACHE_FLAG_DIRTY; + break; + } + } +} + +//0x000012b4 +static void fsckCheckSelfEntry(pfs_cache_t *SelfInodeClink, pfs_cache_t *SelfDEntryClink, pfs_dentry_t *dentry) +{ + if((SelfInodeClink->sub != dentry->sub) || (SelfInodeClink->u.inode->inode_block.number != dentry->inode)) + { + printf("fsck: '.' point not itself.\n"); + if(fsckPromptUserAction(" Fix", 1) != 0) + { + dentry->sub = SelfInodeClink->u.inode->inode_block.subpart; + dentry->inode = SelfInodeClink->u.inode->inode_block.number; + SelfDEntryClink->flags |= PFS_CACHE_FLAG_DIRTY; + } + } +} + +//0x00001374 +static void fsckCheckParentEntry(pfs_cache_t *ParentInodeClink, pfs_cache_t *SelfDEntryClink, pfs_dentry_t *dentry) +{ + if((ParentInodeClink->sub != dentry->sub) || (ParentInodeClink->u.inode->inode_block.number != dentry->inode)) + { + printf("fsck: '..' point not parent.\n"); + if(fsckPromptUserAction(" Fix", 1) != 0) + { + dentry->sub = ParentInodeClink->u.inode->inode_block.subpart; + dentry->inode = ParentInodeClink->u.inode->inode_block.number; + SelfDEntryClink->flags |= PFS_CACHE_FLAG_DIRTY; + } + } +} + +static void fsckCheckFiles(pfs_cache_t *ParentInodeClink, pfs_cache_t *InodeClink); + +//0x00001054 +static void fsckCheckFile(pfs_cache_t *FileInodeClink, pfs_cache_t *FileInodeDataClink, pfs_dentry_t *dentry) +{ + if(fsckRuntimeData.status.PWDLevel < FSCK_MAX_PATH_LEVELS - 1) + { + memset(fsckPathBuffer[fsckRuntimeData.status.PWDLevel], 0, FSCK_MAX_PATH_SEG_LENGTH); + strncpy(fsckPathBuffer[fsckRuntimeData.status.PWDLevel], dentry->path, dentry->pLen); + fsckRuntimeData.status.PWDLevel++; + + if(fsckVerbosityLevel >= 2) + { + pfsPrintPWD(); + if(FIO_S_ISDIR(dentry->aLen)) + printf(": "); + } + + if(FIO_S_ISREG(dentry->aLen)) + { + if(FileInodeDataClink->pfsMount->blockDev->transfer(FileInodeDataClink->pfsMount->fd, IOBuffer, FileInodeDataClink->sub, (FileInodeDataClink->block + 1) << pfsBlockSize, 1 << pfsBlockSize, PFS_IO_MODE_READ) != 0) + { + printf("fsck: could not read extended attribute.\n"); + if(fsckPromptUserAction(" initialize attribute", 1) != 0) + { + memset(IOBuffer, 0, 1024); + ((pfs_aentry_t*)IOBuffer)->aLen = 1024; + if(FileInodeDataClink->pfsMount->blockDev->transfer(FileInodeDataClink->pfsMount->fd, IOBuffer, FileInodeDataClink->sub, (FileInodeDataClink->block + 1) << pfsBlockSize, 1 << pfsBlockSize, PFS_IO_MODE_WRITE) != 0) + fsckRuntimeData.hasError = 1; + } else //This is not actually needed, but it's done in the original. + fsckRuntimeData.hasError = 1; + } + } + + //0x00001220 + fsckCheckDirentryInode(FileInodeDataClink); + if(FIO_S_ISDIR(dentry->aLen)) + { + fsckRuntimeData.status.directories++; + fsckCheckFiles(FileInodeClink, FileInodeDataClink); + }else + fsckRuntimeData.status.files++; + + fsckRuntimeData.status.PWDLevel--; + }else{ + printf("fsck: error: exceed max directory depth.\n"); + fsckRuntimeData.hasError = 1; + } +} + +//0x00001434 +static void fsckCheckFiles(pfs_cache_t *ParentInodeClink, pfs_cache_t *InodeClink) +{ + pfs_blockpos_t BlockPosition; + pfs_dentry_t *pDEntry, *pDEntryEnd; + int result; + u32 inodeOffset, dEntrySize; //inodeOffset doesn't seem to be 64-bit, even though the inode size field is 64-bits wide. + pfs_cache_t *DEntryClink, *FileInodeDataClink; + + inodeOffset = 0; + BlockPosition.inode = pfsCacheUsedAdd(InodeClink); + BlockPosition.block_segment = 1; + BlockPosition.block_offset = 0; + BlockPosition.byte_offset = 0; + if((DEntryClink = pfsGetDentriesChunk(&BlockPosition, &result)) == NULL) + { + pfsCacheFree(BlockPosition.inode); + fsckRuntimeData.hasError = 1; + return; + } + pDEntry = DEntryClink->u.dentry; + + //0x000017c0 + while(inodeOffset < InodeClink->u.inode->size) + { + //0x000014cc + if(pDEntry >= (pfs_dentry_t*)(DEntryClink->u.data + 1024)) + { + //Read next inode + //0x000014e4 + pfsCacheFree(DEntryClink); + if(pfsInodeSync(&BlockPosition, 1024, InodeClink->u.inode->number_data) != 0 || (DEntryClink = pfsGetDentriesChunk(&BlockPosition, &result)) == NULL) + { + fsckRuntimeData.hasError = 1; + goto end; + } + + pDEntry = DEntryClink->u.dentry; + } + + //0x0000153c + for(pDEntryEnd = pDEntry + 1; pDEntry < pDEntryEnd; pDEntry = (pfs_dentry_t*)((u8*)pDEntry + dEntrySize),inodeOffset += dEntrySize) + { + if(fsckRuntimeData.stopFlag != 0) + goto end; + + dEntrySize = pDEntry->aLen & 0x0FFF; + + if(dEntrySize & 3) + { + dEntrySize = (u32)((u8*)pDEntryEnd - (u8*)pDEntry); + printf("fsck: directory entry is not aligned.\n"); + + if(fsckPromptUserAction(" Fix", 1) != 0) + { + pDEntry->aLen = (pDEntry->aLen & 0xF000) | dEntrySize; + DEntryClink->flags |= PFS_CACHE_FLAG_DIRTY; + } + } + + if(dEntrySize < ((pDEntry->pLen + 11) & ~3)) + { + dEntrySize = (u32)((u8*)pDEntryEnd - (u8*)pDEntry); + printf("fsck: directory entry is too small.\n"); + + if(fsckPromptUserAction(" Fix", 1) != 0) + { + if(((pDEntry->pLen + 11) & ~3) < dEntrySize) + { + pDEntry->aLen = (pDEntry->aLen & 0xF000) | dEntrySize; + DEntryClink->flags |= PFS_CACHE_FLAG_DIRTY; + }else{ + fsckFixDEntry(DEntryClink, pDEntry); + pDEntry->inode = 0; + } + }else{ + pDEntry->inode = 0; + } + } + + //0x00001654 + if(pDEntryEnd < (pfs_dentry_t*)((u8*)pDEntry + dEntrySize)) + { + dEntrySize = (u32)((u8*)pDEntryEnd - (u8*)pDEntry); + printf("fsck: directory entry is too long.\n"); + if(fsckPromptUserAction(" Fix", 1) != 0) + fsckFixDEntry(DEntryClink, pDEntry); + + pDEntry->inode = 0; + } + + //0x00001694 + if(pDEntry->inode != 0) + { + if((pDEntry->pLen == 1) && (pDEntry->path[0] == '.')) + fsckCheckSelfEntry(InodeClink, DEntryClink, pDEntry); + else if((pDEntry->pLen == 2) && (pDEntry->path[0] == '.')) + fsckCheckParentEntry(ParentInodeClink, DEntryClink, pDEntry); + else{ + if((FileInodeDataClink = pfsInodeGetData(InodeClink->pfsMount, pDEntry->sub, pDEntry->inode, &result)) != NULL) + { + fsckCheckFile(InodeClink, FileInodeDataClink, pDEntry); + pfsCacheFree(FileInodeDataClink); + }else{ + if(result == -EIO) + { + printf(" contains an unreadable file '%.*s'.\n", pDEntry->pLen, pDEntry->path); + if(fsckPromptUserAction(" Remove", 1) != 0) + fsckFixDEntry(DEntryClink, pDEntry); + }else + fsckRuntimeData.hasError = 1; + } + } + } + } + + //0x000017ac + if(fsckRuntimeData.stopFlag != 0) + break; + } + +end: + pfsCacheFree(DEntryClink); + pfsCacheFree(BlockPosition.inode); +} + +//0x0000054c +static void fsckCompareBitmap(pfs_mount_t *mount, void *buffer) +{ + int hasUpdate; + u32 i, NumZones, zone; + u32 *pBitmap, ZoneStartOffset, *pRawBitmap, RawSize, unaligned, length, offset; + + for(i = 0,offset = 0; i < mount->num_subs + 1; i++,offset++) + { + RawSize = ZoneSizes[i] >> mount->sector_scale; + NumZones = RawSize / (mount->zsize << 3); + unaligned = RawSize % (mount->zsize << 3); + + for(zone = 0; (unaligned == 0 && zone < NumZones) || (unaligned != 0 && zone < NumZones + 1); zone++) + { + length = (zone == NumZones) ? unaligned : mount->zsize << 3; + hasUpdate = 0; + + pBitmap = pfsGetBitmapEntry(offset + (zone * (mount->zsize << 3))); + ZoneStartOffset = (i == 0) ? (0x2000 >> mount->sector_scale) + 1 : 1; + if(mount->blockDev->transfer(mount->fd, IOBuffer, i, (ZoneStartOffset + zone) << mount->sector_scale, 1 << mount->sector_scale, PFS_IO_MODE_READ) < 0) + break; + + for(pRawBitmap = (u32*)IOBuffer; pRawBitmap < (u32*)(IOBuffer + (length >> 3)); pRawBitmap++,pBitmap++) + { + //0x00000698 + if(*pRawBitmap != *pBitmap) + { + printf("fsck: bitmap unmatch %08lx, %08lx\n", *pRawBitmap, *pBitmap); +#ifdef FSCK100 + if(fsckPromptUserAction(" Replace", 1) != 0) +#else + if(fsckPromptUserAction2(" Replace", 1) != 0) +#endif + { + *pRawBitmap = *pBitmap; + hasUpdate = 1; + } + } + } + + if(hasUpdate != 0) + mount->blockDev->transfer(mount->fd, IOBuffer, i, (ZoneStartOffset + zone) << mount->sector_scale, 1 << mount->sector_scale, PFS_IO_MODE_WRITE); + } + } +} + +//0x00001f34 +static void FsckThread(void *arg) +{ + pfs_cache_t *clink; + pfs_mount_t *mount = (pfs_mount_t*)arg; + pfs_super_block_t *super = (pfs_super_block_t*)IOBuffer; + +#ifdef FSCK100 + if(fsckVerbosityLevel > 0) + printf("fsck: Check Root Directory...\n"); +#else + if(fsckVerbosityLevel > 0) + printf("fsck: Check Extended attribute...\n"); + + if(fsckCheckExtendedAttribute(mount) < 0) + { + printf("fsck: error: I cannot continue, giving up.\n"); + goto fsck_thread_end; + } + + if(fsckVerbosityLevel > 0) + { + printf("fsck: done.\n"); + + if(fsckVerbosityLevel > 0) + printf("fsck: Check Root Directory...\n"); + } +#endif + + if((clink = CheckRootDirectory(mount)) == NULL) + { + printf("fsck: error: I cannot continue, giving up.\n"); + goto fsck_thread_end; + } + + fsckRuntimeData.status.directories++; + + if(fsckVerbosityLevel > 0) + printf("fsck: done.\n"); + + if(fsckVerbosityLevel > 0) + printf("fsck: Check all files...\n"); + + fsckCheckFiles(clink, clink); + + if(fsckVerbosityLevel > 0) + printf("fsck: done.\n"); + + pfsCacheFlushAllDirty(mount); + pfsCacheFree(clink); + + //0x00002030 + if(fsckRuntimeData.hasError == 0) + { + if(fsckRuntimeData.stopFlag == 0) + { + if(fsckVerbosityLevel > 0) + printf("fsck: Compare bitmap...\n"); + + fsckCompareBitmap(mount, IOBuffer); + + if(fsckVerbosityLevel > 0) + printf("fsck: done.\n"); + } + + //0x000020ac + if(fsckRuntimeData.hasError == 0) + { + if(fsckRuntimeData.stopFlag == 0) + { //Clear the write error state, if it was set. + if(mount->blockDev->transfer(mount->fd, super, 0, PFS_SUPER_SECTOR, 1, PFS_IO_MODE_READ) == 0) + { + if(super->pfsFsckStat & PFS_FSCK_STAT_WRITE_ERROR) + { + super->pfsFsckStat &= ~PFS_FSCK_STAT_WRITE_ERROR; + mount->blockDev->transfer(mount->fd, super, 0, PFS_SUPER_SECTOR, 1, PFS_IO_MODE_WRITE); + } + } + + ioctl2(mount->fd, HIOCGETPARTERROR, NULL, 0, NULL, 0); + } + } + } + + //0x00002164 + if(fsckRuntimeData.status.fixedErrorCount != 0) + { //Indicate that errors were fixed. + if(mount->blockDev->transfer(mount->fd, super, 0, PFS_SUPER_SECTOR, 1, PFS_IO_MODE_READ) == 0) + { + super->pfsFsckStat |= PFS_FSCK_STAT_ERRORS_FIXED; + mount->blockDev->transfer(mount->fd, super, 0, PFS_SUPER_SECTOR, 1, PFS_IO_MODE_WRITE); + } + } + + mount->blockDev->flushCache(mount->fd); + +fsck_thread_end: + SetEventFlag(fsckEventFlagID, 1); +} + +//0x0000264c +static int FsckUnsupported(void) +{ + return 0; +} + +//0x00000340 +static int fsckCheckBitmap(pfs_mount_t *mount, void *buffer) +{ + u32 i, count, block, BitmapStart, sector; + int result; + + result = 0; + for(i = 0; i < mount->num_subs + 1; i++) + { + block = 0; + for(block = 0,count = pfsGetBitmapSizeBlocks(mount->sector_scale, ZoneSizes[i]); block < count; block++) + { + BitmapStart = block + 1; + if(i == 0) + BitmapStart += 0x2000 >> mount->sector_scale; + + if((result = mount->blockDev->transfer(mount->fd, buffer, i, BitmapStart << mount->sector_scale, 1 << mount->sector_scale, PFS_IO_MODE_READ)) < 0) + { + printf("fsck: cannot read bitmap\n"); + if(fsckPromptUserAction(" Overwrite", 1) == 0) + return result; + + for(sector = 0,result = 0; sector < 1 << mount->sector_scale; sector++) + { + //0x0000044c + if(mount->blockDev->transfer(mount->fd, buffer, i, (BitmapStart << mount->sector_scale) + sector, 1, PFS_IO_MODE_READ) < 0) + { + memset(buffer, -1, 512); + if((result = mount->blockDev->transfer(mount->fd, buffer, i, (BitmapStart << mount->sector_scale) + sector, 1, PFS_IO_MODE_WRITE)) < 0) + { + printf("fsck: error: overwrite bitmap failed.\n"); + return result; + } + } + } + } + } + } + + return result; +} + +//0x000018b8 +static int CheckSuperBlock(pfs_mount_t *pMainPFSMount) +{ + int result, i; + u32 *pFreeZones; + pfs_super_block_t *super = (pfs_super_block_t*)IOBuffer; + + pMainPFSMount->num_subs = pMainPFSMount->blockDev->getSubNumber(pMainPFSMount->fd); + if(pMainPFSMount->blockDev->transfer(pMainPFSMount->fd, super, 0, PFS_SUPER_SECTOR, 1, PFS_IO_MODE_READ) < 0 || (super->magic != PFS_SUPER_MAGIC)) + { + //0x00001930 + printf("fsck: Read super block failed, try another.\n"); + + if(pMainPFSMount->blockDev->transfer(pMainPFSMount->fd, IOBuffer, 0, PFS_SUPER_BACKUP_SECTOR, 1, PFS_IO_MODE_READ) != 0) + { + printf("fsck: error: could not read any super block.\n"); + return -EIO; + } + + result = (super->magic == PFS_SUPER_MAGIC) ? 0 : -EIO; + + if(result != 0) + { + printf("fsck: error: could not read any super block.\n"); + return -EIO; + } + + if(fsckPromptUserAction(" Overwrite super block", 1) == 0) + { + return -EIO; + } + + if((result = pMainPFSMount->blockDev->transfer(pMainPFSMount->fd, IOBuffer, 0, PFS_SUPER_SECTOR, 1, PFS_IO_MODE_WRITE)) < 0) + { + printf("fsck: error: overwrite failed.\n"); + return result; + } + } + + //0x00001a1c + if(super->version > PFS_FORMAT_VERSION) + { + printf("fsck: error: unknown version.\n"); + return -EINVAL; + } + + if(((super->zone_size & (super->zone_size - 1)) != 0) || + (super->zone_size < 0x800) || + (0x20000 < super->zone_size)) + { + printf("fsck: error: invalid zone size.\n"); + return -EINVAL; + } + + if(pMainPFSMount->num_subs < super->num_subs) + { + printf("fsck: filesystem larger than partition size.\n"); + + if(fsckPromptUserAction(" Fix size", 1) == 0) + return -EINVAL; + + super->num_subs = pMainPFSMount->num_subs; + if((result = pMainPFSMount->blockDev->transfer(pMainPFSMount->fd, IOBuffer, 0, PFS_SUPER_SECTOR, 1, PFS_IO_MODE_WRITE)) < 0) + { + printf("fsck: error: could not fix the filesystem size.\n"); + return result; + } + } + + pMainPFSMount->zsize = super->zone_size; + pMainPFSMount->sector_scale = pfsGetScale(super->zone_size, 512); + pMainPFSMount->inode_scale = pfsGetScale(super->zone_size, 1024); + + memcpy(&pMainPFSMount->root_dir, &super->root, sizeof(pMainPFSMount->root_dir)); + memcpy(&pMainPFSMount->log, &super->log, sizeof(pMainPFSMount->log)); + memcpy(&pMainPFSMount->current_dir, &super->root, sizeof(pMainPFSMount->current_dir)); + pMainPFSMount->total_zones = 0; + + if(fsckVerbosityLevel) + printf("fsck: \tlog check...\n"); + + if((result = pfsJournalRestore(pMainPFSMount)) < 0) + return result; + + memset(ZoneSizes, 0, 0x10); + memset(ZoneMap, 0, 0x10); + + for(i = 0; i < pMainPFSMount->num_subs + 1; i++) + { + ZoneSizes[i] = pMainPFSMount->blockDev->getSize(pMainPFSMount->fd, i); + if(i != 0) + ZoneMap[i] = (ZoneSizes[i - 1] >> pMainPFSMount->sector_scale) + ZoneMap[i - 1]; + } + + if(fsckVerbosityLevel > 0) + printf("fsck: \tCheck Bitmaps...\n"); + + //0x00001c80 + if((result = fsckCheckBitmap(pMainPFSMount, IOBuffer)) >= 0) + { + if(fsckVerbosityLevel > 0) + printf("fsck: \tdone.\n"); + + for(i = 0,pFreeZones = pMainPFSMount->free_zone; i < pMainPFSMount->num_subs + 1; i++,pFreeZones++) + { + pMainPFSMount->total_zones += ZoneSizes[i] >> pMainPFSMount->sector_scale; + pMainPFSMount->zfree += (*pFreeZones = pfsBitmapCalcFreeZones(pMainPFSMount, i)); + } + + if(fsckVerbosityLevel > 0) + printf("fsck: zonesz %ld, %ld zones, %ld free.\n", pMainPFSMount->zsize, pMainPFSMount->total_zones, pMainPFSMount->zfree); + } + + return result; +} + +//0x00002224 +static int FsckOpen(iop_file_t *fd, const char *name, int flags, int mode) +{ + int blockfd, result, i; + u32 count; + iox_stat_t StatData; + pfs_block_device_t *pblockDevData; + + fsckWriteEnabled = mode & FSCK_MODE_WRITE; + fsckAutoMode = mode & FSCK_MODE_AUTO; + fsckVerbosityLevel = (mode & 0xF0) >> 4; + + if(MainPFSMount.fd) return -EBUSY; + + if((result = getstat(name, &StatData)) < 0) + { + printf("fsck: error: could not get status.\n"); + return result; + } + + if(StatData.mode != APA_TYPE_PFS) + { + printf("fsck: error: not PFS.\n"); + return -EINVAL; + } + + if((pblockDevData = pfsGetBlockDeviceTable(name)) == NULL) + return -ENXIO; + + if((blockfd = open(name, O_RDWR)) < 0) + { + printf("fsck: error: cannot open.\n"); + return blockfd; + } + + memset(&MainPFSMount, 0, sizeof(MainPFSMount)); + MainPFSMount.fd = blockfd; + MainPFSMount.blockDev = pblockDevData; + + if(fsckVerbosityLevel > 0) + printf("fsck: Check Super Block...\n"); + + if((result = CheckSuperBlock(&MainPFSMount)) < 0) + { + MainPFSMount.fd = 0; + printf("fsck: error: cannot continue.\n"); + return result; + } + + if(fsckVerbosityLevel > 0) + printf("fsck: done.\n"); + + if((result = pfsBitmapPartInit(MainPFSMount.total_zones)) >= 0) + { + //0x000023bc + memset(&fsckRuntimeData, 0, sizeof(fsckRuntimeData)); + + fsckRuntimeData.status.zoneUsed = MainPFSMount.total_zones - MainPFSMount.zfree; + for(i = 0; i < MainPFSMount.num_subs + 1; fsckRuntimeData.status.inodeBlockCount += count,i++) + { + count = pfsGetBitmapSizeBlocks(MainPFSMount.sector_scale, ZoneSizes[i]) + 1; + if(i == 0) + count += (0x2000 >> MainPFSMount.sector_scale) + MainPFSMount.log.count; + + if(fsckCheckZones(ZoneMap[i], count) < 0) + break; + } + + //0x0000246c + fd->privdata = &MainPFSMount; + result = 0; + }else{ + MainPFSMount.fd = 0; + } + + return result; +} + +//0x000024a4 +static int FsckClose(iop_file_t *fd) +{ + close(((pfs_mount_t*)fd->privdata)->fd); + pfsCacheClose((pfs_mount_t*)fd->privdata); + memset(fd->privdata, 0, sizeof(pfs_mount_t)); + + return 0; +} + +//0x00001d7c +static int fsckGetEstimatedTime(pfs_mount_t *mount, int *result) +{ + unsigned int i; + u64 clock; + iop_sys_clock_t SysClock1, SysClock2, SysClockDiff; + u32 sec, usec; + + clock = 1000000; + for(i = 0; i < 4; i++) + { + GetSystemTime(&SysClock1); + mount->blockDev->transfer(mount->fd, IOBuffer, 0, 1 << mount->sector_scale, 1 << mount->sector_scale, PFS_IO_MODE_READ); + GetSystemTime(&SysClock2); + + SysClockDiff.lo = SysClock2.lo - SysClock1.lo; + SysClockDiff.hi = SysClock2.hi - SysClock1.hi - (SysClock2.lo < SysClock1.lo); + SysClock2USec(&SysClockDiff, &sec, &usec); + + printf("fsck: %ld system clocks = %ld.%06ld sec\n", SysClockDiff.lo, sec, usec); + + if(usec < clock) + clock = usec; + } + + //0x00001e8c + if((*result = ((clock + 400) * fsckRuntimeData.status.zoneUsed / 1000000)) == 0) + *result = 1; + + return 0; +} + +//0x000024f0 +static int FsckIoctl2(iop_file_t *fd, int cmd, void *arg, unsigned int arglen, void *buf, unsigned int buflen) +{ + int result; + u32 FlagBits; + + switch(cmd) + { + case FSCK_IOCTL2_CMD_GET_ESTIMATE: //0x00002528 + result = fsckGetEstimatedTime(fd->privdata, buf); + break; + case FSCK_IOCTL2_CMD_START: //0x0000253c + result = StartThread(fsckThreadID, fd->privdata); + break; + case FSCK_IOCTL2_CMD_WAIT: //0x00002554 + result = WaitEventFlag(fsckEventFlagID, 1, WEF_OR|WEF_CLEAR, &FlagBits); + break; + case FSCK_IOCTL2_CMD_POLL: //0x00002574 + result = PollEventFlag(fsckEventFlagID, 1, WEF_OR|WEF_CLEAR, &FlagBits); + if(result == KE_EVF_COND) result = 1; + break; + case FSCK_IOCTL2_CMD_GET_STATUS: //0x000025a8 + memcpy(buf, &fsckRuntimeData.status, sizeof(struct fsckStatus)); + result = 0; + break; + case FSCK_IOCTL2_CMD_STOP: //0x0000262c + fsckRuntimeData.stopFlag = 1; + result = 0; + break; + default: + result = 0; + } + + return result; +} + +static iop_device_ops_t FsckDeviceOps={ + (void*)&FsckUnsupported, + (void*)&FsckUnsupported, + NULL, + &FsckOpen, + &FsckClose, + NULL, + NULL, + NULL, + (void*)&FsckUnsupported, + (void*)&FsckUnsupported, + (void*)&FsckUnsupported, + (void*)&FsckUnsupported, + (void*)&FsckUnsupported, + (void*)&FsckUnsupported, + (void*)&FsckUnsupported, + (void*)&FsckUnsupported, + (void*)&FsckUnsupported, + (void*)&FsckUnsupported, + (void*)&FsckUnsupported, + (void*)&FsckUnsupported, + (void*)&FsckUnsupported, + (void*)&FsckUnsupported, + (void*)&FsckUnsupported, + (void*)&FsckUnsupported, + (void*)&FsckUnsupported, + (void*)&FsckUnsupported, + &FsckIoctl2 +}; + +static iop_device_t FsckDevice={ + "fsck", + IOP_DT_FSEXT|IOP_DT_FS, + 1, + "FSCK", + &FsckDeviceOps +}; + +//0x0000267c +int _start(int argc, char *argv[]) +{ + int buffers; + + buffers = 0x7E; + for(argc--,argv++; argc > 0 && ((*argv)[0] == '-'); argc--,argv++) + { + if(!strcmp("-n", *argv)) + { + argv++; + if(--argc > 0) + { + if(strtol(*argv, NULL, 10) < buffers) + buffers = strtol(*argv, NULL, 10); + } + else return DisplayUsageHelp(); + } + else return DisplayUsageHelp(); + } + + printf("fsck: max depth %d, %d buffers.\n", FSCK_MAX_PATH_LEVELS - 1, buffers); + + if(pfsCacheInit(buffers, 1024) < 0) + { + printf("fsck: error: cache initialization failed.\n"); + return MODULE_NO_RESIDENT_END; + } + + if(pfsBitmapInit() < 0) + { + printf("fsck: error: bitmap initialization failed.\n"); + return MODULE_NO_RESIDENT_END; + } + + if((fsckEventFlagID = fsckCreateEventFlag()) < 0) + return MODULE_NO_RESIDENT_END; + + if((fsckThreadID = fsckCreateThread(&FsckThread, 0x2080)) < 0) + return MODULE_NO_RESIDENT_END; + + DelDrv("fsck"); + if(AddDrv(&FsckDevice) == 0) + { + printf("fsck: version %04x driver start.\n", _irx_id.v); + return MODULE_RESIDENT_END; + } + + return MODULE_NO_RESIDENT_END; +} diff --git a/fsck/imports.lst b/fsck/imports.lst new file mode 100644 index 0000000..955b3dd --- /dev/null +++ b/fsck/imports.lst @@ -0,0 +1,62 @@ +cdvdman_IMPORTS_start +I_sceCdReadClock +cdvdman_IMPORTS_end + +iomanX_IMPORTS_start +I_AddDrv +I_DelDrv +I_open +I_close +#ifndef FSCK100 +I_read +I_write +I_lseek +#endif +I_getstat +I_remove +I_ioctl2 +iomanX_IMPORTS_end + +intrman_IMPORTS_start +I_CpuSuspendIntr +I_CpuResumeIntr +intrman_IMPORTS_end + +stdio_IMPORTS_start +I_printf +I_putchar +I_gets +stdio_IMPORTS_end + +sysclib_IMPORTS_start +I_memset +I_memcpy +I_strcpy +I_strncpy +I_strcmp +I_strncmp +I_strchr +I_strtol +I_sprintf +I_look_ctype_table +sysclib_IMPORTS_end + +sysmem_IMPORTS_start +I_AllocSysMemory +I_FreeSysMemory +sysmem_IMPORTS_end + +thbase_IMPORTS_start +I_CreateThread +I_StartThread +I_DelayThread +I_SysClock2USec +I_GetSystemTime +thbase_IMPORTS_end + +thevent_IMPORTS_start +I_CreateEventFlag +I_WaitEventFlag +I_PollEventFlag +I_SetEventFlag +thevent_IMPORTS_end diff --git a/fsck/irx_imports.h b/fsck/irx_imports.h new file mode 100644 index 0000000..9a87702 --- /dev/null +++ b/fsck/irx_imports.h @@ -0,0 +1,10 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/fsck/misc.c b/fsck/misc.c new file mode 100644 index 0000000..28ee87e --- /dev/null +++ b/fsck/misc.c @@ -0,0 +1,34 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pfs-opt.h" +#include "libpfs.h" +#include "misc.h" + +//0x00002c00 +int fsckCreateEventFlag(void) +{ + iop_event_t EventFlagData; + + EventFlagData.attr = EA_MULTI; + EventFlagData.bits = 0; + return CreateEventFlag(&EventFlagData); +} + +//0x00002c2c +int fsckCreateThread(void (*function)(void *arg), int StackSize) +{ + iop_thread_t ThreadData; + + ThreadData.attr = TH_C; + ThreadData.thread = function; + ThreadData.priority = 0x7b; + ThreadData.stacksize = StackSize; + return CreateThread(&ThreadData); +} diff --git a/fsck/misc.h b/fsck/misc.h new file mode 100644 index 0000000..4f2c719 --- /dev/null +++ b/fsck/misc.h @@ -0,0 +1,2 @@ +int fsckCreateEventFlag(void); +int fsckCreateThread(void (*function)(void *arg), int StackSize); diff --git a/fsck/pfs-opt.h b/fsck/pfs-opt.h new file mode 100644 index 0000000..d6cba55 --- /dev/null +++ b/fsck/pfs-opt.h @@ -0,0 +1,20 @@ +#ifndef _PFS_OPT_H +#define _PFS_OPT_H + +#define PFS_PRINTF(format,...) printf(format, ##__VA_ARGS__) +#define PFS_DRV_NAME "fsck" + +#define PFS_NO_WRITE_ERROR_STAT 1 +//#define FSCK100 1 //If desired, uncomment to build a version of FSCK without the v1.10 features. + +//Module version +#define PFS_MAJOR 1 +#define PFS_MINOR 4 + +/* Define PFS_OSD_VER in your Makefile to build an OSD version, which will: + 1. Enable the PIOCINVINODE IOCTL2 function. */ +#ifdef PFS_OSD_VER +#define PFS_IOCTL2_INC_CHECKSUM 1 +#endif + +#endif diff --git a/fssk/Makefile b/fssk/Makefile new file mode 100644 index 0000000..9ce7cf8 --- /dev/null +++ b/fssk/Makefile @@ -0,0 +1,21 @@ +LIBPFS_PATH = ../common/libpfs + +IOP_BIN = fssk.irx +PFS_OBJS = $(LIBPFS_PATH)/src/bitmap.o $(LIBPFS_PATH)/src/inode.o $(LIBPFS_PATH)/src/dir.o $(LIBPFS_PATH)/src/journal.o $(LIBPFS_PATH)/src/misc.o $(LIBPFS_PATH)/src/super.o $(LIBPFS_PATH)/src/superWrite.o $(LIBPFS_PATH)/src/cache.o $(LIBPFS_PATH)/src/block.o $(LIBPFS_PATH)/src/blockWrite.o +IOP_OBJS = fssk.o misc.o imports.o $(PFS_OBJS) + +IOP_INCS += -I$(CURDIR) -I$(LIBPFS_PATH)/include +IOP_CFLAGS += -Wall -fno-builtin -DPFS_OSD_VER +IOP_LDFLAGS += -s +IOP_LIBS += -lgcc + +all: $(IOP_BIN) + +clean: + rm -f $(IOP_BIN) $(IOP_OBJS) + +%.o : $(LIBPFS_PATH)/%.c + $(IOP_CC) $(IOP_CFLAGS) $< -o $@ + +include $(PS2SDK)/Defs.make +include ../common/Rules.make diff --git a/fssk/fssk-ioctl.h b/fssk/fssk-ioctl.h new file mode 100644 index 0000000..8a79452 --- /dev/null +++ b/fssk/fssk-ioctl.h @@ -0,0 +1,23 @@ +struct fsskStatus{ + u32 zoneUsed; //0x00 + u32 inodeBlockCount; //0x04 + u32 files; //0x08 + u32 directories; //0x0C + u32 PWDLevel; //0x10 + u32 hasError; //0x14 + u32 partsDeleted; //0x18 +}; + +//IOCTL2 codes - none of these commands have any inputs or outputs, unless otherwise specified. +enum FSSK_IOCTL2_CMD{ + FSSK_IOCTL2_CMD_GET_ESTIMATE = 0, //Output = u32 time + FSSK_IOCTL2_CMD_START, + FSSK_IOCTL2_CMD_WAIT, + FSSK_IOCTL2_CMD_POLL, + FSSK_IOCTL2_CMD_GET_STATUS, //Output = struct fsskStatus + FSSK_IOCTL2_CMD_STOP, + FSSK_IOCTL2_CMD_SET_MINFREE, + FSSK_IOCTL2_CMD_SIM +}; + +#define FSSK_MODE_VERBOSITY(x) (((x)&0xF)<<4) diff --git a/fssk/fssk.c b/fssk/fssk.c new file mode 100644 index 0000000..72ee806 --- /dev/null +++ b/fssk/fssk.c @@ -0,0 +1,814 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pfs-opt.h" +#include "libpfs.h" +#include "fssk-ioctl.h" +#include "fssk.h" +#include "misc.h" + +IRX_ID("fssk", PFS_MAJOR, PFS_MINOR); + +struct fsskRuntimeData{ + struct fsskStatus status; + int minFree; //0x1C + int stopFlag; //0x20 +}; + +#define FSSK_MAX_PATH_LEVELS 64 +#define FSSK_MAX_PATH_SEG_LENGTH 256 + +static int fsskEventFlagID; +static int fsskThreadID; +static char fsskPathBuffer[FSSK_MAX_PATH_LEVELS][FSSK_MAX_PATH_SEG_LENGTH]; +static int fsskVerbosityLevel = 2; + +static pfs_mount_t MainPFSMount = {0}; //FIXME: if not explicitly initialized to 0, the generated IRX would somehow have garbage in this structure. + +#define IO_BUFFER_SIZE 256 +#define IO_BUFFER_SIZE_BYTES (IO_BUFFER_SIZE * 512) + +static struct fsskRuntimeData fsskRuntimeData; +static u8 IOBuffer[IO_BUFFER_SIZE_BYTES]; + +static void fsskPrintPWD(void) +{ + int i; + char *pName; + + for(i = 0, pName = fsskPathBuffer[0]; i < fsskRuntimeData.status.PWDLevel; i++, pName += FSSK_MAX_PATH_SEG_LENGTH) + printf("/%s", pName); + + if(i == 0) + printf("/"); +} + +static int fsskHasSpaceToFree(pfs_cache_t *clink) +{ + int result; + u32 i; + pfs_cache_t *cinode; + + result = 0; + cinode = pfsCacheUsedAdd(clink); + + for(i = 0; i < clink->u.inode->number_data; i++) + { + if(i != 0) + { + if(pfsFixIndex(i) == 0) + { + if((cinode = pfsBlockGetNextSegment(cinode, &result)) == NULL) + break; + } + } + + if(clink->pfsMount->num_subs - fsskRuntimeData.status.partsDeleted < cinode->u.inode->data[pfsFixIndex(i)].subpart) + { + result = 1; + break; + } + } + + fsskRuntimeData.status.inodeBlockCount += clink->u.inode->number_blocks; + pfsCacheFree(cinode); + + return result; +} + +static int fsskCalculateSpaceToRemove(pfs_mount_t *mount) +{ + u32 PartsToRemove, i, ratio, SizeOfSub, SizeOfSubZones, free, zfree, total_zones; + + PartsToRemove = 0; + zfree = mount->zfree; + total_zones = mount->total_zones; + for(i = mount->num_subs; i != 0; i--,PartsToRemove++) + { + SizeOfSub = mount->blockDev->getSize(mount->fd, i); + SizeOfSubZones = SizeOfSub >> mount->sector_scale; + total_zones -= SizeOfSubZones; + SizeOfSubZones - mount->free_zone[i] - pfsGetBitmapSizeBlocks(mount->sector_scale, SizeOfSub) - 1; + free = SizeOfSubZones; + if(zfree >= mount->free_zone[i]) + { + zfree -= mount->free_zone[i]; + if(zfree >= free) + { + zfree -= free; + ratio = (int)((u64)zfree * 100 / total_zones); + printf("fssk: free ratio=%ld, freezone=%ld.\n", ratio, zfree); + if(ratio < fsskRuntimeData.minFree) + break; + } else + break; + } else + break; + } + + printf("fssk: %ld partitions will be removed.\n", PartsToRemove); + + return PartsToRemove; +} + +static u16 fsskCalcLastSub(pfs_mount_t *mount) +{ + u32 *zfree, MaxFreeRatio, FreeRatio; + u16 sub, result; + + for(result = sub = 0,zfree = mount->free_zone,MaxFreeRatio = 0; mount->num_subs - (fsskRuntimeData.status.partsDeleted - 1) != 0; sub++,zfree++) + { + FreeRatio = ((u64)*zfree) * 100 / (mount->blockDev->getSize(mount->fd, sub) >> mount->sector_scale); + if(MaxFreeRatio < FreeRatio) + { + MaxFreeRatio = FreeRatio; + result = sub; + } + } + + return result; +} + +static int fsskCopyBlock(pfs_mount_t *mount, pfs_blockinfo_t *block1, pfs_blockinfo_t *block2, u32 length) +{ + u32 i; + int result; + + for(i = 0; i < length; i++) + { + if((result = mount->blockDev->transfer(mount->fd, IOBuffer, block2->subpart, (block2->number + i) << mount->sector_scale, 1 << mount->sector_scale, PFS_IO_MODE_READ)) < 0 || ((result = mount->blockDev->transfer(mount->fd, IOBuffer, block1->subpart, (block1->number + i) << mount->sector_scale, 1 << mount->sector_scale, PFS_IO_MODE_WRITE)) < 0)) + return result; + } + + return 0; +} + +static pfs_cache_t *fsskCreateIndirectSeg(pfs_cache_t *clink, pfs_cache_t *clink2, pfs_blockinfo_t *data, int *result) +{ + pfs_cache_t *clinkfree; + pfs_blockinfo_t block; + + *result = 0; + + if(!pfsCacheIsFull()) + { + block.subpart = data->subpart; + block.number = data->number + data->count; + if((*result = pfsBitmapSearchFreeZone(clink->pfsMount, &block, clink->u.inode->number_blocks)) >= 0) + { + clinkfree = pfsCacheGetData(clink->pfsMount, block.subpart, block.number << clink->pfsMount->inode_scale, PFS_CACHE_FLAG_SEGI | PFS_CACHE_FLAG_NOLOAD, result); + + memset(clinkfree->u.inode, 0, sizeof(pfs_inode_t)); + clinkfree->u.inode->magic = PFS_SEGI_MAGIC; + memcpy(&clinkfree->u.inode->inode_block, &clink->u.inode->inode_block, sizeof(pfs_blockinfo_t)); + memcpy(&clinkfree->u.inode->last_segment, &clink2->u.inode->data[0], sizeof(pfs_blockinfo_t)); + memcpy(&clinkfree->u.inode->data[0], &block, sizeof(pfs_blockinfo_t)); + clinkfree->flags |= PFS_CACHE_FLAG_DIRTY; + clink->u.inode->number_blocks += block.count; + clink->u.inode->number_data++; + memcpy(&clink->u.inode->last_segment, &block, sizeof(pfs_blockinfo_t)); + clink->u.inode->number_segdesg++; + clink->flags |= PFS_CACHE_FLAG_DIRTY; + memcpy(&clink2->u.inode->next_segment, &block, sizeof(pfs_blockinfo_t)); + clink2->flags |= PFS_CACHE_FLAG_DIRTY; + pfsCacheFree(clink2); + + return clinkfree; + } + } else + *result = -ENOMEM; + + return NULL; +} + +//Very similar to pfsBitmapSearchFreeZone(). +static int fsckBitmapSearchFreeZoneSpecial(pfs_mount_t *pfsMount, pfs_blockinfo_t *bi, u32 max_count, u32 deleted) +{ + u32 num, count, i; + + deleted--; + num = pfsMount->num_subs - deleted; + if(bi->subpart < num) + bi->subpart = 0; + if(bi->number != 0) + num++; + count = (max_count < 33) ? max_count : 32; + if(count < bi->count) + count = bi->count; + + for(--num ; num >= 0; num--) + { + for(i = count; i != 0; i /= 2) + { + if((pfsMount->free_zone[bi->subpart] >= i) && (pfsBitmapAllocZones(pfsMount, bi, i) != 0)) + { + pfsMount->free_zone[bi->subpart] -= bi->count; + pfsMount->zfree -= bi->count; + return 0; + } + } + + bi->subpart++; + bi->number = 0; + if(pfsMount->num_subs - deleted == bi->subpart) + bi->subpart = 0; + } + + return -ENOSPC; +} + +//I hate this function. :( +static int fsskMoveInode(pfs_mount_t *mount, pfs_cache_t *dest, pfs_cache_t *start, pfs_dentry_t *dentry) +{ + pfs_cache_t *clink, *clink2, *clink3; + pfs_blockinfo_t block, block2; + int result, i, value; + + printf("\t========== Move"); + if(FIO_S_ISDIR(start->u.inode->mode)) + { + block.subpart = fsskCalcLastSub(mount); + block.number = 0; + } else + memcpy(&block, &dest->u.inode->inode_block, sizeof(pfs_blockinfo_t)); + + block.count = 1; + if((result = fsckBitmapSearchFreeZoneSpecial(mount, &block, start->u.inode->number_blocks, fsskRuntimeData.status.partsDeleted)) >= 0) + { + if((result = fsskCopyBlock(mount, &block, &start->u.inode->inode_block, 1)) >= 0 && + (clink = pfsCacheGetData(mount, block.subpart, block.number << mount->inode_scale, PFS_CACHE_FLAG_SEGD | PFS_CACHE_FLAG_NOLOAD, &result)) != NULL) + { + memcpy(clink->u.inode, start->u.inode, sizeof(pfs_inode_t)); + memcpy(&clink->u.inode->inode_block, &block, sizeof(pfs_blockinfo_t)); + memcpy(&clink->u.inode->last_segment, &block, sizeof(pfs_blockinfo_t)); + memcpy(&clink->u.inode->data[0], &block, sizeof(pfs_blockinfo_t)); + clink->u.inode->number_segdesg = 1; + clink->u.inode->number_data = 1; + clink->u.inode->number_blocks = 1; + clink2 = pfsCacheUsedAdd(start); + clink3 = pfsCacheUsedAdd(clink); + + for(i = 1; i < start->u.inode->number_data && result == 0; i++) + { + if(pfsFixIndex(i) == 0) + { + if((clink2 = pfsBlockGetNextSegment(clink2, &result)) == NULL) + break; + } else { + memcpy(&block2, &clink2->u.inode->data[pfsFixIndex(i)], sizeof(pfs_blockinfo_t)); + if(pfsFixIndex(clink->u.inode->number_data - 1) != 0) + { + if((value = pfsBitmapAllocateAdditionalZones(mount, clink3->u.inode->data, block2.count)) != 0) + { + block.subpart = clink3->u.inode->data[0].subpart; + block.number = clink3->u.inode->data[0].number + clink3->u.inode->data[0].count; + clink3->u.inode->data[0].count += value; + + if((result = fsskCopyBlock(mount, &block, &block2, value)) < 0) + break; + + clink->u.inode->number_blocks += value; + block2.number += value; + block2.count -= value; + } + } + while(block2.count != 0) + { + if(pfsFixIndex(clink->u.inode->number_data) == 0) + { + if((clink3 = fsskCreateIndirectSeg(clink, clink3, clink3->u.inode->data, &result)) == NULL) + break; + } + + block.subpart = clink3->u.inode->data[0].subpart; + block.count = block2.count; + block.number = clink3->u.inode->data[0].number + clink3->u.inode->data[0].count; + + if((result = fsckBitmapSearchFreeZoneSpecial(mount, &block, start->u.inode->number_blocks, fsskRuntimeData.status.partsDeleted)) < 0) + break; + + memcpy(&clink3->u.inode->data[pfsFixIndex(start->u.inode->number_data)], &block, sizeof(pfs_blockinfo_t)); + + clink->u.inode->number_data++; + if((result = fsskCopyBlock(mount, &block, &block2, block.count)) < 0) + break; + + clink->u.inode->number_blocks += block.count; + block2.number += block.count; + block2.count -= block.count; + block2.count--; + } + } + } + + //0x000009d0 + pfsCacheFree(clink2); + if(result == 0) + { + dentry->sub = clink->u.inode->inode_block.subpart; + dentry->inode = clink->u.inode->inode_block.number; + clink3->flags |= PFS_CACHE_FLAG_DIRTY; + clink->flags |= PFS_CACHE_FLAG_DIRTY; + pfsCacheFree(clink3); + pfsCacheFree(clink); + pfsBitmapFreeInodeBlocks(start); + } else { + pfsBitmapFreeInodeBlocks(clink); + pfsCacheFree(clink3); + pfsCacheFree(clink); + } + } else + pfsBitmapFreeBlockSegment(mount, &block); + } + + return result; +} + +static void fsskCheckSelfEntry(pfs_cache_t *SelfInodeClink, pfs_cache_t *SelfDEntryClink, pfs_dentry_t *dentry) +{ + if((SelfInodeClink->sub != dentry->sub) || (SelfInodeClink->u.inode->inode_block.number != dentry->inode)) + { + printf("fssk: '.' point not itself.\n"); + dentry->sub = SelfInodeClink->u.inode->inode_block.subpart; + dentry->inode = SelfInodeClink->u.inode->inode_block.number; + SelfDEntryClink->flags |= PFS_CACHE_FLAG_DIRTY; + } +} + +static void fsskCheckParentEntry(pfs_cache_t *ParentInodeClink, pfs_cache_t *SelfDEntryClink, pfs_dentry_t *dentry) +{ + if((ParentInodeClink->sub != dentry->sub) || (ParentInodeClink->u.inode->inode_block.number != dentry->inode)) + { + printf("fssk: '..' point not parent.\n"); + dentry->sub = ParentInodeClink->u.inode->inode_block.subpart; + dentry->inode = ParentInodeClink->u.inode->inode_block.number; + SelfDEntryClink->flags |= PFS_CACHE_FLAG_DIRTY; + } +} + +static int fsskCheckFiles(pfs_cache_t *ParentInodeClink, pfs_cache_t *InodeClink); + +static int fsskCheckFile(pfs_cache_t *InodeClink, pfs_cache_t *DEntryClink, pfs_dentry_t *dentry) +{ + pfs_cache_t *FileInodeDataClink; + int result; + + if((FileInodeDataClink = pfsInodeGetData(InodeClink->pfsMount, dentry->sub, dentry->inode, &result)) != NULL) + { + if(fsskRuntimeData.status.PWDLevel < FSSK_MAX_PATH_LEVELS - 1) + { + memset(fsskPathBuffer[fsskRuntimeData.status.PWDLevel], 0, FSSK_MAX_PATH_SEG_LENGTH); + strncpy(fsskPathBuffer[fsskRuntimeData.status.PWDLevel], dentry->path, dentry->pLen); + fsskRuntimeData.status.PWDLevel++; + + if(fsskVerbosityLevel >= 2) + { + fsskPrintPWD(); + if(FIO_S_ISDIR(dentry->aLen)) + printf(": "); + } + + if((result = fsskHasSpaceToFree(FileInodeDataClink)) > 0) + { + if((result = fsskMoveInode(InodeClink->pfsMount, InodeClink, FileInodeDataClink, dentry)) == 0) + { + DEntryClink->flags |= PFS_CACHE_FLAG_DIRTY; + pfsCacheFree(FileInodeDataClink); + FileInodeDataClink = pfsInodeGetData(InodeClink->pfsMount, dentry->inode, dentry->sub, &result); + } + } + + putchar('\n'); + + if(result == 0) + { + if(FIO_S_ISDIR(dentry->aLen)) + { + fsskRuntimeData.status.directories++; + result = fsskCheckFiles(InodeClink, FileInodeDataClink); + }else + fsskRuntimeData.status.files++; + } + + fsskRuntimeData.status.PWDLevel--; + }else{ + printf("fssk: error: exceed max directory depth.\n"); + result = -ENOMEM; + } + } + + return result; +} + +static int fsskCheckFiles(pfs_cache_t *ParentInodeClink, pfs_cache_t *InodeClink) +{ + pfs_blockpos_t BlockPosition; + pfs_dentry_t *pDEntry, *pDEntryEnd; + int result; + u32 inodeOffset, dEntrySize; //inodeOffset doesn't seem to be 64-bit, even though the inode size field is 64-bits wide. + pfs_cache_t *DEntryClink; + + if(CheckThreadStack() < 128) + { + printf("fssk: error: there is no stack, giving up.\n"); + return -ENOMEM; + } + + inodeOffset = 0; + BlockPosition.inode = pfsCacheUsedAdd(InodeClink); + BlockPosition.block_segment = 1; + BlockPosition.block_offset = 0; + BlockPosition.byte_offset = 0; + if((DEntryClink = pfsGetDentriesChunk(&BlockPosition, &result)) == NULL) + { + pfsCacheFree(BlockPosition.inode); + return result; + } + pDEntry = DEntryClink->u.dentry; + + while(inodeOffset < InodeClink->u.inode->size) + { + if(pDEntry >= (pfs_dentry_t*)(DEntryClink->u.data + 1024)) + { + pfsCacheFree(DEntryClink); + if((result = pfsInodeSync(&BlockPosition, 1024, InodeClink->u.inode->number_data)) != 0 || (DEntryClink = pfsGetDentriesChunk(&BlockPosition, &result)) == NULL) + break; + } + + for(pDEntry = DEntryClink->u.dentry, pDEntryEnd = DEntryClink->u.dentry + 1; pDEntry < pDEntryEnd; pDEntry = (pfs_dentry_t*)((u8*)pDEntry + dEntrySize),inodeOffset += dEntrySize) + { + if(fsskRuntimeData.stopFlag != 0) + goto end; + + dEntrySize = pDEntry->aLen & 0x0FFF; + + if(dEntrySize & 3) + { + printf("fssk: error: directory entry is not aligned.\n"); + result = -EINVAL; + goto end; + } + + if(dEntrySize < ((pDEntry->pLen + 11) & ~3)) + { + printf("fssk: error: directory entry is too small.\n"); + result = -EINVAL; + goto end; + } + + if(pDEntryEnd < (pfs_dentry_t*)((u8*)pDEntry + dEntrySize)) + { + printf("fssk: error: directory entry is too long.\n"); + result = -EINVAL; + goto end; + } + + if(pDEntry->inode != 0) + { + if((pDEntry->pLen == 1) && (pDEntry->path[0] == '.')) + fsskCheckSelfEntry(InodeClink, DEntryClink, pDEntry); + else if((pDEntry->pLen == 2) && (pDEntry->path[0] == '.')) + fsskCheckParentEntry(ParentInodeClink, DEntryClink, pDEntry); + else{ + if((result = fsskCheckFile(InodeClink, DEntryClink, pDEntry)) < 0) + goto end; + } + } + } + + if(fsskRuntimeData.stopFlag != 0) + break; + } + +end: + pfsCacheFree(DEntryClink); + pfsCacheFree(BlockPosition.inode); + + return result; +} + +static void FsskThread(pfs_mount_t *mount) +{ + pfs_cache_t *clink; + pfs_dentry_t dentry; + int result, i; + + if((fsskRuntimeData.status.partsDeleted = fsskCalculateSpaceToRemove(mount)) != 0) + { + if((clink = pfsInodeGetData(mount, mount->root_dir.subpart, mount->root_dir.number, &result)) != NULL) + { + if((result = fsskHasSpaceToFree(clink)) > 0) + { + printf("fssk: root directory will be moved.\n"); + if((result = fsskMoveInode(mount, clink, clink, &dentry)) == 0) + { + pfsCacheFree(clink); + if((clink = pfsInodeGetData(mount, dentry.sub, dentry.inode, &result)) != NULL) + { + if((result = mount->blockDev->transfer(mount->fd, IOBuffer, 0, PFS_SUPER_SECTOR, 1, PFS_IO_MODE_READ)) == 0) + { + mount->root_dir.subpart = dentry.sub; + mount->root_dir.number = dentry.inode; + memcpy(&((pfs_super_block_t*)IOBuffer)->root, &mount->root_dir, sizeof(pfs_blockinfo_t)); + if((result = mount->blockDev->transfer(mount->fd, IOBuffer, 0, PFS_SUPER_BACKUP_SECTOR, 1, PFS_IO_MODE_WRITE)) == 0) + result = mount->blockDev->transfer(mount->fd, IOBuffer, 0, PFS_SUPER_SECTOR, 1, PFS_IO_MODE_WRITE); + mount->blockDev->flushCache(mount->fd); + } + } + } + } + + if(result >= 0) + { + fsskRuntimeData.status.directories++; + result = fsskCheckFiles(clink, clink); + pfsCacheFree(clink); + pfsCacheFlushAllDirty(mount); + if(result == 0) + { + if(fsskRuntimeData.stopFlag == 0) + { + if((result = mount->blockDev->transfer(mount->fd, IOBuffer, 0, PFS_SUPER_SECTOR, 1, PFS_IO_MODE_READ)) == 0) + { + ((pfs_super_block_t*)IOBuffer)->num_subs -= fsskRuntimeData.status.partsDeleted; + mount->num_subs -= fsskRuntimeData.status.partsDeleted; + if((result = mount->blockDev->transfer(mount->fd, IOBuffer, 0, PFS_SUPER_BACKUP_SECTOR, 1, PFS_IO_MODE_WRITE)) == 0) + result = mount->blockDev->transfer(mount->fd, IOBuffer, 0, PFS_SUPER_SECTOR, 1, PFS_IO_MODE_WRITE); + + mount->blockDev->flushCache(mount->fd); + } + + for(i = 0; i < fsskRuntimeData.status.partsDeleted && result == 0; i++) + ioctl2(mount->fd, HIOCDELSUB, NULL, 0, NULL, 0); + } + } + + if(result < 0) + fsskRuntimeData.status.hasError = 1; + } else { + pfsCacheFree(clink); + fsskRuntimeData.status.hasError = 1; + } + } else { + printf("fssk: error: cannot read root directory.\n"); + fsskRuntimeData.status.hasError = 1; + goto end2; + } + } else { + printf("fssk: error: there is no space to remove.\n"); + goto end2; + } + +end2: + SetEventFlag(fsskEventFlagID, 1); +} + +static int FsskUnsupported(void) +{ + return 0; +} + +static int FsskOpen(iop_file_t *fd, const char *name, int flags, int mode) +{ + int blockfd, result; + iox_stat_t StatData; + pfs_block_device_t *pblockDevData; + + fsskVerbosityLevel = (mode & 0xF0) >> 4; + + if(MainPFSMount.fd) return -EBUSY; + + if((result = getstat(name, &StatData)) < 0) + { + printf("fssk: error: could not get status.\n"); + return result; + } + + if(StatData.mode != APA_TYPE_PFS) + { + printf("fssk: error: not PFS.\n"); + return -EINVAL; + } + + if((pblockDevData = pfsGetBlockDeviceTable(name)) == NULL) + return -ENXIO; + + if((blockfd = open(name, O_RDWR)) < 0) + { + printf("fssk: error: cannot open.\n"); + return blockfd; + } + + memset(&MainPFSMount, 0, sizeof(MainPFSMount)); + MainPFSMount.fd = blockfd; + MainPFSMount.blockDev = pblockDevData; + + if(pfsMountSuperBlock(&MainPFSMount) >= 0) + { + memset(&fsskRuntimeData, 0, sizeof(fsskRuntimeData)); + + fsskRuntimeData.status.zoneUsed = MainPFSMount.total_zones - MainPFSMount.zfree; + fsskRuntimeData.minFree = 3; + fd->privdata = &MainPFSMount; + result = 0; + } else { + MainPFSMount.fd = 0; + printf("fssk: error: cannot continue.\n"); + } + + return result; +} + +static int FsskClose(iop_file_t *fd) +{ + close(((pfs_mount_t*)fd->privdata)->fd); + pfsCacheClose((pfs_mount_t*)fd->privdata); + memset(fd->privdata, 0, sizeof(pfs_mount_t)); + + return 0; +} + +static int fsskSimGetStat(pfs_mount_t *mount) +{ + int result, i; + + fsskRuntimeData.status.partsDeleted = fsskCalculateSpaceToRemove(mount); + result = 0; + if(mount->num_subs - fsskRuntimeData.status.partsDeleted < mount->num_subs) + { + for(i = mount->num_subs; i != 0; i--) + result += mount->blockDev->getSize(mount->fd, i); + } + + return result; +} + +static int fsskGetEstimatedTime(pfs_mount_t *mount, int *result) +{ + unsigned int i; + u64 clock; + iop_sys_clock_t SysClock1, SysClock2, SysClockDiff; + u32 sec, usec; + + clock = 1000000; + for(i = 0; i < 4; i++) + { + GetSystemTime(&SysClock1); + mount->blockDev->transfer(mount->fd, IOBuffer, 0, 1 << mount->sector_scale, 1 << mount->sector_scale, PFS_IO_MODE_READ); + GetSystemTime(&SysClock2); + + SysClockDiff.lo = SysClock2.lo - SysClock1.lo; + SysClockDiff.hi = SysClock2.hi - SysClock1.hi - (SysClock2.lo < SysClock1.lo); + SysClock2USec(&SysClockDiff, &sec, &usec); + + printf("fssk: %ld system clocks = %ld.%06ld sec\n", SysClockDiff.lo, sec, usec); + + if(usec < clock) + clock = usec; + } + + if((*result = ((clock + 400) * fsskRuntimeData.status.zoneUsed / 1000000)) == 0) + *result = 1; + + return 0; +} + +static int FsskIoctl2(iop_file_t *fd, int cmd, void *arg, unsigned int arglen, void *buf, unsigned int buflen) +{ + int result; + u32 FlagBits; + + switch(cmd) + { + case FSSK_IOCTL2_CMD_GET_ESTIMATE: + result = fsskGetEstimatedTime(fd->privdata, buf); + break; + case FSSK_IOCTL2_CMD_START: + result = StartThread(fsskThreadID, fd->privdata); + break; + case FSSK_IOCTL2_CMD_WAIT: + result = WaitEventFlag(fsskEventFlagID, 1, WEF_OR|WEF_CLEAR, &FlagBits); + break; + case FSSK_IOCTL2_CMD_POLL: + result = PollEventFlag(fsskEventFlagID, 1, WEF_OR|WEF_CLEAR, &FlagBits); + if(result == KE_EVF_COND) result = 1; + break; + case FSSK_IOCTL2_CMD_GET_STATUS: + memcpy(buf, &fsskRuntimeData.status, sizeof(struct fsskStatus)); + result = 0; + break; + case FSSK_IOCTL2_CMD_STOP: + fsskRuntimeData.stopFlag = 1; + result = 0; + break; + case FSSK_IOCTL2_CMD_SET_MINFREE: + if(*(int*)arg >= 4) + fsskRuntimeData.minFree = *(int*)arg; + result = 0; + break; + case FSSK_IOCTL2_CMD_SIM: + result = fsskSimGetStat(fd->privdata); + break; + default: + result = 0; + } + + return result; +} + +static iop_device_ops_t FsskDeviceOps={ + (void*)&FsskUnsupported, + (void*)&FsskUnsupported, + NULL, + &FsskOpen, + &FsskClose, + NULL, + NULL, + NULL, + (void*)&FsskUnsupported, + (void*)&FsskUnsupported, + (void*)&FsskUnsupported, + (void*)&FsskUnsupported, + (void*)&FsskUnsupported, + (void*)&FsskUnsupported, + (void*)&FsskUnsupported, + (void*)&FsskUnsupported, + (void*)&FsskUnsupported, + (void*)&FsskUnsupported, + (void*)&FsskUnsupported, + (void*)&FsskUnsupported, + (void*)&FsskUnsupported, + (void*)&FsskUnsupported, + (void*)&FsskUnsupported, + (void*)&FsskUnsupported, + (void*)&FsskUnsupported, + (void*)&FsskUnsupported, + &FsskIoctl2 +}; + +static iop_device_t FsskDevice={ + "fssk", + IOP_DT_FSEXT|IOP_DT_FS, + 1, + "FSSK", + &FsskDeviceOps +}; + +static int DisplayUsageHelp(void) +{ + printf("fssk: error: Usage: fssk [-n ]\n"); + return MODULE_NO_RESIDENT_END; +} + +int _start(int argc, char *argv[]) +{ + int buffers; + + buffers = 0x7E; + for(argc--,argv++; argc > 0 && ((*argv)[0] == '-'); argc--,argv++) + { + if(!strcmp("-n", *argv)) + { + argv++; + if(--argc > 0) + { + if(strtol(*argv, NULL, 10) < buffers) + buffers = strtol(*argv, NULL, 10); + } + else return DisplayUsageHelp(); + } + else return DisplayUsageHelp(); + } + + printf("fssk: max depth %d, %d buffers.\n", FSSK_MAX_PATH_LEVELS - 1, buffers); + + if(pfsCacheInit(buffers, 1024) < 0) + { + printf("fssk: error: cache initialization failed.\n"); + return MODULE_NO_RESIDENT_END; + } + + if((fsskEventFlagID = fsskCreateEventFlag()) < 0) + return MODULE_NO_RESIDENT_END; + + if((fsskThreadID = fsskCreateThread((void*)&FsskThread, 0x2080)) < 0) + return MODULE_NO_RESIDENT_END; + + DelDrv("fssk"); + if(AddDrv(&FsskDevice) == 0) + { + printf("fssk: version %04x driver start.\n", _irx_id.v); + return MODULE_RESIDENT_END; + } + + return MODULE_NO_RESIDENT_END; +} diff --git a/fssk/fssk.h b/fssk/fssk.h new file mode 100644 index 0000000..03a2749 --- /dev/null +++ b/fssk/fssk.h @@ -0,0 +1,17 @@ +typedef struct +{ + u32 totalLBA; + u32 partitionMaxSize; + int format; + int status; +} hdd_device_t; + +struct hdskBitmap{ + struct hdskBitmap *next; //0x00 + struct hdskBitmap *prev; //0x04 + u32 start; //0x08 + u32 length; //0x0C + u32 type; //0x10 +}; + +#define HDSK_BITMAP_SIZE 0x4001 diff --git a/fssk/imports.lst b/fssk/imports.lst new file mode 100644 index 0000000..46ca275 --- /dev/null +++ b/fssk/imports.lst @@ -0,0 +1,58 @@ +cdvdman_IMPORTS_start +I_sceCdReadClock +cdvdman_IMPORTS_end + +intrman_IMPORTS_start +I_CpuSuspendIntr +I_CpuResumeIntr +intrman_IMPORTS_end + +iomanX_IMPORTS_start +I_AddDrv +I_DelDrv +I_open +I_close +I_getstat +I_ioctl2 +iomanX_IMPORTS_end + +stdio_IMPORTS_start +I_printf +I_putchar +stdio_IMPORTS_end + +sysclib_IMPORTS_start +I_look_ctype_table +I_memset +I_memcpy +I_memcmp +I_strcpy +I_strncpy +I_strcmp +I_strncmp +I_strchr +I_strlen +I_sprintf +I_strtol +sysclib_IMPORTS_end + +sysmem_IMPORTS_start +I_AllocSysMemory +I_FreeSysMemory +sysmem_IMPORTS_end + +thbase_IMPORTS_start +I_CreateThread +I_StartThread +I_DelayThread +I_GetSystemTime +I_SysClock2USec +I_CheckThreadStack +thbase_IMPORTS_end + +thevent_IMPORTS_start +I_CreateEventFlag +I_SetEventFlag +I_WaitEventFlag +I_PollEventFlag +thevent_IMPORTS_end diff --git a/fssk/irx_imports.h b/fssk/irx_imports.h new file mode 100644 index 0000000..f44d437 --- /dev/null +++ b/fssk/irx_imports.h @@ -0,0 +1,10 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/fssk/misc.c b/fssk/misc.c new file mode 100644 index 0000000..74ec544 --- /dev/null +++ b/fssk/misc.c @@ -0,0 +1,30 @@ +#include +#include +#include +#include +#include +#include + +#include "pfs-opt.h" +#include "libpfs.h" +#include "misc.h" + +int fsskCreateEventFlag(void) +{ + iop_event_t EventFlagData; + + EventFlagData.attr = EA_MULTI; + EventFlagData.bits = 0; + return CreateEventFlag(&EventFlagData); +} + +int fsskCreateThread(void (*function)(void *arg), int StackSize) +{ + iop_thread_t ThreadData; + + ThreadData.attr = TH_C; + ThreadData.thread = function; + ThreadData.priority = 0x7b; + ThreadData.stacksize = StackSize; + return CreateThread(&ThreadData); +} diff --git a/fssk/misc.h b/fssk/misc.h new file mode 100644 index 0000000..3b53a63 --- /dev/null +++ b/fssk/misc.h @@ -0,0 +1,2 @@ +int fsskCreateEventFlag(void); +int fsskCreateThread(void (*function)(void *arg), int StackSize); diff --git a/fssk/pfs-opt.h b/fssk/pfs-opt.h new file mode 100644 index 0000000..09e73a8 --- /dev/null +++ b/fssk/pfs-opt.h @@ -0,0 +1,17 @@ +#ifndef PFS_OPT +#define PFS_OPT + +#define PFS_PRINTF(args,...) printf(args, ##__VA_ARGS__) +#define PFS_DRV_NAME "fssk" + +//Module version +#define PFS_MAJOR 1 +#define PFS_MINOR 4 + +/* Define PFS_OSD_VER in your Makefile to build an OSD version, which will: + 1. Enable the PIOCINVINODE IOCTL2 function. */ +#ifdef PFS_OSD_VER +#define PFS_IOCTL2_INC_CHECKSUM 1 +#endif + +#endif diff --git a/graphics.c b/graphics.c new file mode 100644 index 0000000..132d44d --- /dev/null +++ b/graphics.c @@ -0,0 +1,461 @@ +#include +#include +#include +#include +#include + +#include + +#include + +#include "main.h" +#include "graphics.h" +#include "pad.h" + +#include "font.h" + +extern int VBlankStartSema; +static GS_GIF_TAG *LastGIFPacket = NULL; + +static char twh(short int val) +{ + char res; + + asm volatile("plzcw %0, %1" : "=r" (res) : "r" (val)); + res = 31 - (res + 1); + if(val > res) + res++; + + return res; +} + +static void PNGReadMem(png_structp pngPtr, png_bytep data, png_size_t length){ + u8 **PngBufferPtr=(u8**)png_get_io_ptr(pngPtr); + + memcpy(data, *PngBufferPtr, length); + *PngBufferPtr += length; +} + +static int LoadPNGImage(struct UIDrawGlobal *gsGlobal, GS_IMAGE *Texture, GS_IMAGE *Clut, const void* buffer, unsigned int size){ + static u8 **PngFileBufferPtr; + png_structp png_ptr; + png_infop info_ptr; + png_uint_32 width, height; + png_bytep *row_pointers; + void *mem, *clut_mem; + png_colorp palette; + int num_palette; + png_byte color_type_new; + + unsigned int sig_read = 0; + int row, i, k=0, j, bit_depth, color_type, interlace_type; + + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp) NULL, NULL, NULL); + + if(!png_ptr) + { + return -1; + } + + info_ptr = png_create_info_struct(png_ptr); + + if(!info_ptr) + { + png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); + return -1; + } + + if(setjmp(png_jmpbuf(png_ptr))) + { + png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); + return -1; + } + + PngFileBufferPtr=(void*)buffer; + png_set_read_fn(png_ptr, &PngFileBufferPtr, &PNGReadMem); + png_set_sig_bytes(png_ptr, sig_read); + png_read_info(png_ptr, info_ptr); + png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, NULL, NULL); + + png_set_strip_16(png_ptr); + + if (color_type == PNG_COLOR_TYPE_PALETTE) + { + if(Clut == NULL) + png_set_expand(png_ptr); + else { + Clut->x = 0; + Clut->y = 0; + Clut->width = 16; + Clut->height = 16; + Clut->psm = GS_CLUT_32; + Clut->vram_width = ((Clut->width + 63) & ~63) / 64; + } + } + + if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) + png_set_expand(png_ptr); + + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) + png_set_tRNS_to_alpha(png_ptr); + + png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER); + + png_read_update_info(png_ptr, info_ptr); + + Texture->x = 0; + Texture->y = 0; + Texture->width = width; + Texture->height = height; + + color_type_new = png_get_color_type(png_ptr, info_ptr); + if(color_type_new == PNG_COLOR_TYPE_RGB_ALPHA) + { + int row_bytes = png_get_rowbytes(png_ptr, info_ptr); + Texture->psm = GS_TEX_32; + mem = memalign(64, Texture->width * Texture->height * 4); + + row_pointers = calloc(height, sizeof(png_bytep)); + + for (row = 0; row < height; row++) row_pointers[row] = malloc(row_bytes); + + png_read_image(png_ptr, row_pointers); + + struct pixel { unsigned char r,g,b,a; }; + struct pixel *Pixels = (struct pixel *) UCAB_SEG(mem); + + for (i=0;ipsm = GS_TEX_24; + mem = memalign(64, Texture->width * Texture->height * 4); + + row_pointers = calloc(height, sizeof(png_bytep)); + + for(row = 0; row < height; row++) row_pointers[row] = malloc(row_bytes); + + png_read_image(png_ptr, row_pointers); + + struct pixel3 { unsigned char r,g,b; }; + struct pixel3 *Pixels = (struct pixel3 *) UCAB_SEG(mem); + + for (i=0;ipsm = GS_TEX_24; + mem = memalign(64, Texture->width * Texture->height); + clut_mem = memalign(64, Clut->width * Clut->height * 4); + + png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette); + + struct pixel4 { unsigned char r,g,b,a; }; + struct pixel4 *clut_mem4 = (struct pixel4*)UCAB_SEG(clut_mem); + for(i = 0; i < num_palette; i++) + { + clut_mem4->r = palette[i].red; + clut_mem4->g = palette[i].green; + clut_mem4->b = palette[i].blue; + clut_mem4->a = 0x80; + } + + row_pointers = calloc(height, sizeof(png_bytep)); + + for(row = 0; row < height; row++) row_pointers[row] = malloc(row_bytes); + + png_read_image(png_ptr, row_pointers); + + u8 *Pixels = (u8 *) UCAB_SEG(mem); + + for (i=0;ivram_addr = GsVramAllocTextureBuffer(Clut->width, Clut->height, Clut->psm); + GsLoadImage(clut_mem, Clut); + free(clut_mem); + } + else + { + printf("This texture depth is not supported yet!\n"); + png_read_end(png_ptr, NULL); + png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL); + return -1; + } + + png_read_end(png_ptr, NULL); + png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL); + + Texture->vram_addr = GsVramAllocTextureBuffer(Texture->width, Texture->height, Texture->psm); + Texture->vram_width = ((Texture->width + 63) & ~63) / 64; + + //Upload texture. + GsLoadImage(mem, Texture); + GsTextureFlush(); + free(mem); + + return 0; +} + +void DrawBackground(struct UIDrawGlobal *gsGlobal, GS_IMAGE *background){ +#ifdef CENTRE_BACKGROUND + short int x, y; + + x=(gsGlobal->width-background->width)/2; + y=(gsGlobal->height-background->height)/2; + + DrawSprite(gsGlobal, 0, 0, gsGlobal->width, gsGlobal->height, 9, (GS_RGBAQ){0x00,0x00,0x00,0x80,0x00}); + DrawSpriteTextured(gsGlobal, background, x, y, + 0, 0, + x+background->width, y+background->height, + background->width, background->height, + 8, (GS_RGBAQ){0x80,0x80,0x80,0x80,0x00}); +#else + DrawSprite(gsGlobal, 0, 0, gsGlobal->width, gsGlobal->height, 9, (GS_RGBAQ){0x00,0x00,0x00,0x80,0x00}); + DrawSpriteTextured(gsGlobal, background, 0, 0, + 0, 0, + gsGlobal->width, gsGlobal->height, + background->width, background->height, + 8, (GS_RGBAQ){0x80,0x80,0x80,0x80,0x00}); +#endif +} + +extern unsigned char buttons[]; +extern unsigned int size_buttons; + +extern unsigned char background[]; +extern unsigned int size_background; + +int LoadBackground(struct UIDrawGlobal *gsGlobal, GS_IMAGE* Texture){ + return LoadPNGImage(gsGlobal, Texture, NULL, background, size_background); +} + +int LoadPadGraphics(struct UIDrawGlobal *gsGlobal, struct ClutImage* PadGraphics){ + return LoadPNGImage(gsGlobal, &PadGraphics->texture, &PadGraphics->clut, buttons, size_buttons); +} + +void DrawSetFilterMode(struct UIDrawGlobal *gsGlobal, int mode) +{ + u64 *p; + + //Use the uncached segment, to avoid needing to flush the data cache. + p = (u64*)UNCACHED_SEG(GsGifPacketsAlloc(&gsGlobal->giftable, 2)); + LastGIFPacket = (GS_GIF_TAG*)p; + + gs_setGIF_TAG(((GS_GIF_TAG *)&p[0]), 1,0,0,0,GS_GIF_PACKED, 1, gif_rd_ad); + gs_setR_TEX1_1(((GS_R_TEX1 *)&p[1]), 0, 0, mode, mode, 0, 0, 0); +} + +void DrawLine(struct UIDrawGlobal *gsGlobal, short int x1, short int y1, short int x2, short int y2, short int z, GS_RGBAQ rgbaq) +{ + u64 *p; + + //Use the uncached segment, to avoid needing to flush the data cache. + p = (u64*)UNCACHED_SEG(GsGifPacketsAlloc(&gsGlobal->giftable, 3)); //Allocate 3 qwords for 1 untextured line + LastGIFPacket = (GS_GIF_TAG*)p; + + gs_setGIF_TAG(((GS_GIF_TAG *)&p[0]), 1,0,0,0,GS_GIF_REGLIST,4, gs_g_prim | gs_g_rgbaq << 4 | gs_g_xyz2 << 8 | gs_g_xyz2 << 12); + //prim_type = GS_PRIM_LINE, abe = 1 + gs_setPRIM(((GS_PRIM *)&p[2]), GS_PRIM_LINE, 0, 0, 0, 1, 0, 0, 0, 0); + *(GS_RGBAQ*)&p[3] = rgbaq; + gs_setXYZ2(((GS_XYZ *)&p[4]), (gsGlobal->draw_env.offset_x+x1)<<4, (gsGlobal->draw_env.offset_y+y1)<<4, z<<4); + gs_setXYZ2(((GS_XYZ *)&p[5]), (gsGlobal->draw_env.offset_x+x2)<<4, (gsGlobal->draw_env.offset_y+y2)<<4, z<<4); +} + +void DrawSprite(struct UIDrawGlobal *gsGlobal, short int x1, short int y1, short int x2, short int y2, short int z, GS_RGBAQ rgbaq) +{ + u64 *p; + + //Use the uncached segment, to avoid needing to flush the data cache. + p = (u64*)UNCACHED_SEG(GsGifPacketsAlloc(&gsGlobal->giftable, 3)); //Allocate 3 qwords for 1 untextured sprite + LastGIFPacket = (GS_GIF_TAG*)p; + + gs_setGIF_TAG(((GS_GIF_TAG *)&p[0]), 1,0,0,0,GS_GIF_REGLIST,4, gs_g_prim | gs_g_rgbaq << 4 | gs_g_xyz2 << 8 | gs_g_xyz2 << 12); + //prim_type = GS_PRIM_SPRITE, abe = 1 + gs_setPRIM(((GS_PRIM *)&p[2]), GS_PRIM_SPRITE, 0, 0, 0, 1, 0, 0, 0, 0); + *(GS_RGBAQ*)&p[3] = rgbaq; + gs_setXYZ2(((GS_XYZ *)&p[4]), (gsGlobal->draw_env.offset_x+x1)<<4, (gsGlobal->draw_env.offset_y+y1)<<4, z<<4); + gs_setXYZ2(((GS_XYZ *)&p[5]), (gsGlobal->draw_env.offset_x+x2)<<4, (gsGlobal->draw_env.offset_y+y2)<<4, z<<4); +} + +void DrawSpriteTextured(struct UIDrawGlobal *gsGlobal, GS_IMAGE *texture, short int x1, short int y1, short int u1, short int v1, short int x2, short int y2, short int u2, short int v2, short int z, GS_RGBAQ rgbaq) +{ + u64 *p; + + //Use the uncached segment, to avoid needing to flush the data cache. + p = (u64*)UNCACHED_SEG(GsGifPacketsAlloc(&gsGlobal->giftable, 5)); //Allocate 5 qwords for 1 textured sprite + LastGIFPacket = (GS_GIF_TAG*)p; + + gs_setGIF_TAG(((GS_GIF_TAG *)&p[0]), 1,0,0,0,GS_GIF_REGLIST,8, gs_g_tex0_1 | gs_g_prim << 4 | gs_g_rgbaq << 8 | gs_g_uv << 12 | gs_g_xyz2 << 16 | gs_g_uv << 20 | gs_g_xyz2 << 24 | gif_rd_nop << 28); + gs_setTEX0_1(((GS_TEX0 *)&p[2]), texture->vram_addr, texture->vram_width, texture->psm, twh(texture->width), twh(texture->height), 1, GS_TEX_MODULATE, 0, 0, 0, 0, 0); + //prim_type = GS_PRIM_SPRITE, tme = 1, fst = 1, abe = 1 + gs_setPRIM(((GS_PRIM *)&p[3]), GS_PRIM_SPRITE, 0, 1, 0, 1, 0, 1, 0, 0); + *(GS_RGBAQ*)&p[4] = rgbaq; + gs_setUV(((GS_UV *)&p[5]), u1 << 4 | 8, v1 << 4 | 8); + gs_setXYZ2(((GS_XYZ *)&p[6]), (gsGlobal->draw_env.offset_x+x1)<<4, (gsGlobal->draw_env.offset_y+y1)<<4, z<<4); + gs_setUV(((GS_UV *)&p[7]), u2 << 4 | 8, v2 << 4 | 8); + gs_setXYZ2(((GS_XYZ *)&p[8]), (gsGlobal->draw_env.offset_x+x2)<<4, (gsGlobal->draw_env.offset_y+y2)<<4, z<<4); + gs_setNOP(((GS_NOP *)&p[9])); +} + +void DrawSpriteTexturedClut(struct UIDrawGlobal *gsGlobal, GS_IMAGE *texture, GS_IMAGE *clut, short int x1, short int y1, short int u1, short int v1, short int x2, short int y2, short int u2, short int v2, short int z, GS_RGBAQ rgbaq) +{ + u64 *p; + + //Use the uncached segment, to avoid needing to flush the data cache. + p = (u64*)UNCACHED_SEG(GsGifPacketsAlloc(&gsGlobal->giftable, 5)); //Allocate 5 qwords for 1 textured sprite with CLUT + LastGIFPacket = (GS_GIF_TAG*)p; + + gs_setGIF_TAG(((GS_GIF_TAG *)&p[0]), 1,0,0,0,GS_GIF_REGLIST,8, gs_g_tex0_1 | gs_g_prim << 4 | gs_g_rgbaq << 8 | gs_g_uv << 12 | gs_g_xyz2 << 16 | gs_g_uv << 20 | gs_g_xyz2 << 24 | gif_rd_nop << 28); + gs_setTEX0_1(((GS_TEX0 *)&p[2]), texture->vram_addr, texture->vram_width, texture->psm, twh(texture->width), twh(texture->height), 1, GS_TEX_MODULATE, clut->vram_addr, clut->psm, 0, 0, 1); + //prim_type = GS_PRIM_SPRITE, tme = 1, fst = 1, abe = 1 + gs_setPRIM(((GS_PRIM *)&p[3]), GS_PRIM_SPRITE, 0, 1, 0, 1, 0, 1, 0, 0); + *(GS_RGBAQ*)&p[4] = rgbaq; + gs_setUV(((GS_UV *)&p[5]), u1 << 4 | 8, v1 << 4 | 8); + gs_setXYZ2(((GS_XYZ *)&p[6]), (gsGlobal->draw_env.offset_x+x1)<<4, (gsGlobal->draw_env.offset_y+y1)<<4, z<<4); + gs_setUV(((GS_UV *)&p[7]), u2 << 4 | 8, v2 << 4 | 8); + gs_setXYZ2(((GS_XYZ *)&p[8]), (gsGlobal->draw_env.offset_x+x2)<<4, (gsGlobal->draw_env.offset_y+y2)<<4, z<<4); + gs_setNOP(((GS_NOP *)&p[9])); +} + +void UploadClut(struct UIDrawGlobal *gsGlobal, GS_IMAGE *clut, const void *buffer) +{ + QWORD *p; + + ExecSyncClear(gsGlobal); + GsLoadImage(buffer, clut); + + p = (QWORD*)UNCACHED_SEG(GsGifPacketsAlloc(&gsGlobal->giftable, 2)); + LastGIFPacket = (GS_GIF_TAG*)p; + + gs_setGIF_TAG(((GS_GIF_TAG *)&p[0]), 1,0,0,0,GS_GIF_PACKED,1,gif_rd_ad); + gs_setR_TEXFLUSH((GS_R_TEXFLUSH *)&p[1]); +} + +struct IconLayout{ + unsigned short int u, v; + unsigned short int length, width; +}; + +static const struct IconLayout ButtonLayoutParameters[BUTTON_TYPE_COUNT]= +{ + {22, 0, 22, 22}, //Circle + {0, 0, 22, 22}, //Cross + {44, 0, 22, 22}, //Square + {66, 0, 22, 22}, //Triangle + {0, 22, 28, 20}, //L1 + {56, 22, 28, 20}, //R1 + {28, 22, 28, 20}, //L2 + {84, 22, 28, 20}, //R2 + {150, 42, 30, 30}, //L3 + {150, 72, 30, 30}, //R3 + {140, 22, 29, 19}, //START + {112, 22, 28, 19}, //SELECT + {120, 72, 30, 30}, //RSTICK + {0, 72, 30, 30}, //UP RSTICK + {30, 72, 30, 30}, //DOWN RSTICK + {60, 72, 30, 30}, //LEFT RSTICK + {90, 72, 30, 30}, //RIGHT RSTICK + {120, 42, 30, 30}, //LSTICK + {0, 42, 30, 30}, //UP LSTICK + {30, 42, 30, 30}, //DOWN LSTICK + {60, 42, 30, 30}, //LEFT LSTICK + {90, 42, 30, 30}, //RIGHT LSTICK + {104, 102, 26, 26}, //DPAD + {130, 102, 26, 26}, //LR-DPAD + {156, 102, 26, 26}, //UD-DPAD + {0, 102, 26, 26}, //UP DPAD + {26, 102, 26, 26}, //DOWN DPAD + {52, 102, 26, 26}, //LEFT DPAD + {78, 102, 26, 26}, //RIGHT DPAD +}; + +void DrawButtonLegendWithFeedback(struct UIDrawGlobal *gsGlobal, struct ClutImage* PadGraphicsTexture, unsigned char ButtonType, short int x, short int y, short int z, short int *xRel) +{ + DrawSpriteTexturedClut(gsGlobal, &PadGraphicsTexture->texture, &PadGraphicsTexture->clut, + x, y, + ButtonLayoutParameters[ButtonType].u, ButtonLayoutParameters[ButtonType].v, + x+ButtonLayoutParameters[ButtonType].length, y+ButtonLayoutParameters[ButtonType].width, + ButtonLayoutParameters[ButtonType].u+ButtonLayoutParameters[ButtonType].length, ButtonLayoutParameters[ButtonType].v+ButtonLayoutParameters[ButtonType].width, + z, (GS_RGBAQ){0x80,0x80,0x80,0x80,0x00}); + + if(xRel != NULL) + *xRel = ButtonLayoutParameters[ButtonType].length; +} + +void DrawButtonLegend(struct UIDrawGlobal *gsGlobal, struct ClutImage* PadGraphicsTexture, unsigned char ButtonType, short int x, short int y, short int z) +{ + DrawButtonLegendWithFeedback(gsGlobal, PadGraphicsTexture, ButtonType, x, y, z, NULL); +} + +void DrawProgressBar(struct UIDrawGlobal *gsGlobal, float percentage, short int x, short int y, short int z, short int len, GS_RGBAQ colour) +{ + char CharBuffer[8]; + float ProgressBarFillEndX; + + /* Draw the progress bar. */ + DrawSprite(gsGlobal, x, y, x+len, y+20, z, GS_LGREY); + ProgressBarFillEndX=x+ (len - 10) *percentage; + /* FIXME: For some unknown reason, the progress bar fill is being offset by -10 pixels. */ + DrawSprite(gsGlobal, x+5, y+5+10, ProgressBarFillEndX, y-5+10, z, colour); + snprintf(CharBuffer, sizeof(CharBuffer)/sizeof(char), "%u%%", (unsigned int)(percentage*100)); + FontPrintf(gsGlobal, x+len/2, y, z - 1, 1.0f, GS_WHITE_FONT, CharBuffer); +} + +void SyncFlipFB(struct UIDrawGlobal *gsGlobal) +{ + if(LastGIFPacket != NULL) + { + LastGIFPacket->eop = 1; + LastGIFPacket = NULL; + } + + PollSema(VBlankStartSema); + WaitSema(VBlankStartSema); + + GsGifPacketsExecute(&gsGlobal->giftable, 1); + GsGifPacketsClear(&gsGlobal->giftable); +} + +void ExecSyncClear(struct UIDrawGlobal *gsGlobal) +{ + if(LastGIFPacket != NULL) + { + LastGIFPacket->eop = 1; + LastGIFPacket = NULL; + } + + GsGifPacketsExecute(&gsGlobal->giftable, 1); + GsGifPacketsClear(&gsGlobal->giftable); +} diff --git a/graphics.h b/graphics.h new file mode 100644 index 0000000..2559224 --- /dev/null +++ b/graphics.h @@ -0,0 +1,87 @@ +#define GS_SETREG_RGBAQ(r, g, b, a, q) (GS_RGBAQ){(r), (g), (b), (a), (q)} + +#define GS_WHITE GS_SETREG_RGBAQ(0x80,0x80,0x80,0x80,0x00) +#define GS_BLACK GS_SETREG_RGBAQ(0x00,0x00,0x00,0x80,0x00) +#define GS_GREY GS_SETREG_RGBAQ(0x30,0x30,0x30,0x80,0x00) +#define GS_LGREY GS_SETREG_RGBAQ(0x50,0x50,0x50,0x80,0x00) +#define GS_DBLUE GS_SETREG_RGBAQ(0x00,0x00,0x50,0x80,0x00) +#define GS_BLUE GS_SETREG_RGBAQ(0x00,0x00,0x80,0x80,0x00) +#define GS_RED GS_SETREG_RGBAQ(0x80,0x00,0x00,0x80,0x00) +#define GS_GREEN GS_SETREG_RGBAQ(0x00,0x80,0x00,0x80,0x00) +#define GS_LBLUE_TRANS GS_SETREG_RGBAQ(0x00,0x00,0x80,0x40,0x00) +#define GS_YELLOW GS_SETREG_RGBAQ(0x80,0x80,0x30,0x80,0x00) + +#define GS_WHITE_FONT GS_SETREG_RGBAQ(0x80,0x80,0x80,0x80,0x00) +#define GS_GREY_FONT GS_SETREG_RGBAQ(0x30,0x30,0x30,0x80,0x00) +#define GS_YELLOW_FONT GS_SETREG_RGBAQ(0x80,0x80,0x30,0x80,0x00) +#define GS_BLUE_FONT GS_SETREG_RGBAQ(0x30,0x30,0x80,0x80,0x00) + +/* Button types, for use with DrawButtonLegend() */ +enum BUTTON_TYPE{ + BUTTON_TYPE_CIRCLE=0, + BUTTON_TYPE_CROSS, + BUTTON_TYPE_SQUARE, + BUTTON_TYPE_TRIANGLE, + BUTTON_TYPE_L1, + BUTTON_TYPE_R1, + BUTTON_TYPE_L2, + BUTTON_TYPE_R2, + BUTTON_TYPE_L3, + BUTTON_TYPE_R3, + BUTTON_TYPE_START, + BUTTON_TYPE_SELECT, + BUTTON_TYPE_RSTICK, + BUTTON_TYPE_UP_RSTICK, + BUTTON_TYPE_DOWN_RSTICK, + BUTTON_TYPE_LEFT_RSTICK, + BUTTON_TYPE_RIGHT_RSTICK, + BUTTON_TYPE_LSTICK, + BUTTON_TYPE_UP_LSTICK, + BUTTON_TYPE_DOWN_LSTICK, + BUTTON_TYPE_LEFT_LSTICK, + BUTTON_TYPE_RIGHT_LSTICK, + BUTTON_TYPE_DPAD, + BUTTON_TYPE_LR_DPAD, + BUTTON_TYPE_UD_DPAD, + BUTTON_TYPE_UP_DPAD, + BUTTON_TYPE_DOWN_DPAD, + BUTTON_TYPE_LEFT_DPAD, + BUTTON_TYPE_RIGHT_DPAD, + + BUTTON_TYPE_COUNT +}; + +//Special button types +#define BUTTON_TYPE_SYS_SELECT 0x40 +#define BUTTON_TYPE_SYS_CANCEL 0x41 + +#define GIF_PACKET_MAX 1 + +struct UIDrawGlobal{ + unsigned char vmode, interlaced, ffmd, psm; + unsigned short int width, height; + GS_DRAWENV draw_env; + GS_DISPENV disp_env; + GS_GIF_PACKET packets[GIF_PACKET_MAX]; + GS_PACKET_TABLE giftable; +}; + +struct ClutImage{ + GS_IMAGE texture; + GS_IMAGE clut; +}; + +int LoadBackground(struct UIDrawGlobal *gsGlobal, GS_IMAGE* Texture); +int LoadPadGraphics(struct UIDrawGlobal *gsGlobal, struct ClutImage* PadGraphics); +void DrawSetFilterMode(struct UIDrawGlobal *gsGlobal, int mode); +void DrawLine(struct UIDrawGlobal *gsGlobal, short int x1, short int y1, short int x2, short int y2, short int z, GS_RGBAQ rgbaq); +void DrawSprite(struct UIDrawGlobal *gsGlobal, short int x1, short int y1, short int x2, short int y2, short int z, GS_RGBAQ rgbaq); +void DrawSpriteTextured(struct UIDrawGlobal *gsGlobal, GS_IMAGE *texture, short int x1, short int y1, short int u1, short int v1, short int x2, short int y2, short int u2, short int v2, short int z, GS_RGBAQ rgbaq); +void DrawSpriteTexturedClut(struct UIDrawGlobal *gsGlobal, GS_IMAGE *texture, GS_IMAGE *clut, short int x1, short int y1, short int u1, short int v1, short int x2, short int y2, short int u2, short int v2, short int z, GS_RGBAQ rgbaq); +void UploadClut(struct UIDrawGlobal *gsGlobal, GS_IMAGE *clut, const void *buffer); +void DrawBackground(struct UIDrawGlobal *gsGlobal, GS_IMAGE *background); +void DrawButtonLegendWithFeedback(struct UIDrawGlobal *gsGlobal, struct ClutImage* PadGraphicsTexture, unsigned char ButtonType, short int x, short int y, short int z, short int *xRel); +void DrawButtonLegend(struct UIDrawGlobal *gsGlobal, struct ClutImage* PadGraphicsTexture, unsigned char ButtonType, short int x, short int y, short int z); +void DrawProgressBar(struct UIDrawGlobal *gsGlobal, float percentage, short int x, short int y, short int z, short int len, GS_RGBAQ colour); +void SyncFlipFB(struct UIDrawGlobal *gsGlobal); +void ExecSyncClear(struct UIDrawGlobal *gsGlobal); diff --git a/hdck/Makefile b/hdck/Makefile new file mode 100644 index 0000000..c36e5ef --- /dev/null +++ b/hdck/Makefile @@ -0,0 +1,20 @@ + +LIBAPA_PATH = ../common/libapa + +IOP_BIN = hdck.irx +APA_OBJS = $(LIBAPA_PATH)/src/misc.o $(LIBAPA_PATH)/src/cache.o $(LIBAPA_PATH)/src/apa.o $(LIBAPA_PATH)/src/journal.o +IOP_OBJS = hdck.o misc.o imports.o $(APA_OBJS) + +IOP_INCS += -I$(CURDIR) -I$(LIBAPA_PATH)/include +IOP_CFLAGS += -Wall -fno-builtin -DAPA_OSD_VER +IOP_LDFLAGS += -s + +IOP_CFLAGS += -DHDCK_CHECK_CROSSLINK=1 #If desired, uncomment to build a version of HDCK that checks for crosslinked partitions (unofficial). + +all: $(IOP_BIN) + +clean: + rm -f $(IOP_BIN) $(IOP_OBJS) + +include $(PS2SDK)/Defs.make +include ../common/Rules.make diff --git a/hdck/apa-opt.h b/hdck/apa-opt.h new file mode 100644 index 0000000..3132c90 --- /dev/null +++ b/hdck/apa-opt.h @@ -0,0 +1,27 @@ +#ifndef _APA_OPT_H +#define _APA_OPT_H + +#define APA_PRINTF(format,...) printf(format, ##__VA_ARGS__) +#define APA_DRV_NAME "hdck" + +/* Define APA_OSD_VER in your Makefile to build an OSD version, which will: + 1. (currently disabled) When formatting, do not create any partitions other than __mbr. + 2. __mbr will be formatted with its password. + 3. All partitions can be accessed, even without the right password. + 4. The starting LBA of the partition will be returned in + the private_5 field of the stat structure (returned by getstat and dread). */ + +#ifdef APA_OSD_VER +#define APA_STAT_RETURN_PART_LBA 1 +#define APA_FORMAT_LOCK_MBR 1 +#define APA_FORMAT_MAKE_PARTITIONS 1 //For now, define this because I don't think we're ready (and want to) deal with the official passwords. +#else +#define APA_ENABLE_PASSWORDS 1 +#define APA_FORMAT_MAKE_PARTITIONS 1 +#endif + +//Module version +#define APA_MODVER_MAJOR 1 +#define APA_MODVER_MINOR 4 + +#endif diff --git a/hdck/hdck.c b/hdck/hdck.c new file mode 100644 index 0000000..5074a9b --- /dev/null +++ b/hdck/hdck.c @@ -0,0 +1,742 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "apa-opt.h" +#include "libapa.h" + +#include "misc.h" + +IRX_ID("hdck", APA_MODVER_MAJOR, APA_MODVER_MINOR); + +//Function prototypes +static int HdckInit(iop_device_t *device); +static int HdckUnsupported(void); +static int HdckDevctl(iop_file_t *fd, const char *name, int cmd, void *arg, unsigned int arglen, void *buf, unsigned int buflen); + +static u8 IOBuffer[128*512]; +static u8 IOBuffer2[128*512]; + +static iop_device_ops_t HdckDeviceOps={ + &HdckInit, + (void*)&HdckUnsupported, + (void*)&HdckUnsupported, + (void*)&HdckUnsupported, + (void*)&HdckUnsupported, + (void*)&HdckUnsupported, + (void*)&HdckUnsupported, + (void*)&HdckUnsupported, + (void*)&HdckUnsupported, + (void*)&HdckUnsupported, + (void*)&HdckUnsupported, + (void*)&HdckUnsupported, + (void*)&HdckUnsupported, + (void*)&HdckUnsupported, + (void*)&HdckUnsupported, + (void*)&HdckUnsupported, + (void*)&HdckUnsupported, + (void*)&HdckUnsupported, + (void*)&HdckUnsupported, + (void*)&HdckUnsupported, + (void*)&HdckUnsupported, + (void*)&HdckUnsupported, + (void*)&HdckUnsupported, + &HdckDevctl, + NULL, + NULL, + NULL +}; + +static iop_device_t HdckDevice={ + "hdck", + IOP_DT_FSEXT|IOP_DT_FS, + 1, + "HDCK", + &HdckDeviceOps +}; + +struct HddInfo{ + u32 sectors; + u32 MaxPartSize; + unsigned int format; + unsigned int status; +}; + +struct HdckPrivateData{ + struct HddInfo HddInfo[2]; + //When the HDD is scanned, it is scanned in both ways (forward and backward). Details on up to two erraneous partitions can be recorded. + u32 ErrorPartitionLBA; + u32 ErrorPartition2LBA; + u32 ErrorPartitionPrevLBA; + u32 ErrorPartition2PrevLBA; +}; + +static struct HdckPrivateData PrivateData={ + { + {0, 0, 0, 3}, + {0, 0, 0, 3}, + }, + 0, + 0, + 0, + 0 +}; + +static int HdckUnsupported(void) +{ + return -1; +} + +static int HdckInit(iop_device_t *device) +{ + return 0; +} + +#ifdef HDCK_CHECK_CROSSLINK +static int AddPartitionToList(u32 *list, unsigned int *pCount, u32 sector) +{ + unsigned int i; + int result; + + for(i = 0; i < *pCount; i++) + { + if(list[i] == sector) + break; + } + + if(i == *pCount) + { + list[i] = sector; + (*pCount)++; + result = 0; + }else{ + printf("hdck: found cross-linked partition: 0x%lx\n", sector); + result = -EEXIST; + } + + return result; +} +#endif + +static int CheckAPAPartitionLinks(int device, apa_cache_t *clink) +{ + int result; +#ifdef HDCK_CHECK_CROSSLINK + unsigned int partitions; +#endif + apa_cache_t *clink2; + u32 CurrentLBA, ParentLBA; + + printf("hdck: scan partitions.\n"); + + result = 0; + PrivateData.ErrorPartition2LBA = 0; + PrivateData.ErrorPartitionLBA = 0; + PrivateData.ErrorPartition2PrevLBA = 0; + PrivateData.ErrorPartitionPrevLBA = 0; + ParentLBA = 0; + +#ifdef HDCK_CHECK_CROSSLINK + partitions = 0; +#endif + CurrentLBA = clink->header->next; + while( CurrentLBA != 0 +#ifdef HDCK_CHECK_CROSSLINK + && ((result = AddPartitionToList((u32*)IOBuffer, &partitions, CurrentLBA)) >= 0) +#endif + && (clink2=apaCacheGetHeader(device, CurrentLBA, 0, &result))!=NULL) + { + if(clink2->header->prev != ParentLBA) + { + printf("hdck: found invalid previous partition address, fix it.\n"); + clink2->header->prev = ParentLBA; + clink2->flags |= APA_CACHE_FLAG_DIRTY; + apaCacheFlushAllDirty(device); + } + + ParentLBA = clink2->header->start; + CurrentLBA = clink2->header->next; + apaCacheFree(clink2); + } + + if(result == 0) + { + if(clink->header->prev != ParentLBA) + { + printf("hdck: found invalid last partition address, fix it.\n"); + clink->header->prev = ParentLBA; + clink->flags |= APA_CACHE_FLAG_DIRTY; + apaCacheFlushAllDirty(device); + } + + printf("hdck: we do not have an error partition.\n"); + }else{ + printf("hdck: found error partition.\n"); + + PrivateData.ErrorPartitionLBA = CurrentLBA; + PrivateData.ErrorPartitionPrevLBA = ParentLBA; + + //Now scan the HDD, backwards. + CurrentLBA = clink->header->prev; + ParentLBA = 0; +#ifdef HDCK_CHECK_CROSSLINK + partitions = 0; +#endif + while( CurrentLBA != 0 +#ifdef HDCK_CHECK_CROSSLINK + && ((result = AddPartitionToList((u32*)IOBuffer, &partitions, CurrentLBA)) >= 0) +#endif + && (clink2=apaCacheGetHeader(device, CurrentLBA, 0, &result))!=NULL) + { + if(clink2->header->next != ParentLBA) + { + printf("hdck: found invalid next partition address, fix it.\n"); + + clink2->header->next = ParentLBA; + clink2->flags |= APA_CACHE_FLAG_DIRTY; + apaCacheFlushAllDirty(device); + } + + ParentLBA = clink2->header->start; + CurrentLBA = clink2->header->prev; + apaCacheFree(clink2); + } + + if(result == 0) + { + if(clink->header->next != ParentLBA) + { + printf("hdck: found invalid first partition address, fix it.\n"); + clink->header->next = ParentLBA; + clink->flags |= APA_CACHE_FLAG_DIRTY; + apaCacheFlushAllDirty(device); + } + + printf("hdck: found inconsistency, but already fixed.\n"); + printf("hdck: we do not have an error partition.\n"); + + PrivateData.ErrorPartitionLBA = 0; + }else{ + printf("hdck: found error partition.\n"); + + PrivateData.ErrorPartition2LBA = CurrentLBA; + PrivateData.ErrorPartition2PrevLBA = ParentLBA; + } + } + + return PrivateData.ErrorPartitionLBA; +} + +static void EraseSector(int unit, void *buffer, u32 lba) +{ + memset(buffer, 0, 512); + ata_device_sector_io(unit, buffer, lba, 1, ATA_DIR_WRITE); + ata_device_flush_cache(unit); +} + +static void RemoveBadPartitions(int device, apa_cache_t *clink) +{ + apa_cache_t *clink2; + int result; + + printf("hdck: remove all partitions after unreadable one.\n"); + + if((clink2=apaCacheGetHeader(device, PrivateData.ErrorPartitionPrevLBA, 0, &result))!=NULL) + { + clink2->header->next = 0; + clink->header->prev = PrivateData.ErrorPartitionPrevLBA; + clink2->header->flags |= APA_CACHE_FLAG_DIRTY; + clink->header->flags |= APA_CACHE_FLAG_DIRTY; + + apaCacheFlushAllDirty(device); + apaCacheFree(clink2); + + PrivateData.ErrorPartition2LBA = 0; + PrivateData.ErrorPartitionLBA = 0; + } +} + +static void RecoverSubPartition(apa_cache_t *clink, u32 lba, u32 previous, u32 sublength, u32 substart, u16 type, int sub) +{ + int result; + apa_cache_t *clink2; + + clink2 = apaCacheGetHeader(clink->device, lba, 1, &result); + + printf("hdck: found this sub partition's main, so I recover it.\n"); + + memset(clink2->header, 0, sizeof(apa_header_t)); + clink2->header->magic = APA_MAGIC; + clink2->header->start = lba; + if(lba < clink->header->prev) clink2->header->next = lba + sublength; + clink2->header->prev = previous; + clink2->header->length = sublength; + clink2->header->type = type; + clink2->header->flags = APA_FLAG_SUB; + clink2->header->main = substart; + clink2->header->number = sub; + + apaGetTime(&clink2->header->created); + + clink2->flags |= APA_CACHE_FLAG_DIRTY; + + apaCacheFlushAllDirty(clink->device); + apaCacheFree(clink2); +} + +//Checks if the damaged partition(s) is/are sub-partition(s) of another partition. If so, attempt to recover it/them. +static void RecoverPartitionIfSub(int device, apa_cache_t *clink) +{ + int result, count, sub; + apa_cache_t *clink2; + u32 NextPartSector; + apa_sub_t *pSubs; + + printf("hdck: check if error partition is belong to someone as sub partition.\n"); + + //Start by cycling forward through the partition list. + //If there is at least one more partition (other than __mbr) and the damaged partition is not the first partition. + if((NextPartSector = clink->header->next) != 0 && PrivateData.ErrorPartitionLBA!=clink->header->next) + { + while((clink2 = apaCacheGetHeader(device, NextPartSector, 0, &result)) != NULL) + { + if(clink2->header->type && !(clink2->header->flags&APA_FLAG_SUB)) + { + for(count=1,sub=0,pSubs=clink2->header->subs; sub < clink2->header->nsub; sub++,pSubs++,count++) + { + if(PrivateData.ErrorPartitionLBA && pSubs->start == PrivateData.ErrorPartitionLBA) + { + RecoverSubPartition(clink, PrivateData.ErrorPartitionLBA, PrivateData.ErrorPartitionPrevLBA, pSubs->length, clink2->header->start, clink2->header->type, count); + + if(PrivateData.ErrorPartition2LBA == PrivateData.ErrorPartitionLBA) PrivateData.ErrorPartition2LBA = 0; + PrivateData.ErrorPartitionLBA = 0; + } + + if(PrivateData.ErrorPartition2LBA && pSubs->start == PrivateData.ErrorPartition2LBA) + { + RecoverSubPartition(clink, PrivateData.ErrorPartition2LBA, PrivateData.ErrorPartitionLBA, pSubs->length, clink2->header->start, clink2->header->type, count); + + PrivateData.ErrorPartition2LBA = 0; + } + } + } + + NextPartSector = clink2->header->next; + apaCacheFree(clink2); + + if(!PrivateData.ErrorPartitionLBA && !PrivateData.ErrorPartition2LBA) + return; + + if(!NextPartSector || NextPartSector==PrivateData.ErrorPartitionLBA) break; + } + } + + /* Cycle backward through the partition list. + If there is at least one more partition (other than __mbr) and the damaged partition is not the last partition. */ + if((NextPartSector = clink->header->prev) != 0 && clink->header->prev != PrivateData.ErrorPartition2LBA) + { + while((clink2 = apaCacheGetHeader(device, NextPartSector, 0, &result)) != NULL) + { + if(clink2->header->type && !(clink2->header->flags&APA_FLAG_SUB)) + { + for(count=1,sub=0,pSubs=clink2->header->subs; sub < clink2->header->nsub; sub++,pSubs++,count++) + { + if(PrivateData.ErrorPartitionLBA && pSubs->start == PrivateData.ErrorPartitionLBA) + { + RecoverSubPartition(clink, PrivateData.ErrorPartitionLBA, PrivateData.ErrorPartitionPrevLBA, pSubs->length, clink2->header->start, clink2->header->type, count); + + if(PrivateData.ErrorPartition2LBA == PrivateData.ErrorPartitionLBA) PrivateData.ErrorPartition2LBA = 0; + PrivateData.ErrorPartitionLBA = 0; + } + + if(PrivateData.ErrorPartition2LBA && pSubs->start == PrivateData.ErrorPartition2LBA) + { + RecoverSubPartition(clink, PrivateData.ErrorPartition2LBA, PrivateData.ErrorPartitionLBA, pSubs->length, clink2->header->start, clink2->header->type, count); + + PrivateData.ErrorPartition2LBA = 0; + } + } + } + + NextPartSector = clink2->header->prev; + apaCacheFree(clink2); + + if(!PrivateData.ErrorPartitionLBA && !PrivateData.ErrorPartition2LBA) + return; + + if(!NextPartSector || NextPartSector==PrivateData.ErrorPartitionLBA) break; + } + } +} + +static apa_cache_t *apaCreateAlignedEmptyPartition(apa_cache_t *clink, u32 lba, u32 length) +{ + apa_cache_t *result; + + for(length>>=1; 0x0003FFFF < length; length>>=1) + { + if(lba % length==0) + { + result = apaRemovePartition(clink->device, lba, lba+length, clink->header->start, length); + clink->header->next = lba; + clink->flags |= APA_CACHE_FLAG_DIRTY; + apaCacheFlushAllDirty(clink->device); + apaCacheFree(clink); + + return result; + } + } + + return NULL; +} + +static void MarkUnreadablePartionAsEmpty(int device, u32 lba, u32 parent, u32 length) +{ + apa_cache_t *clink; + u32 new_length; + int result, i; + + new_length = 0; + clink = apaCacheGetHeader(device, parent, 0, &result); + printf("hdck: make unreadable partition as empty.\n"); + + if(clink != NULL) + { + while(length != 0) + { + for(i = 31; i>=0 ;i--) + { + if((new_length = 1 << i) & length) break; + } + + if(lba%new_length) + { + clink = apaCreateAlignedEmptyPartition(clink, lba, new_length); + }else{ + apaRemovePartition(device, lba, lba+new_length, clink->header->start, new_length); + clink->header->next = lba; + clink->flags |= APA_CACHE_FLAG_DIRTY; + apaCacheFlushAllDirty(device); + apaCacheFree(clink); + } + + length-=clink->header->length; + } + + apaCacheFree(clink); + PrivateData.ErrorPartition2LBA = 0; + PrivateData.ErrorPartitionLBA = 0; + } +} + +static void DeleteFreePartitions(int device, apa_cache_t *clink) +{ + apa_cache_t *clink2; + int result; + u32 sector; + + sector = clink->header->next; + while(sector != 0 && (clink2=apaCacheGetHeader(device, sector, 0, &result)) != NULL) + { + if(clink2->header->type == 0) + if((clink2 = apaDeleteFixPrev(clink2, &result)) == NULL) break; + + sector = clink2->header->next; + apaCacheFree(clink2); + } +} + +//Generates lists of all main and sub-partitions on the disk. +static int ReadPartitionMap(int device, apa_cache_t *clink, int *pMainCount, int *pSubCount) +{ + int result; + apa_cache_t *clink2; + u32 sector, *MainMap, *SubMap; + + *pMainCount = 0; + *pSubCount = 0; + result = 0; + sector = clink->header->next; + MainMap = (u32*)IOBuffer; + SubMap = (u32*)IOBuffer2; + while(sector != 0) + { + if((clink2=apaCacheGetHeader(device, sector, 0, &result)) != NULL) + { + if(clink2->header->type) + { + if(!(clink2->header->flags&APA_FLAG_SUB)) + { + printf("hdck: main, start %08lx, nsector %08lx, nsub %ld, id %s\n", clink2->header->start, clink2->header->length, clink2->header->nsub, clink2->header->id); + + MainMap[(*pMainCount)] = clink2->header->start; + (*pMainCount) ++; + } + if(clink2->header->type && clink2->header->flags&APA_FLAG_SUB) + { + printf("hdck: sub , start %08lx, nsector %08lx, num %2ld, main %08lx\n", clink2->header->start, clink2->header->length, clink2->header->number, clink2->header->main); + SubMap[(*pSubCount)] = clink2->header->start; + (*pSubCount) ++; + } + } + + sector = clink2->header->next; + apaCacheFree(clink2); + }else{ + break; + } + } + + return result; +} + +//Goes through the partitions and checks the relationships between the main and sub-partitions. +static int CheckPartitions(int device, apa_cache_t *clink) +{ + int result, MainCount, SubCount, i, sub, count, flag, missing_sub_found; + apa_cache_t *clink2, *clink_sub; + apa_sub_t *pSubs; + u32 *ptr; + + if((result=ReadPartitionMap(device, clink, &MainCount, &SubCount)) == 0) + { + printf("hdck: check main partitions.\n"); + + for(i=0; iheader->subs; subheader->nsub; sub++,pSubs++) + { + for(flag = 0,count = 0; count < SubCount; count++) + { + if(pSubs->start==((u32*)IOBuffer2)[count]) + { + flag = 1; + break; + } + } + + if(!flag) + { + //Sub-partition does not belong to anything. + printf("hdck: missing sub partition found.\n"); + + if(!missing_sub_found) + { + clink2->header->nsub = sub; + clink2->flags |= APA_CACHE_FLAG_DIRTY; + missing_sub_found = 1; //"missing sub" found. + apaCacheFlushAllDirty(device); + } + }else{ + //Sub-partition belongs to something, but follows a missing partition. + if(missing_sub_found) + { + printf("hdck: remove sub partition follows missing partition.\n"); + + if((clink_sub = apaCacheGetHeader(device, pSubs->start, 0, &result)) != NULL) + result = apaDelete(clink_sub); + } + } //Otherwise, the sub-partition is okay. + } + + apaCacheFree(clink2); + } + } + + if(result >= 0) + { + if((result = ReadPartitionMap(device, clink, &MainCount, &SubCount)) == 0) + { + printf("hdck: check sub partitions.\n"); + + for(sub=0,ptr=(u32*)IOBuffer2; sub < SubCount && result == 0; ptr++,sub++) + { + //Check through the list of main partitions, to see if the sub-partition belongs to a main partition. + if((clink2=apaCacheGetHeader(device, *ptr, 0, &result)) != NULL) + { + for(i = 0; ((u32*)IOBuffer)[i]!=clink2->header->main && i < MainCount; i++); + + if(MainCount == i) + { + printf("hdck: this sub(start = %08lx)is not belong to anyone, remove it.\n", clink2->header->start); + if((result = apaDelete(clink2)) != 0) + printf("hdck: remove partition failed.\n"); + }else{ + //Check if the sub-partition belongs to the main partition's sub-partition list. + if((clink_sub = apaCacheGetHeader(device, clink2->header->main, 0, &result)) != NULL) + { + for(count = 0; count < clink2->header->nsub; count++) + { + if(clink_sub->header->subs[count].start == *ptr) + break; + } + + if(clink_sub->header->nsub == count) + { + printf("hdck: this subpartition is not belong to sub list.\n"); + apaDelete(clink2); + apaCacheFree(clink_sub); + }else{ + apaCacheFree(clink_sub); + apaCacheFree(clink2); + } + }else{ + apaCacheFree(clink2); + } + } + }else{ + break; + } + } + } + } + } + + return result; +} + +static int HdckDevctl(iop_file_t *fd, const char *name, int cmd, void *arg, unsigned int arglen, void *buf, unsigned int buflen) +{ + int result, badParts; + apa_cache_t *clink; + + if(PrivateData.HddInfo[fd->unit].status!=0) + { + return -ENXIO; + } + + if((clink=apaCacheGetHeader(fd->unit, APA_SECTOR_MBR, 0, &result)) != NULL) + { + badParts = 0; + while(CheckAPAPartitionLinks(fd->unit, clink)!=0) + { + //Both pointers point to the same partition. + if(PrivateData.ErrorPartitionLBA == PrivateData.ErrorPartition2LBA) + { + printf("hdck: only one partition has problem, try to fix it.\n"); + RecoverPartitionIfSub(fd->unit, clink); + + //If the error is still there, decide on how to delete the partition. + if(PrivateData.ErrorPartitionLBA && PrivateData.ErrorPartition2LBA) + { + //If the partition is not the last partition, turn it into an empty partition. Otherwise, delete it. + if(clink->header->prev != PrivateData.ErrorPartitionLBA) + MarkUnreadablePartionAsEmpty(fd->unit, PrivateData.ErrorPartitionLBA, PrivateData.ErrorPartitionPrevLBA, PrivateData.ErrorPartition2PrevLBA-PrivateData.ErrorPartitionLBA); + else + RemoveBadPartitions(fd->unit, clink); + }else{ + continue; + } + //More than one partition in-between the pointers are bad. + } else if(PrivateData.ErrorPartitionLBA < PrivateData.ErrorPartition2LBA) { + printf("hdck: two or more partitions have problem, try to fix it.\n"); + + RecoverPartitionIfSub(fd->unit, clink); + + //If the error is still there, decide on how to delete the partition. + if(PrivateData.ErrorPartitionLBA && PrivateData.ErrorPartition2LBA) + { + //If both of the partitions are not the last partition, turn them both into empty partitions. Otherwise, delete them. + if(clink->header->prev != PrivateData.ErrorPartitionLBA && clink->header->prev != PrivateData.ErrorPartition2LBA) + MarkUnreadablePartionAsEmpty(fd->unit, PrivateData.ErrorPartitionLBA, PrivateData.ErrorPartitionPrevLBA, PrivateData.ErrorPartition2PrevLBA-PrivateData.ErrorPartitionLBA); + else + RemoveBadPartitions(fd->unit, clink); + }else{ + continue; + } + //The pointers overlap, making it impossible to tell where the bad partitions certainly are. The only solution is to nuke 'em all. + } else { + printf("hdck: partition table completely inconsistent.\n"); + RemoveBadPartitions(fd->unit, clink); + } + + //If there are more than 8 bad partitions, abort. + badParts++; + if(badParts == 9){ + if(clink) goto check_end; + break; + } + } + + EraseSector(fd->unit, IOBuffer, APA_SECTOR_SECTOR_ERROR); //Erase the error sector record. + DeleteFreePartitions(fd->unit, clink); + result = CheckPartitions(fd->unit, clink); + +check_end: + apaCacheFree(clink); + printf("hdck: done\n"); + }else{ + printf("hdck: cannot read mbr partition, I cannot continue.\n"); + } + + return result; +} + +int _start(int argc, char *argv[]) +{ + apa_ps2time_t time; + ata_devinfo_t *pDevInfo; + int i; + + if(apaGetTime(&time) != 0) + { + printf("hdck: error: could not get date\n"); + return MODULE_NO_RESIDENT_END; + } + + printf("hdck: %02d:%02d:%02d %02d/%02d/%d\n", time.hour, time.min, time.sec, time.month, time.day, time.year); + + for(i = 0; i < 2; i++) + { + if((pDevInfo = ata_get_devinfo(i)) == NULL) + { + printf("hdck: error: ata initialization failed.\n"); + return MODULE_NO_RESIDENT_END; + } + + if(pDevInfo->exists && !pDevInfo->has_packet) + { + PrivateData.HddInfo[i].status--; + PrivateData.HddInfo[i].sectors = pDevInfo->total_sectors; + + PrivateData.HddInfo[i].MaxPartSize = apaGetPartitionMax(pDevInfo->total_sectors); + if(HdckUnlockHdd(i) == 0) + { + PrivateData.HddInfo[i].status--; + } + + printf("hdck: disk%d: 0x%08lx sectors, max 0x%08lx\n", i, PrivateData.HddInfo[i].sectors, PrivateData.HddInfo[i].MaxPartSize); + } + } + + apaCacheInit(0x100); + for(i = 0; i<2; i++) + { + if(PrivateData.HddInfo[i].status < 2) + { + if(apaJournalRestore(i) != 0) + return MODULE_NO_RESIDENT_END; + + if(apaGetFormat(i, &PrivateData.HddInfo[i].format)) + PrivateData.HddInfo[i].status--; + } + } + + DelDrv("hdck"); + if(AddDrv(&HdckDevice) == 0) + { + printf("hdck: driver start.\n"); + return MODULE_RESIDENT_END; + } + + return MODULE_NO_RESIDENT_END; +} diff --git a/hdck/imports.lst b/hdck/imports.lst new file mode 100644 index 0000000..d152c47 --- /dev/null +++ b/hdck/imports.lst @@ -0,0 +1,43 @@ +atad_IMPORTS_start +I_ata_get_devinfo +I_ata_device_sector_io +I_ata_device_flush_cache +I_ata_device_sce_sec_unlock +atad_IMPORTS_end + +cdvdman_IMPORTS_start +I_sceCdReadClock +I_sceCdRI +cdvdman_IMPORTS_end + +ioman_IMPORTS_start +I_AddDrv +I_DelDrv +ioman_IMPORTS_end + +intrman_IMPORTS_start +I_CpuSuspendIntr +I_CpuResumeIntr +intrman_IMPORTS_end + +stdio_IMPORTS_start +I_printf +stdio_IMPORTS_end + +sysclib_IMPORTS_start +I_memset +I_memcpy +I_memcmp +I_strcpy +I_strncpy +I_strncmp +sysclib_IMPORTS_end + +sysmem_IMPORTS_start +I_AllocSysMemory +I_FreeSysMemory +sysmem_IMPORTS_end + +thbase_IMPORTS_start +I_DelayThread +thbase_IMPORTS_end diff --git a/hdck/irx_imports.h b/hdck/irx_imports.h new file mode 100644 index 0000000..f78f34d --- /dev/null +++ b/hdck/irx_imports.h @@ -0,0 +1,10 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/hdck/misc.c b/hdck/misc.c new file mode 100644 index 0000000..b2442a0 --- /dev/null +++ b/hdck/misc.c @@ -0,0 +1,40 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "misc.h" + +static int HdckRI(unsigned char *id) +{ + u32 stat; + int result; + + memset(id, 0, 32); + stat = 0; + if(sceCdRI(id, &stat) == 0 || stat) + { + printf("hdck: error: cannot get ilink id\n"); + result = -EIO; + }else{ + result = 0; + } + + return result; +} + +int HdckUnlockHdd(int unit) +{ + unsigned char id[32]; + int result; + + if((result = HdckRI(id)) ==0) + { + result = ata_device_sce_sec_unlock(unit, id); + } + + return result; +} diff --git a/hdck/misc.h b/hdck/misc.h new file mode 100644 index 0000000..3ae06b0 --- /dev/null +++ b/hdck/misc.h @@ -0,0 +1 @@ +int HdckUnlockHdd(int unit); diff --git a/hdlog/Makefile b/hdlog/Makefile new file mode 100644 index 0000000..0663a3b --- /dev/null +++ b/hdlog/Makefile @@ -0,0 +1,14 @@ + +IOP_BIN = hdlog.irx +IOP_OBJS = main.o imports.o + +IOP_CFLAGS += -Wall -fno-builtin +IOP_LDFLAGS += -s + +all: $(IOP_BIN) + +clean: + rm -f $(IOP_BIN) $(IOP_OBJS) + +include $(PS2SDK)/Defs.make +include ../common/Rules.make diff --git a/hdlog/imports.lst b/hdlog/imports.lst new file mode 100644 index 0000000..332d510 --- /dev/null +++ b/hdlog/imports.lst @@ -0,0 +1,11 @@ +ioman_IMPORTS_start +I_AddDrv +I_DelDrv +I_open +I_close +I_write +ioman_IMPORTS_end + +stdio_IMPORTS_start +I_printf +stdio_IMPORTS_end diff --git a/hdlog/irx_imports.h b/hdlog/irx_imports.h new file mode 100644 index 0000000..b4670f5 --- /dev/null +++ b/hdlog/irx_imports.h @@ -0,0 +1,15 @@ +/* + * irx_imports.h - Defines all IRX imports. + */ + +#ifndef IOP_IRX_IMPORTS_H +#define IOP_IRX_IMPORTS_H + +#include + +/* Please keep these in alphabetical order! */ +#include +#include +#include + +#endif /* IOP_IRX_IMPORTS_H */ diff --git a/hdlog/main.c b/hdlog/main.c new file mode 100644 index 0000000..37db78b --- /dev/null +++ b/hdlog/main.c @@ -0,0 +1,174 @@ +#include +#include +#include +#include +#include +#include + +#include + +#define MODNAME "HDD_logger" +IRX_ID(MODNAME, 1, 1); + +#define NUM_TTY 1 + +static int ttyFds[NUM_TTY]; + +static int _unmount(int unit); + +static int hdlog_init(iop_device_t *fd) +{ + int i; + + for(i=0; i < NUM_TTY; i++) + ttyFds[i] = -1; + + return 0; +} + +static int hdlog_deinit(iop_device_t *fd) +{ + int i; + + for(i=0; i < NUM_TTY; i++) + _unmount(i); + + return 0; +} + +static int hdlog_open(iop_file_t *fd, const char *path, int flags, int mode) +{ + int result; + + if(fd->unit < NUM_TTY && ttyFds[fd->unit] >= 0) + { + fd->privdata = (void*)ttyFds[fd->unit]; + result = fd->unit; + } + else result = -ENXIO; + + return result; +} + +static int hdlog_close(iop_file_t *fd) +{ + return 0; +} + +static int hdlog_write(iop_file_t *fd, void *buffer, int size) +{ + if(size == 0) + return 0; + else if (size < 0) + return -1; + + return write((int)fd->privdata, buffer, size); +} + +static int hdlog_mount(iop_file_t *f, const char *mountpoint, const char *blockdev, int flags, void *arg, int arglen) +{ + int result, fd; + + if(f->unit < NUM_TTY) + { + if(ttyFds[f->unit] < 0) + { + printf("HDLOG: mount %s %s %d\n", mountpoint, blockdev, flags); + + if((fd = open(blockdev, flags)) >= 0) + { + ttyFds[f->unit] = fd; + + if(f->unit == 0) + { + close(0); + close(1); + + open("tty00:", O_RDWR); + open("tty00:", O_WRONLY); + } + } + + result = fd; + } else + result = -EBUSY; + } + else + result = -ENXIO; + + return result; +} + +static int _unmount(int unit) +{ + int result; + + if((unit < NUM_TTY) && (ttyFds[unit] >= 0)) + { + close(ttyFds[unit]); + ttyFds[unit] = -1; + result = 0; + } else + result = -ENXIO; + + return result; +} + +static int hdlog_umount(iop_file_t *fd, const char *mountpoint) +{ + return _unmount(fd->unit); +} + +static int hdlog_NulldevFunction(void) +{ + return -1; +} + +/* Device driver I/O functions */ +static iop_device_ops_t hdlog_functarray={ + &hdlog_init, /* INIT */ + &hdlog_deinit, /* DEINIT */ + (void*)&hdlog_NulldevFunction, /* FORMAT */ + (void*)&hdlog_open, /* OPEN */ + (void*)&hdlog_close, /* CLOSE */ + (void*)&hdlog_NulldevFunction, /* READ */ + (void*)&hdlog_write, /* WRITE */ + (void*)&hdlog_NulldevFunction, /* LSEEK */ + (void*)&hdlog_NulldevFunction, /* IOCTL */ + (void*)&hdlog_NulldevFunction, /* REMOVE */ + (void*)&hdlog_NulldevFunction, /* MKDIR */ + (void*)&hdlog_NulldevFunction, /* RMDIR */ + (void*)&hdlog_NulldevFunction, /* DOPEN */ + (void*)&hdlog_NulldevFunction, /* DCLOSE */ + (void*)&hdlog_NulldevFunction, /* DREAD */ + (void*)&hdlog_NulldevFunction, /* GETSTAT */ + (void*)&hdlog_NulldevFunction, /* CHSTAT */ + (void*)&hdlog_NulldevFunction, /* RENAME */ + (void*)&hdlog_NulldevFunction, /* CHDIR */ + (void*)&hdlog_NulldevFunction, /* SYNC */ + &hdlog_mount, /* MOUNT */ + &hdlog_umount, /* UMOUNT */ +}; + +static const char hdlog_dev_name[]="tty"; + +static iop_device_t hdlog_dev={ + hdlog_dev_name, /* Device name */ + IOP_DT_CHAR|IOP_DT_CONS|IOP_DT_FS|IOP_DT_FSEXT, /* Device type flag */ + 1, /* Version */ + "HDD logger driver", /* Description. */ + &hdlog_functarray /* Device driver function pointer array */ +}; + +/* Entry point */ +int _start(int argc, char *argv[]) +{ + DelDrv(hdlog_dev_name); + if(AddDrv(&hdlog_dev) != 0) + { + printf("hdlog: unable to register device.\n"); + return MODULE_NO_RESIDENT_END; + } + + return MODULE_RESIDENT_END; +} diff --git a/hdsk/Makefile b/hdsk/Makefile new file mode 100644 index 0000000..768bda6 --- /dev/null +++ b/hdsk/Makefile @@ -0,0 +1,29 @@ + +#If desired, set to 1 to build a version of HDSK that supports HDLoader games +HDLFS_SUPPORT = 1 + +LIBAPA_PATH = ../common/libapa + +IOP_BIN = hdsk.irx +APA_OBJS = $(LIBAPA_PATH)/src/misc.o $(LIBAPA_PATH)/src/cache.o $(LIBAPA_PATH)/src/apa.o $(LIBAPA_PATH)/src/journal.o $(LIBAPA_PATH)/src/free.o +IOP_OBJS = hdsk.o misc.o sim.o imports.o $(APA_OBJS) + +IOP_INCS += -I$(CURDIR) -I$(LIBAPA_PATH)/include +IOP_CFLAGS += -Wall -fno-builtin -DAPA_OSD_VER +IOP_LDFLAGS += -s + +ifeq ($(HDLFS_SUPPORT),1) + IOP_CFLAGS += -DHDSK_SUPPORT_HDLFS=1 + IOP_OBJS += hdl.o +endif + +all: $(IOP_BIN) + +clean: + rm -f $(IOP_BIN) $(IOP_OBJS) + +%.o : $(LIBAPA_PATH)/%.c + $(IOP_CC) $(IOP_CFLAGS) $< -o $@ + +include $(PS2SDK)/Defs.make +include ../common/Rules.make diff --git a/hdsk/apa-opt.h b/hdsk/apa-opt.h new file mode 100644 index 0000000..3f3b98a --- /dev/null +++ b/hdsk/apa-opt.h @@ -0,0 +1,27 @@ +#ifndef _APA_OPT_H +#define _APA_OPT_H + +#define APA_PRINTF(format,...) printf(format, ##__VA_ARGS__) +#define APA_DRV_NAME "hdsk" + +/* Define APA_OSD_VER in your Makefile to build an OSD version, which will: + 1. (currently disabled) When formatting, do not create any partitions other than __mbr. + 2. __mbr will be formatted with its password. + 3. All partitions can be accessed, even without the right password. + 4. The starting LBA of the partition will be returned in + the private_5 field of the stat structure (returned by getstat and dread). */ + +#ifdef APA_OSD_VER +#define APA_STAT_RETURN_PART_LBA 1 +#define APA_FORMAT_LOCK_MBR 1 +#define APA_FORMAT_MAKE_PARTITIONS 1 //For now, define this because I don't think we're ready (and want to) deal with the official passwords. +#else +#define APA_ENABLE_PASSWORDS 1 +#define APA_FORMAT_MAKE_PARTITIONS 1 +#endif + +//Module version +#define APA_MODVER_MAJOR 1 +#define APA_MODVER_MINOR 4 + +#endif diff --git a/hdsk/hdl.c b/hdsk/hdl.c new file mode 100644 index 0000000..ff71480 --- /dev/null +++ b/hdsk/hdl.c @@ -0,0 +1,70 @@ +/* Unofficial add-on to HDSK to support HDLoader games. + The HDLoader format stores the LBAs and sizes of each partition at offset 0x100000 + of the main partition's extended attribute area. If the main partition or its sub-partitions + are moved, then the game is broken. */ + +#include +#include +#include +#include +#include +#include +#include + +#include "apa-opt.h" +#include "libapa.h" +#include "hdsk.h" + +#define HDL_INFO_MAGIC 0xDEADFEED +#define HDL_GAME_DATA_OFFSET 0x100000 + +#define HDLFS_GAME_TITLE_LEN 160 +#define HDLFS_STARTUP_PTH_LEN 60 + +typedef struct { + u32 part_offset; // In CD-ROM (2048-byte) sectors + u32 data_start; // In 512-byte HDD sectors + u32 part_size; // In bytes +} part_specs_t; + +typedef struct // size = 1024 bytes +{ + u32 magic; + u16 reserved; + u16 version; + s8 gamename[HDLFS_GAME_TITLE_LEN]; + u8 hdl_compat_flags; + u8 ops2l_compat_flags; + u8 dma_type; + u8 dma_mode; + s8 startup[HDLFS_STARTUP_PTH_LEN]; /* NOTE: The startup file name here must be without the ";1" suffix. */ + u32 layer1_start; + u32 discType; + s32 num_partitions; + part_specs_t part_specs[65]; +} hdl_game_info_t; + +extern u8 IOBuffer[IOBUFFER_SIZE_SECTORS * 512]; + +int hdlUpdateGameSliceInfo(int device, u32 main, int part, u32 OldPartStart, u32 NewPartStart) +{ + u32 InfoLBA, DataOffset; + int result; + + /* Note: The APA specification states that there is a 4KB area used for storing the partition's information, + before the extended attribute area. */ + InfoLBA = main + (HDL_GAME_DATA_OFFSET + 4096) / 512; + + if((result = ata_device_sector_io(device, IOBuffer, InfoLBA, sizeof(hdl_game_info_t) / 512, ATA_DIR_READ)) == 0) + { + if(((hdl_game_info_t*)IOBuffer)->magic == HDL_INFO_MAGIC) + { + DataOffset = ((hdl_game_info_t*)IOBuffer)->part_specs[part].data_start - OldPartStart; + ((hdl_game_info_t*)IOBuffer)->part_specs[part].data_start = NewPartStart + DataOffset; + result = ata_device_sector_io(device, IOBuffer, InfoLBA, sizeof(hdl_game_info_t) / 512, ATA_DIR_WRITE); + } else + result = -1; + } + + return result; +} diff --git a/hdsk/hdl.h b/hdsk/hdl.h new file mode 100644 index 0000000..f6ee2dc --- /dev/null +++ b/hdsk/hdl.h @@ -0,0 +1,3 @@ +#define APA_TYPE_HDLFS 0x1337 + +int hdlUpdateGameSliceInfo(int device, u32 main, int part, u32 OldPartStart, u32 NewPartStart); diff --git a/hdsk/hdsk-devctl.h b/hdsk/hdsk-devctl.h new file mode 100644 index 0000000..42c49b4 --- /dev/null +++ b/hdsk/hdsk-devctl.h @@ -0,0 +1,15 @@ +enum HDSK_DEVCTL{ + HDSK_DEVCTL_GET_FREE = 0, + HDSK_DEVCTL_GET_HDD_STAT, + HDSK_DEVCTL_START, + HDSK_DEVCTL_WAIT, + HDSK_DEVCTL_POLL, + HDSK_DEVCTL_GET_STATUS, + HDSK_DEVCTL_STOP, + HDSK_DEVCTL_GET_PROGRESS, +}; + +struct hdskStat{ + u32 free; + u32 total; +}; diff --git a/hdsk/hdsk.c b/hdsk/hdsk.c new file mode 100644 index 0000000..88b8643 --- /dev/null +++ b/hdsk/hdsk.c @@ -0,0 +1,827 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "apa-opt.h" +#include "libapa.h" +#include "hdsk-devctl.h" +#include "hdsk.h" +#include "sim.h" +#include "misc.h" +#ifdef HDSK_SUPPORT_HDLFS +#include "hdl.h" +#endif + +IRX_ID("hdsk", APA_MODVER_MAJOR, APA_MODVER_MINOR); + +static apa_device_t HddInfo[2]={ + {0, 0, 0, 3}, + {0, 0, 0, 3}, +}; + +static int hdskEventFlagID; //0x000276c8 +static int hdskThreadID; //0x000276cc +static int hdskStopFlag; +static int hdskStatus; +static u32 hdskProgress; + +u8 IOBuffer[IOBUFFER_SIZE_SECTORS * 512]; + +#define HDSK_MIN_PART_SIZE 0x00040000 + +static int hdskRemoveTmp(int device) +{ + apa_cache_t *clink, *clinkSub; + char partition[APA_IDMAX]; + u32 start; + int result, sub; + + clink = apaCacheGetHeader(device, 0, APA_IO_MODE_READ, &result); + memset(partition, 0, sizeof(partition)); + strcpy(partition, "_tmp"); + + while(clink != NULL) + { + if(!(clink->header->flags & APA_FLAG_SUB)) + { + if(!memcmp(clink->header->id, partition, sizeof(clink->header->id))) + break; + } + + clink = apaGetNextHeader(clink, &result); + } + + if(result == 0 && clink != NULL) + { + printf("hdsk: remove _tmp\n"); + sub = clink->header->nsub; + clink->header->nsub = 0; + + clink->flags |= APA_CACHE_FLAG_DIRTY; + start = clink->header->start + 0x2000; + apaCacheFlushAllDirty(device); + + for(--sub; sub != -1; sub--) + { + if((clinkSub = apaCacheGetHeader(device, clink->header->subs[sub].start, APA_IO_MODE_READ, &result)) != NULL) + { + if((result = apaDelete(clinkSub)) != 0) + break; + } + } + + apaDelete(clink); + clink = apaCacheAlloc(); + memset(clink->header, 0, sizeof(apa_header_t)); + ata_device_sector_io(device, clink->header, start, 2, ATA_DIR_WRITE); + apaCacheFree(clink); + } + + return result; +} + +static apa_cache_t *hdskFindEmptyPartition(int device, u32 size) +{ + int result; + apa_cache_t *clink; + + clink = apaCacheGetHeader(device, 0, APA_IO_MODE_READ, &result); + while(clink != NULL) + { + if(clink->header->length != size || clink->header->type != APA_TYPE_FREE) + { + clink = apaGetNextHeader(clink, &result); + if(hdskStopFlag) + { + apaCacheFree(clink); + return NULL; + } + } else + break; + } + + if(clink != NULL) + printf("hdsk: found empty partition at %08lx, size %08lx.\n", clink->header->start, clink->header->length); + + return clink; +} + +static int hdskIsPartitionOfSize(int device, u32 sector, u32 size) +{ + apa_cache_t *clink; + int result, returnValue; + + returnValue = 1; + if((clink = apaCacheGetHeader(device, sector, APA_IO_MODE_READ, &result)) != NULL) + { + if(clink->header->type == APA_TYPE_FREE) + returnValue = 0 < (clink->header->length ^ size); + + apaCacheFree(clink); + } + + return returnValue; +} + +static u32 hdskGetPartitionPrev(int device, u32 size, u32 start) +{ + int result; + apa_cache_t *clink; + + if((clink = apaCacheGetHeader(device, 0, APA_IO_MODE_READ, &result)) != NULL) + { + apaCacheFree(clink); + return(clink->header->prev); + } + + return 0; +} + +static apa_cache_t *hdskFindLastUsedPartition(int device, u32 size, u32 start, int mode) +{ + int result; + apa_cache_t *clink; + u32 previous, DoubleSize, partition; + + clink = NULL; + previous = hdskGetPartitionPrev(device, size, start); + DoubleSize = size * 2; + while(start < previous) + { + if((clink = apaCacheGetHeader(device, previous, APA_IO_MODE_READ, &result)) != NULL) + { + if(clink->header->length == size && clink->header->type != APA_TYPE_FREE) + { + if(mode) + { + if(clink->header->start % DoubleSize == 0) + { + if((partition = clink->header->next) == 0) + break; + } else + partition = clink->header->prev; + + if(!hdskIsPartitionOfSize(device, partition, size)) + break; + }else + break; + } + }else + break; + + previous = clink->header->prev; + apaCacheFree(clink); + + if(hdskStopFlag) + break; + clink = NULL; + } + + if(clink != NULL) + printf("hdsk: found last used partition at %08lx, size %08lx.\n", clink->header->start, clink->header->length); + + return clink; +} + +static int hdskFindPartitionOfSize(int device, apa_header_t *start, u32 MinSize, int mode) +{ + u32 next; + u32 BlockSize; + int result; + apa_cache_t *clink; + + next = start->next; + BlockSize = start->length; + while(BlockSize < MinSize && next != 0) + { + if((clink = apaCacheGetHeader(device, next, APA_IO_MODE_READ, &result)) != NULL) + { + if(clink->header->type == APA_TYPE_FREE) + break; + + next = clink->header->next; + BlockSize += clink->header->length; + apaCacheFree(clink); + } else + break; + } + + if(BlockSize == MinSize) + { + if(mode != 0) + { + if(start->start % (MinSize * 2) != 0) + { + return((hdskIsPartitionOfSize(device, start->prev, MinSize) != 0) ? 0 : 1); + } else { + if(next != 0) + return((hdskIsPartitionOfSize(device, next, MinSize) != 0) ? 0 : 1); + } + } else + return 1; + } + + return 0; +} + +static apa_cache_t *hdskFindLastUsedBlock(int device, u32 MinSize, u32 start, int mode) +{ + int result; + apa_cache_t *last; + u32 NextStart, sector; + + last = NULL; + sector = start; + if((NextStart = hdskGetPartitionPrev(device, MinSize, start)) != 0) + { + while(sector < NextStart) + { + if((last = apaCacheGetHeader(device, NextStart, 0, &result)) != NULL) + { + if(last->header->start % MinSize != 0) + { + if(last->header->length < MinSize && last->header->type != APA_TYPE_FREE) + { + if(hdskFindPartitionOfSize(device, last->header, MinSize, mode) != 0) + break; + } + } + + //0x000006bc + NextStart = last->header->prev; + apaCacheFree(last); + if(hdskStopFlag) + break; + last = NULL; + } else { + return last; + } + } + } + if(last != NULL) + printf("hdsk: found last used block of partitions at %08lx, size %08lx.\n", last->header->start, last->header->length); + + return last; +} + +static int CopyPartition(int device, apa_header_t *dest, apa_header_t *start) +{ + u32 blocks, i; + int result; + + blocks = dest->length / IOBUFFER_SIZE_SECTORS; + printf("hdsk: copy start..."); + + //Copy data, but skip the APA header. + result = ata_device_sector_io(device, IOBuffer, start->start + 2, IOBUFFER_SIZE_SECTORS - 2, ATA_DIR_READ) == 0 ? 0 : -EIO; + + if(result == 0) + { + result = ata_device_sector_io(device, IOBuffer, dest->start + 2, IOBUFFER_SIZE_SECTORS - 2, ATA_DIR_WRITE) == 0 ? 0 : -EIO; + + if(result == 0) + { + hdskProgress += IOBUFFER_SIZE_SECTORS; + + for(i = 1; i < blocks; i++) + { + result = ata_device_sector_io(device, IOBuffer, start->start + i * IOBUFFER_SIZE_SECTORS, IOBUFFER_SIZE_SECTORS, ATA_DIR_READ) == 0 ? 0 : -EIO; + + if(result == 0) + { + result = ata_device_sector_io(device, IOBuffer, dest->start + i * IOBUFFER_SIZE_SECTORS, IOBUFFER_SIZE_SECTORS, ATA_DIR_WRITE) == 0 ? 0 : -EIO; + + if(result == 0) + { + hdskProgress += IOBUFFER_SIZE_SECTORS; + if(hdskStopFlag) + break; + } else { + printf("hdsk: error: write failed at %08lx.\n", dest->start + i * IOBUFFER_SIZE_SECTORS); + break; + } + } else { + printf("hdsk: error: read failed at %08lx.\n", start->start + i * IOBUFFER_SIZE_SECTORS); + break; + } + } + + printf("done\n"); + } + } + + return result; +} + +static int SwapPartition(int device, apa_cache_t *dest, apa_cache_t *start) +{ + apa_cache_t *clink[64]; + int result, i; + u32 StartSector, next, prev; + + StartSector = dest->header->start; + next = dest->header->next; + prev = dest->header->prev; + + printf("hdsk: swap %s partition start...", (start->header->flags & APA_FLAG_SUB) ? "sub" : "main"); + + memcpy(dest->header, start->header, sizeof(apa_header_t)); + dest->header->start = StartSector; + dest->header->next = next; + dest->header->prev = prev; + StartSector = start->header->start; + next = start->header->next; + prev = start->header->prev; + memset(start->header, 0, sizeof(apa_header_t)); + + start->header->magic = APA_MAGIC; + start->header->start = StartSector; + start->header->next = next; + start->header->prev = prev; + start->header->length = dest->header->length; + start->header->modver = _irx_id.v; + start->header->type = dest->header->type; + strcpy(start->header->id, "_tmp"); + + memset(clink, 0, sizeof(clink)); + if(dest->header->flags & APA_FLAG_SUB) + { + if((clink[0] = apaCacheGetHeader(device, dest->header->main, APA_IO_MODE_READ, &result)) != NULL) + { + for(i = 0; i < clink[0]->header->nsub; i++) + { + if(start->header->start == clink[0]->header->subs[i].start) + { + clink[0]->header->subs[i].start = dest->header->start; + clink[0]->flags |= APA_CACHE_FLAG_DIRTY; +#ifdef HDSK_SUPPORT_HDLFS + if(dest->header->type == APA_TYPE_HDLFS) + hdlUpdateGameSliceInfo(device, dest->header->main, i, start->header->start, dest->header->start); +#endif + break; + } + } + + if(i == clink[0]->header->nsub) + { + apaCacheFree(clink[0]); + return -1; + } + } else + return result; + } else { + //0x00000944 + for(i = 0; i < dest->header->nsub; i++) + { + if((clink[i] = apaCacheGetHeader(device, dest->header->subs[i].start, APA_IO_MODE_READ, &result)) == NULL) + { + for(--i; i >= 0; i--) + apaCacheFree(clink[i]); + + return result; + } + } + + for(i = 0; i < dest->header->nsub; i++) + { + clink[i]->header->main = dest->header->start; + clink[i]->flags |= APA_CACHE_FLAG_DIRTY; + } + +#ifdef HDSK_SUPPORT_HDLFS + if(dest->header->type == APA_TYPE_HDLFS) + hdlUpdateGameSliceInfo(device, dest->header->start, 0, start->header->start, dest->header->start); +#endif + } + + dest->flags |= APA_CACHE_FLAG_DIRTY; + start->flags |= APA_CACHE_FLAG_DIRTY; + apaCacheFlushAllDirty(device); + for(i = 0; i < 64; i++) + { + if(clink[i] != NULL) + apaCacheFree(clink[i]); + } + + printf("done\n"); + + return result; +} + +static int MovePartition(int device, apa_cache_t *dest, apa_cache_t *start) +{ + int result; + + printf("hdsk: MovePartition: %08lx to %08lx. sector count = %08lx.\n", start->header->start, dest->header->start, start->header->length); + + if(dest->header->type != APA_TYPE_FREE) + { + printf("hdsk: error: destination is not empty.\n"); + return -1; + } + + if(dest->header->length != start->header->length) + { + printf("hdsk: error: source and destination size are not same.\n"); + return -1; + } + + if((result = hdskRemoveTmp(device)) == 0) + { + if((result = CopyPartition(device, dest->header, start->header)) == 0) + { + if(hdskStopFlag == 0) + SwapPartition(device, dest, start); + } + } + + printf("hdsk: MovePartition: done\n"); + + return result; +} + +static int SplitEmptyPartition(int device, apa_cache_t *partition, u32 length) +{ + apa_cache_t *clink, *empty; + int result; + + printf("hdsk: split empty partition.\n"); + + while(partition->header->length != length) + { + if((empty = apaCacheGetHeader(device, partition->header->next, APA_IO_MODE_READ, &result)) != NULL) + { + partition->header->length /= 2; + clink = apaRemovePartition(device, partition->header->start + partition->header->length, partition->header->next, partition->header->start, partition->header->length); + + partition->header->next = clink->header->start; + partition->flags |= APA_CACHE_FLAG_DIRTY; + + empty->header->prev = clink->header->start; + empty->flags |= APA_CACHE_FLAG_DIRTY; + apaCacheFlushAllDirty(device); + apaCacheFree(clink); + apaCacheFree(empty); + } + } + + return result; +} + +//Move a group of partitions. +static int MovePartitionsBlock(int device, apa_cache_t *dest, apa_cache_t *start) +{ + u32 remaining; + int result, stat; + + remaining = dest->header->length; + printf("hdsk: MovePartitionsBlock: %08lx to %08lx. sector count = %08lx.\n", start->header->start, dest->header->start, dest->header->length); + + while(1) + { + if((result = SplitEmptyPartition(device, dest, start->header->length)) == 0) + { + if((result = MovePartition(device, dest, start)) == 0) + { + remaining -= dest->header->length; + if(hdskStopFlag == 0 && remaining != 0) + { + //Stop if there are no more partitions to copy (either to or from). + if((dest = apaGetNextHeader(dest, &stat)) == NULL || (start = apaGetNextHeader(start, &stat)) == NULL) + break; + } else + break; + } else + break; + } else + break; + } + + apaCacheFree(dest); + apaCacheFree(start); + + printf("hdsk: MovePartitionsBlock: done\n"); + + return result; +} + +static void HdskThread(int device) +{ + u32 PartSize; + apa_cache_t *clink, *clink2; + + hdskProgress = 0; + hdskStatus = 0; + hdskStopFlag = 0; + + if ((hdskStatus = hdskRemoveTmp(device)) >= 0) + { + for (PartSize = HDSK_MIN_PART_SIZE; PartSize < HddInfo[device].partitionMaxSize; PartSize *= 2) + { + while ((hdskStatus = hdskRemoveTmp(device)) >= 0) + { + if ((clink = hdskFindEmptyPartition(device, PartSize)) != NULL) + { + if ((clink2 = hdskFindLastUsedPartition(device, PartSize, clink->header->start, 1)) == NULL) + { + if ((HDSK_MIN_PART_SIZE < PartSize) && (clink2 = hdskFindLastUsedBlock(device, PartSize, clink->header->start, 1)) != NULL) + goto move_partition_block; + + //0x0000110c + if ((clink2 = hdskFindLastUsedPartition(device, PartSize, clink->header->start, 0)) == NULL) + { + //0x00001160 + if (HDSK_MIN_PART_SIZE < PartSize) + { + if ((clink2 = hdskFindLastUsedBlock(device, PartSize, clink->header->start, 0)) != NULL) + goto move_partition_block; + } + + printf("hdsk: there is no copyable partition/partitions block.\n"); + apaCacheFree(clink); + break; + } + else + goto move_partition; + +move_partition_block: + //0x00001190 + if ((hdskStatus = MovePartitionsBlock(device, clink, clink2)) != 0) + goto hdsk_thread_end; + } + else { +move_partition: + //0x00001120 + hdskStatus = MovePartition(device, clink, clink2); + // apaCacheFree(clink); //BUGBUG: SONY original frees clink here, and again below ("unused cache returned"). However, clink must be freed here before the user terminates the defrag operation. + apaCacheFree(clink2); + if (hdskStatus != 0) + { + apaCacheFree(clink); //Move this here. See comment above. + goto hdsk_thread_end; + } + } + + //0x000011d4 + apaCacheFree(clink); + } + else + break; + } + + //0x000011f4 + if (hdskStatus != 0 || hdskStopFlag != 0) + break; + } + } + +hdsk_thread_end: + hdskRemoveTmp(device); + SetEventFlag(hdskEventFlagID, 1); +} + +static int HdskInit(iop_device_t *device) +{ + return 0; +} + +static int HdskUnsupported(void) +{ + return -1; +} + +int BitmapUsed; +u32 TotalCopied; +struct hdskBitmap hdskBitmap[HDSK_BITMAP_SIZE]; + +static int hdskBitmapInit(int device) +{ + struct hdskBitmap *pBitmap; + apa_cache_t *clink; + int result; + + clink = apaCacheGetHeader(device, 0, APA_IO_MODE_READ, &result); + memset(hdskBitmap, 0, sizeof(hdskBitmap)); + + hdskBitmap[0].next = hdskBitmap; + hdskBitmap[0].prev = hdskBitmap; + + for(BitmapUsed = 0; clink != NULL; ) + { + pBitmap = &hdskBitmap[BitmapUsed + 1]; + pBitmap->start = clink->header->start; + pBitmap->length = clink->header->length; + pBitmap->type = clink->header->type; + + apaCacheLink((apa_cache_t*)hdskBitmap[0].prev, (apa_cache_t*)pBitmap); + + BitmapUsed++; + clink = apaGetNextHeader(clink, &result); + } + + return result; +} + +static int hdskGetStat(int device, struct hdskStat *buf, apa_device_t *deviceInfo) +{ + int result, IsValidPartSize; + apa_device_t *pDeviceInfo; + struct hdskBitmap *pPartBitmap, *pSelEmptyPartBM; + u32 PartSize; + + TotalCopied = 0; + + PartSize = HDSK_MIN_PART_SIZE; + if((result = hdskBitmapInit(device)) == 0) + { + hdskSimGetFreeSectors(device, buf, deviceInfo); + + for(pDeviceInfo = &deviceInfo[device]; PartSize < pDeviceInfo->partitionMaxSize; PartSize *= 2) + { + IsValidPartSize = HDSK_MIN_PART_SIZE < PartSize; + + while((pPartBitmap = hdskSimFindEmptyPartition(PartSize)) != NULL) + { + if(((pSelEmptyPartBM = hdskSimFindLastUsedPartition(PartSize, pPartBitmap->start, 1)) == NULL) && + (!IsValidPartSize || (pSelEmptyPartBM = hdskSimFindLastUsedBlock(PartSize, pPartBitmap->start, 1)) == NULL) && + ((pSelEmptyPartBM = hdskSimFindLastUsedPartition(PartSize, pPartBitmap->start, 0)) == NULL) + ) { + if(IsValidPartSize && (pSelEmptyPartBM = hdskSimFindLastUsedBlock(PartSize, pPartBitmap->start, 0)) != NULL) + { + printf("hdsk: found last used block of partitions at %08lx, size %08lx.\n", pSelEmptyPartBM->start, pSelEmptyPartBM->length); + hdskSimMovePartitionsBlock(pPartBitmap, pSelEmptyPartBM); + } else { + printf("hdsk: there is no copyable partition/partitions block.\n"); + break; + } + } else { + printf("hdsk: found last used partition at %08lx, size %08lx.\n", pSelEmptyPartBM->start, pSelEmptyPartBM->length); + hdskSimMovePartition(pPartBitmap, pSelEmptyPartBM); + } + } + } + + //0x00004368 + hdskSimGetFreeSectors(device, buf, deviceInfo); + printf("copy total %08lx sectors\n", TotalCopied); + buf->total = TotalCopied; + } + + return result; +} + +static int HdskDevctl(iop_file_t *fd, const char *name, int cmd, void *arg, unsigned int arglen, void *buf, unsigned int buflen) +{ + u32 bits; + int result; + + if(HddInfo[fd->unit].status!=0) + return -ENXIO; + + switch(cmd) + { + case HDSK_DEVCTL_GET_FREE: + result = apaGetFreeSectors(fd->unit, buf, HddInfo); + break; + case HDSK_DEVCTL_GET_HDD_STAT: + if((result = hdskRemoveTmp(fd->unit)) == 0) + result = hdskGetStat(fd->unit, buf, HddInfo); + break; + case HDSK_DEVCTL_START: + result = StartThread(hdskThreadID, (void*)fd->unit); + break; + case HDSK_DEVCTL_WAIT: + result = WaitEventFlag(hdskEventFlagID, 1, WEF_CLEAR|WEF_OR, &bits); + break; + case HDSK_DEVCTL_POLL: + result = PollEventFlag(hdskEventFlagID, 1, WEF_CLEAR|WEF_OR, &bits); + if(result == KE_EVF_COND) + result = 1; + break; + case HDSK_DEVCTL_GET_STATUS: + result = hdskStatus; + break; + case HDSK_DEVCTL_STOP: + hdskStopFlag = 1; + result = -EINVAL; + break; + case HDSK_DEVCTL_GET_PROGRESS: + result = (int)hdskProgress; + break; + default: + result = -EINVAL; + } + + return result; +} + +static iop_device_ops_t HdskDeviceOps={ + &HdskInit, + (void*)&HdskUnsupported, + (void*)&HdskUnsupported, + (void*)&HdskUnsupported, + (void*)&HdskUnsupported, + (void*)&HdskUnsupported, + (void*)&HdskUnsupported, + (void*)&HdskUnsupported, + (void*)&HdskUnsupported, + (void*)&HdskUnsupported, + (void*)&HdskUnsupported, + (void*)&HdskUnsupported, + (void*)&HdskUnsupported, + (void*)&HdskUnsupported, + (void*)&HdskUnsupported, + (void*)&HdskUnsupported, + (void*)&HdskUnsupported, + (void*)&HdskUnsupported, + (void*)&HdskUnsupported, + (void*)&HdskUnsupported, + (void*)&HdskUnsupported, + (void*)&HdskUnsupported, + (void*)&HdskUnsupported, + &HdskDevctl, +}; + +static iop_device_t HdskDevice={ + "hdsk", + IOP_DT_FSEXT|IOP_DT_FS, + 1, + "HDSK", + &HdskDeviceOps +}; + +int _start(int argc, char *argv[]) +{ + apa_ps2time_t time; + ata_devinfo_t *pDevInfo; + int i; + + if(HdskReadClock(&time) != 0) + { + printf("hdsk: error: could not get date\n"); + return MODULE_NO_RESIDENT_END; + } + + printf("hdsk: %02d:%02d:%02d %02d/%02d/%d\n", time.hour, time.min, time.sec, time.month, time.day, time.year); + + for(i = 0; i < 2; i++) + { + if((pDevInfo = ata_get_devinfo(i)) == NULL) + { + printf("hdsk: error: ata initialization failed.\n"); + return MODULE_NO_RESIDENT_END; + } + + if(pDevInfo->exists && !pDevInfo->has_packet) + { + HddInfo[i].status--; + HddInfo[i].totalLBA = pDevInfo->total_sectors; + + HddInfo[i].partitionMaxSize = apaGetPartitionMax(pDevInfo->total_sectors); + if(HdskUnlockHdd(i) == 0) + { + HddInfo[i].status--; + } + + printf("hdsk: disk%d: 0x%08lx sectors, max 0x%08lx\n", i, HddInfo[i].totalLBA, HddInfo[i].partitionMaxSize); + } + } + + //0x00001440 + apaCacheInit(128); + for(i = 0; i<2; i++) + { + if(HddInfo[i].status < 2) + { + if(apaJournalRestore(i) != 0) + return MODULE_NO_RESIDENT_END; + + if(apaGetFormat(i, &HddInfo[i].format)) + HddInfo[i].status--; + } + } + + if((hdskEventFlagID = HdskCreateEventFlag()) < 0) + return MODULE_NO_RESIDENT_END; + + if((hdskThreadID = HdskCreateThread((void*)&HdskThread, 0x2080)) < 0) + return MODULE_NO_RESIDENT_END; + + DelDrv("hdsk"); + if(AddDrv(&HdskDevice) == 0) + { + printf("hdsk: driver start.\n"); + return MODULE_RESIDENT_END; + } + + return MODULE_NO_RESIDENT_END; +} diff --git a/hdsk/hdsk.h b/hdsk/hdsk.h new file mode 100644 index 0000000..2e161ca --- /dev/null +++ b/hdsk/hdsk.h @@ -0,0 +1,10 @@ +struct hdskBitmap{ + struct hdskBitmap *next; //0x00 + struct hdskBitmap *prev; //0x04 + u32 start; //0x08 + u32 length; //0x0C + u32 type; //0x10 +}; + +#define HDSK_BITMAP_SIZE 0x4001 +#define IOBUFFER_SIZE_SECTORS 256 //Equal to the size of a 128MB partition. Do not alter (partitions must be a multiple of this). diff --git a/hdsk/imports.lst b/hdsk/imports.lst new file mode 100644 index 0000000..605acd3 --- /dev/null +++ b/hdsk/imports.lst @@ -0,0 +1,52 @@ +atad_IMPORTS_start +I_ata_get_devinfo +I_ata_device_sector_io +I_ata_device_flush_cache +I_ata_device_sce_sec_unlock +atad_IMPORTS_end + +cdvdman_IMPORTS_start +I_sceCdReadClock +I_sceCdRI +cdvdman_IMPORTS_end + +ioman_IMPORTS_start +I_AddDrv +I_DelDrv +ioman_IMPORTS_end + +intrman_IMPORTS_start +I_CpuSuspendIntr +I_CpuResumeIntr +intrman_IMPORTS_end + +stdio_IMPORTS_start +I_printf +stdio_IMPORTS_end + +sysclib_IMPORTS_start +I_memset +I_memcpy +I_memcmp +I_strcpy +I_strncpy +I_strncmp +sysclib_IMPORTS_end + +sysmem_IMPORTS_start +I_AllocSysMemory +I_FreeSysMemory +sysmem_IMPORTS_end + +thbase_IMPORTS_start +I_CreateThread +I_StartThread +I_DelayThread +thbase_IMPORTS_end + +thevent_IMPORTS_start +I_CreateEventFlag +I_SetEventFlag +I_WaitEventFlag +I_PollEventFlag +thevent_IMPORTS_end diff --git a/hdsk/irx_imports.h b/hdsk/irx_imports.h new file mode 100644 index 0000000..c8f9f74 --- /dev/null +++ b/hdsk/irx_imports.h @@ -0,0 +1,11 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/hdsk/misc.c b/hdsk/misc.c new file mode 100644 index 0000000..b0ef299 --- /dev/null +++ b/hdsk/misc.c @@ -0,0 +1,99 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "apa-opt.h" +#include "libapa.h" +#include "misc.h" + +int HdskReadClock(apa_ps2time_t *time) +{ + static apa_ps2time_t CurrentTime={0, 7, 6, 5, 4, 3, 2000}; + sceCdCLOCK CdClock; + + if(sceCdReadClock(&CdClock) != 0) + { + CurrentTime.sec = btoi(CdClock.second); + CurrentTime.min = btoi(CdClock.minute); + CurrentTime.hour = btoi(CdClock.hour); + CurrentTime.day = btoi(CdClock.day); + CurrentTime.month = btoi(CdClock.month & 0x7F); //NOTE: the newer CDVDMAN module automatically file off the highest bit of this field. + CurrentTime.year = btoi(CdClock.year) + 2000; + } + memcpy(time, &CurrentTime, sizeof(apa_ps2time_t)); + + return 0; +} + +void *AllocMemory(int size) +{ + int OldState; + void *result; + + CpuSuspendIntr(&OldState); + + if((result = AllocSysMemory(ALLOC_FIRST, size, NULL)) == NULL) + printf("hdsk: error: out of memory\n"); + + CpuResumeIntr(OldState); + + return result; +} + +int HdskRI(unsigned char *id) +{ + u32 stat; + int result; + + memset(id, 0, 32); + stat = 0; + if(sceCdRI(id, &stat) == 0 || stat) + { + printf("hdsk: error: cannot get ilink id\n"); + result = -EIO; + }else{ + result = 0; + } + + return result; +} + +int HdskUnlockHdd(int unit) +{ + unsigned char id[32]; + int result; + + if((result = HdskRI(id)) ==0) + { + result = ata_device_sce_sec_unlock(unit, id); + } + + return result; +} + +int HdskCreateEventFlag(void) +{ + iop_event_t EventFlagData; + + EventFlagData.attr = EA_MULTI; + EventFlagData.bits = 0; + return CreateEventFlag(&EventFlagData); +} + +int HdskCreateThread(void (*function)(void *arg), int StackSize) +{ + iop_thread_t ThreadData; + + ThreadData.attr = TH_C; + ThreadData.thread = function; + ThreadData.priority = 0x7b; + ThreadData.stacksize = StackSize; + return CreateThread(&ThreadData); +} diff --git a/hdsk/misc.h b/hdsk/misc.h new file mode 100644 index 0000000..2b690bf --- /dev/null +++ b/hdsk/misc.h @@ -0,0 +1,6 @@ +int HdskReadClock(apa_ps2time_t *time); +void *AllocMemory(int size); +int HdskRI(unsigned char *id); +int HdskUnlockHdd(int unit); +int HdskCreateEventFlag(void); +int HdskCreateThread(void (*function)(void *arg), int StackSize); diff --git a/hdsk/sim.c b/hdsk/sim.c new file mode 100644 index 0000000..4286a85 --- /dev/null +++ b/hdsk/sim.c @@ -0,0 +1,304 @@ +#include +#include +#include + +#include "apa-opt.h" +#include "libapa.h" +#include "hdsk-devctl.h" +#include "hdsk.h" +#include "sim.h" + +extern u32 TotalCopied; +extern struct hdskBitmap hdskBitmap[]; + +void hdskSimGetFreeSectors(s32 device, struct hdskStat *stat, apa_device_t *deviceinfo) +{ + u32 sectors, partMax; + int i; + + sectors = 0; + stat->free = 0; + for(i = 0; hdskBitmap[i].next != hdskBitmap; sectors+=hdskBitmap[i].length,i++) + { + if(hdskBitmap[i].type == 0) + { + if((0x001FFFFF < hdskBitmap[i].length) || ((stat->free & hdskBitmap[i].length) == 0)) + stat->free += hdskBitmap[i].length; + sectors += hdskBitmap[i].length; + } + } + + for(partMax = deviceinfo[device].partitionMaxSize; 0x0003FFFF < partMax; partMax = deviceinfo[device].partitionMaxSize) + { //As weird as it looks, this was how it was done in the original HDD.IRX. + for( ; 0x0003FFFF < partMax; partMax /= 2) + { + //Non-SONY: Perform 64-bit arithmetic here to avoid overflows when dealing with large disks. + if((sectors % partMax == 0) && ((u64)sectors + partMax < deviceinfo[device].totalLBA)) + { + if((0x001FFFFF < partMax) || (stat->free & partMax) == 0) + stat->free += partMax; + sectors += partMax; + break; + } + } + + if(0x0003FFFF >= partMax) + break; + } + + APA_PRINTF(APA_DRV_NAME": total = %08lx sectors, installable = %08lx sectors.\n", sectors, stat->free); +} + +static void hdskSimClearHeader(struct hdskBitmap *header) +{ + memset(header, 0, sizeof(struct hdskBitmap)); +} + +static struct hdskBitmap *hdskSimDeleteFixPrev(struct hdskBitmap *part) +{ + struct hdskBitmap *prev; + u32 length; + + while(hdskBitmap[0].next != part) + { + prev = part->prev; + if(prev->type == 0) + { + length = prev->length + part->length; + if(prev->start % length != 0) + break; + + if((length & (length - 1)) == 0) + { + prev->length = length; + hdskSimClearHeader((struct hdskBitmap*)apaCacheUnLink((apa_cache_t*)part)); + } else + break; + } else + break; + + part = prev; + } + + return part; +} + +static struct hdskBitmap *hdskSimDeleteFixNext(struct hdskBitmap *part) +{ + struct hdskBitmap *next; + u32 length; + + while(hdskBitmap[0].prev != part) + { + next = part->next; + if(next->type == 0) + { + length = next->length + part->length; + if(part->start % length != 0) + break; + + if((length & (length - 1)) == 0) + { + part->length = length; + part->type = 0; + hdskSimClearHeader((struct hdskBitmap*)apaCacheUnLink((apa_cache_t*)part)); + } else + break; + } else + break; + } + + return part; +} + +static void hdskSimSwapPartition(struct hdskBitmap *start) +{ + u32 OrigStart, OrigLen; + int i; + struct hdskBitmap *part; + + part = hdskBitmap[0].prev; + if(start == part) + { + do{ + hdskSimClearHeader((struct hdskBitmap*)apaCacheUnLink((apa_cache_t*)part)); + part = hdskBitmap[0].prev; + if(part == NULL) + break; + }while(part->type != 0); + } else { + OrigStart = start->start; + OrigLen = start->length; + for(i = 0; i < 2; i++) + start = hdskSimDeleteFixNext(hdskSimDeleteFixPrev(start)); + + if(start->start == OrigStart && start->length == OrigLen) + start->type = 0; + } +} + +void hdskSimMovePartition(struct hdskBitmap *dest, struct hdskBitmap *start) +{ + printf("hdsk: MovePartition: %08lx to %08lx. sector count = %08lx.\n", start->start, dest->start, start->length); + + TotalCopied += start->length; + printf("hdsk: swap partition start..."); + + dest->type = start->type; + hdskSimSwapPartition(start); + + printf("done\n"); + printf("hdsk: MovePartition: done\n"); +} + +struct hdskBitmap *hdskSimFindEmptyPartition(u32 size) +{ + struct hdskBitmap *part; + + for(part = hdskBitmap; part->next != hdskBitmap; part = part->next) + { + if(part->length == size && part->type == 0) + { + printf("hdsk: sim: found empty partition at %08lx, size %08lx.\n", part->start, part->length); + return part; + } + } + + return NULL; +} + +static int hdskCheckIfPrevPartEmpty(struct hdskBitmap *part, u32 size) +{ + return((part->prev->length == size) && (part->prev->type == 0)); +} + +static int hdskCheckIfNextPartEmpty(struct hdskBitmap *part, u32 size) +{ + return((part->next->length == size) && (part->next->type == 0)); +} + +struct hdskBitmap *hdskSimFindLastUsedPartition(u32 size, u32 start, int mode) +{ + u32 SliceSize; + struct hdskBitmap *part; + + SliceSize = size * 2; + for(part = hdskBitmap[0].prev; start < part->start && part != hdskBitmap; part = part->prev) + { + if(part->length == size && part->type != 0) + { + if(mode != 0) + { + if(part->start % SliceSize != 0) + { + if(hdskCheckIfPrevPartEmpty(part, size) != 0) + return part; + } else { + if(part == hdskBitmap[0].prev || hdskCheckIfNextPartEmpty(part, size) != 0) + return part; + } + } else + return part; + } + } + + return NULL; +} + +static int hdskCheckIsLastPart(struct hdskBitmap *part, u32 size, int mode) +{ + struct hdskBitmap *CurrPart; + u32 PartSize; + + for(CurrPart = part,PartSize = part->length; (PartSize < size) && (CurrPart != hdskBitmap); PartSize += CurrPart->length) + { + CurrPart = CurrPart->next; + if(CurrPart->type == 0) + break; + } + + if(PartSize == size) + { + if(mode != 0) + { + if(part->start % (size * 2) != 0) + { + return(hdskCheckIfPrevPartEmpty(part, size) == 0 ? 0 : 1); + } else { + return(CurrPart != hdskBitmap[0].prev ? (hdskCheckIfNextPartEmpty(CurrPart, size) != 0) : 1); + } + } else + return 1; + } + + return 0; +} + +struct hdskBitmap *hdskSimFindLastUsedBlock(u32 size, u32 start, int mode) +{ + struct hdskBitmap *part; + + for(part = hdskBitmap[0].prev; start < part->start && part != hdskBitmap; part = part->prev) + { + if((part->start % size == 0) && (part->length < size) && (part->type != 0)) + { + if(hdskCheckIsLastPart(part, size, mode) != 0) + return part; + } + } + + return NULL; +} + +static struct hdskBitmap *hdskBitmapAlloc(void) +{ + struct hdskBitmap *part; + unsigned int i; + + for(part = &hdskBitmap[1],i = 1; i < HDSK_BITMAP_SIZE; i++,part++) + { + if(part->length == 0) + return part; + } + + return NULL; +} + +static void hdskSimSplitEmptyPartition(struct hdskBitmap *part, u32 length) +{ + struct hdskBitmap *end; + + printf("hdsk: split empty partition.\n"); + while(part->length != length) + { + end = hdskBitmapAlloc(); + part->length /= 2; + end->start = part->start + part->length; + end->type = 0; + end->length = part->length; + apaCacheLink((apa_cache_t*)part, (apa_cache_t*)end); + } +} + +void hdskSimMovePartitionsBlock(struct hdskBitmap *dest, struct hdskBitmap *src) +{ + u32 size; + + size = dest->length; + printf("hdsk: MovePartitionsBlock: %08lx to %08lx. sector count = %08lx.\n", src->start, dest->start, dest->length); + do{ + hdskSimSplitEmptyPartition(dest, src->length); + if(src->next == hdskBitmap) + break; + + hdskSimMovePartition(dest, src); + dest = dest->next; + size -= dest->length; + if(size == 0) + break; + + src = src->next; + }while(dest != hdskBitmap); + + printf("hdsk: MovePartitionsBlock: done\n"); +} diff --git a/hdsk/sim.h b/hdsk/sim.h new file mode 100644 index 0000000..4f7e3a7 --- /dev/null +++ b/hdsk/sim.h @@ -0,0 +1,6 @@ +void hdskSimGetFreeSectors(s32 device, struct hdskStat *stat, apa_device_t *deviceinfo); +void hdskSimMovePartition(struct hdskBitmap *dest, struct hdskBitmap *start); +struct hdskBitmap *hdskSimFindEmptyPartition(u32 size); +struct hdskBitmap *hdskSimFindLastUsedPartition(u32 size, u32 start, int mode); +struct hdskBitmap *hdskSimFindLastUsedBlock(u32 size, u32 start, int mode); +void hdskSimMovePartitionsBlock(struct hdskBitmap *dest, struct hdskBitmap *src); diff --git a/hdst.c b/hdst.c new file mode 100644 index 0000000..a946683 --- /dev/null +++ b/hdst.c @@ -0,0 +1,267 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hdst.h" + +int SetHDSTIOBufferSize(int sectors) +{ + return fileXioDevctl("hdst:", HDST_DEVCTL_SET_IO_BUFFER_SIZE, §ors, sizeof(sectors), NULL, 0); +} + +struct AtaDeviceData{ + u16 IdentificationData[256]; + ata_devinfo_t PS2AtadData; + int SMARTStatus; + u32 NumSectors; + char model[42], serial[22], FWVersion[10]; +}; +static struct AtaDeviceData AtadDeviceData[NUM_SUPPORTED_DEVICES]; + +static void TrimWhitespacing(char *string, int maxlen) +{ + int i, CharsStart; + //Locate the start of the string; + for(i=0; i>8)&0x7){ + selected_xfer_type=xfer_types[1]; + + if(((DeviceIdentificationInfo[unit][63]>>8)&0x7)&1) selected_xfer_mode=0; + else if(((DeviceIdentificationInfo[unit][63]>>8)&0x7)&2) selected_xfer_mode=1; + else if(((DeviceIdentificationInfo[unit][63]>>8)&0x7)&4) selected_xfer_mode=2; + } + printf("\n"); + + printf("\t\tUDMA: "); + if((DeviceIdentificationInfo[unit][53]&2)!=0){ + if((DeviceIdentificationInfo[unit][88]&0x3F)&0x01) printf("0 "); + if((DeviceIdentificationInfo[unit][88]&0x3F)&0x02) printf("1 "); + if((DeviceIdentificationInfo[unit][88]&0x3F)&0x04) printf("2 "); + if((DeviceIdentificationInfo[unit][88]&0x3F)&0x08) printf("3 "); + if((DeviceIdentificationInfo[unit][88]&0x3F)&0x10) printf("4 "); + if((DeviceIdentificationInfo[unit][88]&0x3F)&0x20) printf("5 "); + printf("\n"); + + /* If any of the UDMA modes were selected: */ + if((DeviceIdentificationInfo[unit][88]>>8)&0x3F){ + selected_xfer_type=xfer_types[2]; + + if(((DeviceIdentificationInfo[unit][88]>>8)&0x3F)&0x01) selected_xfer_mode=0; + else if(((DeviceIdentificationInfo[unit][88]>>8)&0x3F)&0x02) selected_xfer_mode=1; + else if(((DeviceIdentificationInfo[unit][88]>>8)&0x3F)&0x04) selected_xfer_mode=2; + else if(((DeviceIdentificationInfo[unit][88]>>8)&0x3F)&0x08) selected_xfer_mode=3; + else if(((DeviceIdentificationInfo[unit][88]>>8)&0x3F)&0x10) selected_xfer_mode=4; + else if(((DeviceIdentificationInfo[unit][88]>>8)&0x3F)&0x20) selected_xfer_mode=5; + } + } + else printf("Not available.\n"); + + printf("\tCurrent transfer mode: %s mode %u\n", selected_xfer_type, selected_xfer_mode); + + if(DeviceIdentificationInfo[unit][82]==DeviceIdentificationInfo[unit][83]==0x0000 || DeviceIdentificationInfo[unit][82]==DeviceIdentificationInfo[unit][83]==0xFFFF){ + printf("\tCommand set notification is NOT enabled or NOT supported.\n"); + } + else{ + printf("\tSMART is "); + if(!(DeviceIdentificationInfo[unit][82]&1)){ + printf("NOT "); + } + else printf("supported.\n"); + } + + if(DeviceIdentificationInfo[unit][85]==DeviceIdentificationInfo[unit][86]==DeviceIdentificationInfo[unit][87]==0x0000 || DeviceIdentificationInfo[unit][85]==DeviceIdentificationInfo[unit][86]==DeviceIdentificationInfo[unit][87]==0xFFFF){ + printf("\tCommand set enabled notification is NOT enabled or NOT supported.\n"); + } + else{ + printf("\tSMART is "); + if(DeviceIdentificationInfo[unit][85]&1){ + printf("ENABLED.\n"); + } + else printf("DISABLED.\n"); + } + + + printf("\tThis drive "); + if(DeviceIdentificationInfo[unit][ATA_ID_SECURITY_STATUS]&1){ + printf("supports"); + } + else printf("does NOT support"); + printf(" security features.\n"); + + printf("\tSecurity features are "); + if(DeviceIdentificationInfo[unit][ATA_ID_SECURITY_STATUS]&ATA_F_SEC_ENABLED){ + printf("enabled\n"); + } + else printf("disabled\n"); + + printf("\tDrive is "); + if(!(DeviceIdentificationInfo[unit][ATA_ID_SECURITY_STATUS]&ATA_F_SEC_LOCKED)){ + printf("not "); + } + printf("locked\n"); + +/* printf("\tDrive is "); + if(!ata_device_is_sce(unit)){ + printf("not "); + } + printf("a Sony Playstation 2 HDD unit.\n"); */ +} +#endif diff --git a/hdst.h b/hdst.h new file mode 100644 index 0000000..7189629 --- /dev/null +++ b/hdst.h @@ -0,0 +1,26 @@ +#include "hdst/hdst.h" + +typedef struct _ata_devinfo { + s32 exists; /* Was successfully probed. */ + s32 has_packet; /* Supports the PACKET command set. */ + u32 total_sectors; /* Total number of user sectors. */ + u32 security_status;/* Word 0x100 of the identify info. */ +} ata_devinfo_t; + +#define NUM_SUPPORTED_DEVICES 2 + +#ifndef BSWAP16 +#define BSWAP16(x) ((x<<8 | x>>8)) +#endif + +int SetHDSTIOBufferSize(int sectors); +int InitATADevice(int unit); +const char *GetATADeviceModel(int unit); +const char *GetATADeviceSerial(int unit); +const char *GetATADeviceFWVersion(int unit); +u32 GetATADeviceCapacity(int unit); +int GetATADeviceSMARTStatus(int unit); +int PatchSector(const char *device, u32 lba); +int IsATADeviceInstalled(int unit); + +//void ShowHDDInfo(int unit); diff --git a/hdst/Makefile b/hdst/Makefile new file mode 100644 index 0000000..d65e1cb --- /dev/null +++ b/hdst/Makefile @@ -0,0 +1,14 @@ + +IOP_BIN = hdst.irx +IOP_OBJS = main.o imports.o + +IOP_CFLAGS += -Wall -fno-builtin +IOP_LDFLAGS += -s + +all: $(IOP_BIN) + +clean: + rm -f $(IOP_BIN) $(IOP_OBJS) + +include $(PS2SDK)/Defs.make +include ../common/Rules.make diff --git a/hdst/hdst.h b/hdst/hdst.h new file mode 100644 index 0000000..a82c903 --- /dev/null +++ b/hdst/hdst.h @@ -0,0 +1,14 @@ +typedef struct HdstSectorMiscIOParams{ + u32 lba; + u32 sectors; +} HdstSectorIOParams_t; + +enum HDST_DEVCTL_CMDS{ + HDST_DEVCTL_DEVICE_PS2_DETECT = 0x00, //Output = ata_devinfo_t + HDST_DEVCTL_DEVICE_IDENTIFY, //Output = 512 byte area. + HDST_DEVCTL_DEVICE_VERIFY_SECTORS, //Input = HdckSectorIOParams_t. Output = 0 if no error, >0 for sector errors (sector offset+1), other codes for other errors. + HDST_DEVCTL_DEVICE_ERASE_SECTORS, //Input = HdckSectorIOParams_t. Output = 0 if no error, !=0 for errors. + HDST_DEVCTL_DEVICE_SMART_STATUS, //Output = 0 if no error, !=0 for errors (1 = SMART threshold exceeded error). + HDST_DEVCTL_DEVICE_FLUSH_CACHE, //Output = 0 if no error, !=0 for errors. + HDST_DEVCTL_SET_IO_BUFFER_SIZE = 0x80, //Input = buffer size in sectors (int). +}; diff --git a/hdst/imports.lst b/hdst/imports.lst new file mode 100644 index 0000000..442e3f9 --- /dev/null +++ b/hdst/imports.lst @@ -0,0 +1,38 @@ +atad_IMPORTS_start +I_ata_get_devinfo +I_ata_io_start +I_ata_io_finish +I_ata_get_error +I_ata_device_sector_io +I_ata_device_smart_get_status +I_ata_device_flush_cache +I_ata_device_sce_sec_unlock +atad_IMPORTS_end + +cdvdman_IMPORTS_start +I_sceCdRI +cdvdman_IMPORTS_end + +ioman_IMPORTS_start +I_AddDrv +I_DelDrv +ioman_IMPORTS_end + +stdio_IMPORTS_start +I_printf +stdio_IMPORTS_end + +sysclib_IMPORTS_start +I_memcpy +I_memset +sysclib_IMPORTS_end + +intrman_IMPORTS_start +I_CpuSuspendIntr +I_CpuResumeIntr +intrman_IMPORTS_end + +sysmem_IMPORTS_start +I_AllocSysMemory +I_FreeSysMemory +sysmem_IMPORTS_end diff --git a/hdst/irx_imports.h b/hdst/irx_imports.h new file mode 100644 index 0000000..3694add --- /dev/null +++ b/hdst/irx_imports.h @@ -0,0 +1,21 @@ +/* + * irx_imports.h - Defines all IRX imports. + */ + +#ifndef IOP_IRX_IMPORTS_H +#define IOP_IRX_IMPORTS_H + +#include + +/* Please keep these in alphabetical order! */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif /* IOP_IRX_IMPORTS_H */ diff --git a/hdst/main.c b/hdst/main.c new file mode 100644 index 0000000..b42d7c3 --- /dev/null +++ b/hdst/main.c @@ -0,0 +1,288 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include "hdst.h" + +#define MODNAME "HDD_status" +IRX_ID(MODNAME, 1, 2); + +//#define DEBUG + +#ifdef DEBUG +#define DEBUG_PRINTF(args...) printf(args) +#else +#define DEBUG_PRINTF(args...) +#endif + +#define MAX_SUPPORTED_UNITS 2 +#define HDD_SECTOR_SIZE 512 +#define DEFAULT_IO_BUFFER_SIZE 16 + +static ata_devinfo_t *AtadDevInfo[MAX_SUPPORTED_UNITS]; + +static void *IOBuffer=NULL; +static unsigned int IOBufferSize; //In sectors. + +static int SetIOBufferSize(int size) +{ + int OldState, result; + + CpuSuspendIntr(&OldState); + if(IOBuffer!=NULL) FreeSysMemory(IOBuffer); + IOBuffer=AllocSysMemory(ALLOC_FIRST, size*HDD_SECTOR_SIZE, NULL); + CpuResumeIntr(OldState); + + if(IOBuffer!=NULL) + { + result=0; + IOBufferSize=size; + } + else{ + result=-ENOMEM; + IOBufferSize=0; + } + + return result; +} + +int sceCdRI(unsigned char *id, int *stat); + +static int hdst_init(iop_device_t *fd){ + unsigned int i; + int result; + unsigned char iLinkID[32]; + + sceCdRI(iLinkID, &result); + + for(i=0; iexists) + ata_device_sce_sec_unlock(i, iLinkID); + } + + SetIOBufferSize(DEFAULT_IO_BUFFER_SIZE); + + return 0; +} + +static int hdst_deinit(iop_device_t *fd){ + return 0; +} + +static int hdst_PS2DetectDevice(int device, ata_devinfo_t *info) +{ + ata_devinfo_t *data; + int result; + + if((data=ata_get_devinfo(device))!=NULL) + { + memcpy(info, data, sizeof(ata_devinfo_t)); + result=0; + } + else result=-ENODEV; + + return result; +} + +static int hdst_EraseSectors(int device, u32 lba, u32 sectors) +{ + u32 nsectors; + int result; + + result=0; + memset(IOBuffer, 0, (sectors>IOBufferSize?IOBufferSize:sectors)*HDD_SECTOR_SIZE); + while(sectors>0) + { + nsectors=sectors>IOBufferSize?IOBufferSize:sectors; + + if((result=ata_device_sector_io(device, IOBuffer, lba, nsectors, ATA_DIR_WRITE))!=0) + break; + + lba+=nsectors; + sectors-=nsectors; + } + + return result; +} + +static int ata_device_identify(int device, void *info) +{ + int res; + + if(!(res = ata_io_start(info, 1, 0, 0, 0, 0, 0, (device << 4) & 0xffff, ATA_C_IDENTIFY_DEVICE))) res = ata_io_finish(); + + return res; +} + +static int ata_device_read_verify(int device, u32 lba, u32 sectors) +{ + USE_ATA_REGS; + int res = 0; + u16 sector, lcyl, hcyl, command, select; + u32 StartLBA, len; + + StartLBA = lba; + while (sectors > 0) + { + /* Variable lba is only 32 bits so no change for lcyl and hcyl. */ + lcyl = (lba >> 8) & 0xff; + hcyl = (lba >> 16) & 0xff; + + if (AtadDevInfo[device]->lba48) + { + /* Setup for 48-bit LBA. */ + len = (sectors > 65536) ? 65536 : sectors; + + /* Combine bits 24-31 and bits 0-7 of lba into sector. */ + sector = ((lba >> 16) & 0xff00) | (lba & 0xff); + /* 0x40 enables LBA. */ + select = ((device << 4) | 0x40) & 0xffff; + command = ATA_C_READ_VERIFY_SECTOR_EXT; + } else { + /* Setup for 28-bit LBA. */ + len = (sectors > 256) ? 256 : sectors; + sector = lba & 0xff; + /* 0x40 enables LBA. */ + select = ((device << 4) | ((lba >> 24) & 0xf) | 0x40) & 0xffff; + command = ATA_C_READ_VERIFY_SECTOR; + } + + if ((res = ata_io_start(NULL, 0, 0, (u16)len, sector, lcyl, hcyl, select, command))==0) + { + if((res = ata_io_finish()) != 0) + { + if(res==ATA_RES_ERR_IO) + { + if(AtadDevInfo[device]->lba48) + { + lba=(ata_hwport->r_sector&0xFF) | ((u32)ata_hwport->r_lcyl&0xFF)<<8 | ((u32)ata_hwport->r_hcyl&0xFF)<<16; + ata_hwport->r_control=0x80; //Toggle the HOB bit. + lba|=((u32)ata_hwport->r_sector&0xFF)<<24; + ata_hwport->r_control=0x00; + } else + lba=(ata_hwport->r_sector&0xFF) | ((u32)ata_hwport->r_lcyl&0xFF)<<8 | ((u32)ata_hwport->r_hcyl&0xFF)<<16 | ((u32)ata_hwport->r_select&0xF)<<24; + + printf("atad: READ VERIFY LBA 0x%lx, ERR: 0x%02x\n", lba, ata_get_error()); + + //Check hardware status and obtain the bad sector number. + if(ata_get_error() & ATA_ERR_ECC) + res=lba-StartLBA+1; + } + + break; + } + } + else + break; + + lba += len; + sectors -= len; + } + + return res; +} + +static int hdst_devctl(iop_file_t *fd, const char *path, int cmd, void *arg, unsigned int arglen, void *buf, unsigned int buflen) +{ + int result; + + if(fd->unitunit]->exists) + { + switch(cmd) + { + case HDST_DEVCTL_DEVICE_PS2_DETECT: + result=hdst_PS2DetectDevice(fd->unit, buf); + break; + case HDST_DEVCTL_DEVICE_IDENTIFY: + result=ata_device_identify(fd->unit, buf); + break; + case HDST_DEVCTL_DEVICE_VERIFY_SECTORS: + result=ata_device_read_verify(fd->unit, ((HdstSectorIOParams_t*)arg)->lba, ((HdstSectorIOParams_t*)arg)->sectors); + break; + case HDST_DEVCTL_DEVICE_ERASE_SECTORS: + result=hdst_EraseSectors(fd->unit, ((HdstSectorIOParams_t*)arg)->lba, ((HdstSectorIOParams_t*)arg)->sectors); + break; + case HDST_DEVCTL_DEVICE_SMART_STATUS: + result=ata_device_smart_get_status(fd->unit); + break; + case HDST_DEVCTL_DEVICE_FLUSH_CACHE: + result=ata_device_flush_cache(fd->unit); + case HDST_DEVCTL_SET_IO_BUFFER_SIZE: + result=SetIOBufferSize(*(int*)arg); + break; + default: + result=-EINVAL; + } + } + else result=-ENODEV; + + return result; +} + +static int hdst_NulldevFunction(void) +{ + return -EIO; +} + +/* Device driver I/O functions */ +static iop_device_ops_t hdst_functarray={ + &hdst_init, /* INIT */ + &hdst_deinit, /* DEINIT */ + (void*)&hdst_NulldevFunction, /* FORMAT */ + (void*)&hdst_NulldevFunction, /* OPEN */ + (void*)&hdst_NulldevFunction, /* CLOSE */ + (void*)&hdst_NulldevFunction, /* READ */ + (void*)&hdst_NulldevFunction, /* WRITE */ + (void*)&hdst_NulldevFunction, /* LSEEK */ + (void*)&hdst_NulldevFunction, /* IOCTL */ + (void*)&hdst_NulldevFunction, /* REMOVE */ + (void*)&hdst_NulldevFunction, /* MKDIR */ + (void*)&hdst_NulldevFunction, /* RMDIR */ + (void*)&hdst_NulldevFunction, /* DOPEN */ + (void*)&hdst_NulldevFunction, /* DCLOSE */ + (void*)&hdst_NulldevFunction, /* DREAD */ + (void*)&hdst_NulldevFunction, /* GETSTAT */ + (void*)&hdst_NulldevFunction, /* CHSTAT */ + (void*)&hdst_NulldevFunction, /* RENAME */ + (void*)&hdst_NulldevFunction, /* CHDIR */ + (void*)&hdst_NulldevFunction, /* SYNC */ + (void*)&hdst_NulldevFunction, /* MOUNT */ + (void*)&hdst_NulldevFunction, /* UMOUNT */ + (void*)&hdst_NulldevFunction, /* LSEEK64 */ + &hdst_devctl, /* DEVCTL */ + (void*)&hdst_NulldevFunction, /* SYMLINK */ + (void*)&hdst_NulldevFunction, /* READLINK */ + (void*)&hdst_NulldevFunction /* IOCTL2 */ +}; + +static const char hdst_dev_name[]="hdst"; + +static iop_device_t hdst_dev={ + hdst_dev_name, /* Device name */ + IOP_DT_FS|IOP_DT_FSEXT, /* Device type flag */ + 1, /* Version */ + "HDD status driver", /* Description. */ + &hdst_functarray /* Device driver function pointer array */ +}; + +/* Entry point */ +int _start(int argc, char **argv) +{ + DelDrv(hdst_dev_name); + AddDrv(&hdst_dev); + + return MODULE_RESIDENT_END; +} diff --git a/iop.c b/iop.c new file mode 100644 index 0000000..8a53246 --- /dev/null +++ b/iop.c @@ -0,0 +1,181 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "main.h" +#include "iop.h" +#ifndef FSCK +#include "hdst.h" +#endif + +extern unsigned char SIO2MAN_irx[]; +extern unsigned int size_SIO2MAN_irx; + +extern unsigned char PADMAN_irx[]; +extern unsigned int size_PADMAN_irx; + +#ifndef FSCK +extern unsigned char MCMAN_irx[]; +extern unsigned int size_MCMAN_irx; + +extern unsigned char USBD_irx[]; +extern unsigned int size_USBD_irx; + +extern unsigned char USBHDFSD_irx[]; +extern unsigned int size_USBHDFSD_irx; +#endif + +extern unsigned char DEV9_irx[]; +extern unsigned int size_DEV9_irx; + +extern unsigned char ATAD_irx[]; +extern unsigned int size_ATAD_irx; + +extern unsigned char HDD_irx[]; +extern unsigned int size_HDD_irx; + +extern unsigned char PFS_irx[]; +extern unsigned int size_PFS_irx; + +#ifndef FSCK +extern unsigned char HDLOG_irx[]; +extern unsigned int size_HDLOG_irx; + +extern unsigned char HDST_irx[]; +extern unsigned int size_HDST_irx; + +extern unsigned char HDCK_irx[]; +extern unsigned int size_HDCK_irx; + +extern unsigned char HDSK_irx[]; +extern unsigned int size_HDSK_irx; + +extern unsigned char FSSK_irx[]; +extern unsigned int size_FSSK_irx; +#endif + +extern unsigned char FSCK_irx[]; +extern unsigned int size_FSCK_irx; + +extern unsigned char IOMANX_irx[]; +extern unsigned int size_IOMANX_irx; + +extern unsigned char FILEXIO_irx[]; +extern unsigned int size_FILEXIO_irx; + +#define SYSTEM_INIT_THREAD_STACK_SIZE 0x1000 + +struct SystemInitParams{ + int InitCompleteSema; + unsigned int flags; +}; + +static void SystemInitThread(struct SystemInitParams *SystemInitParams) +{ + static const char HDD_args[]="-o\0""3\0""-n\0""16"; + static const char PFS_args[]="-o\0""2\0""-n\0""12"; + int i; + + SifExecModuleBuffer(ATAD_irx, size_ATAD_irx, 0, NULL, NULL); + if(SystemInitParams->flags & IOP_MOD_HDD) + SifExecModuleBuffer(HDD_irx, size_HDD_irx, sizeof(HDD_args), HDD_args, NULL); + if(SystemInitParams->flags & IOP_MOD_PFS) + SifExecModuleBuffer(PFS_irx, size_PFS_irx, sizeof(PFS_args), PFS_args, NULL); +#ifndef FSCK +#ifdef LOG_MESSAGES + if(SystemInitParams->flags & IOP_MOD_HDLOG) + SifExecModuleBuffer(HDLOG_irx, size_HDLOG_irx, 0, NULL, NULL); +#endif + + if(SystemInitParams->flags & IOP_MOD_HDST) + { + SifExecModuleBuffer(HDST_irx, size_HDST_irx, 0, NULL, NULL); + for(i=0; iflags & IOP_MOD_HDCK) + SifExecModuleBuffer(HDCK_irx, size_HDSK_irx, 0, NULL, NULL); + if(SystemInitParams->flags & IOP_MOD_HDSK) + SifExecModuleBuffer(HDSK_irx, size_HDSK_irx, 0, NULL, NULL); + if(SystemInitParams->flags & IOP_MOD_FSSK) + SifExecModuleBuffer(FSSK_irx, size_FSSK_irx, 0, NULL, NULL); +#endif + if(SystemInitParams->flags & IOP_MOD_FSCK) + SifExecModuleBuffer(FSCK_irx, size_FSCK_irx, 0, NULL, NULL); + + SifExitIopHeap(); + SifLoadFileExit(); + + SignalSema(SystemInitParams->InitCompleteSema); + ExitDeleteThread(); +} + +int IopInitStart(unsigned int flags) +{ + ee_sema_t sema; + static struct SystemInitParams InitThreadParams; + static unsigned char SysInitThreadStack[SYSTEM_INIT_THREAD_STACK_SIZE] __attribute__((aligned(16))); + + if(!(flags & IOP_REBOOT)) + { + SifInitRpc(0); + } else { + PadDeinitPads(); + } + + while(!SifIopReset(NULL, 0)){}; + + //Do something useful while the IOP resets. + sema.init_count=0; + sema.max_count=1; + sema.attr=sema.option=0; + InitThreadParams.InitCompleteSema=CreateSema(&sema); + InitThreadParams.flags = flags; + + while(!SifIopSync()){}; + + SifInitRpc(0); + SifInitIopHeap(); + SifLoadFileInit(); + + sbv_patch_enable_lmb(); + + SifExecModuleBuffer(IOMANX_irx, size_IOMANX_irx, 0, NULL, NULL); + SifExecModuleBuffer(FILEXIO_irx, size_FILEXIO_irx, 0, NULL, NULL); + + fileXioInit(); + + SifExecModuleBuffer(DEV9_irx, size_DEV9_irx, 0, NULL, NULL); + + SifExecModuleBuffer(SIO2MAN_irx, size_SIO2MAN_irx, 0, NULL, NULL); + SifExecModuleBuffer(PADMAN_irx, size_PADMAN_irx, 0, NULL, NULL); + +#ifndef FSCK + SifExecModuleBuffer(MCMAN_irx, size_MCMAN_irx, 0, NULL, NULL); + SifExecModuleBuffer(USBD_irx, size_USBD_irx, 0, NULL, NULL); + SifExecModuleBuffer(USBHDFSD_irx, size_USBHDFSD_irx, 0, NULL, NULL); +#endif + + SysCreateThread(SystemInitThread, SysInitThreadStack, SYSTEM_INIT_THREAD_STACK_SIZE, &InitThreadParams, 0x2); + + PadInitPads(); + + return InitThreadParams.InitCompleteSema; +} + +void IopDeinit(void) +{ + PadDeinitPads(); + + fileXioExit(); + SifExitRpc(); +} diff --git a/iop.h b/iop.h new file mode 100644 index 0000000..fd868b0 --- /dev/null +++ b/iop.h @@ -0,0 +1,35 @@ +//Module bits +#define IOP_MOD_HDD 0x01 +#define IOP_MOD_PFS 0x02 +#define IOP_MOD_HDST 0x04 +#define IOP_MOD_HDCK 0x08 +#define IOP_MOD_FSCK 0x10 +#define IOP_MOD_HDSK 0x20 +#define IOP_MOD_FSSK 0x40 +#define IOP_REBOOT 0x80 +#define IOP_MOD_HDLOG 0x100 + +//Module set bits +#define IOP_MODSET_INIT (IOP_MOD_HDD | IOP_MOD_PFS | IOP_MOD_HDST) +#define IOP_MODSET_SA_INIT (IOP_MOD_HDD | IOP_MOD_PFS) //For the standalone FSCK tool +#define IOP_MODSET_MAIN (IOP_REBOOT | IOP_MOD_HDD | IOP_MOD_PFS | IOP_MOD_HDST) +#define IOP_MODSET_SA_MAIN (IOP_REBOOT | IOP_MOD_HDD | IOP_MOD_PFS) //For the standalone FSCK tool + +#define IOP_MODSET_HDCK (IOP_REBOOT | IOP_MOD_HDCK | IOP_MOD_HDLOG) +#define IOP_MODSET_FSCK (IOP_REBOOT | IOP_MOD_FSCK | IOP_MOD_HDD | IOP_MOD_HDLOG) +#define IOP_MODSET_SA_FSCK (IOP_REBOOT | IOP_MOD_FSCK | IOP_MOD_HDD) //For the standalone FSCK tool +#define IOP_MODSET_HDSK (IOP_REBOOT | IOP_MOD_HDSK | IOP_MOD_HDLOG) +#define IOP_MODSET_FSSK (IOP_REBOOT | IOP_MOD_FSSK | IOP_MOD_HDD | IOP_MOD_HDLOG) + +#ifndef LOG_MESSAGES +#define FSCK_VERBOSITY 0 +#define FSSK_VERBOSITY 0 +#else +#define FSCK_VERBOSITY 2 +#define FSSK_VERBOSITY 2 +#endif + +int IopInitStart(unsigned int flags); +void IopDeinit(void); +void IopStartLog(const char *log); +void IopStopLog(void); diff --git a/lang.c b/lang.c new file mode 100644 index 0000000..ac69ec5 --- /dev/null +++ b/lang.c @@ -0,0 +1,119 @@ +#ifdef FSCK + +static const char *DefaultLanguageStringTable[SYS_UI_MSG_COUNT]={ + "No HardDisk Drive (HDD) unit was detected.", + "The HardDisk Drive (HDD) unit is not formatted.", + "Please wait...", + "There was a problem accessing the disk.\nThe operation cannot be completed.", + "Disk scan completed.", + "The disk has corrupted data and must be checked.\nCorrupted data will be deleted." +}; + +static const char *DefaultLanguageLabelStringTable[SYS_UI_LBL_COUNT]={ + "OK", + "Cancel", + "Yes", + "No", + "Next", + "Back", + "Enabled", + "Disabled", + "Toggle option", + "Select field", + "Abort", + "Warning", + "Error", + "Information", + "Confirmation", + "Please wait", + "Scan Results", + "Errors found:", + "Errors fixed:", + "Some errors could not be fixed." +}; + +#else + +static const char *DefaultLanguageStringTable[SYS_UI_MSG_COUNT]={ + "No HardDisk Drive (HDD) unit was detected.", + "The HardDisk Drive (HDD) unit is not formatted. Format?\nWarning: All data will be erased.", + "Failed to format the HardDisk Drive (HDD) unit.", + "Quit program?", + "Please wait...", + "There was a problem accessing the disk.\nThe operation cannot be completed.", + "Check the disk for problems", + "Optimize the disk.\nAvailable space may be increased.", + "Perform a surface scan on the disk", + "Write zeros to every sector on the disk.", + "Quit program.", + "The bad sector could not be remapped.", + "Bad sector found at LBA %lu.\n\nThe file stored within the remapped sectors will be lost.\n\nSelect action:", + "An unexpected I/O error has occurred.", + "Proceed with scanning the disk?\nCorrupted data will be deleted.", + "Disk scan completed.", + "Abort scan operation?", + "Proceed with optimizing the disk?", + "Disk optimization completed.", + "Abort optimization?", + "Proceed with the surface scan?", + "Disk surface scan completed.", + "Abort surface scan operation?", + "Proceed with zero-filling the disk?", + "Disk zero-filled successfully.", + "Abort zero-fill operation?", + "Press START+SELECT to continue.\nPress any other button to abort.", + "The HardDisk Drive (HDD) unit has a problem.\nPlease run a disk check first.", + "S.M.A.R.T. has reported that the HardDisk Drive (HDD) unit has failed.\n\nThe HDD unit must be replaced." +}; + +static const char *DefaultLanguageLabelStringTable[SYS_UI_LBL_COUNT]={ + "OK", + "Cancel", + "PASS", + "FAIL", + "Yes", + "No", + "Next", + "Back", + "Enabled", + "Disabled", + "Toggle option", + "Select field", + "Quit program", + "Abort", + "Warning", + "Error", + "Information", + "Confirmation", + "Please wait", + "Scanning disk", + "Surface scanning disk", + "Zero-filling disk", + "Time remaining:", + "Remap", + "Skip", + "Remap all", + "Skip all", + "Bad sectors:", + "ATA unit 0:", + "Model:", + "Serial:", + "Firmware:", + "SMART status:", + "Capacity:", + "Check disk for errors", + "Optimize disk", + "Perform surface scan", + "Zero-fill disk", + "Optimizing disk (1/2)", + "Optimizing disk (2/2)", + "MB", + "GB", + "TB", + "Scan Results", + "Errors found:", + "Errors fixed:", + "Some errors could not be fixed." +}; + +#endif diff --git a/lang.h b/lang.h new file mode 100644 index 0000000..4070e3a --- /dev/null +++ b/lang.h @@ -0,0 +1,127 @@ +#ifdef FSCK + +enum SYS_UI_MESSAGES{ + SYS_UI_MSG_NO_HDD = 0, + SYS_UI_MSG_HDD_NOT_FORMATTED, + SYS_UI_MSG_PLEASE_WAIT, + SYS_UI_MSG_HDD_FAULT, + SYS_UI_MSG_SCAN_DISK_COMPLETED_OK, + SYS_UI_MSG_AUTO_SCAN_DISK_CFM, + + SYS_UI_MSG_COUNT +}; + +enum SYS_UI_LABEL_TEXT{ + SYS_UI_LBL_OK = 0, + SYS_UI_LBL_CANCEL, + SYS_UI_LBL_YES, + SYS_UI_LBL_NO, + SYS_UI_LBL_NEXT, + SYS_UI_LBL_BACK, + SYS_UI_LBL_ENABLED, + SYS_UI_LBL_DISABLED, + SYS_UI_LBL_TOGGLE_OPTION, + SYS_UI_LBL_SELECT_FIELD, + SYS_UI_LBL_ABORT, + SYS_UI_LBL_WARNING, + SYS_UI_LBL_ERROR, + SYS_UI_LBL_INFO, + SYS_UI_LBL_CONFIRM, + SYS_UI_LBL_WAIT, + SYS_UI_LBL_SCAN_RESULTS, + SYS_UI_LBL_ERRORS_FOUND, + SYS_UI_LBL_ERRORS_FIXED, + SYS_UI_LBL_SOME_ERRORS_NOT_FIXED, + + SYS_UI_LBL_COUNT +}; + +#else + +enum SYS_UI_MESSAGES{ + SYS_UI_MSG_NO_HDD = 0, + SYS_UI_MSG_FORMAT_HDD, + SYS_UI_MSG_FORMAT_HDD_FAILED, + SYS_UI_MSG_QUIT, + SYS_UI_MSG_PLEASE_WAIT, + SYS_UI_MSG_HDD_FAULT, + SYS_UI_MSG_DSC_SCAN_DISK, + SYS_UI_MSG_DSC_OPT_DISK, + SYS_UI_MSG_DSC_SURF_SCAN_DISK, + SYS_UI_MSG_DSC_ZERO_FILL_DISK, + SYS_UI_MSG_DSC_QUIT, + SYS_UI_MSG_SECTOR_PATCH_FAIL, + SYS_UI_MSG_BAD_SECTOR_FOUND, + SYS_UI_MSG_UNEXPECTED_ATA_ERR, + SYS_UI_MSG_SCAN_DISK_CFM, + SYS_UI_MSG_SCAN_DISK_COMPLETED_OK, + SYS_UI_MSG_SCAN_DISK_ABORT_CFM, + SYS_UI_MSG_OPT_DISK_CFM, + SYS_UI_MSG_OPT_DISK_COMPLETED_OK, + SYS_UI_MSG_OPT_DISK_ABORT_CFM, + SYS_UI_MSG_SURF_SCAN_DISK_CFM, + SYS_UI_MSG_SURF_SCAN_DISK_COMPLETED_OK, + SYS_UI_MSG_SURF_SCAN_DISK_ABORT_CFM, + SYS_UI_MSG_ZERO_FILL_DISK_CFM, + SYS_UI_MSG_ZERO_FILL_DISK_COMPLETED_OK, + SYS_UI_MSG_ZERO_FILL_DISK_ABORT_CFM, + SYS_UI_MSG_ZERO_FILL_DISK_CFM_2, + SYS_UI_MSG_HDD_CORRUPTED, + SYS_UI_MSG_HDD_SMART_FAILED, + + SYS_UI_MSG_COUNT +}; + +enum SYS_UI_LABEL_TEXT{ + SYS_UI_LBL_OK = 0, + SYS_UI_LBL_CANCEL, + SYS_UI_LBL_PASS, + SYS_UI_LBL_FAIL, + SYS_UI_LBL_YES, + SYS_UI_LBL_NO, + SYS_UI_LBL_NEXT, + SYS_UI_LBL_BACK, + SYS_UI_LBL_ENABLED, + SYS_UI_LBL_DISABLED, + SYS_UI_LBL_TOGGLE_OPTION, + SYS_UI_LBL_SELECT_FIELD, + SYS_UI_LBL_QUIT, + SYS_UI_LBL_ABORT, + SYS_UI_LBL_WARNING, + SYS_UI_LBL_ERROR, + SYS_UI_LBL_INFO, + SYS_UI_LBL_CONFIRM, + SYS_UI_LBL_WAIT, + SYS_UI_LBL_SCANNING_DISK, + SYS_UI_LBL_SURF_SCANNING_DISK, + SYS_UI_LBL_ZERO_FILLING_DISK, + SYS_UI_LBL_ETA, + SYS_UI_LBL_REMAP, + SYS_UI_LBL_REMAP_ALL, + SYS_UI_LBL_SKIP, + SYS_UI_LBL_SKIP_ALL, + SYS_UI_LBL_BAD_SECTORS, + SYS_UI_LBL_ATA_UNIT_0, + SYS_UI_LBL_MODEL, + SYS_UI_LBL_SERIAL, + SYS_UI_LBL_FIRMWARE, + SYS_UI_LBL_SMART_STATUS, + SYS_UI_LBL_CAPACITY, + SYS_UI_LBL_SCAN_DISK, + SYS_UI_LBL_OPT_DISK, + SYS_UI_LBL_SURF_SCAN_DISK, + SYS_UI_LBL_ZERO_FILL_DISK, + SYS_UI_LBL_OPTIMIZING_DISK_P1, + SYS_UI_LBL_OPTIMIZING_DISK_P2, + SYS_UI_LBL_MB, + SYS_UI_LBL_GB, + SYS_UI_LBL_TB, + SYS_UI_LBL_SCAN_RESULTS, + SYS_UI_LBL_ERRORS_FOUND, + SYS_UI_LBL_ERRORS_FIXED, + SYS_UI_LBL_SOME_ERRORS_NOT_FIXED, + + SYS_UI_LBL_COUNT +}; + +#endif diff --git a/linkfile b/linkfile new file mode 100644 index 0000000..8685aaa --- /dev/null +++ b/linkfile @@ -0,0 +1,101 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +# +# $Id$ +# Linkfile script for ee-ld +*/ + +ENTRY(_start); + +SECTIONS { + .text 0x00200000: { + _ftext = . ; + *(.text) + *(.text.*) + *(.gnu.linkonce.t*) + KEEP(*(.init)) + KEEP(*(.fini)) + QUAD(0) + } + + PROVIDE(_etext = .); + PROVIDE(etext = .); + + .reginfo : { *(.reginfo) } + + /* Global/static constructors and deconstructors. */ + .ctors ALIGN(16): { + KEEP(*crtbegin*.o(.ctors)) + KEEP(*(EXCLUDE_FILE(*crtend*.o) .ctors)) + KEEP(*(SORT(.ctors.*))) + KEEP(*(.ctors)) + } + .dtors ALIGN(16): { + KEEP(*crtbegin*.o(.dtors)) + KEEP(*(EXCLUDE_FILE(*crtend*.o) .dtors)) + KEEP(*(SORT(.dtors.*))) + KEEP(*(.dtors)) + } + + /* Static data. */ + .rodata ALIGN(128): { + *(.rodata) + *(.rodata.*) + *(.gnu.linkonce.r*) + } + + .data ALIGN(128): { + _fdata = . ; + *(.data) + *(.data.*) + *(.gnu.linkonce.d*) + SORT(CONSTRUCTORS) + } + + .rdata ALIGN(128): { *(.rdata) } + .gcc_except_table ALIGN(128): { *(.gcc_except_table) } + + _gp = ALIGN(128) + 0x7ff0; + .lit4 ALIGN(128): { *(.lit4) } + .lit8 ALIGN(128): { *(.lit8) } + + .sdata ALIGN(128): { + *(.sdata) + *(.sdata.*) + *(.gnu.linkonce.s*) + } + + _edata = .; + PROVIDE(edata = .); + + /* Uninitialized data. */ + .sbss ALIGN(128) : { + _fbss = . ; + *(.sbss) + *(.sbss.*) + *(.gnu.linkonce.sb*) + *(.scommon) + } + + .bss ALIGN(128) : { + *(.bss) + *(.bss.*) + *(.gnu.linkonce.b*) + *(COMMON) + } + _end_bss = .; + + _end = . ; + PROVIDE(end = .); + + /* Symbols needed by crt0.s. */ + PROVIDE(_heap_size = -1); + PROVIDE(_stack = -1); + PROVIDE(_stack_size = 128 * 1024); +} diff --git a/main.c b/main.c new file mode 100644 index 0000000..7700e24 --- /dev/null +++ b/main.c @@ -0,0 +1,116 @@ +#include +#include +#include +#include +#include + +#include + +#include "main.h" +#include "iop.h" +#include "hdst.h" +#include "system.h" +#include "UI.h" +#include "menu.h" +#include "pad.h" + +int VBlankStartSema; + +static int VBlankStartHandler(int cause) +{ + ee_sema_t sema; + iReferSemaStatus(VBlankStartSema, &sema); + if(sema.count +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "main.h" +#include "system.h" +#include "pad.h" +#include "graphics.h" +#include "font.h" +#include "UI.h" +#include "menu.h" +#ifndef FSCK +#include "hdst.h" +#endif + +extern struct UIDrawGlobal UIDrawGlobal; +extern GS_IMAGE BackgroundTexture; +extern GS_IMAGE PadLayoutTexture; + +#ifndef FSCK +enum MAIN_MENU_ID{ + MAIN_MENU_ID_HDD0_MODEL = 1, + MAIN_MENU_ID_HDD0_SERIAL, + MAIN_MENU_ID_HDD0_FIRMWARE, + MAIN_MENU_ID_HDD0_SMART, + MAIN_MENU_ID_HDD0_CAPACITY, + MAIN_MENU_ID_HDD0_CAPACITY_UNIT, + MAIN_MENU_ID_DESCRIPTION, + MAIN_MENU_ID_VERSION, + MAIN_MENU_ID_BTN_SCAN, + MAIN_MENU_ID_BTN_OPT, + MAIN_MENU_ID_BTN_SURF_SCAN, + MAIN_MENU_ID_BTN_ZERO_FILL, + MAIN_MENU_ID_BTN_EXIT, +}; + +enum PRG_SCREEN_ID{ + PRG_SCREEN_ID_TITLE = 1, + PRG_SCREEN_ID_ETA_HOURS, + PRG_SCREEN_ID_ETA_MINS, + PRG_SCREEN_ID_ETA_SECS, + PRG_SCREEN_ID_LBL_RD_ERRS, + PRG_SCREEN_ID_RD_ERRS, + PRG_SCREEN_ID_PROGRESS, + PRG_SCREEN_ID_TOTAL_PROGRESS, +}; + +static struct UIMenuItem HDDMainMenuItems[]={ + {MITEM_LABEL, 0, 0, 0, 0, 0, 0, SYS_UI_LBL_ATA_UNIT_0}, + {MITEM_SEPERATOR}, + + {MITEM_LABEL, 0, 0, 0, 0, 0, 0, SYS_UI_LBL_MODEL}, {MITEM_TAB}, {MITEM_TAB}, {MITEM_TAB}, {MITEM_STRING, MAIN_MENU_ID_HDD0_MODEL, MITEM_FLAG_READONLY}, {MITEM_BREAK}, + {MITEM_LABEL, 0, 0, 0, 0, 0, 0, SYS_UI_LBL_SERIAL}, {MITEM_TAB}, {MITEM_TAB}, {MITEM_TAB}, {MITEM_STRING, MAIN_MENU_ID_HDD0_SERIAL, MITEM_FLAG_READONLY}, {MITEM_BREAK}, + {MITEM_LABEL, 0, 0, 0, 0, 0, 0, SYS_UI_LBL_FIRMWARE}, {MITEM_TAB}, {MITEM_TAB}, {MITEM_STRING, MAIN_MENU_ID_HDD0_FIRMWARE, MITEM_FLAG_READONLY}, {MITEM_BREAK}, + {MITEM_LABEL, 0, 0, 0, 0, 0, 0, SYS_UI_LBL_SMART_STATUS}, {MITEM_TAB}, {MITEM_TAB}, {MITEM_LABEL, MAIN_MENU_ID_HDD0_SMART}, {MITEM_BREAK}, + {MITEM_LABEL, 0, 0, 0, 0, 0, 0, SYS_UI_LBL_CAPACITY}, {MITEM_TAB}, {MITEM_TAB}, {MITEM_VALUE, MAIN_MENU_ID_HDD0_CAPACITY, MITEM_FLAG_READONLY}, {MITEM_SPACE}, + {MITEM_LABEL, MAIN_MENU_ID_HDD0_CAPACITY_UNIT}, {MITEM_BREAK}, {MITEM_BREAK}, + + {MITEM_BUTTON, MAIN_MENU_ID_BTN_SCAN, MITEM_FLAG_POS_MID, 0, 24, 0, 0, SYS_UI_LBL_SCAN_DISK}, {MITEM_BREAK}, {MITEM_BREAK}, + {MITEM_BUTTON, MAIN_MENU_ID_BTN_OPT, MITEM_FLAG_POS_MID, 0, 24, 0, 0, SYS_UI_LBL_OPT_DISK}, {MITEM_BREAK}, {MITEM_BREAK}, + {MITEM_BUTTON, MAIN_MENU_ID_BTN_SURF_SCAN, MITEM_FLAG_POS_MID, 0, 24, 0, 0, SYS_UI_LBL_SURF_SCAN_DISK}, {MITEM_BREAK}, {MITEM_BREAK}, + {MITEM_BUTTON, MAIN_MENU_ID_BTN_ZERO_FILL, MITEM_FLAG_POS_MID, 0, 24, 0, 0, SYS_UI_LBL_ZERO_FILL_DISK}, {MITEM_BREAK}, {MITEM_BREAK}, + {MITEM_BUTTON, MAIN_MENU_ID_BTN_EXIT, MITEM_FLAG_POS_MID, 0, 24, 0, 0, SYS_UI_LBL_QUIT}, {MITEM_BREAK}, {MITEM_BREAK}, + + {MITEM_STRING, MAIN_MENU_ID_DESCRIPTION, MITEM_FLAG_POS_ABS|MITEM_FLAG_READONLY, 0, 0, 32, 370}, {MITEM_BREAK}, + {MITEM_STRING, MAIN_MENU_ID_VERSION, MITEM_FLAG_POS_ABS|MITEM_FLAG_READONLY, 0, 0, 520, 420}, {MITEM_BREAK}, + + {MITEM_TERMINATOR} +}; + +static struct UIMenuItem ProgressScreenItems[]={ + {MITEM_LABEL, PRG_SCREEN_ID_TITLE}, + {MITEM_SEPERATOR}, + {MITEM_LABEL, 0, 0, 0, 0, 0, 0, SYS_UI_LBL_ETA}, {MITEM_TAB}, {MITEM_VALUE, PRG_SCREEN_ID_ETA_HOURS, MITEM_FLAG_READONLY, MITEM_FORMAT_UDEC, 2}, {MITEM_COLON}, {MITEM_VALUE, PRG_SCREEN_ID_ETA_MINS, MITEM_FLAG_READONLY, MITEM_FORMAT_UDEC, 2}, {MITEM_COLON}, {MITEM_VALUE, PRG_SCREEN_ID_ETA_SECS, MITEM_FLAG_READONLY, MITEM_FORMAT_UDEC, 2}, {MITEM_BREAK}, + {MITEM_LABEL, PRG_SCREEN_ID_LBL_RD_ERRS, 0, 0, 0, 0, 0, SYS_UI_LBL_BAD_SECTORS}, {MITEM_TAB}, {MITEM_VALUE, PRG_SCREEN_ID_RD_ERRS, MITEM_FORMAT_UDEC | MITEM_FLAG_READONLY}, {MITEM_BREAK}, {MITEM_BREAK}, + {MITEM_PROGRESS, PRG_SCREEN_ID_PROGRESS, MITEM_FLAG_POS_ABS, 0, 0, 0, 280}, {MITEM_BREAK}, {MITEM_BREAK}, + {MITEM_PROGRESS, PRG_SCREEN_ID_TOTAL_PROGRESS}, {MITEM_BREAK}, + + {MITEM_TERMINATOR} +}; + +static struct UIMenu HDDMainMenu = {NULL, NULL, HDDMainMenuItems, {{BUTTON_TYPE_SYS_SELECT, SYS_UI_LBL_OK}, {BUTTON_TYPE_SYS_CANCEL, SYS_UI_LBL_QUIT}}}; +static struct UIMenu ProgressScreen = {NULL, NULL, ProgressScreenItems, {{BUTTON_TYPE_SYS_CANCEL, SYS_UI_LBL_CANCEL}, {-1, -1}}}; +#endif + +enum SCAN_RESULTS_SCREEN_ID{ + SCN_SCREEN_ID_TITLE = 1, + SCN_SCREEN_ID_RESULT, + SCN_SCREEN_ID_LBL_ERRS_FOUND, + SCN_SCREEN_ID_ERRS_FOUND, + SCN_SCREEN_ID_LBL_ERRS_FIXED, + SCN_SCREEN_ID_ERRS_FIXED, + SCN_SCREEN_ID_LBL_SOME_ERRS_NOT_FIXED, + SCN_SCREEN_ID_BTN_OK, +}; + +static struct UIMenuItem ScanResultsScreenItems[]={ + {MITEM_LABEL, SCN_SCREEN_ID_TITLE, 0, 0, 0, 0, 0, SYS_UI_LBL_SCAN_RESULTS}, + {MITEM_SEPERATOR}, + {MITEM_BREAK}, + + {MITEM_STRING, SCN_SCREEN_ID_RESULT, MITEM_FLAG_READONLY}, {MITEM_BREAK}, {MITEM_BREAK}, + + {MITEM_LABEL, SCN_SCREEN_ID_LBL_ERRS_FOUND, 0, 0, 0, 0, 0, SYS_UI_LBL_ERRORS_FOUND}, {MITEM_TAB}, {MITEM_VALUE, SCN_SCREEN_ID_ERRS_FOUND, MITEM_FLAG_READONLY}, {MITEM_BREAK}, + {MITEM_LABEL, SCN_SCREEN_ID_LBL_ERRS_FIXED, 0, 0, 0, 0, 0, SYS_UI_LBL_ERRORS_FIXED}, {MITEM_TAB}, {MITEM_VALUE, SCN_SCREEN_ID_ERRS_FIXED, MITEM_FLAG_READONLY}, {MITEM_BREAK}, + {MITEM_BREAK}, + + {MITEM_LABEL, SCN_SCREEN_ID_LBL_SOME_ERRS_NOT_FIXED, 0, 0, 0, 0, 0, SYS_UI_LBL_SOME_ERRORS_NOT_FIXED}, {MITEM_BREAK}, + + {MITEM_BREAK}, + {MITEM_BREAK}, + {MITEM_BREAK}, + {MITEM_BREAK}, + {MITEM_BREAK}, + {MITEM_BREAK}, + {MITEM_BREAK}, + {MITEM_BREAK}, + {MITEM_BREAK}, + + {MITEM_BUTTON, SCN_SCREEN_ID_BTN_OK, MITEM_FLAG_POS_MID, 0, 16}, + + {MITEM_TERMINATOR} +}; + +static struct UIMenu ScanResultsScreen = {NULL, NULL, ScanResultsScreenItems, {{BUTTON_TYPE_SYS_SELECT, SYS_UI_LBL_OK}, {-1, -1}}}; + +static void DrawMenuEntranceSlideInMenuAnimation(int SelectedOption) +{ + int i; + GS_RGBAQ rgbaq; + + rgbaq.r = 0; + rgbaq.g = 0; + rgbaq.b = 0; + rgbaq.q = 0; + for(i=30; i>0; i--) + { + rgbaq.a = 0x80-(i*4); + DrawSprite(&UIDrawGlobal, 0, 0, + UIDrawGlobal.width, UIDrawGlobal.height, + 0, rgbaq); +#ifndef FSCK + UIDrawMenu(&HDDMainMenu, i, UI_OFFSET_X + i * 6, UI_OFFSET_Y, SelectedOption); +#endif + SyncFlipFB(&UIDrawGlobal); + } +} + +static void DrawMenuExitAnimation(void) +{ + int i; + GS_RGBAQ rgbaq; + + rgbaq.r = 0; + rgbaq.g = 0; + rgbaq.b = 0; + rgbaq.q = 0; + for(i=30; i>0; i--) + { + rgbaq.a = 0x80-(i*4); + DrawSprite(&UIDrawGlobal, 0, 0, + UIDrawGlobal.width, UIDrawGlobal.height, + 0, rgbaq); + SyncFlipFB(&UIDrawGlobal); + } +} + +#ifndef FSCK +static int PerformZeroFillDoubleConfirmation(void) +{ + int done, result; + unsigned int PadStatus, frame; + + done=0; + result=0; + frame=0; + while(!done) + { + DrawBackground(&UIDrawGlobal, &BackgroundTexture); + + FontPrintf(&UIDrawGlobal, 20, 64, 1, 1.0f, GS_WHITE_FONT, GetUIString(SYS_UI_MSG_ZERO_FILL_DISK_CFM_2)); + + if(frame>0 && frame%30==0) + { + PadStatus=ReadCombinedPadStatus(); + + if(PadStatus!=0) + { + if((PadStatus&PAD_SELECT) && (PadStatus&PAD_START)) + result=1; + + done=1; + } + + frame=0; + } + + frame++; + SyncFlipFB(&UIDrawGlobal); + } + + return result; +} + +static int MainMenuUpdateCallback(struct UIMenu *menu, unsigned short int frame, int selection, u32 padstatus) +{ + if(padstatus != 0) + { + if(selection >= 0) + { + switch(menu->items[selection].id) + { + case MAIN_MENU_ID_BTN_SCAN: + UISetString(menu, MAIN_MENU_ID_DESCRIPTION, GetUIString(SYS_UI_MSG_DSC_SCAN_DISK)); + break; + case MAIN_MENU_ID_BTN_OPT: + UISetString(menu, MAIN_MENU_ID_DESCRIPTION, GetUIString(SYS_UI_MSG_DSC_OPT_DISK)); + break; + case MAIN_MENU_ID_BTN_SURF_SCAN: + UISetString(menu, MAIN_MENU_ID_DESCRIPTION, GetUIString(SYS_UI_MSG_DSC_SURF_SCAN_DISK)); + break; + case MAIN_MENU_ID_BTN_ZERO_FILL: + UISetString(menu, MAIN_MENU_ID_DESCRIPTION, GetUIString(SYS_UI_MSG_DSC_ZERO_FILL_DISK)); + break; + case MAIN_MENU_ID_BTN_EXIT: + UISetString(menu, MAIN_MENU_ID_DESCRIPTION, GetUIString(SYS_UI_MSG_DSC_QUIT)); + break; + default: + UISetString(menu, MAIN_MENU_ID_DESCRIPTION, NULL); + } + } else + UISetString(menu, MAIN_MENU_ID_DESCRIPTION, NULL); + } + + return 0; +} + +static int ProcessSpaceValue(u32 space, u32 *ProcessedSpace) +{ + u32 temp; + int unit; + + unit=0; + temp=space/2048; //*512 / 1024 / 1024 + //0 = MB, 1 = GB, 2 = TB + while(temp >= 1000 && unit <= 2) + { + unit++; + temp /= 1000; + } + + *ProcessedSpace = temp; + return(SYS_UI_LBL_MB + unit); +} +#endif + +static int CheckFormat(void) +{ + int status; + + status = HDDCheckStatus(); + switch(status) + { + case 1: //Not formatted +#ifdef FSCK + DisplayErrorMessage(SYS_UI_MSG_HDD_NOT_FORMATTED); +#else + if(DisplayPromptMessage(SYS_UI_MSG_FORMAT_HDD, SYS_UI_LBL_CANCEL, SYS_UI_LBL_OK) == 2) + { + status = 0; + + if(hddFormat() != 0) + DisplayErrorMessage(SYS_UI_MSG_FORMAT_HDD_FAILED); + } +#endif + break; + case 0: //Formatted + break; + case 2: //Not a usable HDD + case 3: //No HDD connected + default: //Unknown errors + DisplayErrorMessage(SYS_UI_MSG_NO_HDD); + break; + } + + return status; +} + +void MainMenu(void) +{ +#ifndef FSCK + int result; + short int option; + unsigned char done; + u32 ProcessedSpace; + int SpaceUnitLabel; + struct UIMenu *CurrentMenu; +#endif + + if(CheckFormat() != 0) + return; + +#ifdef FSCK + //While an indication of whether S.M.A.R.T. has failed or not would be great at this point, the MBR program won't boot FSCK if S.M.A.R.T. has failed. + if(DisplayPromptMessage(SYS_UI_MSG_AUTO_SCAN_DISK_CFM, SYS_UI_LBL_OK, SYS_UI_LBL_CANCEL)==1) + ScanDisk(0); +#else + if(HDDCheckSMARTStatus()) + DisplayErrorMessage(SYS_UI_MSG_HDD_SMART_FAILED); + + if(HDDCheckSectorErrorStatus() || HDDCheckPartErrorStatus()) + DisplayErrorMessage(SYS_UI_MSG_HDD_CORRUPTED); + + //Setup menu. + UISetString(&HDDMainMenu, MAIN_MENU_ID_HDD0_MODEL, GetATADeviceModel(0)); + UISetString(&HDDMainMenu, MAIN_MENU_ID_HDD0_SERIAL, GetATADeviceSerial(0)); + UISetString(&HDDMainMenu, MAIN_MENU_ID_HDD0_FIRMWARE, GetATADeviceFWVersion(0)); + UISetLabel(&HDDMainMenu, MAIN_MENU_ID_HDD0_SMART, GetATADeviceSMARTStatus(0)==0? SYS_UI_LBL_PASS : SYS_UI_LBL_FAIL); + SpaceUnitLabel = ProcessSpaceValue(GetATADeviceCapacity(0), &ProcessedSpace); + UISetValue(&HDDMainMenu, MAIN_MENU_ID_HDD0_CAPACITY, ProcessedSpace); + UISetLabel(&HDDMainMenu, MAIN_MENU_ID_HDD0_CAPACITY_UNIT, SpaceUnitLabel); + UISetString(&HDDMainMenu, MAIN_MENU_ID_VERSION, "v"HDDC_VERSION); + CurrentMenu = &HDDMainMenu; + option = 0; + + DrawMenuEntranceSlideInMenuAnimation(0); + + done=0; + while(!done) + { + option = UIExecMenu(CurrentMenu, option, &CurrentMenu, &MainMenuUpdateCallback); + + UITransition(&HDDMainMenu, UIMT_LEFT_OUT, option); + + switch(option) + { + case MAIN_MENU_ID_BTN_SCAN: + if(DisplayPromptMessage(SYS_UI_MSG_SCAN_DISK_CFM, SYS_UI_LBL_CANCEL, SYS_UI_LBL_OK) == 2) + ScanDisk(0); + break; + case MAIN_MENU_ID_BTN_OPT: + if(DisplayPromptMessage(SYS_UI_MSG_OPT_DISK_CFM, SYS_UI_LBL_CANCEL, SYS_UI_LBL_OK) == 2) + OptimizeDisk(0); + break; + case MAIN_MENU_ID_BTN_SURF_SCAN: + if(DisplayPromptMessage(SYS_UI_MSG_SURF_SCAN_DISK_CFM, SYS_UI_LBL_CANCEL, SYS_UI_LBL_OK) == 2) + SurfScanDisk(0); + break; + case MAIN_MENU_ID_BTN_ZERO_FILL: + if(DisplayPromptMessage(SYS_UI_MSG_ZERO_FILL_DISK_CFM, SYS_UI_LBL_CANCEL, SYS_UI_LBL_OK) == 2 && PerformZeroFillDoubleConfirmation()==1) + { + ZeroFillDisk(0); + if(CheckFormat() != 0) + done = 1; + } + break; + case 1: //User cancelled + case MAIN_MENU_ID_BTN_EXIT: + if(DisplayPromptMessage(SYS_UI_MSG_QUIT, SYS_UI_LBL_CANCEL, SYS_UI_LBL_OK) == 2) + done = 1; + break; + } + + if(!done) UITransition(&HDDMainMenu, UIMT_LEFT_IN, option); + } +#endif + + DrawMenuExitAnimation(); +} + +#ifndef FSCK +static unsigned char ETADisplayed = 1; + +void InitProgressScreen(int label) +{ + int ReadErrorDisplay, TotalProgressDisplay; + + switch(label) + { + case SYS_UI_LBL_SURF_SCANNING_DISK: + ReadErrorDisplay = 1; + TotalProgressDisplay = 0; + break; + case SYS_UI_LBL_OPTIMIZING_DISK_P1: + case SYS_UI_LBL_OPTIMIZING_DISK_P2: + ReadErrorDisplay = 0; + TotalProgressDisplay = 1; + break; + default: + ReadErrorDisplay = 0; + TotalProgressDisplay = 0; + } + + UISetLabel(&ProgressScreen, PRG_SCREEN_ID_TITLE, label); + UISetVisible(&ProgressScreen, PRG_SCREEN_ID_LBL_RD_ERRS, ReadErrorDisplay); + UISetVisible(&ProgressScreen, PRG_SCREEN_ID_RD_ERRS, ReadErrorDisplay); + UISetVisible(&ProgressScreen, PRG_SCREEN_ID_TOTAL_PROGRESS, TotalProgressDisplay); + ETADisplayed = 1; + UISetType(&ProgressScreen, PRG_SCREEN_ID_ETA_HOURS, MITEM_VALUE); + UISetType(&ProgressScreen, PRG_SCREEN_ID_ETA_MINS, MITEM_VALUE); + UISetType(&ProgressScreen, PRG_SCREEN_ID_ETA_SECS, MITEM_VALUE); +} + +void DrawDiskSurfScanningScreen(int PercentageComplete, unsigned int SecondsRemaining, unsigned int NumBadSectors) +{ + unsigned int HoursRemaining; + unsigned char MinutesRemaining; + + UISetValue(&ProgressScreen, PRG_SCREEN_ID_PROGRESS, PercentageComplete); + UISetValue(&ProgressScreen, PRG_SCREEN_ID_RD_ERRS, NumBadSectors); + if(SecondsRemaining +#include +#include +#include +#include + +#include "pad.h" + +static unsigned char padArea[2][256] ALIGNED(64); +static unsigned int old_pad[2]={0, 0}; + +void PadInitPads(void) +{ + padInit(0); + padPortOpen(0, 0, padArea[0]); + padPortOpen(1, 0, padArea[1]); + + old_pad[0] = 0; + old_pad[1] = 0; +} + +void PadDeinitPads(void) +{ + padPortClose(0, 0); + padPortClose(1, 0); + padEnd(); +} + +int ReadPadStatus_raw(int port, int slot){ + struct padButtonStatus buttons; + u32 paddata; + + paddata=0; + if(padRead(port, slot, &buttons) != 0){ + paddata = 0xffff ^ buttons.btns; + } + + return paddata; +} + +int ReadCombinedPadStatus_raw(void){ + return(ReadPadStatus_raw(0, 0)|ReadPadStatus_raw(1, 0)); +} + +int ReadPadStatus(int port, int slot){ + struct padButtonStatus buttons; + u32 new_pad, paddata; + + new_pad=0; + if (padRead(port, slot, &buttons) != 0) { + paddata = 0xffff ^ buttons.btns; + + new_pad = paddata & ~old_pad[port]; + old_pad[port] = paddata; + } + + return new_pad; +} + +int ReadCombinedPadStatus(void){ + return(ReadPadStatus(0, 0)|ReadPadStatus(1, 0)); +} diff --git a/pad.h b/pad.h new file mode 100644 index 0000000..425e19d --- /dev/null +++ b/pad.h @@ -0,0 +1,7 @@ +void PadInitPads(void); +void PadDeinitPads(void); + +int ReadPadStatus_raw(int port, int slot); +int ReadCombinedPadStatus_raw(void); +int ReadPadStatus(int port, int slot); +int ReadCombinedPadStatus(void); diff --git a/resources/background.png b/resources/background.png new file mode 100644 index 0000000..7649765 Binary files /dev/null and b/resources/background.png differ diff --git a/resources/buttons.png b/resources/buttons.png new file mode 100644 index 0000000..3e192fd Binary files /dev/null and b/resources/buttons.png differ diff --git a/system.c b/system.c new file mode 100644 index 0000000..dee384f --- /dev/null +++ b/system.c @@ -0,0 +1,901 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "main.h" +#include "iop.h" +#include "menu.h" +#include "UI.h" +#include "fsck/fsck-ioctl.h" +#include "hdsk/hdsk-devctl.h" +#include "fssk/fssk-ioctl.h" +#include "hdst.h" +#include "system.h" + +extern void *_gp; + +extern unsigned short int SelectButton, CancelButton; + +int GetBootDeviceID(void) +{ + static int BootDevice = -2; + char path[256]; + int result; + + if(BootDevice < BOOT_DEVICE_UNKNOWN) + { + getcwd(path, sizeof(path)); + + if(!strncmp(path, "hdd:", 4) || !strncmp(path, "hdd0:", 5)) result=BOOT_DEVICE_HDD; +#ifndef FSCK + else if(!strncmp(path, "mc0:", 4)) result=BOOT_DEVICE_MC0; + else if(!strncmp(path, "mc1:", 4)) result=BOOT_DEVICE_MC1; + else if(!strncmp(path, "mass:", 5) || !strncmp(path, "mass0:", 6)) result=BOOT_DEVICE_MASS; +#endif + else result=BOOT_DEVICE_UNKNOWN; + + BootDevice = result; + } else + result = BootDevice; + + return result; +} + +static int SysInitMount(void) +{ + static char BlockDevice[38] = ""; + char command[256]; + const char *MountPath; + int BlockDeviceNameLen, result; + + if(BlockDevice[0] == '\0') + { + //Format: hdd0:partition:pfs:/path_to_file_on_partition + //However, getcwd will return the path, without the filename (as parsed by libc's init.c). + getcwd(command, sizeof(command)); + if(strlen(command)>6 && (MountPath=strchr(&command[5], ':'))!=NULL) + { + BlockDeviceNameLen = (unsigned int)MountPath-(unsigned int)command; + strncpy(BlockDevice, command, BlockDeviceNameLen); + BlockDevice[BlockDeviceNameLen]='\0'; + + MountPath++; //This is the location of the mount path; + + if((result = fileXioMount("pfs0:", BlockDevice, FIO_MT_RDONLY)) >= 0) + result = chdir(MountPath); + + return result; + } else + result = -EINVAL; + } else { + if((result = fileXioMount("pfs0:", BlockDevice, FIO_MT_RDONLY)) >= 0) + result = 0; + } + + return result; +} + +int SysBootDeviceInit(void) +{ + int result; + + switch(GetBootDeviceID()) + { + case BOOT_DEVICE_HDD: + result = SysInitMount(); + break; + default: + result = 0; + } + + return result; +} + +int GetConsoleRegion(void) +{ + static int region = -1; + FILE *file; + + if(region < 0) + { + if((file = fopen("rom0:ROMVER", "r")) != NULL) + { + fseek(file, 4, SEEK_SET); + switch(fgetc(file)) + { + case 'J': + region = CONSOLE_REGION_JAPAN; + break; + case 'A': + case 'H': + region = CONSOLE_REGION_USA; + break; + case 'E': + region = CONSOLE_REGION_EUROPE; + break; + case 'C': + region = CONSOLE_REGION_CHINA; + break; + } + + fclose(file); + } + } + + return region; +} + +int GetConsoleVMode(void) +{ + switch(GetConsoleRegion()) + { + case CONSOLE_REGION_EUROPE: + return 1; + default: + return 0; + } +} + +int IsPSX(void) +{ + static int psx = -1; + FILE * file; + int result; + + if((result = psx) < 0) + { + if((file = fopen("rom0:PSXVER", "r")) != NULL) + { + psx = 1; + fclose(file); + } else + psx = 0; + + result = psx; + } + + return result; +} + +int SysCreateThread(void *function, void *stack, unsigned int StackSize, void *arg, int priority) +{ + ee_thread_t ThreadData; + int ThreadID; + + ThreadData.func=function; + ThreadData.stack=stack; + ThreadData.stack_size=StackSize; + ThreadData.gp_reg=&_gp; + ThreadData.initial_priority=priority; + ThreadData.attr=ThreadData.option=0; + + if((ThreadID=CreateThread(&ThreadData))>=0) + { + if(StartThread(ThreadID, arg)<0) + { + DeleteThread(ThreadID); + ThreadID=-1; + } + } + + return ThreadID; +} + +#ifndef FSCK +static unsigned char UserAborted; +#endif +static unsigned int ErrorsFound; +static unsigned int ErrorsFixed; + +#ifdef FSCK +static int FsckPartition(const char *partition) +{ + char cmd[64]; + struct fsckStatus status; + int fd, result, InitSemaID; + + DisplayFlashStatusUpdate(SYS_UI_MSG_PLEASE_WAIT); + + InitSemaID = IopInitStart(IOP_MODSET_SA_FSCK); + result = 0; + + WaitSema(InitSemaID); + DeleteSema(InitSemaID); + + sprintf(cmd, "fsck:%s", partition); + if((fd = fileXioOpen(cmd, 0, FSCK_MODE_VERBOSITY(0)|FSCK_MODE_AUTO|FSCK_MODE_WRITE)) >= 0) + { + if((result = fileXioIoctl2(fd, FSCK_IOCTL2_CMD_START, NULL, 0, NULL, 0)) == 0) + { + result = fileXioIoctl2(fd, FSCK_IOCTL2_CMD_WAIT, NULL, 0, NULL, 0); + } + + if (result == 0 && (result = fileXioIoctl2(fd, FSCK_IOCTL2_CMD_GET_STATUS, NULL, 0, &status, sizeof(status))) == 0) + result = 0; + + if(result == 0) + { + ErrorsFound += status.errorCount; + ErrorsFixed += status.fixedErrorCount; + } + + fileXioClose(fd); + } else + result = fd; + + return result; +} + +int ScanDisk(int unit) +{ + char ErrorPartName[64] = "hdd0:"; + + int result, InitSemaID; + + DisplayFlashStatusUpdate(SYS_UI_MSG_PLEASE_WAIT); + + if(fileXioDevctl("hdd0:", HDIOC_GETSECTORERROR, NULL, 0, NULL, 0) == 0) + { + if(fileXioDevctl("hdd0:", HDIOC_GETERRORPARTNAME, NULL, 0, &ErrorPartName[5], sizeof(ErrorPartName) - 5) != 0) + { + ErrorsFound = 0; + ErrorsFixed = 0; + + if((result = FsckPartition(ErrorPartName)) == 0) + DisplayScanCompleteResults(ErrorsFound, ErrorsFixed); + else + DisplayErrorMessage(SYS_UI_MSG_HDD_FAULT); + + DisplayFlashStatusUpdate(SYS_UI_MSG_PLEASE_WAIT); + InitSemaID = IopInitStart(IOP_MODSET_SA_MAIN); + WaitSema(InitSemaID); + DeleteSema(InitSemaID); + } else + DisplayScanCompleteResults(0, 0); //No fault. Why are we here then? + } else + DisplayErrorMessage(SYS_UI_MSG_HDD_FAULT); + + return result; +} + +#else +static int HdckDisk(int unit) +{ + char device[]="hdck0:"; + int InitSemaID, result; + + InitSemaID = IopInitStart(IOP_MODSET_HDCK); + device[4] = '0' + unit; + + WaitSema(InitSemaID); + DeleteSema(InitSemaID); + + SysBootDeviceInit(); + ReinitializeUI(); + +#ifdef LOG_MESSAGES + IopStartLog("hdck.log"); + printf("# Log generated by HDDChecker v"HDDC_VERSION", built on "__DATE__" "__TIME__"\n"); +#endif + + result = fileXioDevctl(device, 0, NULL, 0, NULL, 0); + +#ifdef LOG_MESSAGES + IopStopLog(); +#endif + + return result; +} + +static int FsckDisk(int unit) +{ + char bdevice[]="hdd0:", cmd[64]; + iox_dirent_t dirent; + struct fsckStatus status; + unsigned int PadStatus, CurrentCPUTicks, PreviousCPUTicks, seconds, TimeElasped, rate, partitions, i; + int PercentageComplete; + int bfd, fd, result, InitSemaID; + + InitSemaID = IopInitStart(IOP_MODSET_FSCK); + bdevice[3] = '0' + unit; + result = 0; + + InitProgressScreen(SYS_UI_LBL_SCANNING_DISK); + + WaitSema(InitSemaID); + DeleteSema(InitSemaID); + + SysBootDeviceInit(); + ReinitializeUI(); + + //Count the number of partitions on the disk. + partitions = 0; + if((bfd = fileXioDopen(bdevice)) >= 0) + { + while(fileXioDread(bfd, &dirent) > 0) + partitions++; + + fileXioDclose(bfd); + } + + if(partitions < 1) //No partitions? + return 0; + +#ifdef LOG_MESSAGES + IopStartLog("fsck.log"); + printf("# Log generated by HDDChecker v"HDDC_VERSION", built on "__DATE__" "__TIME__"\n"); +#endif + + //Now, scan the disk. + if((bfd = fileXioDopen(bdevice)) >= 0) + { + for(i = 0; fileXioDread(bfd, &dirent) > 0; i++) + { + if(!(dirent.stat.attr & APA_FLAG_SUB) && dirent.stat.mode == APA_TYPE_PFS) + { + printf("# fsck hdd%d:%s\n", unit, dirent.name); + + sprintf(cmd, "fsck:hdd%d:%s", unit, dirent.name); + if((fd = fileXioOpen(cmd, 0, FSCK_MODE_VERBOSITY(FSCK_VERBOSITY)|FSCK_MODE_AUTO|FSCK_MODE_WRITE)) >= 0) + { + if((result = fileXioIoctl2(fd, FSCK_IOCTL2_CMD_START, NULL, 0, NULL, 0)) == 0) + { + result=0; + TimeElasped=0; + PreviousCPUTicks=cpu_ticks(); + while(fileXioIoctl2(fd, FSCK_IOCTL2_CMD_POLL, NULL, 0, NULL, 0) == 1) + { + CurrentCPUTicks=cpu_ticks(); + if((seconds=(CurrentCPUTicks>PreviousCPUTicks?CurrentCPUTicks-PreviousCPUTicks:UINT_MAX-PreviousCPUTicks+CurrentCPUTicks)/295000000)>0) + { + TimeElasped+=seconds; + PreviousCPUTicks=CurrentCPUTicks; + } + PercentageComplete = (int)((u64)i * 100 / partitions); + rate=(TimeElasped>0)?i/TimeElasped:0; //In partitions/second + + DrawDiskScanningScreen(PercentageComplete, rate>0?(partitions - i)/rate:UINT_MAX); + + PadStatus=ReadCombinedPadStatus(); + if(PadStatus&CancelButton) + { + if(DisplayPromptMessage(SYS_UI_MSG_SCAN_DISK_ABORT_CFM, SYS_UI_LBL_NO, SYS_UI_LBL_YES) == 2) + { + fileXioIoctl2(fd, FSCK_IOCTL2_CMD_STOP, NULL, 0, NULL, 0); + result = 0; + UserAborted = 1; + break; + } + } + } + } + + if(result != 0 || (result = fileXioIoctl2(fd, FSCK_IOCTL2_CMD_GET_STATUS, NULL, 0, &status, sizeof(status))) != 0 || UserAborted) + { + fileXioClose(fd); + break; + } + + ErrorsFound += status.errorCount; + ErrorsFixed += status.fixedErrorCount; + + fileXioClose(fd); + } else { //Ignore partitions that can't be opened, so that the remainder of the disk can be checked.. + result = fd; + ErrorsFound++; + } + + putchar('\n'); + } + } + + fileXioDclose(bfd); + } else + result = bfd; + +#ifdef LOG_MESSAGES + IopStopLog(); +#endif + + return result; +} + +int ScanDisk(int unit) +{ + int result, InitSemaID; + + DisplayFlashStatusUpdate(SYS_UI_MSG_PLEASE_WAIT); + + ErrorsFound = 0; + ErrorsFixed = 0; + + if((result = HdckDisk(unit)) == 0 && !UserAborted) + result = FsckDisk(unit); + + if(result == 0) + { + if(!UserAborted) + DisplayScanCompleteResults(ErrorsFound, ErrorsFixed); + } else + DisplayErrorMessage(SYS_UI_MSG_HDD_FAULT); + + DisplayFlashStatusUpdate(SYS_UI_MSG_PLEASE_WAIT); + InitSemaID = IopInitStart(IOP_MODSET_MAIN); + + WaitSema(InitSemaID); + DeleteSema(InitSemaID); + + SysBootDeviceInit(); + ReinitializeUI(); + + return result; +} + +static int HdskDisk(int unit) +{ + char bdevice[]="hdsk0:"; + struct hdskStat status; + unsigned int PadStatus, CurrentCPUTicks, PreviousCPUTicks, seconds, TimeElasped, rate; + int PercentageComplete; + int result, InitSemaID; + u32 progress; + + InitSemaID = IopInitStart(IOP_MODSET_HDSK); + bdevice[4] = '0' + unit; + + InitProgressScreen(SYS_UI_LBL_OPTIMIZING_DISK_P2); + + WaitSema(InitSemaID); + DeleteSema(InitSemaID); + + SysBootDeviceInit(); + ReinitializeUI(); + +#ifdef LOG_MESSAGES + IopStartLog("hdsk.log"); + printf("# Log generated by HDDChecker v"HDDC_VERSION", built on "__DATE__" "__TIME__"\n"); +#endif + + //Now, scan the disk. + if((result = fileXioDevctl(bdevice, HDSK_DEVCTL_GET_HDD_STAT, NULL, 0, &status, sizeof(struct hdskStat))) >= 0) + { + printf("# hdsk: total: %x, free: %x\n", status.total, status.free); + + if((status.total > 0) && (result = fileXioDevctl(bdevice, HDSK_DEVCTL_START, NULL, 0, NULL, 0)) == 0) + { + result=0; + TimeElasped=0; + PreviousCPUTicks=cpu_ticks(); + while(fileXioDevctl(bdevice, HDSK_DEVCTL_POLL, NULL, 0, NULL, 0) == 1) + { + progress = (u32)fileXioDevctl(bdevice, HDSK_DEVCTL_GET_PROGRESS, NULL, 0, NULL, 0); + PercentageComplete = (int)((u64)progress * 100 / status.total); + + CurrentCPUTicks=cpu_ticks(); + if((seconds=(CurrentCPUTicks>PreviousCPUTicks?CurrentCPUTicks-PreviousCPUTicks:UINT_MAX-PreviousCPUTicks+CurrentCPUTicks)/295000000)>0) + { + TimeElasped+=seconds; + PreviousCPUTicks=CurrentCPUTicks; + } + rate=(TimeElasped>0)?progress/TimeElasped:0; //In sectors/second + + DrawDiskOptimizationScreen(PercentageComplete, 50 + PercentageComplete / 2, rate>0?(status.total - progress)/rate:UINT_MAX); + + PadStatus=ReadCombinedPadStatus(); + if(PadStatus&CancelButton) + { + if(DisplayPromptMessage(SYS_UI_MSG_OPT_DISK_ABORT_CFM, SYS_UI_LBL_NO, SYS_UI_LBL_YES) == 2) + { + fileXioDevctl(bdevice, HDSK_DEVCTL_STOP, NULL, 0, NULL, 0); + result = 0; + UserAborted = 1; + break; + } + } + + if(result != 0 || (result = fileXioDevctl(bdevice, HDSK_DEVCTL_GET_STATUS, NULL, 0, NULL, 0)) != 0 || UserAborted) + break; + } + } + } + +#ifdef LOG_MESSAGES + IopStopLog(); +#endif + + return result; +} + +static int FsskDisk(int unit) +{ + char bdevice[]="hdd0:", cmd[64]; + iox_dirent_t dirent; + struct fsskStatus status; + unsigned int PadStatus, CurrentCPUTicks, PreviousCPUTicks, seconds, TimeElasped, rate, partitions, i; + int PercentageComplete; + int bfd, fd, result, InitSemaID; + + InitSemaID = IopInitStart(IOP_MODSET_FSSK); + bdevice[3] = '0' + unit; + partitions = 0; + result = 0; + + InitProgressScreen(SYS_UI_LBL_OPTIMIZING_DISK_P1); + + WaitSema(InitSemaID); + DeleteSema(InitSemaID); + + SysBootDeviceInit(); + ReinitializeUI(); + + //Count the number of partitions on the disk. + if((bfd = fileXioDopen(bdevice)) >= 0) + { + while(fileXioDread(bfd, &dirent) > 0) + partitions++; + + fileXioDclose(bfd); + } + + if(partitions < 1) //No partitions? + return 0; + +#ifdef LOG_MESSAGES + IopStartLog("fssk.log"); + printf("# Log generated by HDDChecker v"HDDC_VERSION", built on "__DATE__" "__TIME__"\n"); +#endif + + //Now, scan the disk. + if((bfd = fileXioDopen(bdevice)) >= 0) + { + for(i = 0; fileXioDread(bfd, &dirent) > 0; i++) + { + if(!(dirent.stat.attr & APA_FLAG_SUB) && dirent.stat.mode == APA_TYPE_PFS) + { + printf("# fssk hdd%d:%s\n", unit, dirent.name); + + sprintf(cmd, "fssk:hdd%d:%s", unit, dirent.name); + if((fd = fileXioOpen(cmd, 0, FSSK_MODE_VERBOSITY(FSSK_VERBOSITY))) >= 0) + { + if((result = fileXioIoctl2(fd, FSSK_IOCTL2_CMD_START, NULL, 0, NULL, 0)) == 0) + { + result=0; + TimeElasped=0; + PreviousCPUTicks=cpu_ticks(); + while(fileXioIoctl2(fd, FSSK_IOCTL2_CMD_POLL, NULL, 0, NULL, 0) == 1) + { + CurrentCPUTicks=cpu_ticks(); + if((seconds=(CurrentCPUTicks>PreviousCPUTicks?CurrentCPUTicks-PreviousCPUTicks:UINT_MAX-PreviousCPUTicks+CurrentCPUTicks)/295000000)>0) + { + TimeElasped+=seconds; + PreviousCPUTicks=CurrentCPUTicks; + } + PercentageComplete = (int)((u64)i * 100 / partitions); + rate = (TimeElasped > 0) ? i / TimeElasped : 0; //In partitions/second + + DrawDiskOptimizationScreen(PercentageComplete, PercentageComplete / 2, rate>0?(partitions - i)/rate:UINT_MAX); + + PadStatus=ReadCombinedPadStatus(); + if(PadStatus&CancelButton) + { + if(DisplayPromptMessage(SYS_UI_MSG_OPT_DISK_ABORT_CFM, SYS_UI_LBL_NO, SYS_UI_LBL_YES) == 2) + { + fileXioIoctl2(fd, FSSK_IOCTL2_CMD_STOP, NULL, 0, NULL, 0); + result = 0; + UserAborted = 1; + break; + } + } + } + } + + if(result != 0 || (result = fileXioIoctl2(fd, FSSK_IOCTL2_CMD_GET_STATUS, NULL, 0, &status, sizeof(status))) != 0 || (result = status.hasError) != 0 || UserAborted) + { + fileXioClose(fd); + break; + } + + fileXioClose(fd); + } else + result = fd; + + putchar('\n'); + } + } + + fileXioDclose(bfd); + } else + result = bfd; + +#ifdef LOG_MESSAGES + IopStopLog(); +#endif + + return result; +} + +int OptimizeDisk(int unit) +{ + int result, InitSemaID; + + DisplayFlashStatusUpdate(SYS_UI_MSG_PLEASE_WAIT); + + if((result = FsskDisk(unit)) == 0 && !UserAborted) + result = HdskDisk(unit); + + if(result == 0) + { + if(!UserAborted) + DisplayInfoMessage(SYS_UI_MSG_OPT_DISK_COMPLETED_OK); + } else + DisplayErrorMessage(SYS_UI_MSG_HDD_FAULT); + + DisplayFlashStatusUpdate(SYS_UI_MSG_PLEASE_WAIT); + InitSemaID = IopInitStart(IOP_MODSET_MAIN); + + WaitSema(InitSemaID); + DeleteSema(InitSemaID); + + SysBootDeviceInit(); + ReinitializeUI(); + + return result; +} + +int SurfScanDisk(int unit) +{ + u32 lba, SectorsRemaining, TotalSectors; + u32 NumSectors, NumBadSectors, PadStatus, CurrentCPUTicks, PreviousCPUTicks, seconds, TimeElasped, rate; + char DeviceName[8]; + int result, BadSectorHandlingMode, IsRetryCycle, InitSemaID; + HdstSectorIOParams_t SectorIOParams; + int PercentageComplete; + + InitProgressScreen(SYS_UI_LBL_SURF_SCANNING_DISK); + + sprintf(DeviceName, "hdst%u:", unit); + result=0; + TotalSectors = GetATADeviceCapacity(unit); + TimeElasped=0; + PreviousCPUTicks=cpu_ticks(); + NumBadSectors=0; + IsRetryCycle=0; + BadSectorHandlingMode=BAD_SECTOR_HANDLING_MODE_PROMPT; + for(lba=0,SectorsRemaining=TotalSectors; SectorsRemaining>0; ) + { + CurrentCPUTicks=cpu_ticks(); + if((seconds=(CurrentCPUTicks>PreviousCPUTicks?CurrentCPUTicks-PreviousCPUTicks:UINT_MAX-PreviousCPUTicks+CurrentCPUTicks)/295000000)>0) + { + TimeElasped+=seconds; + PreviousCPUTicks=CurrentCPUTicks; + } + PercentageComplete = (int)((u64)lba * 100 / TotalSectors); + rate = (TimeElasped > 0) ? (TotalSectors - SectorsRemaining) / TimeElasped : 0; //In sectors/second + + DrawDiskSurfScanningScreen(PercentageComplete, (rate > 0 ? SectorsRemaining / rate : UINT_MAX), NumBadSectors); + PadStatus=ReadCombinedPadStatus(); + if(PadStatus&CancelButton) + { + if(DisplayPromptMessage(SYS_UI_MSG_SCAN_DISK_ABORT_CFM, SYS_UI_LBL_NO, SYS_UI_LBL_YES) == 2) + { + result=1; + break; + } + } + + NumSectors=SectorsRemaining>65536?65536:(u32)SectorsRemaining; + SectorIOParams.lba=lba; + SectorIOParams.sectors=NumSectors; + if((result=fileXioDevctl(DeviceName, HDST_DEVCTL_DEVICE_VERIFY_SECTORS, &SectorIOParams, sizeof(SectorIOParams), NULL, 0))!=0) + { + if(!IsRetryCycle) + NumBadSectors++; + + if(result>0) + { + result--; + lba+=result; + SectorsRemaining-=result; + + if(BadSectorHandlingMode==BAD_SECTOR_HANDLING_MODE_PROMPT) + BadSectorHandlingMode=GetBadSectorAction(lba); + switch(BadSectorHandlingMode) + { + case BAD_SECTOR_HANDLING_MODE_REMAP: + BadSectorHandlingMode=BAD_SECTOR_HANDLING_MODE_PROMPT; + case BAD_SECTOR_HANDLING_MODE_REMAP_ALL: + fileXioSetBlockMode(FXIO_WAIT); + + if((result=PatchSector(DeviceName, lba))!=0) + { + if(DisplayPromptMessage(SYS_UI_MSG_SECTOR_PATCH_FAIL, SYS_UI_LBL_OK, SYS_UI_LBL_ABORT) == 2) + goto SurfaceScan_end; + + lba++; + SectorsRemaining--; + result=0; + } else + IsRetryCycle=1; + + fileXioSetBlockMode(FXIO_NOWAIT); + break; + case BAD_SECTOR_HANDLING_MODE_SKIP: + BadSectorHandlingMode=BAD_SECTOR_HANDLING_MODE_PROMPT; + case BAD_SECTOR_HANDLING_MODE_SKIP_ALL: + lba++; + SectorsRemaining--; + result=0; + break; + } + + PreviousCPUTicks=cpu_ticks(); //Don't include the time spent on patching the disk. + } + else{ + DisplayErrorMessage(SYS_UI_MSG_UNEXPECTED_ATA_ERR); + break; + } + } + else{ + lba+=NumSectors; + SectorsRemaining-=NumSectors; + IsRetryCycle=0; + } + } + + if(result == 0) + DisplayInfoMessage(SYS_UI_MSG_SURF_SCAN_DISK_COMPLETED_OK); + +SurfaceScan_end: + + //Reboot IOP to load the filesystem modules again (they'll assess the condition of the disk's format). + DisplayFlashStatusUpdate(SYS_UI_MSG_PLEASE_WAIT); + InitSemaID = IopInitStart(IOP_MODSET_MAIN); + + WaitSema(InitSemaID); + DeleteSema(InitSemaID); + + SysBootDeviceInit(); + ReinitializeUI(); + + return result; +} + +int ZeroFillDisk(int unit) +{ + u32 lba, SectorsRemaining, TotalSectors; + u32 NumSectors, PadStatus, CurrentCPUTicks, PreviousCPUTicks, seconds, TimeElasped, rate; + char DeviceName[8]; + int result, InitSemaID; + HdstSectorIOParams_t SectorIOParams; + int PercentageComplete; + + InitProgressScreen(SYS_UI_LBL_ZERO_FILLING_DISK); + + sprintf(DeviceName, "hdst%u:", unit); + result=0; + TotalSectors = GetATADeviceCapacity(unit); + TimeElasped=0; + PreviousCPUTicks=cpu_ticks(); + for(lba=0,SectorsRemaining=TotalSectors; SectorsRemaining>0; ) + { + CurrentCPUTicks=cpu_ticks(); + if((seconds=(CurrentCPUTicks>PreviousCPUTicks?CurrentCPUTicks-PreviousCPUTicks:UINT_MAX-PreviousCPUTicks+CurrentCPUTicks)/295000000)>0) + { + TimeElasped+=seconds; + PreviousCPUTicks=CurrentCPUTicks; + } + PercentageComplete = (int)((u64)lba * 100 / TotalSectors); + rate = (TimeElasped > 0) ? (TotalSectors - SectorsRemaining) / TimeElasped : 0; //In sectors/second + + DrawDiskZeroFillingScreen(PercentageComplete, (rate > 0 ? SectorsRemaining / rate : UINT_MAX)); + PadStatus=ReadCombinedPadStatus(); + if(PadStatus&CancelButton) + { + if(DisplayPromptMessage(SYS_UI_MSG_ZERO_FILL_DISK_ABORT_CFM, SYS_UI_LBL_NO, SYS_UI_LBL_YES) == 2) + { + result=1; + break; + } + } + + NumSectors=SectorsRemaining>65536?65536:(u32)SectorsRemaining; + SectorIOParams.lba=lba; + SectorIOParams.sectors=NumSectors; + if((result=fileXioDevctl(DeviceName, HDST_DEVCTL_DEVICE_ERASE_SECTORS, &SectorIOParams, sizeof(SectorIOParams), NULL, 0))!=0) + { + DisplayErrorMessage(SYS_UI_MSG_HDD_FAULT); + break; + } + else{ + lba+=NumSectors; + SectorsRemaining-=NumSectors; + } + } + + if(result == 0) + DisplayInfoMessage(SYS_UI_MSG_ZERO_FILL_DISK_COMPLETED_OK); + + //Reboot IOP to load the filesystem modules again (they'll assess the condition of the disk's format). + DisplayFlashStatusUpdate(SYS_UI_MSG_PLEASE_WAIT); + InitSemaID = IopInitStart(IOP_MODSET_MAIN); + + WaitSema(InitSemaID); + DeleteSema(InitSemaID); + + SysBootDeviceInit(); + ReinitializeUI(); + + return result; +} +#endif + +int HDDCheckSMARTStatus(void) +{ + return(fileXioDevctl("hdd0:", HDIOC_SMARTSTAT, NULL, 0, NULL, 0) != 0); +} + +int HDDCheckSectorErrorStatus(void) +{ + return(fileXioDevctl("hdd0:", HDIOC_GETSECTORERROR, NULL, 0, NULL, 0) != 0); +} + +int HDDCheckPartErrorStatus(void) +{ + return(fileXioDevctl("hdd0:", HDIOC_GETERRORPARTNAME, NULL, 0, NULL, 0) != 0); +} + +int HDDCheckStatus(void) +{ + int status; + + status = fileXioDevctl("hdd0:", HDIOC_STATUS, NULL, 0, NULL, 0); + + if(status == 0) + fileXioRemove("hdd0:_tmp"); //Remove _tmp, if it exists. + + return status; +} + +#ifndef FSCK +#ifdef LOG_MESSAGES +static void WaitLogStart(s32 alarm_id, u16 time, void *common) +{ + iWakeupThread((int)common); +} + +void IopStartLog(const char *log) +{ + char blockdev[256]; + int len; + + getcwd(blockdev, sizeof(blockdev)); + len = strlen(blockdev); + if((len > 1) && blockdev[len - 1] != '/') + { + blockdev[len] = '/'; + len++; + } + strcpy(&blockdev[len], log); + + while(fileXioMount("tty0:", blockdev, O_WRONLY|O_TRUNC|O_CREAT) == -ENODEV) + { + SetAlarm(16 * 500, &WaitLogStart, (void*)GetThreadId()); + SleepThread(); + } +} + +void IopStopLog(void) +{ + fileXioUmount("tty0:"); +} +#endif +#endif diff --git a/system.h b/system.h new file mode 100644 index 0000000..eaaf8a7 --- /dev/null +++ b/system.h @@ -0,0 +1,50 @@ +enum BootDeviceIDs{ + BOOT_DEVICE_UNKNOWN = -1, + BOOT_DEVICE_MC0 = 0, + BOOT_DEVICE_MC1, + BOOT_DEVICE_MASS, + BOOT_DEVICE_HDD, + BOOT_DEVICE_HOST, + + BOOT_DEVICE_COUNT, +}; + +enum CONSOLE_REGION{ + CONSOLE_REGION_JAPAN = 0, + CONSOLE_REGION_USA, //USA and Asia + CONSOLE_REGION_EUROPE, + CONSOLE_REGION_CHINA, + + CONSOLE_REGION_COUNT +}; + +#ifndef FSCK +enum BAD_SECTOR_HANDLING_MODES{ + BAD_SECTOR_HANDLING_MODE_PROMPT = 0, + BAD_SECTOR_HANDLING_MODE_REMAP, + BAD_SECTOR_HANDLING_MODE_SKIP, + BAD_SECTOR_HANDLING_MODE_REMAP_ALL, + BAD_SECTOR_HANDLING_MODE_SKIP_ALL, + + BAD_SECTOR_HANDLING_MODE_COUNT +}; +#endif + +int GetBootDeviceID(void); +int GetConsoleRegion(void); +int GetConsoleVMode(void); +int IsPSX(void); +int ScanDisk(int unit); +#ifndef FSCK +int OptimizeDisk(int unit); +int SurfScanDisk(int unit); +int ZeroFillDisk(int unit); +#endif + +int HDDCheckSMARTStatus(void); +int HDDCheckSectorErrorStatus(void); +int HDDCheckPartErrorStatus(void); +int HDDCheckStatus(void); + +int SysBootDeviceInit(void); +int SysCreateThread(void *function, void *stack, unsigned int StackSize, void *arg, int priority);