melonDS/src/SPI_Firmware.cpp

379 lines
11 KiB
C++
Raw Normal View History

Refactor the core's handling of firmware and BIOS images to rely less on the file system (#1826) * Introduce firmware-related structs * Fix some indents * Move the generated firmware identifier to a constant * Document the WifiAccessPoint constructors * Add some constants * Remove a stray comment * Implement Firmware::UserData * Add Firmware::Mask * Document Firmware::Buffer * Add a Firmware constructor that uses a FileHandle * Set the default username in UserData * Update the UserData checksum * Forgot to include Platform.h * Remove some redundant assignments in the default Firmware constructor * const-ify CRC16 * Replace the plain Firmware buffer with a Firmware object - Remove some functions that were reimplemented in the Firmware constructors * Fix some crashes due to undefined behavior * Fix the user data initialization - Initialize both user data objects to default - Set both user data objects to the same touch screen calibration * Follow the DS logic in determining which user data section is current * Remove an unneeded include * Remove another unneeded include * Initialize FirmwareMask in Firmware::Firmware * Use the DEFAULT_SSID constant * Add SPI_Firmware::InstallFirmware and SPI_Firmware::RemoveFirmware * Move a logging call to after the file is written * Add a SaveManager for the firmware * Touch up the SPI_Firmware::Firmware declaration * Move all firmware loading and customization to the frontend * Call Platform::WriteFirmware when it's time to write the firmware back to disk * Fix some missing stuff * Remove the Get* functions from SPI_Firmware in favor of GetFirmware() * Implement SPI_Firmware::DeInit in terms of RemoveFirmware * Add Firmware::UpdateChecksums * Fix an incorrect length * Update all checksums in the firmware after setting touch screen calibration data * Use the Firmware object's Position methods * Remove register fields from the Firmware object * Install the firmware before seeing if direct boot is necessary * Install the firmware before calling NDS::Reset in LoadROM * Slight cleanup in ROMManager * Fix the default access point name * Shorten the various getters in Firmware * Add qualifiers for certain uses of firmware types - GCC can get picky if -fpermissive isn't defined * Add an InstallFirmware overload that takes a unique_ptr * Log when firmware is added or removed * Don't clear the firmware in SPI_Firmware::Init - The original code didn't, it just set the pointer to nullptr * Fix a typo * Write back the full firmware if it's not generated * Move the FreeBIOS to an external file * Load wfcsettings.bin into the correct part of the generated firmware blob * Load BIOS files in the frontend, not in the core * Fix logging the firmware ID * Add some utility functions * Mark Firmware's constructors as explicit * Remove obsolete ConfigEntry values * Include <locale> explicitly in ROMManager * Fix up some includes * Add Firmware::IsBootable() * Add a newline to a log entry - Whoops * Log the number of bytes written out in SaveManager * Mark FirmwareHeader's constructor as explicit * Clean up GenerateDefaultFirmware and LoadFirmwareFromFile - Now they return a pair instead of two by-ref values * Refactor SaveManager a little bit - Manage its buffers as unique_ptrs to mitigate leaks - Reallocate the internal buffer if SetPath is asked to reload the file (and the new length is different) * Remove some stray parens * Fix some firmware-related bugs I introduced - Firmware settings are now properly saved to disk (beforehand I misunderstood when the firmware blob was written) - Firmware is no longer overwritten by contents of wfcsettings.bin * Slight cleanup
2023-09-18 19:09:11 +00:00
/*
Copyright 2016-2023 melonDS team
This file is part of melonDS.
melonDS 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 3 of the License, or (at your option)
any later version.
melonDS 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 melonDS. If not, see http://www.gnu.org/licenses/.
*/
#include "SPI_Firmware.h"
#include "SPI.h"
#include <string.h>
constexpr u8 BBINIT[0x69]
{
0x03, 0x17, 0x40, 0x00, 0x1B, 0x6C, 0x48, 0x80, 0x38, 0x00, 0x35, 0x07, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC7, 0xBB, 0x01, 0x24, 0x7F,
0x5A, 0x01, 0x3F, 0x01, 0x3F, 0x36, 0x1D, 0x00, 0x78, 0x35, 0x55, 0x12, 0x34, 0x1C, 0x00, 0x01,
0x0E, 0x38, 0x03, 0x70, 0xC5, 0x2A, 0x0A, 0x08, 0x04, 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFE,
0xFE, 0xFE, 0xFE, 0xFC, 0xFC, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xF8, 0xF8, 0xF6, 0x00, 0x12, 0x14,
0x12, 0x41, 0x23, 0x03, 0x04, 0x70, 0x35, 0x0E, 0x2C, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x12, 0x28, 0x1C
};
constexpr u8 RFINIT[0x29]
{
0x31, 0x4C, 0x4F, 0x21, 0x00, 0x10, 0xB0, 0x08, 0xFA, 0x15, 0x26, 0xE6, 0xC1, 0x01, 0x0E, 0x50,
0x05, 0x00, 0x6D, 0x12, 0x00, 0x00, 0x01, 0xFF, 0x0E, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x06,
0x06, 0x00, 0x00, 0x00, 0x18, 0x00, 0x02, 0x00, 0x00
};
constexpr u8 CHANDATA[0x3C]
{
0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x16,
0x26, 0x1C, 0x1C, 0x1C, 0x1D, 0x1D, 0x1D, 0x1E, 0x1E, 0x1E, 0x1E, 0x1F, 0x1E, 0x1F, 0x18,
0x01, 0x4B, 0x4B, 0x4B, 0x4B, 0x4C, 0x4C, 0x4C, 0x4C, 0x4C, 0x4C, 0x4C, 0x4D, 0x4D, 0x4D,
0x02, 0x6C, 0x71, 0x76, 0x5B, 0x40, 0x45, 0x4A, 0x2F, 0x34, 0x39, 0x3E, 0x03, 0x08, 0x14
};
constexpr u8 DEFAULT_UNUSED3[6] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00 };
SPI_Firmware::WifiAccessPoint::WifiAccessPoint()
{
memset(Bytes, 0, sizeof(Bytes));
Status = AccessPointStatus::NotConfigured;
ConnectionConfigured = 0x01;
UpdateChecksum();
}
SPI_Firmware::WifiAccessPoint::WifiAccessPoint(int consoletype)
{
memset(Bytes, 0, sizeof(Bytes));
strncpy(SSID, DEFAULT_SSID, sizeof(SSID));
if (consoletype == 1) Mtu = 1400;
Status = AccessPointStatus::Normal;
ConnectionConfigured = 0x01;
UpdateChecksum();
}
void SPI_Firmware::WifiAccessPoint::UpdateChecksum()
{
Checksum = CRC16(Bytes, 0xFE, 0x0000);
}
SPI_Firmware::ExtendedWifiAccessPoint::ExtendedWifiAccessPoint()
{
Data.Base = WifiAccessPoint();
UpdateChecksum();
}
void SPI_Firmware::ExtendedWifiAccessPoint::UpdateChecksum()
{
Data.Base.UpdateChecksum();
Data.ExtendedChecksum = CRC16(&Bytes[0x100], 0xFE, 0x0000);
}
SPI_Firmware::FirmwareHeader::FirmwareHeader(int consoletype)
{
if (consoletype == 1)
{
ConsoleType = FirmwareConsoleType::DSi;
WifiVersion = WifiVersion::W015;
WifiBoard = WifiBoard::W015;
WifiFlash = 0x20;
// these need to be zero (part of the stage2 firmware signature!)
memset(&Bytes[0x22], 0, 8);
}
else
{
ConsoleType = FirmwareConsoleType::DSLite; // TODO: make configurable?
WifiVersion = WifiVersion::W006;
}
Identifier = GENERATED_FIRMWARE_IDENTIFIER;
// wifi calibration
WifiConfigLength = 0x138;
Unused1 = 0;
memcpy(&Unused3, DEFAULT_UNUSED3, sizeof(DEFAULT_UNUSED3));
MacAddress = DEFAULT_MAC;
EnabledChannels = 0x3FFE;
memset(&Unknown2, 0xFF, sizeof(Unknown2));
RFChipType = RFChipType::Type3;
RFBitsPerEntry = 0x94;
RFEntries = 0x29;
Unknown3 = 0x02;
*(u16*)&Bytes[0x44] = 0x0002;
*(u16*)&Bytes[0x46] = 0x0017;
*(u16*)&Bytes[0x48] = 0x0026;
*(u16*)&Bytes[0x4A] = 0x1818;
*(u16*)&Bytes[0x4C] = 0x0048;
*(u16*)&Bytes[0x4E] = 0x4840;
*(u16*)&Bytes[0x50] = 0x0058;
*(u16*)&Bytes[0x52] = 0x0042;
*(u16*)&Bytes[0x54] = 0x0146;
*(u16*)&Bytes[0x56] = 0x8064;
*(u16*)&Bytes[0x58] = 0xE6E6;
*(u16*)&Bytes[0x5A] = 0x2443;
*(u16*)&Bytes[0x5C] = 0x000E;
*(u16*)&Bytes[0x5E] = 0x0001;
*(u16*)&Bytes[0x60] = 0x0001;
*(u16*)&Bytes[0x62] = 0x0402;
memcpy(&InitialBBValues, BBINIT, sizeof(BBINIT));
Unused4 = 0;
memcpy(&Type3Config.InitialRFValues, RFINIT, sizeof(RFINIT));
Type3Config.BBIndicesPerChannel = 0x02;
memcpy(&Bytes[0xF8], CHANDATA, sizeof(CHANDATA));
memset(&Type3Config.Unused0, 0xFF, sizeof(Type3Config.Unused0));
UpdateChecksum();
}
void SPI_Firmware::FirmwareHeader::UpdateChecksum()
{
WifiConfigChecksum = SPI_Firmware::CRC16(&Bytes[0x2C], WifiConfigLength, 0x0000);
}
SPI_Firmware::UserData::UserData()
{
memset(Bytes, 0, 0x74);
Version = 5;
BirthdayMonth = 1;
BirthdayDay = 1;
Settings = Language::English | BacklightLevel::Max; // NOLINT(*-suspicious-enum-usage)
memcpy(Nickname, DEFAULT_USERNAME.data(), DEFAULT_USERNAME.size() * sizeof(std::u16string_view::value_type));
NameLength = DEFAULT_USERNAME.size();
Checksum = CRC16(Bytes, 0x70, 0xFFFF);
}
void SPI_Firmware::UserData::UpdateChecksum()
{
Checksum = CRC16(Bytes, 0x70, 0xFFFF);
if (ExtendedSettings.Unknown0 == 0x01)
{
ExtendedSettings.Checksum = CRC16(&Bytes[0x74], 0x8A, 0xFFFF);
}
}
SPI_Firmware::Firmware::Firmware(int consoletype)
{
FirmwareBufferLength = DEFAULT_FIRMWARE_LENGTH;
FirmwareBuffer = new u8[FirmwareBufferLength];
memset(FirmwareBuffer, 0xFF, FirmwareBufferLength);
FirmwareMask = FirmwareBufferLength - 1;
memset(FirmwareBuffer, 0, 0x1D);
FirmwareHeader& header = *reinterpret_cast<FirmwareHeader*>(FirmwareBuffer);
header = FirmwareHeader(consoletype);
FirmwareBuffer[0x2FF] = 0x80; // boot0: use NAND as stage2 medium
// user data
header.UserSettingsOffset = (0x7FE00 & FirmwareMask) >> 3;
std::array<union UserData, 2>& settings = *reinterpret_cast<std::array<union UserData, 2>*>(UserDataPosition());
settings = {
SPI_Firmware::UserData(),
SPI_Firmware::UserData(),
};
// wifi access points
// TODO: WFC ID??
std::array<WifiAccessPoint, 3>& accesspoints = *reinterpret_cast<std::array<WifiAccessPoint, 3>*>(WifiAccessPointPosition());
accesspoints = {
WifiAccessPoint(consoletype),
WifiAccessPoint(),
WifiAccessPoint(),
};
if (consoletype == 1)
{
std::array<ExtendedWifiAccessPoint, 3>& extendedaccesspoints = *reinterpret_cast<std::array<ExtendedWifiAccessPoint, 3>*>(ExtendedAccessPointPosition());
extendedaccesspoints = {
ExtendedWifiAccessPoint(),
ExtendedWifiAccessPoint(),
ExtendedWifiAccessPoint(),
};
}
}
SPI_Firmware::Firmware::Firmware(Platform::FileHandle* file) : FirmwareBuffer(nullptr), FirmwareBufferLength(0), FirmwareMask(0)
{
if (file)
{
u64 length = Platform::FileLength(file);
if (length > 0)
{
FirmwareBufferLength = FixFirmwareLength(length);
FirmwareBuffer = new u8[FirmwareBufferLength];
FirmwareMask = FirmwareBufferLength - 1;
memset(FirmwareBuffer, 0, FirmwareBufferLength);
Platform::FileRewind(file);
if (!Platform::FileRead(FirmwareBuffer, length, 1, file))
{
delete[] FirmwareBuffer;
FirmwareBuffer = nullptr;
FirmwareBufferLength = 0;
FirmwareMask = 0;
}
}
Platform::FileRewind(file);
}
}
SPI_Firmware::Firmware::Firmware(const u8* data, u32 length) : FirmwareBuffer(nullptr), FirmwareBufferLength(FixFirmwareLength(length))
{
if (data)
{
FirmwareBuffer = new u8[FirmwareBufferLength];
memcpy(FirmwareBuffer, data, FirmwareBufferLength);
FirmwareMask = FirmwareBufferLength - 1;
}
}
SPI_Firmware::Firmware::Firmware(const Firmware& other) : FirmwareBuffer(nullptr), FirmwareBufferLength(other.FirmwareBufferLength)
{
FirmwareBuffer = new u8[FirmwareBufferLength];
memcpy(FirmwareBuffer, other.FirmwareBuffer, FirmwareBufferLength);
FirmwareMask = other.FirmwareMask;
}
SPI_Firmware::Firmware::Firmware(Firmware&& other) noexcept
{
FirmwareBuffer = other.FirmwareBuffer;
FirmwareBufferLength = other.FirmwareBufferLength;
FirmwareMask = other.FirmwareMask;
other.FirmwareBuffer = nullptr;
other.FirmwareBufferLength = 0;
other.FirmwareMask = 0;
}
SPI_Firmware::Firmware& SPI_Firmware::Firmware::operator=(const Firmware& other)
{
if (this != &other)
{
delete[] FirmwareBuffer;
FirmwareBufferLength = other.FirmwareBufferLength;
FirmwareBuffer = new u8[other.FirmwareBufferLength];
memcpy(FirmwareBuffer, other.FirmwareBuffer, other.FirmwareBufferLength);
FirmwareMask = other.FirmwareMask;
}
return *this;
}
SPI_Firmware::Firmware& SPI_Firmware::Firmware::operator=(Firmware&& other) noexcept
{
if (this != &other)
{
delete[] FirmwareBuffer;
FirmwareBuffer = other.FirmwareBuffer;
FirmwareBufferLength = other.FirmwareBufferLength;
FirmwareMask = other.FirmwareMask;
other.FirmwareBuffer = nullptr;
other.FirmwareBufferLength = 0;
other.FirmwareMask = 0;
}
return *this;
}
SPI_Firmware::Firmware::~Firmware()
{
delete[] FirmwareBuffer;
}
bool SPI_Firmware::Firmware::IsBootable() const
{
return
FirmwareBufferLength != DEFAULT_FIRMWARE_LENGTH &&
Header().Identifier != GENERATED_FIRMWARE_IDENTIFIER
;
}
const SPI_Firmware::UserData& SPI_Firmware::Firmware::EffectiveUserData() const {
const std::array<union UserData, 2>& userdata = UserData();
bool userdata0ChecksumOk = userdata[0].ChecksumValid();
bool userdata1ChecksumOk = userdata[1].ChecksumValid();
if (userdata0ChecksumOk && userdata1ChecksumOk)
{
return userdata[0].UpdateCounter > userdata[1].UpdateCounter ? userdata[0] : userdata[1];
}
else if (userdata0ChecksumOk)
{
return userdata[0];
}
else if (userdata1ChecksumOk)
{
return userdata[1];
}
else
{
return userdata[0];
}
}
SPI_Firmware::UserData& SPI_Firmware::Firmware::EffectiveUserData() {
std::array<union UserData, 2>& userdata = UserData();
bool userdata0ChecksumOk = userdata[0].ChecksumValid();
bool userdata1ChecksumOk = userdata[1].ChecksumValid();
if (userdata0ChecksumOk && userdata1ChecksumOk)
{
return userdata[0].UpdateCounter > userdata[1].UpdateCounter ? userdata[0] : userdata[1];
}
else if (userdata0ChecksumOk)
{
return userdata[0];
}
else if (userdata1ChecksumOk)
{
return userdata[1];
}
else
{
return userdata[0];
}
}
void SPI_Firmware::Firmware::UpdateChecksums()
{
Header().UpdateChecksum();
for (SPI_Firmware::WifiAccessPoint& ap : AccessPoints())
{
ap.UpdateChecksum();
}
if (Header().ConsoleType == FirmwareConsoleType::DSi)
Refactor the core's handling of firmware and BIOS images to rely less on the file system (#1826) * Introduce firmware-related structs * Fix some indents * Move the generated firmware identifier to a constant * Document the WifiAccessPoint constructors * Add some constants * Remove a stray comment * Implement Firmware::UserData * Add Firmware::Mask * Document Firmware::Buffer * Add a Firmware constructor that uses a FileHandle * Set the default username in UserData * Update the UserData checksum * Forgot to include Platform.h * Remove some redundant assignments in the default Firmware constructor * const-ify CRC16 * Replace the plain Firmware buffer with a Firmware object - Remove some functions that were reimplemented in the Firmware constructors * Fix some crashes due to undefined behavior * Fix the user data initialization - Initialize both user data objects to default - Set both user data objects to the same touch screen calibration * Follow the DS logic in determining which user data section is current * Remove an unneeded include * Remove another unneeded include * Initialize FirmwareMask in Firmware::Firmware * Use the DEFAULT_SSID constant * Add SPI_Firmware::InstallFirmware and SPI_Firmware::RemoveFirmware * Move a logging call to after the file is written * Add a SaveManager for the firmware * Touch up the SPI_Firmware::Firmware declaration * Move all firmware loading and customization to the frontend * Call Platform::WriteFirmware when it's time to write the firmware back to disk * Fix some missing stuff * Remove the Get* functions from SPI_Firmware in favor of GetFirmware() * Implement SPI_Firmware::DeInit in terms of RemoveFirmware * Add Firmware::UpdateChecksums * Fix an incorrect length * Update all checksums in the firmware after setting touch screen calibration data * Use the Firmware object's Position methods * Remove register fields from the Firmware object * Install the firmware before seeing if direct boot is necessary * Install the firmware before calling NDS::Reset in LoadROM * Slight cleanup in ROMManager * Fix the default access point name * Shorten the various getters in Firmware * Add qualifiers for certain uses of firmware types - GCC can get picky if -fpermissive isn't defined * Add an InstallFirmware overload that takes a unique_ptr * Log when firmware is added or removed * Don't clear the firmware in SPI_Firmware::Init - The original code didn't, it just set the pointer to nullptr * Fix a typo * Write back the full firmware if it's not generated * Move the FreeBIOS to an external file * Load wfcsettings.bin into the correct part of the generated firmware blob * Load BIOS files in the frontend, not in the core * Fix logging the firmware ID * Add some utility functions * Mark Firmware's constructors as explicit * Remove obsolete ConfigEntry values * Include <locale> explicitly in ROMManager * Fix up some includes * Add Firmware::IsBootable() * Add a newline to a log entry - Whoops * Log the number of bytes written out in SaveManager * Mark FirmwareHeader's constructor as explicit * Clean up GenerateDefaultFirmware and LoadFirmwareFromFile - Now they return a pair instead of two by-ref values * Refactor SaveManager a little bit - Manage its buffers as unique_ptrs to mitigate leaks - Reallocate the internal buffer if SetPath is asked to reload the file (and the new length is different) * Remove some stray parens * Fix some firmware-related bugs I introduced - Firmware settings are now properly saved to disk (beforehand I misunderstood when the firmware blob was written) - Firmware is no longer overwritten by contents of wfcsettings.bin * Slight cleanup
2023-09-18 19:09:11 +00:00
{
for (SPI_Firmware::ExtendedWifiAccessPoint& eap : ExtendedAccessPoints())
{
eap.UpdateChecksum();
}
Refactor the core's handling of firmware and BIOS images to rely less on the file system (#1826) * Introduce firmware-related structs * Fix some indents * Move the generated firmware identifier to a constant * Document the WifiAccessPoint constructors * Add some constants * Remove a stray comment * Implement Firmware::UserData * Add Firmware::Mask * Document Firmware::Buffer * Add a Firmware constructor that uses a FileHandle * Set the default username in UserData * Update the UserData checksum * Forgot to include Platform.h * Remove some redundant assignments in the default Firmware constructor * const-ify CRC16 * Replace the plain Firmware buffer with a Firmware object - Remove some functions that were reimplemented in the Firmware constructors * Fix some crashes due to undefined behavior * Fix the user data initialization - Initialize both user data objects to default - Set both user data objects to the same touch screen calibration * Follow the DS logic in determining which user data section is current * Remove an unneeded include * Remove another unneeded include * Initialize FirmwareMask in Firmware::Firmware * Use the DEFAULT_SSID constant * Add SPI_Firmware::InstallFirmware and SPI_Firmware::RemoveFirmware * Move a logging call to after the file is written * Add a SaveManager for the firmware * Touch up the SPI_Firmware::Firmware declaration * Move all firmware loading and customization to the frontend * Call Platform::WriteFirmware when it's time to write the firmware back to disk * Fix some missing stuff * Remove the Get* functions from SPI_Firmware in favor of GetFirmware() * Implement SPI_Firmware::DeInit in terms of RemoveFirmware * Add Firmware::UpdateChecksums * Fix an incorrect length * Update all checksums in the firmware after setting touch screen calibration data * Use the Firmware object's Position methods * Remove register fields from the Firmware object * Install the firmware before seeing if direct boot is necessary * Install the firmware before calling NDS::Reset in LoadROM * Slight cleanup in ROMManager * Fix the default access point name * Shorten the various getters in Firmware * Add qualifiers for certain uses of firmware types - GCC can get picky if -fpermissive isn't defined * Add an InstallFirmware overload that takes a unique_ptr * Log when firmware is added or removed * Don't clear the firmware in SPI_Firmware::Init - The original code didn't, it just set the pointer to nullptr * Fix a typo * Write back the full firmware if it's not generated * Move the FreeBIOS to an external file * Load wfcsettings.bin into the correct part of the generated firmware blob * Load BIOS files in the frontend, not in the core * Fix logging the firmware ID * Add some utility functions * Mark Firmware's constructors as explicit * Remove obsolete ConfigEntry values * Include <locale> explicitly in ROMManager * Fix up some includes * Add Firmware::IsBootable() * Add a newline to a log entry - Whoops * Log the number of bytes written out in SaveManager * Mark FirmwareHeader's constructor as explicit * Clean up GenerateDefaultFirmware and LoadFirmwareFromFile - Now they return a pair instead of two by-ref values * Refactor SaveManager a little bit - Manage its buffers as unique_ptrs to mitigate leaks - Reallocate the internal buffer if SetPath is asked to reload the file (and the new length is different) * Remove some stray parens * Fix some firmware-related bugs I introduced - Firmware settings are now properly saved to disk (beforehand I misunderstood when the firmware blob was written) - Firmware is no longer overwritten by contents of wfcsettings.bin * Slight cleanup
2023-09-18 19:09:11 +00:00
}
for (SPI_Firmware::UserData& u : UserData())
{
u.UpdateChecksum();
}
}