diff --git a/.editorconfig b/.editorconfig
index 9e8d5cd..bf4269a 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -4,18 +4,16 @@ root = true
charset = utf-8
indent_style = space
indent_size = 2
-
-# .gitattributes is configured to make this work on Windows too, irrespective of git config
-end_of_line = lf
insert_final_newline = true
+# *WARNING*: If you use the Visual Studio Designer to edit this file, it may
+# change this to 'crlf'. Revert it back to 'unset' or x-plat things will
+# break.
+end_of_line = unset
+
[*.sln]
-end_of_line = crlf
charset = utf-8-bom
-[*.lutconfig]
-end_of_line = crlf
-
#### C# Coding Conventions ####
[*.cs]
indent_size = 4
@@ -171,6 +169,8 @@ dotnet_diagnostic.CA1303.severity = silent
# CS1591: Missing XML comment for publicly visible type or member
dotnet_diagnostic.CS1591.severity = silent
+# IDE0072: Add missing cases
+dotnet_diagnostic.IDE0072.severity = silent
[*.{cs,vb}]
#### .NET Coding Conventions ####
@@ -424,3 +424,76 @@ dotnet_naming_style.s_camelcase.capitalization = camel_case
dotnet_style_allow_multiple_blank_lines_experimental = true:silent
dotnet_style_allow_statement_immediately_after_block_experimental = true:silent
+
+### C++ Coding Conventions ###
+[*.{c,c++,cc,cpp,cppm,cxx,h,h++,hh,hpp,hxx,inl,ipp,ixx,tlh,tli}]
+indent_size = 4
+cpp_generate_documentation_comments = xml
+cpp_indent_braces = false
+cpp_indent_multi_line_relative_to = statement_begin
+cpp_indent_within_parentheses = indent
+cpp_indent_preserve_within_parentheses = true
+cpp_indent_case_contents = false
+cpp_indent_case_labels = false
+cpp_indent_case_contents_when_block = false
+cpp_indent_lambda_braces_when_parameter = false
+cpp_indent_goto_labels = none
+cpp_indent_preprocessor = none
+cpp_indent_access_specifiers = false
+cpp_indent_namespace_contents = false
+cpp_indent_preserve_comments = false
+cpp_new_line_before_open_brace_namespace = ignore
+cpp_new_line_before_open_brace_type = ignore
+cpp_new_line_before_open_brace_function = ignore
+cpp_new_line_before_open_brace_block = ignore
+cpp_new_line_before_open_brace_lambda = ignore
+cpp_new_line_scope_braces_on_separate_lines = false
+cpp_new_line_close_brace_same_line_empty_type = true
+cpp_new_line_close_brace_same_line_empty_function = true
+cpp_new_line_before_catch = false
+cpp_new_line_before_else = false
+cpp_new_line_before_while_in_do_while = false
+cpp_space_before_function_open_parenthesis = ignore
+cpp_space_within_parameter_list_parentheses = false
+cpp_space_between_empty_parameter_list_parentheses = false
+cpp_space_after_keywords_in_control_flow_statements = true
+cpp_space_within_control_flow_statement_parentheses = false
+cpp_space_before_lambda_open_parenthesis = false
+cpp_space_within_cast_parentheses = false
+cpp_space_after_cast_close_parenthesis = false
+cpp_space_within_expression_parentheses = false
+cpp_space_before_block_open_brace = false
+cpp_space_between_empty_braces = false
+cpp_space_before_initializer_list_open_brace = false
+cpp_space_within_initializer_list_braces = false
+cpp_space_preserve_in_initializer_list = false
+cpp_space_before_open_square_bracket = false
+cpp_space_within_square_brackets = false
+cpp_space_before_empty_square_brackets = false
+cpp_space_between_empty_square_brackets = false
+cpp_space_group_square_brackets = false
+cpp_space_within_lambda_brackets = false
+cpp_space_between_empty_lambda_brackets = false
+cpp_space_before_comma = false
+cpp_space_after_comma = true
+cpp_space_remove_around_member_operators = false
+cpp_space_before_inheritance_colon = false
+cpp_space_before_constructor_colon = false
+cpp_space_remove_before_semicolon = false
+cpp_space_after_semicolon = false
+cpp_space_remove_around_unary_operator = false
+cpp_space_around_binary_operator = ignore
+cpp_space_around_assignment_operator = ignore
+cpp_space_pointer_reference_alignment = ignore
+cpp_space_around_ternary_operator = ignore
+cpp_use_unreal_engine_macro_formatting = false
+cpp_wrap_preserve_blocks = never
+cpp_include_cleanup_add_missing_error_tag_type = suggestion
+cpp_include_cleanup_remove_unused_error_tag_type = dimmed
+cpp_include_cleanup_optimize_unused_error_tag_type = suggestion
+cpp_include_cleanup_sort_after_edits = false
+cpp_sort_includes_error_tag_type = none
+cpp_sort_includes_priority_case_sensitive = false
+cpp_sort_includes_priority_style = quoted
+cpp_includes_style = default
+cpp_includes_use_forward_slash = false
diff --git a/.gitattributes b/.gitattributes
index 3d765eb..d8b06f7 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,9 +1,5 @@
-# Use unix line endings always, even on Windows
-* text=auto eol=lf
-
-# Exceptions to above for files that VS saves with CRLF always
-*.sln eol=crlf
-*.lutconfig eol=crlf
+# Normalize line endings to LF in repo, checkout CRLF on Windows
+* text=auto
# Allow comments in JSON in GitHub rendering
*.json linguist-language=JSON-with-Comments
diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml
index cc579c2..abd2642 100644
--- a/.github/workflows/validate.yml
+++ b/.github/workflows/validate.yml
@@ -28,10 +28,13 @@ jobs:
run: dotnet restore src
- name: Check Formatting
- run: dotnet format --verify-no-changes src
-
+ run: dotnet format --verify-no-changes src --verbosity diagnostic
- name: Build
run: dotnet build src -c ${{matrix.configuration}} --no-restore
+ - name: Build With C++ support
+ if: matrix.os == 'windows-latest'
+ run: msbuild src /p:Configuration=${{matrix.configuration}} /p:Platform=x64
+
- name: Test
run: dotnet test src -c ${{matrix.configuration}} --no-build
diff --git a/.vscode/settings.json b/.vscode/settings.json
index bf229fa..95ce982 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -4,7 +4,6 @@
"editor.insertSpaces": true,
"editor.tabSize": 2,
"files.encoding": "utf8",
- "files.eol": "\n",
"files.insertFinalNewline": true,
"files.exclude": {
"bld/**": true,
diff --git a/src/Cask.sln b/src/Cask.sln
index 342b4f3..1dbb83a 100644
--- a/src/Cask.sln
+++ b/src/Cask.sln
@@ -16,6 +16,8 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{0C3A2105-9369-461A-92AB-3D39CA120B83}"
ProjectSection(SolutionItems) = preProject
..\.editorconfig = ..\.editorconfig
+ ..\.gitattributes = ..\.gitattributes
+ ..\.gitignore = ..\.gitignore
Cask.lutconfig = Cask.lutconfig
Directory.Build.props = Directory.Build.props
Directory.Build.rsp = Directory.Build.rsp
@@ -51,6 +53,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Workflows", "Workflows", "{
..\.github\workflows\validate.yml = ..\.github\workflows\validate.yml
EndProjectSection
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libcask", "libcask\libcask.vcxproj", "{14013CD3-B963-4851-AA9A-7C7A2F110A52}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -73,6 +77,8 @@ Global
{FB74046B-2FF6-4316-85B1-39A28D945A18}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FB74046B-2FF6-4316-85B1-39A28D945A18}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FB74046B-2FF6-4316-85B1-39A28D945A18}.Release|Any CPU.Build.0 = Release|Any CPU
+ {14013CD3-B963-4851-AA9A-7C7A2F110A52}.Debug|Any CPU.ActiveCfg = Debug|x64
+ {14013CD3-B963-4851-AA9A-7C7A2F110A52}.Release|Any CPU.ActiveCfg = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/src/Tests/Cask.Tests/Cask.Tests.csproj b/src/Tests/Cask.Tests/Cask.Tests.csproj
index 0b91fc9..5a24d5a 100644
--- a/src/Tests/Cask.Tests/Cask.Tests.csproj
+++ b/src/Tests/Cask.Tests/Cask.Tests.csproj
@@ -1,9 +1,30 @@
net8.0;net472
+ true
+ true
+ true
+ true
+
+ True
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Tests/Cask.Tests/CppCaskTests.cs b/src/Tests/Cask.Tests/CppCaskTests.cs
new file mode 100644
index 0000000..daa7dda
--- /dev/null
+++ b/src/Tests/Cask.Tests/CppCaskTests.cs
@@ -0,0 +1,139 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.Runtime.InteropServices;
+using System.Text;
+
+using Xunit;
+
+
+using static System.Runtime.InteropServices.UnmanagedType;
+
+[assembly: DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories)]
+
+namespace CommonAnnotatedSecurityKeys.Tests;
+
+// WIP: These tests are disabled because the C++ implementation is stubbed out.
+// To enable them, flip the return value of IsSupportedTestClass in
+// TestFilter.cs.
+
+
+// CA2101: Specify marshaling for P/Invoke string arguments
+// Supppressed due to false positives: https://github.com/dotnet/roslyn-analyzers/issues/7502
+#pragma warning disable CA2101
+
+// SYSLIB1054: Use 'LibraryImportAttribute' instead of 'DllImportAttribute' /o
+// generate P/Invoke marshalling code at compile time This is very cool but
+// would require additional work to switch between LibraryImport and DllImport
+// based on target framework.
+#pragma warning disable SYSLIB1054
+
+public class CppCaskTests : CaskTestsBase
+{
+ public CppCaskTests() : base(new Implementation())
+ {
+ }
+
+ private sealed class Implementation : ICask
+ {
+ public bool CompareHash(string candidateHash,
+ byte[] derivationInput,
+ string secret,
+ int secretEntropyInBytes = 32)
+ {
+ return NativeMethods.Cask_CompareHash(candidateHash, derivationInput, derivationInput.Length, secret, secretEntropyInBytes);
+ }
+
+ public string GenerateHash(byte[] derivationInput,
+ string secret,
+ int secretEntropyInBytes = 32)
+ {
+ int size = NativeMethods.Cask_GenerateHash(derivationInput, derivationInput.Length, secret, secretEntropyInBytes, null, 0);
+ byte[] bytes = new byte[size];
+ size = NativeMethods.Cask_GenerateHash(derivationInput, derivationInput.Length, secret, secretEntropyInBytes, bytes, size);
+ Assert.True(size == bytes.Length, "Cask_GenerateKey did not use as many bytes as it said it would.");
+ return Encoding.UTF8.GetString(bytes, 0, size - 1); // - 1 to remove null terminator
+ }
+
+ public string GenerateKey(string providerSignature,
+ string allocatorCode,
+ string? reserved = null,
+ int secretEntropyInBytes = 32)
+ {
+ int size = NativeMethods.Cask_GenerateKey(providerSignature, allocatorCode, reserved, secretEntropyInBytes, null, 0);
+ byte[] bytes = new byte[size];
+ size = NativeMethods.Cask_GenerateKey(providerSignature, allocatorCode, reserved, secretEntropyInBytes, bytes, size);
+ Assert.True(size == bytes.Length, "Cask_GenerateKey did not use as many bytes as it said it would.");
+ return Encoding.UTF8.GetString(bytes, 0, size - 1); // -1 to remove null terminator
+ }
+
+ public bool IsCask(string keyOrHash)
+ {
+ return NativeMethods.Cask_IsCask(keyOrHash);
+ }
+
+ public bool IsCaskBytes(byte[] bytes)
+ {
+ return NativeMethods.Cask_IsCaskBytes(bytes, bytes.Length);
+ }
+
+ Mock ICask.MockFillRandom(FillRandomAction fillRandom)
+ {
+ throw new NotImplementedException();
+ }
+
+ Mock ICask.MockUtcNow(UtcNowFunc getUtcNow)
+ {
+ throw new NotImplementedException();
+ }
+
+ private static class NativeMethods
+ {
+ [DllImport("libcask")]
+ [return: MarshalAs(I1)]
+ public static extern bool Cask_IsCask(
+ [MarshalAs(LPUTF8Str)] string keyOrHash);
+
+ [DllImport("libcask")]
+ [return: MarshalAs(I1)]
+ public static extern bool Cask_IsCaskBytes(
+ byte[] keyOrHash,
+ int length);
+
+ [DllImport("libcask")]
+ [return: MarshalAs(I1)]
+ public static extern bool Cask_CompareHash(
+ [MarshalAs(LPUTF8Str)]
+ string candidateHash,
+ byte[] derivationInput,
+ int derivationInputLength,
+ [MarshalAs(LPUTF8Str)] string secret,
+ int secretEntropyInBytes);
+
+ [DllImport("libcask")]
+ public static extern int Cask_GenerateKey(
+ [MarshalAs(LPUTF8Str)]
+ string providerSignature,
+ [MarshalAs(LPUTF8Str)]
+ string allocatorCode,
+ [MarshalAs(LPUTF8Str)]
+ string? providerData,
+ int secretEntropyInBytes,
+ byte[]? output,
+ int outputCapacity);
+
+ [DllImport("libcask")]
+ public static extern int Cask_GenerateHash(
+ byte[] derivationInput,
+ int derivationInputLength,
+ [MarshalAs(LPUTF8Str)]
+ string secret,
+ int secretEntropyInBytes,
+ byte[]? output,
+ int outputCapacity);
+ }
+ }
+}
+
+#pragma warning restore CA2101
+#pragma warning restore SYSLIB1054
diff --git a/src/Tests/Cask.Tests/Properties/launchSettings.json b/src/Tests/Cask.Tests/Properties/launchSettings.json
new file mode 100644
index 0000000..4397fe1
--- /dev/null
+++ b/src/Tests/Cask.Tests/Properties/launchSettings.json
@@ -0,0 +1,8 @@
+{
+ "profiles": {
+ "Cask.Tests": {
+ "commandName": "Project",
+ "nativeDebugging": true
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Tests/Cask.Tests/TestFilter.cs b/src/Tests/Cask.Tests/TestFilter.cs
new file mode 100644
index 0000000..1caa533
--- /dev/null
+++ b/src/Tests/Cask.Tests/TestFilter.cs
@@ -0,0 +1,62 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.Runtime.InteropServices;
+
+using Xunit;
+using Xunit.Abstractions;
+using Xunit.Sdk;
+
+[assembly: TestFramework("CommonAnnotatedSecurityKeys.Tests.TestFilter", "Cask.Tests")]
+
+namespace CommonAnnotatedSecurityKeys.Tests;
+
+public sealed class TestFilter : XunitTestFramework
+{
+ public TestFilter(IMessageSink messageSink) : base(messageSink) { }
+
+ protected override ITestFrameworkDiscoverer CreateDiscoverer(IAssemblyInfo assemblyInfo)
+ {
+ return new Discoverer(assemblyInfo, SourceInformationProvider, DiagnosticMessageSink);
+ }
+
+ private static bool IsSupportedTestClass(ITypeInfo type)
+ {
+ if (type.Name.EndsWith($".{nameof(CppCaskTests)}", StringComparison.Ordinal))
+ {
+ if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ Console.WriteLine("INFO: Skipping C++ tests on non-Windows.");
+ return false;
+ }
+
+ if (RuntimeInformation.OSArchitecture != Architecture.X64)
+ {
+ Console.WriteLine("INFO: Skipping C++ tests on non-x64 OS.");
+ return false;
+ }
+
+ // WIP: Flip this to true to enable C++ tests. They are not yet
+ // enabled because they fail as the C++ implementation is
+ // stubbed out..
+ return true;
+ }
+
+ return true;
+ }
+
+ private sealed class Discoverer : XunitTestFrameworkDiscoverer
+ {
+ public Discoverer(
+ IAssemblyInfo assemblyInfo,
+ ISourceInformationProvider sourceProvider,
+ IMessageSink diagnosticMessageSink,
+ IXunitTestCollectionFactory? collectionFactory = null)
+ : base(assemblyInfo, sourceProvider, diagnosticMessageSink, collectionFactory) { }
+
+ protected override bool IsValidTestClass(ITypeInfo type)
+ {
+ return base.IsValidTestClass(type) && IsSupportedTestClass(type);
+ }
+ }
+}
diff --git a/src/libcask/.gitignore b/src/libcask/.gitignore
new file mode 100644
index 0000000..6be6c65
--- /dev/null
+++ b/src/libcask/.gitignore
@@ -0,0 +1 @@
+libcask.sln
diff --git a/src/libcask/cask.cpp b/src/libcask/cask.cpp
new file mode 100644
index 0000000..2f157a8
--- /dev/null
+++ b/src/libcask/cask.cpp
@@ -0,0 +1,73 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+// WIP: This file implements the interop-friendly libcask API
+// The implementation can use C++.
+
+#include
+
+#include "cask.h"
+#include "cask_dependencies.h"
+
+CASK_API bool Cask_IsCask(const char* keyOrHash)
+{
+ return false;
+}
+
+CASK_API bool Cask_IsCaskBytes(const uint8_t* keyOrHashBytes,
+ int32_t length)
+{
+ return false;
+}
+
+CASK_API int32_t Cask_GenerateKey(const char* allocatorCode,
+ const char* providerSignature,
+ const char* providerData,
+ int32_t secretEntropyInBytes,
+ char* output,
+ int32_t outputSizeInBytes)
+{
+ // WIP: Demonstrating a calling pattern for caller to be able to ask for
+ // buffer size. We can mimic this in GenerateHash. This is always a
+ // challenge for C API and I'm open to other approaches.
+
+ const char* key = "not_a_real_key";
+ int32_t requiredSizeInBytes = int32_t(strlen(key) + 1); // +1 for null terminator
+
+ if (output == nullptr)
+ {
+ // no buffer: return the minimum size of the buffer needed to succeed.
+ // caller can then allocate a buffer of that size and call again.
+ return requiredSizeInBytes;
+ }
+
+ if (outputSizeInBytes < requiredSizeInBytes)
+ {
+ // buffer is too small: return 0
+ return 0;
+ }
+
+ // buffer is big enough: write to buffer and return the number of bytes written
+ strncpy_s(output, outputSizeInBytes, key, requiredSizeInBytes);
+ return requiredSizeInBytes;
+}
+
+CASK_API int32_t Cask_GenerateHash(const uint8_t* derivationInputBytes,
+ const int32_t derivationInputLength,
+ const char* secret,
+ int32_t secretEntropyInBytes,
+ char* buffer,
+ int32_t bufferSize)
+{
+ return 0;
+}
+
+CASK_API bool Cask_CompareHash(const char* candidateHash,
+ const uint8_t* derivationInputBytes,
+ const int32_t derivationInputLength,
+ const char* secret,
+ int32_t secretEntropyInBytes)
+{
+ return false;
+}
+
diff --git a/src/libcask/cask.h b/src/libcask/cask.h
new file mode 100644
index 0000000..811e086
--- /dev/null
+++ b/src/libcask/cask.h
@@ -0,0 +1,51 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+// WIP: This header wil define the interop-friendly libcask API that will be
+// exported from .dll/.so. This surface area must remain C compatible and
+// cannot use C++-only types and features
+
+#ifndef CASK_H
+#define CASK_H
+
+#include
+#include
+
+#ifdef __cplusplus
+#define CASK_EXTERN_C extern "C"
+#else
+#define CASK_EXTERN_C
+#endif
+
+#ifdef LIBCASK_EXPORTS
+#define CASK_API CASK_EXTERN_C __declspec(dllexport)
+#else
+#define CASK_API CASK_EXTERN_C __declspec(dllimport)
+#endif
+
+CASK_API bool Cask_IsCask(const char* keyOrHash);
+
+CASK_API bool Cask_IsCaskBytes(const uint8_t* keyOrHashBytes,
+ int32_t length);
+
+CASK_API int32_t Cask_GenerateKey(const char* allocatorCode,
+ const char* providerSignature,
+ const char* providerData,
+ int32_t secretEntropyInBytes,
+ char* buffer,
+ int32_t bufferSize);
+
+CASK_API int32_t Cask_GenerateHash(const uint8_t* derivationInputBytes,
+ const int32_t derivationInputLength,
+ const char* secret,
+ int32_t secretEntropyInBytes,
+ char* buffer,
+ int32_t bufferSize);
+
+CASK_API bool Cask_CompareHash(const char* candidateHash,
+ const uint8_t* derivationInputBytes,
+ const int32_t derivationInputLength,
+ const char* secret,
+ int32_t secretEntropyInBytes);
+
+#endif // CASK_H
diff --git a/src/libcask/cask_cimport_test.c b/src/libcask/cask_cimport_test.c
new file mode 100644
index 0000000..4b7386d
--- /dev/null
+++ b/src/libcask/cask_cimport_test.c
@@ -0,0 +1,5 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+// Dummy file to validate cask.h can be #included from C
+#include "cask.h"
diff --git a/src/libcask/cask_dependencies.cpp b/src/libcask/cask_dependencies.cpp
new file mode 100644
index 0000000..cde91f0
--- /dev/null
+++ b/src/libcask/cask_dependencies.cpp
@@ -0,0 +1,19 @@
+
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+// WIP: This is our reference implementation for dependencies. We can pull in
+// external libraries of our choice here and only here. We can use C++
+// here.
+
+#include "cask_dependencies.h"
+
+std::string Cask::Base64UrlEncode(const std::span& bytes)
+{
+ return "";
+}
+
+int32_t Cask::ComputeCrc32(const std::span& bytes)
+{
+ return 0;
+}
diff --git a/src/libcask/cask_dependencies.h b/src/libcask/cask_dependencies.h
new file mode 100644
index 0000000..9d73230
--- /dev/null
+++ b/src/libcask/cask_dependencies.h
@@ -0,0 +1,18 @@
+#pragma once
+
+// WIP: This header will define a facade over all dependencies that libcask has that
+// are not provided by the C++ standard library. Our reference implementation
+// will choose external depenencies to implement this. Someone can then take the
+// reference source implementation, and replace/edit cask_dependencies.cpp. We
+// can use C++ here.
+
+#include
+#include
+#include
+
+namespace Cask {
+
+std::string Base64UrlEncode(const std::span& bytes);
+int32_t ComputeCrc32(const std::span& bytes);
+
+} // namespace Cask
diff --git a/src/libcask/libcask.vcxproj b/src/libcask/libcask.vcxproj
new file mode 100644
index 0000000..67bdc88
--- /dev/null
+++ b/src/libcask/libcask.vcxproj
@@ -0,0 +1,102 @@
+
+
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ 17.0
+ Win32Proj
+ {14013cd3-b963-4851-aa9a-7c7a2f110a52}
+ libcask
+ 10.0
+
+
+
+ DynamicLibrary
+ true
+ v143
+ Unicode
+
+
+ DynamicLibrary
+ false
+ v143
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $(SolutionDir)..\bld\bin\$(ShortProjectName)\release_x64\
+ $(SolutionDir)..\bld\obj\$(ShortProjectName)\release_x64\/
+
+
+ $(SolutionDir)..\bld\bin\$(ShortProjectName)\debug_x64\
+ $(SolutionDir)..\bld\obj\$(ShortProjectName)\debug_x64\
+
+
+
+ Level3
+ true
+ _DEBUG;LIBCASK_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
+ true
+ NotUsing
+ stdcpp20
+ stdc17
+
+
+ Windows
+ true
+ false
+
+
+
+
+ Level3
+ true
+ true
+ true
+ NDEBUG;LIBCASK_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
+ true
+ NotUsing
+ stdcpp20
+ stdc17
+
+
+ Windows
+ true
+ true
+ true
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file