#include "basedll.h"

const OBJECT_STRING NickFile = CONSTANT_OBJECT_STRING(OTEXT("\\Device\\Harddisk0\\partition1\\UDATA\\NICKNAME.XBN"));

BOOL
WINAPI
XSetNicknameW(
    IN LPCWSTR lpNickname,
    IN BOOL fPreserveCase)
{
    NTSTATUS Status;
    OBJECT_ATTRIBUTES Obja;
    IO_STATUS_BLOCK Iosb;
    HANDLE FileHandle;
    X_NICK_DB_ENTRY Nicknames[NUM_NICK_ENTRIES];
    DWORD dwTitleID;

    RIP_ON_NOT_TRUE("XSetNicknameW()", (NULL != lpNickname));
    RIP_ON_NOT_TRUE("XSetNicknameW()", (wcslen(lpNickname) < MAX_NICKNAME));

    if (L'\0' == *lpNickname)
    {
        return FALSE;
    }

    dwTitleID = XeImageHeader()->Certificate->TitleID;

    InitializeObjectAttributes(&Obja, (POBJECT_STRING) &NickFile, OBJ_CASE_INSENSITIVE, NULL, NULL);

    //
    // Attempt to open an existing nickname file
    //

    Status = NtCreateFile(&FileHandle,
                          SYNCHRONIZE | GENERIC_READ | GENERIC_WRITE,
                          &Obja,
                          &Iosb,
                          NULL,
                          FILE_ATTRIBUTE_SYSTEM,
                          FILE_SHARE_READ,
                          FILE_OPEN,
                          FILE_WRITE_THROUGH | FILE_SYNCHRONOUS_IO_NONALERT);

    if (NT_SUCCESS(Status))
    {
        Status = NtReadFile(FileHandle,
                            NULL,
                            NULL,
                            NULL,
                            &Iosb,
                            Nicknames,
                            sizeof(Nicknames),
                            NULL);

        if (NT_SUCCESS(Status) && (sizeof(Nicknames) != (DWORD) Iosb.Information))
        {
            //
            // We didn't read the right number of bytes, so don't do anything with this
            // data (the file will need to be recreated below)
            //
            Status = STATUS_END_OF_FILE;
        }

        if (NT_SUCCESS(Status))
        {
            WCHAR szNewNick[MAX_NICKNAME];
            int i;
            BOOL fNewEntry;

            lstrcpynW(szNewNick, lpNickname, ARRAYSIZE(szNewNick));

            //
            // Look to see if this nickname/titleid combo already exists in the table
            //

            for (i = 0; i < ARRAYSIZE(Nicknames); i++)
            {
                if ((FALSE != (fNewEntry = (L'\0' == Nicknames[i].szNickname[0]))) ||
                    ((dwTitleID == Nicknames[i].dwTitleID) &&
                        (0 == _wcsicmp(lpNickname, Nicknames[i].szNickname))))
                {
                    if (!fNewEntry && !fPreserveCase)
                    {
                        //
                        // The title didn't want us to preserve the case passed in, so
                        // use the name as it was last written instead
                        //

                        lstrcpynW(szNewNick, Nicknames[i].szNickname, ARRAYSIZE(szNewNick));
                    }
                    break;
                }
            }

            if (ARRAYSIZE(Nicknames) == i)
            {
                fNewEntry = TRUE;
            }

            if (!fNewEntry && (0 == i) && (0 == (wcscmp(szNewNick, Nicknames[0].szNickname))))
            {
                //
                // No change - the most recent nickname is exactly the same, so just close the file
                // and return TRUE without rewriting the exact same data.
                //

                NtClose(FileHandle);
                return TRUE;
            }

            if (0 != i)
            {
                //
                // Shift all of the entries further down the table to make room for this entry
                // at the top of the table
                //

                RtlMoveMemory(&(Nicknames[1]),
                              &(Nicknames[0]),
                              sizeof(X_NICK_DB_ENTRY) * min(i, ARRAYSIZE(Nicknames) - 1));
            }

            //
            // Write the new entry at the top of the table
            //

            Nicknames[0].dwTitleID = dwTitleID;
            lstrcpynW(Nicknames[0].szNickname, szNewNick, ARRAYSIZE(Nicknames[0].szNickname));
        }
        else
        {
            NtClose(FileHandle);
        }
    }
    else if (Status != STATUS_OBJECT_NAME_NOT_FOUND)
    {
        //
        // The open failed for an unexpected reason - we don't want to proceed and
        // overwrite a valid nickname file...
        //

        return FALSE;
    }

    if (!NT_SUCCESS(Status))
    {
        //
        // Attempt to create a new nickname file
        //

        Status = NtCreateFile(&FileHandle,
                              SYNCHRONIZE | GENERIC_READ | GENERIC_WRITE,
                              &Obja,
                              &Iosb,
                              NULL,
                              FILE_ATTRIBUTE_SYSTEM,
                              FILE_SHARE_READ,
                              FILE_OVERWRITE_IF,
                              FILE_WRITE_THROUGH | FILE_SYNCHRONOUS_IO_NONALERT);

        if (NT_SUCCESS(Status))
        {
            //
            // Zero out the memory that we're going to write to the file
            //

            RtlZeroMemory(&Nicknames, sizeof(Nicknames));

            //
            // Write the new entry at the top of the table
            //

            Nicknames[0].dwTitleID = dwTitleID;
            lstrcpynW(Nicknames[0].szNickname, lpNickname, ARRAYSIZE(Nicknames[0].szNickname));
        }
    }

    if (NT_SUCCESS(Status))
    {
        //
        // We have a valid FileHandle and Nicknames array at this point.
        // It is time to write it back to disk and close the file..
        //

        LARGE_INTEGER liByteOffset;

        liByteOffset.QuadPart = 0;
        
        Status = NtWriteFile(FileHandle,
                             NULL,
                             NULL,
                             NULL,
                             &Iosb,
                             Nicknames,
                             sizeof(Nicknames),
                             &liByteOffset);

        NtClose(FileHandle);
    }

    return(NT_SUCCESS(Status));
}

BOOL
GetNextNickname(
    PFINDNICKNAME_HANDLE pFindNick,
    LPWSTR lpNickname,
    UINT uSize)
{
    DWORD dwTitleID;

    ASSERT(pFindNick && (FH_SIG_NICKNAME == pFindNick->dwSignature));

    dwTitleID = XeImageHeader()->Certificate->TitleID;

    for (; pFindNick->uCurIndex < ARRAYSIZE(pFindNick->Nicknames); (pFindNick->uCurIndex)++)
    {
        if ((L'\0' != pFindNick->Nicknames[pFindNick->uCurIndex].szNickname[0]) &&
            (wcslen(pFindNick->Nicknames[pFindNick->uCurIndex].szNickname) < uSize) &&
            (!pFindNick->fThisTitleOnly ||
             (dwTitleID == pFindNick->Nicknames[pFindNick->uCurIndex].dwTitleID)))
        {
            BOOL fPrevMatch = FALSE;
#if DBG
            //
            // Always check for previous matches in the debug build
            // (so we can validate that the nickfile doesn't have duplicates with
            // the same title id)
            //
#else  // DBG
            //
            // If we're searching all title's, we need to check for previous matches
            // so that we don't return duplicate names (the same name can appear
            // many times with a different title id each time)
            //
            if (!pFindNick->fThisTitleOnly)
#endif // DBG
            {
                UINT uCompIndex;
                for (uCompIndex = 0; uCompIndex < pFindNick->uCurIndex; uCompIndex++)
                {
                    if (0 == _wcsicmp(pFindNick->Nicknames[pFindNick->uCurIndex].szNickname,
                                      pFindNick->Nicknames[uCompIndex].szNickname))
                    {
                        fPrevMatch = TRUE;
                        break;
                    }
                }
            }

            if (pFindNick->fThisTitleOnly || !fPrevMatch)
            {
#if DBG
                if (pFindNick->fThisTitleOnly && fPrevMatch)
                {
                    XDBGERR("XAPI", "XFindNextNicknameW() found corrupt data (duplicate nicknames)");
                }
#endif // DBG
                wcscpy(lpNickname, pFindNick->Nicknames[pFindNick->uCurIndex].szNickname);
                (pFindNick->uCurIndex)++;
                return TRUE;
            }
        }
    }
    
    return FALSE;    
}

HANDLE
WINAPI
XFindFirstNicknameW(
    IN BOOL fThisTitleOnly,
    OUT LPWSTR lpNickname,
    IN UINT uSize)
{
    NTSTATUS Status;
    OBJECT_ATTRIBUTES Obja;
    IO_STATUS_BLOCK Iosb;
    HANDLE FileHandle;
    PFINDNICKNAME_HANDLE pFindNick = NULL;
    
    RIP_ON_NOT_TRUE("XFindFirstNicknameW()", (NULL != lpNickname));

    InitializeObjectAttributes(&Obja, (POBJECT_STRING) &NickFile, OBJ_CASE_INSENSITIVE, NULL, NULL);

    //
    // Attempt to open an existing nickname file
    //
    
    Status = NtCreateFile(&FileHandle,
                          SYNCHRONIZE | GENERIC_READ,
                          &Obja,
                          &Iosb,
                          NULL,
                          FILE_ATTRIBUTE_SYSTEM,
                          FILE_SHARE_READ | FILE_SHARE_WRITE,
                          FILE_OPEN,
                          FILE_SYNCHRONOUS_IO_NONALERT);

    if (NT_SUCCESS(Status))
    {
        pFindNick = LocalAlloc(LMEM_FIXED, sizeof(FINDNICKNAME_HANDLE));
        
        if (pFindNick)
        {
            pFindNick->dwSignature = FH_SIG_NICKNAME;
            pFindNick->uCurIndex = 0;
            pFindNick->fThisTitleOnly = fThisTitleOnly;
            
            Status = NtReadFile(FileHandle,
                                NULL,
                                NULL,
                                NULL,
                                &Iosb,
                                pFindNick->Nicknames,
                                sizeof(pFindNick->Nicknames),
                                NULL);

            if (NT_SUCCESS(Status) && (sizeof(pFindNick->Nicknames) != (DWORD) Iosb.Information))
            {
                //
                // We didn't read the right number of bytes, so don't do anything with this
                // data (the file will need to be recreated below)
                //
                Status = STATUS_END_OF_FILE;
            }
        }
        else
        {
            Status = STATUS_INSUFFICIENT_RESOURCES;
        }

        NtClose(FileHandle);
    }

    if (NT_SUCCESS(Status))
    {
        if (!GetNextNickname(pFindNick, lpNickname, uSize))
        {
            Status = STATUS_UNSUCCESSFUL;
        }
    }

    if (!NT_SUCCESS(Status) && (NULL != pFindNick))
    {
        LocalFree(pFindNick);
        pFindNick = NULL;
    }

    return (HANDLE) (pFindNick ? pFindNick : INVALID_HANDLE_VALUE);
}

BOOL
WINAPI
XFindNextNicknameW(
    IN HANDLE hFindNickname,
    OUT LPWSTR lpNickname,
    IN UINT uSize)
{
    PFINDNICKNAME_HANDLE pFindNick = (PFINDNICKNAME_HANDLE) hFindNickname;

    RIP_ON_NOT_TRUE("XFindNextNicknameW()", (NULL != hFindNickname));
    RIP_ON_NOT_TRUE("XFindNextNicknameW()", (INVALID_HANDLE_VALUE != hFindNickname));
    RIP_ON_NOT_TRUE("XFindNextNicknameW()", (NULL != lpNickname));

#if DBG
    if (FH_SIG_NICKNAME != pFindNick->dwSignature)
    {
        RIP("XFindNextNicknameW() - invalid parameter (hFindNickname)");
    }
#endif // DBG
    
    return GetNextNickname(pFindNick, lpNickname, uSize);
}


