/*
 * PROJECT:    .inf file parser
 * LICENSE:    GPL - See COPYING in the top level directory
 * PROGRAMMER: Royce Mitchell III
 *             Eric Kohl
 *             Ge van Geldorp <gvg@reactos.org>
 */

/* INCLUDES *****************************************************************/

#include "inflib.h"
#include "infros.h"

#define NDEBUG
#include <debug.h>

/* PRIVATE FUNCTIONS ********************************************************/

static int InfpHeapRefCount;

static VOID
CheckHeap(VOID)
{
  if (NULL == InfpHeap)
    {
      InfpHeap = RtlCreateHeap(HEAP_GROWABLE, NULL, 0, 0, NULL, NULL);
    }
  if (0 <= InfpHeapRefCount)
    {
      InfpHeapRefCount++;
    }
}


/* PUBLIC FUNCTIONS *********************************************************/

PVOID InfpHeap;

VOID
InfSetHeap(PVOID Heap)
{
  if (NULL == InfpHeap)
    {
      InfpHeap = Heap;
      InfpHeapRefCount = -1;
    }
}


NTSTATUS
InfOpenBufferedFile(PHINF InfHandle,
                    PVOID Buffer,
                    ULONG BufferSize,
                    LANGID LanguageId,
                    PULONG ErrorLine)
{
  INFSTATUS Status;
  PINFCACHE Cache;
  PCHAR FileBuffer;
  ULONG FileBufferSize;

  CheckHeap();

  *InfHandle = NULL;
  *ErrorLine = (ULONG)-1;

  /* Allocate file buffer */
  FileBufferSize = BufferSize + 2;
  FileBuffer = MALLOC(FileBufferSize);
  if (FileBuffer == NULL)
    {
      DPRINT1("MALLOC() failed\n");
      return(INF_STATUS_INSUFFICIENT_RESOURCES);
    }

  MEMCPY(FileBuffer, Buffer, BufferSize);

  /* Append string terminator */
  FileBuffer[BufferSize] = 0;
  FileBuffer[BufferSize + 1] = 0;

  /* Allocate infcache header */
  Cache = (PINFCACHE)MALLOC(sizeof(INFCACHE));
  if (Cache == NULL)
    {
      DPRINT("MALLOC() failed\n");
      FREE(FileBuffer);
      return(INF_STATUS_INSUFFICIENT_RESOURCES);
    }

  /* Initialize inicache header */
  ZEROMEMORY(Cache,
             sizeof(INFCACHE));

    Cache->LanguageId = LanguageId;

    /* Parse the inf buffer */
    if (!RtlIsTextUnicode(FileBuffer, FileBufferSize, NULL))
    {
//        static const BYTE utf8_bom[3] = { 0xef, 0xbb, 0xbf };
        WCHAR *new_buff;
//        UINT codepage = CP_ACP;
        UINT offset = 0;

//        if (BufferSize > sizeof(utf8_bom) && !memcmp(FileBuffer, utf8_bom, sizeof(utf8_bom) ))
//        {
//            codepage = CP_UTF8;
//            offset = sizeof(utf8_bom);
//        }

        new_buff = MALLOC(FileBufferSize * sizeof(WCHAR));
        if (new_buff != NULL)
        {
            ULONG len;
            Status = RtlMultiByteToUnicodeN(new_buff,
                                            FileBufferSize * sizeof(WCHAR),
                                            &len,
                                            (char *)FileBuffer + offset,
                                            FileBufferSize - offset);

            Status = InfpParseBuffer(Cache,
                                     new_buff,
                                     new_buff + len / sizeof(WCHAR),
                                     ErrorLine);
            FREE(new_buff);
        }
        else
            Status = INF_STATUS_INSUFFICIENT_RESOURCES;
    }
    else
    {
        WCHAR *new_buff = (WCHAR *)FileBuffer;
        /* UCS-16 files should start with the Unicode BOM; we should skip it */
        if (*new_buff == 0xfeff)
        {
            new_buff++;
            FileBufferSize -= sizeof(WCHAR);
        }
        Status = InfpParseBuffer(Cache,
                                 new_buff,
                                 (WCHAR*)((char*)new_buff + FileBufferSize),
                                 ErrorLine);
    }

  if (!INF_SUCCESS(Status))
    {
      FREE(Cache);
      Cache = NULL;
    }

  /* Free file buffer */
  FREE(FileBuffer);

  *InfHandle = (HINF)Cache;

  return(Status);
}


NTSTATUS
InfOpenFile(PHINF InfHandle,
	    PUNICODE_STRING FileName,
	    LANGID LanguageId,
	    PULONG ErrorLine)
{
  OBJECT_ATTRIBUTES ObjectAttributes;
  FILE_STANDARD_INFORMATION FileInfo;
  IO_STATUS_BLOCK IoStatusBlock;
  HANDLE FileHandle;
  NTSTATUS Status;
  PCHAR FileBuffer;
  ULONG FileLength;
  ULONG FileBufferLength;
  LARGE_INTEGER FileOffset;
  PINFCACHE Cache;

  CheckHeap();

  *InfHandle = NULL;
  *ErrorLine = (ULONG)-1;

  /* Open the inf file */
  InitializeObjectAttributes(&ObjectAttributes,
			     FileName,
			     0,
			     NULL,
			     NULL);

  Status = NtOpenFile(&FileHandle,
		      GENERIC_READ | SYNCHRONIZE,
		      &ObjectAttributes,
		      &IoStatusBlock,
		      FILE_SHARE_READ,
		      FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE);
  if (!INF_SUCCESS(Status))
    {
      DPRINT("NtOpenFile() failed (Status %lx)\n", Status);
      return(Status);
    }

  DPRINT("NtOpenFile() successful\n");

  /* Query file size */
  Status = NtQueryInformationFile(FileHandle,
				  &IoStatusBlock,
				  &FileInfo,
				  sizeof(FILE_STANDARD_INFORMATION),
				  FileStandardInformation);
  if (!INF_SUCCESS(Status))
    {
      DPRINT("NtQueryInformationFile() failed (Status %lx)\n", Status);
      NtClose(FileHandle);
      return(Status);
    }

  FileLength = FileInfo.EndOfFile.u.LowPart;

  DPRINT("File size: %lu\n", FileLength);

  /* Allocate file buffer */
  FileBufferLength = FileLength + 2;
  FileBuffer = MALLOC(FileBufferLength);
  if (FileBuffer == NULL)
    {
      DPRINT1("MALLOC() failed\n");
      NtClose(FileHandle);
      return(INF_STATUS_INSUFFICIENT_RESOURCES);
    }

  /* Read file */
  FileOffset.QuadPart = 0ULL;
  Status = NtReadFile(FileHandle,
		      NULL,
		      NULL,
		      NULL,
		      &IoStatusBlock,
		      FileBuffer,
		      FileLength,
		      &FileOffset,
		      NULL);

  /* Append string terminator */
  FileBuffer[FileLength] = 0;
  FileBuffer[FileLength + 1] = 0;

  NtClose(FileHandle);

  if (!INF_SUCCESS(Status))
    {
      DPRINT("NtReadFile() failed (Status %lx)\n", Status);
      FREE(FileBuffer);
      return(Status);
    }

  /* Allocate infcache header */
  Cache = (PINFCACHE)MALLOC(sizeof(INFCACHE));
  if (Cache == NULL)
    {
      DPRINT("MALLOC() failed\n");
      FREE(FileBuffer);
      return(INF_STATUS_INSUFFICIENT_RESOURCES);
    }

  /* Initialize inicache header */
  ZEROMEMORY(Cache,
             sizeof(INFCACHE));

    Cache->LanguageId = LanguageId;

    /* Parse the inf buffer */
    if (!RtlIsTextUnicode(FileBuffer, FileBufferLength, NULL))
    {
//        static const BYTE utf8_bom[3] = { 0xef, 0xbb, 0xbf };
        WCHAR *new_buff;
//        UINT codepage = CP_ACP;
        UINT offset = 0;

//        if (FileLength > sizeof(utf8_bom) && !memcmp(FileBuffer, utf8_bom, sizeof(utf8_bom) ))
//        {
//            codepage = CP_UTF8;
//            offset = sizeof(utf8_bom);
//        }

        new_buff = MALLOC(FileBufferLength * sizeof(WCHAR));
        if (new_buff != NULL)
        {
            ULONG len;
            Status = RtlMultiByteToUnicodeN(new_buff,
                                            FileBufferLength * sizeof(WCHAR),
                                            &len,
                                            (char *)FileBuffer + offset,
                                            FileBufferLength - offset);

            Status = InfpParseBuffer(Cache,
                                     new_buff,
                                     new_buff + len / sizeof(WCHAR),
                                     ErrorLine);
            FREE(new_buff);
        }
        else
            Status = INF_STATUS_INSUFFICIENT_RESOURCES;
    }
    else
    {
        WCHAR *new_buff = (WCHAR *)FileBuffer;
        /* UCS-16 files should start with the Unicode BOM; we should skip it */
        if (*new_buff == 0xfeff)
        {
            new_buff++;
            FileBufferLength -= sizeof(WCHAR);
        }
        Status = InfpParseBuffer(Cache,
                                 new_buff,
                                 (WCHAR*)((char*)new_buff + FileBufferLength),
                                 ErrorLine);
    }

  if (!INF_SUCCESS(Status))
    {
      FREE(Cache);
      Cache = NULL;
    }

  /* Free file buffer */
  FREE(FileBuffer);

  *InfHandle = (HINF)Cache;

  return(Status);
}


VOID
InfCloseFile(HINF InfHandle)
{
  PINFCACHE Cache;

  Cache = (PINFCACHE)InfHandle;

  if (Cache == NULL)
    {
      return;
    }

  while (Cache->FirstSection != NULL)
    {
      Cache->FirstSection = InfpFreeSection(Cache->FirstSection);
    }
  Cache->LastSection = NULL;

  FREE(Cache);

  if (0 < InfpHeapRefCount)
    {
      InfpHeapRefCount--;
      if (0 == InfpHeapRefCount)
        {
          RtlDestroyHeap(InfpHeap);
          InfpHeap = NULL;
        }
    }
}


/* EOF */