/*++

Copyright (c) 1997 Microsoft Corporation

Module Name:

    dataconv.c

Abstract:

    This source file implements data conversion routines that know
    the format of Windows 95 screen saver settings and can convert
    them to the Windows NT format.

Author:

    Jim Schmidt (jimschm) 14-Apr-1997

Revision History:


--*/

#include "pch.h"

static CHAR g_Data[MAX_PATH];

BOOL
pHasSpecialProcessing (
    IN      LPCSTR ScreenSaverName
    );

BOOL
pTranslateBezier (
    IN      HKEY RegRoot
    );

BOOL
pTranslateMarquee (
    IN      HKEY RegRoot
    );



HKEY
pCreateControlPanelKey (
    IN      HKEY RootKey,
    IN      LPCSTR SubKeyName,
    IN      BOOL CreateEmptyKey
    )
{
    CHAR FullRegKey[MAX_PATH];

    wsprintf (FullRegKey, S_CONTROL_PANEL_MASK, SubKeyName);

    if (CreateEmptyKey) {
        RegDeleteKey (RootKey, FullRegKey);
    }

    return CreateRegKey (RootKey, FullRegKey);
}


HKEY
pCreateScreenSaverKey (
    IN      HKEY RegRoot, 
    IN      LPCSTR ScreenSaverName
    )
{
    CHAR FullScreenSaverName[MAX_PATH];

    wsprintf (FullScreenSaverName, S_SCRNSAVE_MASK, ScreenSaverName);
    return pCreateControlPanelKey (RegRoot, FullScreenSaverName, FALSE);
}


BOOL
pCopyValuesFromSettingsFileToRegistry (
    IN      HKEY RegKeyRoot,
    IN      LPCSTR RegKeyName,
    IN      LPCSTR ScreenSaverName,
    IN      LPCSTR ValueArray[]
    )
{
    INT i;
    CHAR IniKeyName[MAX_PATH];
    HKEY RegKey;
    BOOL b = TRUE;

    //
    // This function takes the values stored in our settings file and
    // copies them to a brand new key in the NT registry.
    //
    // In the settings file, we store screen saver parameters in the
    // format of <screen saver name len>/<screen saver name>/<parameter>=<value>.
    //

    //
    // Create new registry key
    //

    RegKey = pCreateControlPanelKey (RegKeyRoot, RegKeyName, TRUE);
    if (!RegKey) {
        return FALSE;
    }

    //
    // Copy values to reg key
    //

    for (i = 0 ; ValueArray[i] ; i++) {
        if (!CreateScreenSaverParamKey (ScreenSaverName, ValueArray[i], IniKeyName)) {
            // fail if screen saver name is huge for some unknown reason
            LOG ((LOG_WARNING, MSG_HUGEDATA_ERROR));
            b = FALSE;
            break;
        }

        GetSettingsFileVal (IniKeyName);
        if (!SetRegValueString (RegKey, ValueArray[i], g_Data)) {
            b = FALSE;
            break;
        }
    }

    CloseRegKey (RegKey);
    return b;
}


BOOL
TranslateGeneralSetting (
    IN      HKEY RegKey,
    IN      LPCSTR Win9xSetting,
    IN      LPCSTR WinNTSetting
    )
{
    BOOL b = TRUE;

    if (!WinNTSetting) {
        WinNTSetting = Win9xSetting;
    } else {
        //
        // Delete the Win9x setting that was copied to NT, ignore
        // any failures.
        //
        RegDeleteValue (RegKey, Win9xSetting);
    }

    //
    // Obtain setting from data file
    //

    if (GetSettingsFileVal (Win9xSetting)) {
        //
        // Save settings to registry
        //

        b = SetRegValueString (RegKey, WinNTSetting, g_Data);
    }

    return b;
}


typedef struct {
    LPCSTR Win9xName;
    LPCSTR WinNtName;
} FILE_TRANS, *PFILE_TRANS;

FILE_TRANS g_FileNameTranslation[] = {
    // Win9x name                   // WinNT name (NULL=no change)
    "black16.scr",                  NULL,
    "Blank Screen.scr",             "black16.scr",

    "ssbezier.scr",                 NULL,
    "Curves and Colors.scr",        "ssbezier.scr",

    "ssstars.scr",                  NULL,
    "Flying Through Space.scr",     "ssstars.scr",

    "ssmarque.scr",                 NULL,
    "Scrolling Marquee.scr",        "ssmarque.scr",

    "ssmyst.scr",                   NULL,
    "Mystify Your Mind.scr",        "ssmyst.scr",

    NULL, NULL
};


LPCSTR
GetSettingsFileVal (
    IN      LPCSTR Key
    )
{
    GetPrivateProfileString (
        g_User, 
        Key, 
        S_EMPTY, 
        g_Data, 
        MAX_PATH, 
        g_SettingsFile
        );

    return g_Data[0] ? g_Data : NULL;
}


BOOL
pTranslateScrName (
    IN OUT  LPSTR KeyName,
    OUT     LPSTR FullPath      OPTIONAL
    )
{
    int i;

    //
    // Compare against translation list
    //

    for (i = 0 ; g_FileNameTranslation[i].Win9xName ; i++) {
        if (!_mbsicmp (KeyName, g_FileNameTranslation[i].Win9xName)) {
            break;
        }
    }

    //
    // Translate filename only if a match was found in our list
    //

    if (g_FileNameTranslation[i].Win9xName) {

        //
        // If WinNtName is NULL, there is no renaming necessary.  Otherwise,
        // use the NT name, which is always a file in system32.
        //

        if (g_FileNameTranslation[i].WinNtName && FullPath) {
            // Rebuild path
            GetSystemDirectory (FullPath, MAX_PATH);
            _mbscat (FullPath, "\\");
            _mbscat (FullPath, g_FileNameTranslation[i].WinNtName);
        }

        _mbscpy (KeyName, g_FileNameTranslation[i].WinNtName);
    }
    else if (FullPath) {
        FullPath[0] = 0;
    }

    return TRUE;
}


BOOL
SaveScrName (
    IN      HKEY RegKey, 
    IN      LPCSTR KeyName
    )
{
    LPSTR p;
    CHAR FullPath[MAX_PATH];
    CHAR ShortName[MAX_PATH];

    //
    // The Windows 95 screen saver names are different than
    // Windows NT.
    //

    if (!GetSettingsFileVal (KeyName)) {
        // Unexpected: .SCR name does not exist in our file
        return TRUE;
    }

    //
    // Locate the screen saver name within the full path
    //

    p = _mbsrchr (g_Data, '\\');
    if (!p) {
        p = g_Data;
    } else {
        p = _mbsinc (p);
    }

    //
    // Translate it if necessary
    //

    if (!pTranslateScrName (p, FullPath)) {
        return FALSE;
    }

    if (!FullPath[0]) {
        //
        // No change was made, so copy original path to FullPath
        //
        
        _mbscpy (FullPath, g_Data);
    }

    //
    // Screen savers are always stored in short filename format
    //

    GetShortPathName (FullPath, ShortName, MAX_PATH);

    return SetRegValueString (RegKey, KeyName, ShortName);
}

INT
GetHexDigit (
    IN      CHAR c
    )
{
    if (c >= '0' && c <= '9') {
        return c - '0';
    }

    c = tolower (c);
    if (c >= 'a' && c <= 'f') {
        return c - 'a' + 10;
    }

    return -1;
}

BYTE
GetNextHexByte (
    IN      LPCSTR HexString,
    OUT     LPCSTR *HexStringReturn
    )
{
    INT a, b;

    a = GetHexDigit (HexString[0]);
    b = GetHexDigit (HexString[1]);

    if (a == -1 || b == -1) {
        *HexStringReturn = NULL;
        return 0;
    }

    *HexStringReturn = &(HexString[2]);

    return a * 16 + b;
}

BOOL
GetNextDword (
    IN      LPCSTR HexString,
    OUT     LPCSTR *HexStringReturn,
    OUT     PDWORD ValuePtr
    )
{
    INT i;
    BYTE NextByte;

    *ValuePtr = 0;

    for (i = 0 ; i < 4 ; i++) {
        NextByte = GetNextHexByte (HexString, &HexString);
        if (!HexString) {
            return FALSE;
        }

        *ValuePtr = ((*ValuePtr) << 8) | NextByte;
    }

    return TRUE;
}

BOOL
VerifyBezierChecksum (
    IN      LPCSTR HexString
    )
{
    BYTE Checksum = 0;
    INT Len;

    Len = _mbslen (HexString);
    Len -= 2;

    if (Len < 1) {
        return FALSE;
    }

    while (Len > 0) {
        Checksum += GetNextHexByte (HexString, &HexString);
        if (!HexString) {
            return FALSE;
        }
    }

    if (Checksum != GetNextHexByte (HexString, &HexString)) {
        return FALSE;
    }

    return TRUE;
}


BOOL
CopyUntranslatedSettings (
    IN      HKEY RegRoot
    )
{
    LPSTR KeyBuffer;
    DWORD KeyBufferSize;
    LPSTR p;
    CHAR ScreenSaverName[MAX_PATH];
    CHAR ValueName[MAX_PATH];
    HKEY Key;

    //
    // Enumerate each entry in our private settings file for the user
    //

    KeyBufferSize = 32768;
    KeyBuffer = (LPSTR) HeapAlloc (g_hHeap, 0, KeyBufferSize);
    if (!KeyBuffer) {
        return FALSE;
    }

    //
    // Get all keys in the user's section
    //

    GetPrivateProfileString (
        g_User,
        NULL,
        S_DOUBLE_EMPTY,
        KeyBuffer,
        KeyBufferSize,
        g_SettingsFile
        );

    for (p = KeyBuffer ; *p ; p = _mbschr (p, 0) + 1) {
        //
        // Process only if key is encoded
        //

        if (!DecodeScreenSaverParamKey (p, ScreenSaverName, ValueName)) {
            continue;
        }

        //
        // Key is encoded, so perform migration!
        //

        pTranslateScrName (ScreenSaverName, NULL);

        //
        // Skip screen savers that have special processing
        //

        if (pHasSpecialProcessing (ScreenSaverName)) {
            continue;
        }

        //
        // Save the value to the registry
        //

        GetSettingsFileVal (p);

        Key = pCreateScreenSaverKey (RegRoot, ScreenSaverName);
        if (Key) {
            if (SetRegValueString (Key, ValueName, g_Data))
            {
                CHAR DebugMsg[MAX_PATH*2];
                wsprintf (DebugMsg, "Saved %s=%s\r\n", ValueName, g_Data);
                SetupLogError (DebugMsg, LogSevInformation);
            } else {
                CHAR DebugMsg[MAX_PATH*2];
                wsprintf (DebugMsg, "Could not save %s=%s\r\n", ValueName, g_Data);
                SetupLogError (DebugMsg, LogSevError);
            }

            CloseRegKey (Key);
        }
    }

    HeapFree (g_hHeap, 0, KeyBuffer);

    return TRUE;
}


BOOL
pHasSpecialProcessing (
    IN      LPCSTR ScreenSaverName
    )
{
    //
    // Return TRUE if we are doing something special for the
    // named screen saver.
    //

    if (!_mbsicmp (ScreenSaverName, S_BEZIER) ||
        !_mbsicmp (ScreenSaverName, S_MARQUEE)
        ) {
        return TRUE;
    }

    return FALSE;
}


BOOL
TranslateScreenSavers (
    IN      HKEY RegRoot
    )
{
    BOOL b = TRUE;

    b &= pTranslateBezier (RegRoot);
    b &= pTranslateMarquee (RegRoot);

    return b;
}


BOOL
pTranslateBezier (
    IN      HKEY RegRoot
    )
{
    DWORD Value;
    CHAR StrValue[32];
    LPCSTR p;
    HKEY RegKey;
    BOOL b;

    //
    // NT's Bezier has three settings:
    //
    // Length (REG_SZ)      = Curve Count on Win9x
    // LineSpeed (REG_SZ)   = Speed on Win9x
    // Width (REG_SZ)       = Density on Win9x
    //
    // Win9x's Bezier has a big string of hex in the following format:
    // 
    // Clear Screen Flag (DWORD)
    // Random Colors Flag (DWORD)
    // Curve Count (DWORD)
    // Line Count (DWORD)
    // Density (DWORD)
    // Speed (DWORD)
    // Current Color (DWORD RGB)
    // Checksum (BYTE)
    //

    //
    // Verify structure
    //

    GetSettingsFileVal (S_BEZIER_SETTINGS);

    if (!VerifyBezierChecksum (g_Data)) {
        return TRUE;
    }

    //
    // Open reg key
    //

    RegKey = pCreateControlPanelKey (RegRoot, S_BEZIER_SETTINGS, TRUE);
    if (!RegKey) {
        return FALSE;
    }

    p = g_Data;

    // Get clear screen flag (but ignore it)
    b = GetNextDword (p, &p, &Value);

    // Get random colors flag (but ignore it)
    if (b) {
        b = GetNextDword (p, &p, &Value);
    }

    //
    // Get curve count
    //

    if (b) {
        b = GetNextDword (p, &p, &Value);
    }

    if (b) {
        wsprintf (StrValue, "%u", Value);
        b = SetRegValueString (RegKey, S_LENGTH, StrValue);
    }

    // Get line count (but ignore it)
    if (b) {
        b = GetNextDword (p, &p, &Value);
    }

    //
    // Get density
    //

    if (b) {
        b = GetNextDword (p, &p, &Value);
    }

    if (b) {
        wsprintf (StrValue, "%u", Value);
        b = SetRegValueString (RegKey, S_WIDTH, StrValue);
    }

    //
    // Get speed
    //

    if (b) {
        b = GetNextDword (p, &p, &Value);
    }

    if (b) {
        wsprintf (StrValue, "%u", Value);
        b = SetRegValueString (RegKey, S_LINESPEED, StrValue);
    }

    CloseRegKey (RegKey);

    if (!b) {
        LOG ((LOG_ERROR, MSG_BEZIER_DATA_ERROR));
    }

    return TRUE;
}


LPCSTR g_MarqueeValues[] = {
    S_BACKGROUND_COLOR,
    S_CHARSET,
    S_FONT,
    S_MODE,
    S_SIZE,
    S_SPEED,
    S_TEXT,
    S_TEXTCOLOR,
    NULL
};


BOOL
pTranslateMarquee (
    IN      HKEY RegRoot
    )
{
    BOOL b;

    //
    // Marquee has the same settings on Win9x and NT.  They just need
    // to be copied from the control.ini file to the NT registry.
    //

    b = pCopyValuesFromSettingsFileToRegistry (
                RegRoot,
                S_MARQUEE_SETTINGS, 
                S_MARQUEE,
                g_MarqueeValues
                );

    //
    // We need to divide the speed by two to be compatible
    //

    if (b) {
        HKEY MarqueeKey;
        LPCSTR Value;
        CHAR NewValue[32];

        // Read the setting we just wrote in the registry
        MarqueeKey = pCreateControlPanelKey (RegRoot, S_MARQUEE_SETTINGS, FALSE);

        if (MarqueeKey) {
            Value = GetRegValueString (MarqueeKey, S_SPEED);
            if (Value) {
                // Write speed divided by two
                wsprintf (NewValue, "%i", atoi (Value) / 2);
                SetRegValueString (MarqueeKey, S_SPEED, NewValue);
            }

            CloseRegKey (MarqueeKey);
        }
    }

    return b;
}