You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
872 lines
23 KiB
872 lines
23 KiB
/*++
|
|
|
|
Copyright (c) 1990, 1991 Microsoft Corporation
|
|
|
|
|
|
Module Name:
|
|
|
|
hwheap.c
|
|
|
|
Abstract:
|
|
|
|
This module goes through ROM area and tries to pick up all the ROM
|
|
blocks.
|
|
|
|
Author:
|
|
|
|
Shie-Lin Tzong (shielint) 21-Jan-92
|
|
|
|
|
|
Environment:
|
|
|
|
Real mode.
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "hwdetect.h"
|
|
#include "hwvbios.h"
|
|
|
|
FPTEMPORARY_ROM_BLOCK BlockHead;
|
|
FPTEMPORARY_ROM_BLOCK BlockPointer;
|
|
|
|
BOOLEAN
|
|
AddRomBlock (
|
|
ULONG RomAddress,
|
|
ULONG RomSize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine adds a ROM/RAM block to our ROM list.
|
|
|
|
Arguments:
|
|
|
|
RomAddress - the starting address of the ROM/RAM block to be added.
|
|
|
|
RomSize - the size of the ROM/RAM block (in byte).
|
|
|
|
Return Value:
|
|
|
|
A value of TRUE is returned if success. Otherwise, a value of
|
|
FALSE is returned.
|
|
|
|
--*/
|
|
|
|
{
|
|
LONG AddSize;
|
|
ULONG AddAddress;
|
|
FPTEMPORARY_ROM_BLOCK pCurrentBlock, pNextBlock;
|
|
ULONG CurrentBlock, NextBlock, AddBlock;
|
|
ULONG EndAddBlock, EndCurrentBlock, EndNextBlock;
|
|
BOOLEAN fOverlap=FALSE;
|
|
|
|
pCurrentBlock = NULL;
|
|
pNextBlock = NULL;
|
|
AddSize = RomSize;
|
|
AddAddress = RomAddress;
|
|
AddBlock = RomAddress;
|
|
|
|
//
|
|
// If there are other blocks, make sure there is no overlap with them
|
|
//
|
|
|
|
if (BlockHead) {
|
|
|
|
pCurrentBlock = BlockHead;
|
|
pNextBlock = pCurrentBlock->Next;
|
|
CurrentBlock = pCurrentBlock->RomBlock.Address;
|
|
EndCurrentBlock = CurrentBlock + pCurrentBlock->RomBlock.Size;
|
|
EndAddBlock = RomAddress + RomSize;
|
|
|
|
while (pCurrentBlock!=NULL) {
|
|
|
|
//
|
|
// calculate location of next block (if it's there)
|
|
//
|
|
|
|
if(pNextBlock) {
|
|
NextBlock = pNextBlock->RomBlock.Address;
|
|
EndNextBlock = NextBlock + pNextBlock->RomBlock.Size;
|
|
}
|
|
|
|
//
|
|
// if overlapping with current block, then stop and
|
|
// resolve overlap
|
|
//
|
|
|
|
if((RomAddress < EndCurrentBlock)&& (RomAddress >= CurrentBlock)){
|
|
fOverlap = TRUE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// if add block is lower than the current one,
|
|
// or there is not a next block, then no need to search further
|
|
//
|
|
|
|
if((EndAddBlock <= CurrentBlock) || (pNextBlock == NULL)) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// if block is lower than next one, but greater than current
|
|
// one, we have found the right area
|
|
//
|
|
|
|
if ((EndAddBlock <= NextBlock) && (AddBlock >= EndCurrentBlock)) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// if conflicting with next block, stop searching and
|
|
// resolve conflict after this loop
|
|
//
|
|
|
|
if((EndAddBlock > NextBlock) && (EndAddBlock <= EndNextBlock)) {
|
|
fOverlap = TRUE;
|
|
break;
|
|
}
|
|
|
|
pCurrentBlock = pNextBlock;
|
|
pNextBlock = pCurrentBlock->Next;
|
|
CurrentBlock = NextBlock;
|
|
EndCurrentBlock = EndNextBlock;
|
|
}
|
|
}
|
|
|
|
//
|
|
// if we have reached this point, there may be a conflict
|
|
// with the current block.
|
|
//
|
|
|
|
if(fOverlap) {
|
|
if(AddBlock < EndCurrentBlock) {
|
|
AddAddress = EndCurrentBlock;
|
|
AddSize = EndAddBlock - EndCurrentBlock;
|
|
if(AddSize <= 0) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
if((pNextBlock != NULL) && (EndAddBlock > NextBlock)) {
|
|
AddSize = NextBlock - AddBlock;
|
|
if(AddSize <= 0) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
BlockPointer->RomBlock.Address = AddAddress;
|
|
BlockPointer->RomBlock.Size = AddSize;
|
|
|
|
//
|
|
// Put it on the list.
|
|
// if it belongs on top, put it there
|
|
//
|
|
|
|
if ((pCurrentBlock == NULL)||
|
|
((pCurrentBlock == BlockHead) && (CurrentBlock > AddBlock))) {
|
|
BlockPointer->Next = pCurrentBlock;
|
|
BlockHead = BlockPointer;
|
|
} else {
|
|
|
|
//
|
|
// else add to middle or bottom depending on NextBlock
|
|
//
|
|
|
|
BlockPointer->Next = pNextBlock;
|
|
pCurrentBlock->Next = BlockPointer;
|
|
}
|
|
BlockPointer++; // Note that this works because
|
|
// we know the offset part of
|
|
// the addr is always < 64k.
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN
|
|
ScanRomBlocks(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine scans the ROM IO area and checks for 55AA at every
|
|
512 bytes for valid ROM blocks.
|
|
|
|
|
|
NOTES:
|
|
|
|
-------------
|
|
| |
|
|
| |
|
|
------------------100000
|
|
^ | |
|
|
| | |
|
|
| -------------f0000 (ROMBIOS_START) ---
|
|
| | | ^
|
|
| | | |
|
|
EXTROM_LEN -------------e0000 (PS2BIOS_START) --- |
|
|
| | | ^ Search |
|
|
| | | Search | Range |
|
|
| -------------d0000 Range | on AT |
|
|
| | | on PS/2| |
|
|
V | | V V
|
|
------------------c0000 (EXTROM_START) --- ---
|
|
|
|
ON AT:
|
|
Scan through EXTROM_START-effff for ROM Blocks
|
|
ON PS2
|
|
Scan through EXTROM_START-dffff for ROM Blocks
|
|
|
|
Arguments:
|
|
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG BlockSize;
|
|
BOOLEAN Success = TRUE;
|
|
FPUCHAR Current;
|
|
ULONG RomAddr, RomEnd;
|
|
|
|
//
|
|
// As per the machine type restrict the search range
|
|
//
|
|
|
|
MAKE_FP(Current, EXTROM_START);
|
|
RomAddr = EXTROM_START;
|
|
|
|
if ((HwBusType == MACHINE_TYPE_MCA) ||
|
|
(BiosSystemEnvironment.Model == PS2_L40) ||
|
|
(BiosSystemEnvironment.Model == PS1_386) ||
|
|
(BiosSystemEnvironment.Model == PS2_AT)) {
|
|
|
|
RomEnd = PS2BIOS_START;
|
|
} else {
|
|
RomEnd = ROMBIOS_START;
|
|
}
|
|
|
|
while (RomAddr < RomEnd) {
|
|
|
|
if (((FPROM_HEADER)Current)->Signature == ROM_HEADER_SIGNATURE) {
|
|
|
|
BlockSize = (ULONG)((FPROM_HEADER)Current)->NumberBlocks * BLOCKSIZE;
|
|
|
|
if ((RomAddr + BlockSize) > RomEnd) {
|
|
BlockSize = RomEnd - RomAddr;
|
|
}
|
|
|
|
//
|
|
// V7 VRAM card does not correctly report its BlockSize. Since
|
|
// this is a very popular video card, we provide a workaround
|
|
// for it.
|
|
//
|
|
|
|
if ((RomAddr == 0xC0000) && (BlockSize < 0x8000)) {
|
|
BlockSize = 0x8000;
|
|
}
|
|
if (BlockSize != 0) {
|
|
if (!AddRomBlock(RomAddr, BlockSize)) {
|
|
Success = FALSE;
|
|
break;
|
|
}
|
|
RomAddr += BlockSize;
|
|
RomAddr = ALIGN_UP(RomAddr, ROM_HEADER_INCREMENT);
|
|
MAKE_FP(Current, RomAddr);
|
|
continue;
|
|
}
|
|
}
|
|
RomAddr += ROM_HEADER_INCREMENT;
|
|
MAKE_FP(Current, RomAddr);
|
|
}
|
|
|
|
//
|
|
// Last but not least, add the system ROM to the list
|
|
//
|
|
|
|
if (Success) {
|
|
|
|
RomAddr = ROMBIOS_START;
|
|
BlockSize = ROMBIOS_LEN;
|
|
if ((HwBusType == MACHINE_TYPE_MCA) ||
|
|
(BiosSystemEnvironment.Model == PS2_L40) ||
|
|
(BiosSystemEnvironment.Model == PS1_386) ||
|
|
(BiosSystemEnvironment.Model == PS2_AT)) {
|
|
RomAddr = PS2BIOS_START;
|
|
BlockSize = PS2BIOS_LEN;
|
|
}
|
|
|
|
if (!AddRomBlock(RomAddr, BlockSize)) {
|
|
Success = FALSE;
|
|
}
|
|
}
|
|
|
|
return Success;
|
|
}
|
|
|
|
FPTEMPORARY_ROM_BLOCK
|
|
MatchRomBlock (
|
|
ULONG PhysicalAddr
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine finds the ROM block which the 'PhysicalAddr' is in.
|
|
|
|
Arguments:
|
|
|
|
PhysicalAddr - the physical address ...
|
|
|
|
Return Value:
|
|
|
|
A pointer to the detected ROM block.
|
|
|
|
--*/
|
|
|
|
{
|
|
FPTEMPORARY_ROM_BLOCK CurrentBlock;
|
|
ROM_BLOCK RomBlock;
|
|
|
|
CurrentBlock = BlockHead;
|
|
while (CurrentBlock) {
|
|
RomBlock = CurrentBlock->RomBlock;
|
|
if (RomBlock.Address <= PhysicalAddr &&
|
|
RomBlock.Address + RomBlock.Size > PhysicalAddr) {
|
|
break;
|
|
} else {
|
|
CurrentBlock = CurrentBlock->Next;
|
|
}
|
|
}
|
|
return(CurrentBlock);
|
|
}
|
|
|
|
BOOLEAN
|
|
IsSameRomBlock (
|
|
FPTEMPORARY_ROM_BLOCK Source,
|
|
FPTEMPORARY_ROM_BLOCK Destination
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine checks if the passed in ROM blocks contain the same
|
|
information. This ususally happens when the two ROM blocks are for
|
|
video ROM and shadowed video ROM.
|
|
|
|
Arguments:
|
|
|
|
Source - the source ROM block.
|
|
|
|
Destination - the ROM block to compare with.
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN TRUE if the two ROM blocks are the same else FALSE is returned.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
if (Source == NULL || Destination == NULL) {
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// First make sure their sizes are the same.
|
|
//
|
|
|
|
if (Source->RomBlock.Size == Destination->RomBlock.Size) {
|
|
if (!HwRomCompare(Source->RomBlock.Address,
|
|
Destination->RomBlock.Address,
|
|
Source->RomBlock.Size)){
|
|
return(TRUE);
|
|
}
|
|
}
|
|
return(FALSE);
|
|
|
|
}
|
|
|
|
VOID
|
|
CheckVideoRom (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine checks if the int 10h video handler is in the video
|
|
ROM block detected by us. If not, the video ROM must have been
|
|
remapped/shadowed to other area (usually 0xE0000.)
|
|
|
|
NOTE: In this function, I commented out the code which removes the
|
|
Video ROM block if it has been shadowed. I found out
|
|
machine POST code does not modify ALL the VIDEO ROM related
|
|
pointers.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG Vector, Handler, VectorAddr = 0x10 * sizeof(ULONG);
|
|
FPULONG pVectorAddr;
|
|
FPTEMPORARY_ROM_BLOCK RomBlock, VideoRomBlock;
|
|
ULONG Size;
|
|
|
|
MAKE_FP(pVectorAddr, VectorAddr);
|
|
Vector = *pVectorAddr;
|
|
Handler = ((Vector >> 16) << 4) + (Vector & 0xffff);
|
|
RomBlock = MatchRomBlock(Handler);
|
|
|
|
//
|
|
// Check if the int 10h handler falls in one of our ROM blocks.
|
|
//
|
|
|
|
if (RomBlock) {
|
|
if (RomBlock->RomBlock.Address >= 0xC0000 &&
|
|
RomBlock->RomBlock.Address < 0xC8000) {
|
|
|
|
//
|
|
// if int 10h handler is in the standard video ROM area, we simply
|
|
// return. Either the video ROM is not shadowed or it
|
|
// is a in-place shadow.
|
|
//
|
|
|
|
return;
|
|
} else {
|
|
|
|
//
|
|
// The ROM block associated with the int 10h handler is not in
|
|
// standard video bios ROM area. It must have been mapped to
|
|
// the current location. We now need to make sure we have the
|
|
// ROM block which contains the 40:a8 VGA parameter.
|
|
//
|
|
|
|
VectorAddr = VGA_PARAMETER_POINTER;
|
|
MAKE_FP(pVectorAddr, VectorAddr);
|
|
Vector = *pVectorAddr;
|
|
Handler = ((Vector >> 16) << 4) + (Vector & 0xffff);
|
|
VideoRomBlock = MatchRomBlock(Handler);
|
|
if (VideoRomBlock == NULL) {
|
|
|
|
//
|
|
// We did not find the Video ROM associated with the
|
|
// VGA parameters. Try detect it.
|
|
//
|
|
|
|
//
|
|
// In the following memory comparison, we skip the first 16 bytes.
|
|
// Because most likely the reason we did not find the standard
|
|
// Video ROM is because the signature word is missing.
|
|
//
|
|
|
|
Handler = (Handler & 0xF0000) +
|
|
(RomBlock->RomBlock.Address & 0xFFFF);
|
|
if (!HwRomCompare(RomBlock->RomBlock.Address + 0x10,
|
|
Handler + 0x10,
|
|
RomBlock->RomBlock.Size - 0x10)) {
|
|
//
|
|
// Note: The old code looked like this for many years:
|
|
//
|
|
|
|
/*
|
|
if ((Handler & 0xFFFF == 0) && (RomBlock->RomBlock.Size < 0x8000)){
|
|
Size = 0x8000;
|
|
} else {
|
|
Size = RomBlock->RomBlock.Size;
|
|
}
|
|
*/
|
|
|
|
//
|
|
// But (Handler & 0xFFFF == 0) is always false. So
|
|
// Size always equals RomBlock->RomBlock.Size. Rather than
|
|
// fix the comparison, which might cause machines to break,
|
|
// I'm going to assume that it's fine to just make the code
|
|
// do what it's always done. - JakeO 8/9/00
|
|
//
|
|
|
|
Size = RomBlock->RomBlock.Size;
|
|
|
|
AddRomBlock(Handler, Size);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// There is no ROM block associated with the int 10h handler.
|
|
// We can find the shadowed video ROM block if:
|
|
// We detected the original video ROM in 0xC0000 - 0xC8000 range
|
|
//
|
|
|
|
VideoRomBlock = MatchRomBlock((Handler & 0xFFFF) + 0xC0000);
|
|
if (VideoRomBlock != NULL) {
|
|
|
|
//
|
|
// In the following memory comparison, we skip the first 16 bytes.
|
|
// Because most likely the reason we did not find the shadow rom
|
|
// is the signature word is missing.
|
|
//
|
|
|
|
if (!HwRomCompare(VideoRomBlock->RomBlock.Address + 0x10,
|
|
(Handler & 0xF0000) +
|
|
(VideoRomBlock->RomBlock.Address & 0xFFFF) + 0x10,
|
|
VideoRomBlock->RomBlock.Size - 0x10)) {
|
|
|
|
AddRomBlock((VideoRomBlock->RomBlock.Address & 0xFFFF) +
|
|
(Handler & 0xF0000),
|
|
VideoRomBlock->RomBlock.Size);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID
|
|
GetRomBlocks(
|
|
FPUCHAR ReservedBuffer,
|
|
PUSHORT Size
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine scans the ROM IO area and collects all the ROM blocks.
|
|
|
|
Arguments:
|
|
|
|
ReservedBuffer - Supplies a far pointer to the buffer.
|
|
|
|
Size - Supplies a near pointer to a variable to receive the size
|
|
of the ROM block.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
FPTEMPORARY_ROM_BLOCK Source;
|
|
ULONG StartAddr, EndAddr;
|
|
FPUSHORT TestAddr;
|
|
FPROM_BLOCK Destination;
|
|
USHORT BufferSize;
|
|
ULONG EBiosAddress = 0, EBiosLength = 0;
|
|
ULONG far *EBiosInformation = (ULONG far *)
|
|
((DOS_BEGIN_SEGMENT << 4) + EBIOS_INFO_OFFSET);
|
|
|
|
//
|
|
// First we reserve the max space needed and build our temporary rom
|
|
// block list in the heap space below the space reservedand. After
|
|
// the temporary list is built, we then copy it to the caller supplied
|
|
// reserved space.
|
|
//
|
|
|
|
BlockPointer = (FPTEMPORARY_ROM_BLOCK)HwAllocateHeap(0, FALSE);
|
|
BlockHead = NULL;
|
|
*Size = 0;
|
|
|
|
GetBiosSystemEnvironment((PUCHAR)&BiosSystemEnvironment);
|
|
if (BiosSystemEnvironment.ConfigurationFlags & 0x4) {
|
|
|
|
//
|
|
// If extened BIOS data area is allocated, we will find out its
|
|
// location and size and save in ROM blocks.
|
|
//
|
|
|
|
_asm {
|
|
push es
|
|
mov ah, 0xC1
|
|
int 15h
|
|
jc short Exit
|
|
|
|
cmp ah, 0x86
|
|
je short Exit
|
|
|
|
mov bx, 0
|
|
mov dx, 0
|
|
mov ax, 0
|
|
mov al, es:[bx]
|
|
shl ax, 10
|
|
mov word ptr EBiosLength, ax
|
|
mov ax, es
|
|
mov dx, es
|
|
shl ax, 4
|
|
shr dx, 12
|
|
mov word ptr EBiosAddress, ax
|
|
mov word ptr EBiosAddress + 2, dx
|
|
Exit:
|
|
pop es
|
|
}
|
|
}
|
|
|
|
//
|
|
// Save the Extended BIOS data area address and size at 700:40
|
|
//
|
|
|
|
if (EBiosLength) {
|
|
*EBiosInformation++ = EBiosAddress;
|
|
*EBiosInformation = EBiosLength;
|
|
} else {
|
|
*EBiosInformation++ = 0L;
|
|
*EBiosInformation = 0L;
|
|
}
|
|
if (!ScanRomBlocks()) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// On some machines, when they shadow video ROM from 0xC0000 to
|
|
// 0xE0000, they copy code only (no signature.) So, we need
|
|
// special code to work around the problem.
|
|
//
|
|
|
|
CheckVideoRom();
|
|
|
|
//
|
|
// Now do our special hack for IBM. On SOME IBM PCs, they use
|
|
// E0000-FFFFF for system BIOS (even on non PS/2 machines.) Since
|
|
// system BIOS has no ROM header, it is very hard to know the starting
|
|
// address of system ROM. So we:
|
|
//
|
|
// 1. Make sure there is no ROM block in E0000-EFFFF area.
|
|
// 2. and E0000-EFFFF contains valid data.
|
|
//
|
|
// If both 1 and 2 are true, we assume E0000-EFFFF is part of system
|
|
// ROM.
|
|
//
|
|
|
|
Source = BlockHead;
|
|
while (Source) {
|
|
StartAddr = Source->RomBlock.Address;
|
|
EndAddr = StartAddr + Source->RomBlock.Size - 1;
|
|
if ((StartAddr < 0xE0000 && EndAddr < 0xE0000) ||
|
|
(StartAddr >= 0xF0000)) {
|
|
Source = Source->Next;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
if (Source == NULL) {
|
|
for (StartAddr = 0xE0000; StartAddr < 0xF0000; StartAddr += 0x800) {
|
|
MAKE_FP(TestAddr, StartAddr);
|
|
if (*TestAddr != 0xffff) {
|
|
AddRomBlock(0xE0000, 0x10000);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now copy the rom block list to our reserved space and release
|
|
// the extra space we reserved.
|
|
//
|
|
|
|
Source = BlockHead;
|
|
Destination = (FPROM_BLOCK)ReservedBuffer;
|
|
BufferSize = 0;
|
|
while (Source) {
|
|
*Destination = *((FPROM_BLOCK)&Source->RomBlock);
|
|
BufferSize += sizeof(ROM_BLOCK);
|
|
Source = Source->Next;
|
|
Destination++;
|
|
}
|
|
*Size = BufferSize;
|
|
}
|
|
|
|
VOID
|
|
HwGetBiosDate(
|
|
ULONG StartingAddress,
|
|
USHORT Length,
|
|
PUSHORT Year,
|
|
PUSHORT Month,
|
|
PUSHORT Day
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Scans the specified area for the most recent date of the
|
|
form xx/xx/xx.
|
|
|
|
Arguments:
|
|
|
|
StartingAddress - First address to scan
|
|
Length - Length of area to scan
|
|
|
|
Return Value:
|
|
|
|
Year - If non-zero, the year of the date (1991, 1992, ...)
|
|
Month - If non-zero, then month of the date found
|
|
Day - If non-zero, the day of the date found
|
|
|
|
|
|
--*/
|
|
{
|
|
FPUCHAR fp, date;
|
|
USHORT y, m, d;
|
|
UCHAR c;
|
|
ULONG i, temp;
|
|
|
|
//
|
|
// Zero return values
|
|
//
|
|
|
|
*Year = 0;
|
|
*Month = 0;
|
|
*Day = 0;
|
|
|
|
//
|
|
// Search for date with the format MM/DD/YY or M1M1M2M2//D1D1D2D2//Y1Y1Y2Y2
|
|
//
|
|
|
|
MAKE_FP(fp, StartingAddress); // initialize fp pointer
|
|
while (Length > 8) {
|
|
|
|
c = fp[7];
|
|
if ((c < '0' || c > '9') && (c != '/' && c != '-')) {
|
|
// these 8 bytes are not a date, next location
|
|
|
|
fp += 8;
|
|
Length -= 8;
|
|
continue;
|
|
}
|
|
|
|
date = fp; // check for date at this pointer
|
|
fp += 1; // skip to next byte
|
|
Length -= 1;
|
|
|
|
//
|
|
// Check for date of the form MM/DD/YY
|
|
//
|
|
|
|
y = 0;
|
|
if (date[0] >= '0' && date[0] <= '9' &&
|
|
date[1] >= '0' && date[1] <= '9' &&
|
|
(date[2] == '/' || date[2] == '-') &&
|
|
date[3] >= '0' && date[3] <= '9' &&
|
|
date[4] >= '0' && date[4] <= '9' &&
|
|
(date[5] == '/' || date[5] == '-') &&
|
|
date[6] >= '0' && date[6] <= '9' &&
|
|
date[7] >= '0' && date[7] <= '9' ) {
|
|
|
|
|
|
//
|
|
// A valid looking date field at date, crack it
|
|
//
|
|
|
|
y = (date[6] - '0') * 10 + date[7] - '0' + 1900;
|
|
m = (date[0] - '0') * 10 + date[1] - '0';
|
|
d = (date[3] - '0') * 10 + date[4] - '0';
|
|
}
|
|
|
|
//
|
|
// Check for date of the form M1M1M2M2//D1D1D2D2//Y1Y1Y2Y2
|
|
//
|
|
|
|
if (Length >= 15 &&
|
|
date[ 0] >= '0' && date[ 0] <= '9' && date[ 0] == date[ 1] &&
|
|
date[ 2] >= '0' && date[ 2] <= '9' && date[ 2] == date[ 3] &&
|
|
(date[ 4] == '/' || date[ 4] == '-') && date[ 4] == date[ 5] &&
|
|
date[ 6] >= '0' && date[ 6] <= '9' && date[ 6] == date[ 7] &&
|
|
date[ 8] >= '0' && date[ 8] <= '9' && date[ 8] == date[ 9] &&
|
|
(date[10] == '/' || date[10] == '-') && date[10] == date[11] &&
|
|
date[12] >= '0' && date[12] <= '9' && date[12] == date[13] &&
|
|
date[14] >= '0' && date[14] <= '9' && date[14] == date[15]) {
|
|
|
|
//
|
|
// A valid looking date field at date, crack it
|
|
//
|
|
|
|
y = (date[12] - '0') * 10 + date[14] - '0' + 1900;
|
|
m = (date[ 0] - '0') * 10 + date[ 2] - '0';
|
|
d = (date[ 6] - '0') * 10 + date[ 8] - '0';
|
|
}
|
|
|
|
if (y != 0) {
|
|
if (m < 1 || m > 12 || d < 1 || d > 31) {
|
|
y = 0; // bad field in date, skip it
|
|
} else {
|
|
if (y < 1980) {
|
|
|
|
//
|
|
// Roll to next century.
|
|
//
|
|
|
|
y += 100;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check for date of the form 19xx or 20xx
|
|
//
|
|
// First, check the 5th character is not a digit.
|
|
//
|
|
|
|
#define IS_DIGIT(x) (((x) >= '0') && ((x) <= '9'))
|
|
|
|
if (!IS_DIGIT(date[4])) {
|
|
for (i = 0, temp = 0; i < 4; i++) {
|
|
if (!IS_DIGIT(date[i])) {
|
|
temp = 0;
|
|
break;
|
|
}
|
|
temp = (temp * 10) + date[i] - '0';
|
|
}
|
|
if ((temp >= 1980) && (temp < 2599)) {
|
|
|
|
//
|
|
// Looks like a reasonable date, use it.
|
|
//
|
|
|
|
y = (USHORT)temp;
|
|
m = 0;
|
|
d = 0;
|
|
}
|
|
}
|
|
|
|
if (!y) {
|
|
// not a date - skip it
|
|
continue;
|
|
}
|
|
|
|
if ((y > *Year) ||
|
|
(y == *Year && m > *Month) ||
|
|
(y == *Year && m == *Month && d > *Day) ) {
|
|
|
|
//
|
|
// This date is more recent
|
|
//
|
|
|
|
*Year = y;
|
|
*Month = m;
|
|
*Day = d;
|
|
}
|
|
}
|
|
}
|
|
|
|
|