/* * ReactOS kernel * Copyright (C) 2002, 2003, 2004, 2005 ReactOS Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS text-mode setup * FILE: base/setup/usetup/partlist.c * PURPOSE: Partition list functions * PROGRAMMER: Casper S. Hornstrup (chorns@users.sourceforge.net) */ #include "usetup.h" #define NDEBUG #include /* HELPERS FOR DISK AND PARTITION DESCRIPTIONS ******************************/ VOID GetPartitionTypeString( IN PPARTENTRY PartEntry, OUT PSTR strBuffer, IN ULONG cchBuffer) { if (PartEntry->PartitionType == PARTITION_ENTRY_UNUSED) { RtlStringCchCopyA(strBuffer, cchBuffer, MUIGetString(STRING_FORMATUNUSED)); } else if (IsContainerPartition(PartEntry->PartitionType)) { RtlStringCchCopyA(strBuffer, cchBuffer, MUIGetString(STRING_EXTENDED_PARTITION)); } else { UINT i; /* Do the table lookup */ if (PartEntry->DiskEntry->DiskStyle == PARTITION_STYLE_MBR) { for (i = 0; i < ARRAYSIZE(MbrPartitionTypes); ++i) { if (PartEntry->PartitionType == MbrPartitionTypes[i].Type) { RtlStringCchCopyA(strBuffer, cchBuffer, MbrPartitionTypes[i].Description); return; } } } #if 0 // TODO: GPT support! else if (PartEntry->DiskEntry->DiskStyle == PARTITION_STYLE_GPT) { for (i = 0; i < ARRAYSIZE(GptPartitionTypes); ++i) { if (IsEqualPartitionType(PartEntry->PartitionType, GptPartitionTypes[i].Guid)) { RtlStringCchCopyA(strBuffer, cchBuffer, GptPartitionTypes[i].Description); return; } } } #endif /* We are here because the partition type is unknown */ if (cchBuffer > 0) *strBuffer = '\0'; } if ((cchBuffer > 0) && (*strBuffer == '\0')) { RtlStringCchPrintfA(strBuffer, cchBuffer, MUIGetString(STRING_PARTTYPE), PartEntry->PartitionType); } } VOID PrettifySize1( IN OUT PULONGLONG Size, OUT PCSTR* Unit) { ULONGLONG DiskSize = *Size; if (DiskSize >= 10 * GB) /* 10 GB */ { DiskSize = RoundingDivide(DiskSize, GB); *Unit = MUIGetString(STRING_GB); } else { DiskSize = RoundingDivide(DiskSize, MB); if (DiskSize == 0) DiskSize = 1; *Unit = MUIGetString(STRING_MB); } *Size = DiskSize; } VOID PrettifySize2( IN OUT PULONGLONG Size, OUT PCSTR* Unit) { ULONGLONG PartSize = *Size; #if 0 if (PartSize >= 10 * GB) /* 10 GB */ { PartSize = RoundingDivide(PartSize, GB); *Unit = MUIGetString(STRING_GB); } else #endif if (PartSize >= 10 * MB) /* 10 MB */ { PartSize = RoundingDivide(PartSize, MB); *Unit = MUIGetString(STRING_MB); } else { PartSize = RoundingDivide(PartSize, KB); *Unit = MUIGetString(STRING_KB); } *Size = PartSize; } VOID PartitionDescription( IN PPARTENTRY PartEntry, OUT PSTR strBuffer, IN SIZE_T cchBuffer) { PSTR pBuffer = strBuffer; size_t cchBufferSize = cchBuffer; ULONGLONG PartSize; PCSTR Unit; /* Get the partition size */ PartSize = PartEntry->SectorCount.QuadPart * PartEntry->DiskEntry->BytesPerSector; PrettifySize2(&PartSize, &Unit); if (PartEntry->IsPartitioned == FALSE) { /* Unpartitioned space: Just display the description and size */ RtlStringCchPrintfExA(pBuffer, cchBufferSize, &pBuffer, &cchBufferSize, 0, " %s%-.30s", PartEntry->LogicalPartition ? " " : "", // Optional indentation MUIGetString(STRING_UNPSPACE)); RtlStringCchPrintfA(pBuffer, cchBufferSize, "%*s%6I64u %s", 38 - min(strlen(strBuffer), 38), "", // Indentation PartSize, Unit); return; } // // NOTE: This could be done with the next case. // if ((PartEntry->DiskEntry->DiskStyle == PARTITION_STYLE_MBR) && IsContainerPartition(PartEntry->PartitionType)) { /* Extended partition container: Just display the partition's type and size */ RtlStringCchPrintfExA(pBuffer, cchBufferSize, &pBuffer, &cchBufferSize, 0, " %-.30s", MUIGetString(STRING_EXTENDED_PARTITION)); RtlStringCchPrintfA(pBuffer, cchBufferSize, "%*s%6I64u %s", 38 - min(strlen(strBuffer), 38), "", // Indentation PartSize, Unit); return; } /* * Not an extended partition container. */ /* Drive letter and partition number */ RtlStringCchPrintfExA(pBuffer, cchBufferSize, &pBuffer, &cchBufferSize, 0, "%c%c %c %s(%lu) ", (PartEntry->DriveLetter == 0) ? '-' : (CHAR)PartEntry->DriveLetter, (PartEntry->DriveLetter == 0) ? '-' : ':', PartEntry->BootIndicator ? '*' : ' ', PartEntry->LogicalPartition ? " " : "", // Optional indentation PartEntry->PartitionNumber); /* * If the volume's file system is recognized, display the volume label * (if any) and the file system name. Otherwise, display the partition * type if it's not a new partition. */ if (!PartEntry->New && *PartEntry->FileSystem && _wcsicmp(PartEntry->FileSystem, L"RAW") != 0) { size_t cchLabelSize = 0; if (*PartEntry->VolumeLabel) { RtlStringCchPrintfExA(pBuffer, cchBufferSize, &pBuffer, &cchLabelSize, 0, "\"%-.11S\" ", PartEntry->VolumeLabel); cchLabelSize = cchBufferSize - cchLabelSize; // Actual length of the label part. cchBufferSize -= cchLabelSize; // And reset cchBufferSize to what it should be. } // TODO: Group this part together with the similar one // from below once the strings are in the same encoding... RtlStringCchPrintfExA(pBuffer, cchBufferSize, &pBuffer, &cchBufferSize, 0, "[%-.*S]", /* The minimum length can be at most 11 since * cchLabelSize can be at most == 11 + 3 == 14 */ 25 - min(cchLabelSize, 25), PartEntry->FileSystem); } else { CHAR PartTypeString[32]; PCSTR PartType = PartTypeString; if (PartEntry->New) { /* Use this description if the partition is new (and thus, not formatted) */ PartType = MUIGetString(STRING_UNFORMATTED); } else { /* If the partition is not new but its file system is not recognized * (or is not formatted), use the partition type description. */ GetPartitionTypeString(PartEntry, PartTypeString, ARRAYSIZE(PartTypeString)); PartType = PartTypeString; } if (!PartType || !*PartType) { PartType = MUIGetString(STRING_FORMATUNKNOWN); } // TODO: Group this part together with the similar one // from above once the strings are in the same encoding... RtlStringCchPrintfExA(pBuffer, cchBufferSize, &pBuffer, &cchBufferSize, 0, "[%-.*s]", 25, PartType); } /* Show the remaining free space only if a FS is mounted */ // FIXME: We don't support that yet! #if 0 if (*PartEntry->FileSystem) { RtlStringCchPrintfA(pBuffer, cchBufferSize, "%*s%6I64u %s (%6I64u %s %s)", 38 - min(strlen(strBuffer), 38), "", // Indentation PartSize, Unit, PartFreeSize, Unit, "free"); } else #endif { RtlStringCchPrintfA(pBuffer, cchBufferSize, "%*s%6I64u %s", 38 - min(strlen(strBuffer), 38), "", // Indentation PartSize, Unit); } } VOID DiskDescription( IN PDISKENTRY DiskEntry, OUT PSTR strBuffer, IN SIZE_T cchBuffer) { ULONGLONG DiskSize; PCSTR Unit; /* Get the disk size */ DiskSize = DiskEntry->SectorCount.QuadPart * DiskEntry->BytesPerSector; PrettifySize1(&DiskSize, &Unit); // // FIXME: We *MUST* use TXTSETUP.SIF strings from section "DiskDriverMap" !! // if (DiskEntry->DriverName.Length > 0) { RtlStringCchPrintfA(strBuffer, cchBuffer, MUIGetString(STRING_HDDINFO_1), DiskSize, Unit, DiskEntry->DiskNumber, DiskEntry->Port, DiskEntry->Bus, DiskEntry->Id, &DiskEntry->DriverName, DiskEntry->DiskStyle == PARTITION_STYLE_MBR ? "MBR" : DiskEntry->DiskStyle == PARTITION_STYLE_GPT ? "GPT" : "RAW"); } else { RtlStringCchPrintfA(strBuffer, cchBuffer, MUIGetString(STRING_HDDINFO_2), DiskSize, Unit, DiskEntry->DiskNumber, DiskEntry->Port, DiskEntry->Bus, DiskEntry->Id, DiskEntry->DiskStyle == PARTITION_STYLE_MBR ? "MBR" : DiskEntry->DiskStyle == PARTITION_STYLE_GPT ? "GPT" : "RAW"); } } /* FUNCTIONS ****************************************************************/ VOID InitPartitionListUi( IN OUT PPARTLIST_UI ListUi, IN PPARTLIST List, IN PPARTENTRY CurrentEntry OPTIONAL, IN SHORT Left, IN SHORT Top, IN SHORT Right, IN SHORT Bottom) { ListUi->List = List; // ListUi->FirstShown = NULL; // ListUi->LastShown = NULL; ListUi->Left = Left; ListUi->Top = Top; ListUi->Right = Right; ListUi->Bottom = Bottom; ListUi->Line = 0; ListUi->Offset = 0; // ListUi->Redraw = TRUE; /* Search for first usable disk and partition */ if (!CurrentEntry) { ListUi->CurrentDisk = NULL; ListUi->CurrentPartition = NULL; if (!IsListEmpty(&List->DiskListHead)) { ListUi->CurrentDisk = CONTAINING_RECORD(List->DiskListHead.Flink, DISKENTRY, ListEntry); if (!IsListEmpty(&ListUi->CurrentDisk->PrimaryPartListHead)) { ListUi->CurrentPartition = CONTAINING_RECORD(ListUi->CurrentDisk->PrimaryPartListHead.Flink, PARTENTRY, ListEntry); } } } else { /* * The CurrentEntry must belong to the associated partition list, * and the latter must therefore not be empty. */ ASSERT(!IsListEmpty(&List->DiskListHead)); ASSERT(CurrentEntry->DiskEntry->PartList == List); ListUi->CurrentPartition = CurrentEntry; ListUi->CurrentDisk = CurrentEntry->DiskEntry; } } static VOID PrintEmptyLine( IN PPARTLIST_UI ListUi) { COORD coPos; ULONG Written; USHORT Width; USHORT Height; Width = ListUi->Right - ListUi->Left - 1; Height = ListUi->Bottom - ListUi->Top - 2; coPos.X = ListUi->Left + 1; coPos.Y = ListUi->Top + 1 + ListUi->Line; if (ListUi->Line >= 0 && ListUi->Line <= Height) { FillConsoleOutputAttribute(StdOutput, FOREGROUND_WHITE | BACKGROUND_BLUE, Width, coPos, &Written); FillConsoleOutputCharacterA(StdOutput, ' ', Width, coPos, &Written); } ListUi->Line++; } static VOID PrintPartitionData( IN PPARTLIST_UI ListUi, IN PDISKENTRY DiskEntry, IN PPARTENTRY PartEntry) { COORD coPos; ULONG Written; USHORT Width; USHORT Height; UCHAR Attribute; CHAR LineBuffer[100]; PartitionDescription(PartEntry, LineBuffer, ARRAYSIZE(LineBuffer)); Width = ListUi->Right - ListUi->Left - 1; Height = ListUi->Bottom - ListUi->Top - 2; coPos.X = ListUi->Left + 1; coPos.Y = ListUi->Top + 1 + ListUi->Line; Attribute = (ListUi->CurrentDisk == DiskEntry && ListUi->CurrentPartition == PartEntry) ? FOREGROUND_BLUE | BACKGROUND_WHITE : FOREGROUND_WHITE | BACKGROUND_BLUE; if (ListUi->Line >= 0 && ListUi->Line <= Height) { FillConsoleOutputCharacterA(StdOutput, ' ', Width, coPos, &Written); } coPos.X += 4; Width -= 8; if (ListUi->Line >= 0 && ListUi->Line <= Height) { FillConsoleOutputAttribute(StdOutput, Attribute, Width, coPos, &Written); } coPos.X++; Width -= 2; if (ListUi->Line >= 0 && ListUi->Line <= Height) { WriteConsoleOutputCharacterA(StdOutput, LineBuffer, min(strlen(LineBuffer), Width), coPos, &Written); } ListUi->Line++; } static VOID PrintDiskData( IN PPARTLIST_UI ListUi, IN PDISKENTRY DiskEntry) { PPARTENTRY PrimaryPartEntry, LogicalPartEntry; PLIST_ENTRY PrimaryEntry, LogicalEntry; COORD coPos; ULONG Written; USHORT Width; USHORT Height; CHAR LineBuffer[100]; DiskDescription(DiskEntry, LineBuffer, ARRAYSIZE(LineBuffer)); Width = ListUi->Right - ListUi->Left - 1; Height = ListUi->Bottom - ListUi->Top - 2; coPos.X = ListUi->Left + 1; coPos.Y = ListUi->Top + 1 + ListUi->Line; if (ListUi->Line >= 0 && ListUi->Line <= Height) { FillConsoleOutputAttribute(StdOutput, FOREGROUND_WHITE | BACKGROUND_BLUE, Width, coPos, &Written); FillConsoleOutputCharacterA(StdOutput, ' ', Width, coPos, &Written); } coPos.X++; if (ListUi->Line >= 0 && ListUi->Line <= Height) { WriteConsoleOutputCharacterA(StdOutput, LineBuffer, min((USHORT)strlen(LineBuffer), Width - 2), coPos, &Written); } ListUi->Line++; /* Print separator line */ PrintEmptyLine(ListUi); /* Print partition lines */ for (PrimaryEntry = DiskEntry->PrimaryPartListHead.Flink; PrimaryEntry != &DiskEntry->PrimaryPartListHead; PrimaryEntry = PrimaryEntry->Flink) { PrimaryPartEntry = CONTAINING_RECORD(PrimaryEntry, PARTENTRY, ListEntry); PrintPartitionData(ListUi, DiskEntry, PrimaryPartEntry); if (IsContainerPartition(PrimaryPartEntry->PartitionType)) { for (LogicalEntry = DiskEntry->LogicalPartListHead.Flink; LogicalEntry != &DiskEntry->LogicalPartListHead; LogicalEntry = LogicalEntry->Flink) { LogicalPartEntry = CONTAINING_RECORD(LogicalEntry, PARTENTRY, ListEntry); PrintPartitionData(ListUi, DiskEntry, LogicalPartEntry); } } } /* Print separator line */ PrintEmptyLine(ListUi); } VOID DrawPartitionList( IN PPARTLIST_UI ListUi) { PPARTLIST List = ListUi->List; PLIST_ENTRY Entry, Entry2; PDISKENTRY DiskEntry; PPARTENTRY PartEntry = NULL; COORD coPos; ULONG Written; USHORT Width; USHORT Height; SHORT i; SHORT CurrentDiskLine; SHORT CurrentPartLine; SHORT LastLine; BOOLEAN CurrentPartLineFound = FALSE; BOOLEAN CurrentDiskLineFound = FALSE; Width = ListUi->Right - ListUi->Left - 1; Height = ListUi->Bottom - ListUi->Top - 2; /* Calculate the line of the current disk and partition */ CurrentDiskLine = 0; CurrentPartLine = 0; LastLine = 0; for (Entry = List->DiskListHead.Flink; Entry != &List->DiskListHead; Entry = Entry->Flink) { DiskEntry = CONTAINING_RECORD(Entry, DISKENTRY, ListEntry); LastLine += 2; if (CurrentPartLineFound == FALSE) { CurrentPartLine += 2; } for (Entry2 = DiskEntry->PrimaryPartListHead.Flink; Entry2 != &DiskEntry->PrimaryPartListHead; Entry2 = Entry2->Flink) { PartEntry = CONTAINING_RECORD(Entry2, PARTENTRY, ListEntry); if (PartEntry == ListUi->CurrentPartition) { CurrentPartLineFound = TRUE; } if (CurrentPartLineFound == FALSE) { CurrentPartLine++; } LastLine++; } if (CurrentPartLineFound == FALSE) { for (Entry2 = DiskEntry->LogicalPartListHead.Flink; Entry2 != &DiskEntry->LogicalPartListHead; Entry2 = Entry2->Flink) { PartEntry = CONTAINING_RECORD(Entry2, PARTENTRY, ListEntry); if (PartEntry == ListUi->CurrentPartition) { CurrentPartLineFound = TRUE; } if (CurrentPartLineFound == FALSE) { CurrentPartLine++; } LastLine++; } } if (DiskEntry == ListUi->CurrentDisk) { CurrentDiskLineFound = TRUE; } if (Entry->Flink != &List->DiskListHead) { if (CurrentDiskLineFound == FALSE) { CurrentPartLine++; CurrentDiskLine = CurrentPartLine; } LastLine++; } else { LastLine--; } } /* If it possible, make the disk name visible */ if (CurrentPartLine < ListUi->Offset) { ListUi->Offset = CurrentPartLine; } else if (CurrentPartLine - ListUi->Offset > Height) { ListUi->Offset = CurrentPartLine - Height; } if (CurrentDiskLine < ListUi->Offset && CurrentPartLine - CurrentDiskLine < Height) { ListUi->Offset = CurrentDiskLine; } /* Draw upper left corner */ coPos.X = ListUi->Left; coPos.Y = ListUi->Top; FillConsoleOutputCharacterA(StdOutput, 0xDA, // '+', 1, coPos, &Written); /* Draw upper edge */ coPos.X = ListUi->Left + 1; coPos.Y = ListUi->Top; if (ListUi->Offset == 0) { FillConsoleOutputCharacterA(StdOutput, 0xC4, // '-', Width, coPos, &Written); } else { FillConsoleOutputCharacterA(StdOutput, 0xC4, // '-', Width - 4, coPos, &Written); coPos.X = ListUi->Right - 5; WriteConsoleOutputCharacterA(StdOutput, "(\x18)", // "(up)" 3, coPos, &Written); coPos.X = ListUi->Right - 2; FillConsoleOutputCharacterA(StdOutput, 0xC4, // '-', 2, coPos, &Written); } /* Draw upper right corner */ coPos.X = ListUi->Right; coPos.Y = ListUi->Top; FillConsoleOutputCharacterA(StdOutput, 0xBF, // '+', 1, coPos, &Written); /* Draw left and right edge */ for (i = ListUi->Top + 1; i < ListUi->Bottom; i++) { coPos.X = ListUi->Left; coPos.Y = i; FillConsoleOutputCharacterA(StdOutput, 0xB3, // '|', 1, coPos, &Written); coPos.X = ListUi->Right; FillConsoleOutputCharacterA(StdOutput, 0xB3, //'|', 1, coPos, &Written); } /* Draw lower left corner */ coPos.X = ListUi->Left; coPos.Y = ListUi->Bottom; FillConsoleOutputCharacterA(StdOutput, 0xC0, // '+', 1, coPos, &Written); /* Draw lower edge */ coPos.X = ListUi->Left + 1; coPos.Y = ListUi->Bottom; if (LastLine - ListUi->Offset <= Height) { FillConsoleOutputCharacterA(StdOutput, 0xC4, // '-', Width, coPos, &Written); } else { FillConsoleOutputCharacterA(StdOutput, 0xC4, // '-', Width - 4, coPos, &Written); coPos.X = ListUi->Right - 5; WriteConsoleOutputCharacterA(StdOutput, "(\x19)", // "(down)" 3, coPos, &Written); coPos.X = ListUi->Right - 2; FillConsoleOutputCharacterA(StdOutput, 0xC4, // '-', 2, coPos, &Written); } /* Draw lower right corner */ coPos.X = ListUi->Right; coPos.Y = ListUi->Bottom; FillConsoleOutputCharacterA(StdOutput, 0xD9, // '+', 1, coPos, &Written); /* Print list entries */ ListUi->Line = -ListUi->Offset; for (Entry = List->DiskListHead.Flink; Entry != &List->DiskListHead; Entry = Entry->Flink) { DiskEntry = CONTAINING_RECORD(Entry, DISKENTRY, ListEntry); /* Print disk entry */ PrintDiskData(ListUi, DiskEntry); } } VOID ScrollDownPartitionList( IN PPARTLIST_UI ListUi) { PPARTENTRY NextPart = GetNextPartition(ListUi->List, ListUi->CurrentPartition); if (NextPart) { ListUi->CurrentPartition = NextPart; ListUi->CurrentDisk = NextPart->DiskEntry; DrawPartitionList(ListUi); } } VOID ScrollUpPartitionList( IN PPARTLIST_UI ListUi) { PPARTENTRY PrevPart = GetPrevPartition(ListUi->List, ListUi->CurrentPartition); if (PrevPart) { ListUi->CurrentPartition = PrevPart; ListUi->CurrentDisk = PrevPart->DiskEntry; DrawPartitionList(ListUi); } } /* EOF */