Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for manifest file flags #1

Merged
merged 3 commits into from
Jan 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion TEKSteamClient.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<BaseVersion>1.1.1</BaseVersion>
<BaseVersion>1.2.0</BaseVersion>
<Version>$(BaseVersion)</Version>
<Version Condition="'$(GITHUB_RUN_NUMBER)' != ''">$(BaseVersion)-alpha.$(GITHUB_RUN_NUMBER)</Version>
<Authors>Nuclearist</Authors>
Expand Down
3 changes: 3 additions & 0 deletions protos/manifest/payload.proto
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ message Payload
enum FileFlag
{
NONE = 0;
READONLY = 8;
HIDDEN = 16;
EXECUTABLE = 32;
DIRECTORY = 64;
}
message Chunk
Expand Down
24 changes: 19 additions & 5 deletions src/AppManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public AppManager(uint appId, string installationPath, [Optional]string? worksho
if (!Directory.Exists(WorkshopContentPath))
Directory.CreateDirectory(WorkshopContentPath);
var attributes = File.GetAttributes(_scDataPath);
if ((attributes & FileAttributes.ReadOnly) is not FileAttributes.None)
if (attributes.HasFlag(FileAttributes.ReadOnly))
File.SetAttributes(_scDataPath, attributes & ~FileAttributes.ReadOnly);
}
/// <summary>Path to SCData directory.</summary>
Expand Down Expand Up @@ -117,7 +117,7 @@ void processDir(in DirectoryEntry dir, in DirectoryEntry.AuxiliaryEntry auxiliar
var file = dir.Files[auxiliaryFile.Index];
string filePath = Path.Combine(path, file.Name);
var attributes = File.GetAttributes(filePath);
if ((attributes & FileAttributes.ReadOnly) is not FileAttributes.None)
if (attributes.HasFlag(FileAttributes.ReadOnly))
File.SetAttributes(filePath, attributes & ~FileAttributes.ReadOnly);
using var fileHandle = File.OpenHandle(filePath, access: FileAccess.ReadWrite, options: FileOptions.RandomAccess);
long fileSize = RandomAccess.GetLength(fileHandle);
Expand Down Expand Up @@ -395,7 +395,6 @@ static long countTotalDirSize(in DirectoryEntry dir)
foreach (var subdir in dir.Subdirectories)
result += countTotalDirSize(in subdir);
return result;

}
if (acquisitionDir.IsNew)
{
Expand Down Expand Up @@ -423,7 +422,22 @@ static long countTotalDirSize(in DirectoryEntry dir)
var file = dir.Files[acquisitonFile.Index];
if (acquisitonFile.Chunks.Count is 0)
{
File.Move(Path.Combine(downloadPath, file.Name), Path.Combine(localPath, file.Name), true);
string destinationFile = Path.Combine(localPath, file.Name);
if (File.Exists(destinationFile))
File.Delete(destinationFile);
File.Move(Path.Combine(downloadPath, file.Name), destinationFile);
if (file.Flags is not 0)
{
var attributes = (FileAttributes)0;
if (file.Flags.HasFlag(FileEntry.Flag.ReadOnly))
attributes = FileAttributes.ReadOnly;
if (file.Flags.HasFlag(FileEntry.Flag.Hidden))
attributes |= FileAttributes.Hidden;
if (attributes is not 0)
File.SetAttributes(destinationFile, attributes);
if (file.Flags.HasFlag(FileEntry.Flag.Executable) && !OperatingSystem.IsWindows())
File.SetUnixFileMode(destinationFile, File.GetUnixFileMode(destinationFile) | UnixFileMode.UserExecute | UnixFileMode.GroupExecute | UnixFileMode.OtherExecute);
}
state.DisplayProgress += file.Size;
ProgressUpdated?.Invoke(state.DisplayProgress);
}
Expand All @@ -440,7 +454,7 @@ static long countTotalDirSize(in DirectoryEntry dir)
}
string filePath = Path.Combine(localPath, file.Name);
var attributes = File.GetAttributes(filePath);
if ((attributes & FileAttributes.ReadOnly) is not FileAttributes.None)
if (attributes.HasFlag(FileAttributes.ReadOnly))
File.SetAttributes(filePath, attributes & ~FileAttributes.ReadOnly);
using var fileHandle = File.OpenHandle(filePath, access: FileAccess.Write, options: FileOptions.RandomAccess);
for (; chunkIndex < acquisitonFile.Chunks.Count; chunkIndex++)
Expand Down
19 changes: 13 additions & 6 deletions src/Manifest/DepotManifest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ internal DepotManifest(byte[] compressedData, ItemIdentifier item)
foreach (var file in files)
{
_nameBufferSize += Encoding.UTF8.GetByteCount(Path.GetFileName(file.Name));
if ((file.Flags & FileFlag.Directory) is not 0)
if (file.Flags.HasFlag(FileFlag.Directory))
numDirs++;
else
{
Expand Down Expand Up @@ -111,7 +111,7 @@ void loadDirectory(int index, int payloadEntryIndex)
int numSepChars = file.Name.AsSpan().Count(Path.DirectorySeparatorChar);
if (numSepChars - dirNumSepChars is not 1 || numSepChars > 0 && file.Name[path.Length] != Path.DirectorySeparatorChar)
continue;
if ((file.Flags & FileFlag.Directory) is not 0)
if (file.Flags.HasFlag(FileFlag.Directory))
{
dirOffset++;
continue;
Expand All @@ -128,10 +128,17 @@ void loadDirectory(int index, int payloadEntryIndex)
Checksum = chunk.Checksum
};
new Span<ChunkEntry>(ChunkBuffer, chunkStartOffset, numChunks).Sort();
FileEntry.Flag flags = 0;
if (file.Flags.HasFlag(FileFlag.Readonly))
flags = FileEntry.Flag.ReadOnly;
if (file.Flags.HasFlag(FileFlag.Hidden))
flags |= FileEntry.Flag.Hidden;
if (file.Flags.HasFlag(FileFlag.Executable))
flags |= FileEntry.Flag.Executable;
FileBuffer[fileOffset++] = new()
{
Name = numSepChars > 0 ? Path.GetFileName(file.Name) : file.Name,
Size = file.Size,
SizeAndFlags = file.Size | ((long)flags << 56),
Chunks = new(ChunkBuffer, chunkStartOffset, numChunks)
};
}
Expand All @@ -142,7 +149,7 @@ void loadDirectory(int index, int payloadEntryIndex)
for (int i = startIndex; i < endIndex; i++)
{
var file = files[i];
if ((file.Flags & FileFlag.Directory) is not 0)
if (file.Flags.HasFlag(FileFlag.Directory))
{
int numSepChars = file.Name.AsSpan().Count(Path.DirectorySeparatorChar);
if (numSepChars - dirNumSepChars is 1 && (numSepChars < 1 || file.Name[path.Length] == Path.DirectorySeparatorChar))
Expand Down Expand Up @@ -209,7 +216,7 @@ public DepotManifest(string filePath, ItemIdentifier item, ulong id)
FileBuffer[i] = new()
{
Name = Encoding.UTF8.GetString(buffer.Slice(nameOffset, nameLength)),
Size = Unsafe.As<int, long>(ref Unsafe.Add(ref spanRef, offset)),
SizeAndFlags = Unsafe.As<int, long>(ref Unsafe.Add(ref spanRef, offset)),
Chunks = new(ChunkBuffer, chunkOffset, numChunks)
};
offset += 4;
Expand Down Expand Up @@ -289,7 +296,7 @@ public void WriteToFile(string filePath)
{
int nameLength = Encoding.UTF8.GetBytes(file.Name, buffer[nameOffset..]);
nameOffset += nameLength;
Unsafe.As<int, long>(ref Unsafe.Add(ref spanRef, offset)) = file.Size;
Unsafe.As<int, long>(ref Unsafe.Add(ref spanRef, offset)) = file.SizeAndFlags;
offset += 2;
entriesSpan[offset++] = nameLength;
entriesSpan[offset++] = file.Chunks.Count;
Expand Down
20 changes: 18 additions & 2 deletions src/Manifest/FileEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,34 @@ public readonly struct FileEntry
{
/// <summary>Name of the file.</summary>
public required string Name { get; init; }
/// <summary>Total size of the file OR'ed with its flags. Used only during initialization.</summary>
public required long SizeAndFlags { get; init; }
/// <summary>Total size of the file.</summary>
public required long Size { get; init; }
public long Size => SizeAndFlags & 0x00FFFFFFFFFFFFFF;
/// <summary>Extra flags of the file.</summary>
public Flag Flags => (Flag)((SizeAndFlags & 0x7F00000000000000) >> 56);
/// <summary>Chunks that compose the file.</summary>
public required ArraySegment<ChunkEntry> Chunks { get; init; }
/// <summary>Extra flags describing file's attributes or permissions.</summary>
[Flags]
public enum Flag
{
/// <summary>The file has <see cref="FileAttributes.ReadOnly"/> attribute.</summary>
ReadOnly = 1 << 0,
/// <summary>The file has <see cref="FileAttributes.Hidden"/> attribute.</summary>
Hidden = 1 << 1,
/// <summary>The file has <see cref="UnixFileMode.UserExecute"/>, <see cref="UnixFileMode.GroupExecute"/> and <see cref="UnixFileMode.OtherExecute"/> permissions.</summary>
Executable = 1 << 2
}
/// <summary>Structure used in <see cref="DepotDelta"/> to store indexes of files and chunks that must be acquired.</summary>
internal readonly struct AcquisitionEntry
{
/// <summary>Index of the file entry in its parent directory.</summary>
public required int Index { get; init; }
/// <summary>File's chunks that must be acquired. If empty, the whole file is acquired.</summary>
public required ArraySegment<ChunkEntry> Chunks { get; init; }
//struct int long
/// <summary>Structure storing chunk's index along with its optional offset in chunk buffer file.</summary>
/// <param name="index">Index of the chunk in its file.</param>
public readonly struct ChunkEntry(int index)
{
/// <summary>Index of the chunk entry in its file.</summary>
Expand Down