diff --git a/ContentLib/RecipePatches/Recipe_DS_Drive_10K_Fluid.json b/ContentLib/RecipePatches/Recipe_DS_Drive_10K_Fluid.json index db884da..6ca75d8 100644 Binary files a/ContentLib/RecipePatches/Recipe_DS_Drive_10K_Fluid.json and b/ContentLib/RecipePatches/Recipe_DS_Drive_10K_Fluid.json differ diff --git a/ContentLib/RecipePatches/Recipe_DS_Drive_120K_Fluid.json b/ContentLib/RecipePatches/Recipe_DS_Drive_120K_Fluid.json index fe467e9..7693955 100644 Binary files a/ContentLib/RecipePatches/Recipe_DS_Drive_120K_Fluid.json and b/ContentLib/RecipePatches/Recipe_DS_Drive_120K_Fluid.json differ diff --git a/ContentLib/RecipePatches/Recipe_DS_Drive_120K_Gas.json b/ContentLib/RecipePatches/Recipe_DS_Drive_120K_Gas.json index edeb317..1ca9f73 100644 Binary files a/ContentLib/RecipePatches/Recipe_DS_Drive_120K_Gas.json and b/ContentLib/RecipePatches/Recipe_DS_Drive_120K_Gas.json differ diff --git a/ContentLib/RecipePatches/Recipe_DS_Drive_16K.json b/ContentLib/RecipePatches/Recipe_DS_Drive_16K.json index 6898af7..53818ca 100644 --- a/ContentLib/RecipePatches/Recipe_DS_Drive_16K.json +++ b/ContentLib/RecipePatches/Recipe_DS_Drive_16K.json @@ -34,7 +34,7 @@ "UnlockedBy": [ "Schematic_DS_Mam_Drives_16K_C" ], - "ManualManufacturingDuration": 45.0, + "ManualManufacturingMultiplier": 0.0, "VariablePowerConsumptionConstant": 0.0, "VariablePowerConsumptionFactor": 1.0, "ClearIngredients": true, diff --git a/ContentLib/RecipePatches/Recipe_DS_Drive_1K.json b/ContentLib/RecipePatches/Recipe_DS_Drive_1K.json index 9df10ae..9d17c03 100644 --- a/ContentLib/RecipePatches/Recipe_DS_Drive_1K.json +++ b/ContentLib/RecipePatches/Recipe_DS_Drive_1K.json @@ -26,7 +26,7 @@ "UnlockedBy": [ "Schematic_DS_Mam_Drives_1K_C" ], - "ManualManufacturingDuration": 45.0, + "ManualManufacturingMultiplier": 0.0, "VariablePowerConsumptionConstant": 0.0, "VariablePowerConsumptionFactor": 1.0, "ClearIngredients": true, diff --git a/ContentLib/RecipePatches/Recipe_DS_Drive_20K_Fluid.json b/ContentLib/RecipePatches/Recipe_DS_Drive_20K_Fluid.json index 5788b03..ab8c256 100644 Binary files a/ContentLib/RecipePatches/Recipe_DS_Drive_20K_Fluid.json and b/ContentLib/RecipePatches/Recipe_DS_Drive_20K_Fluid.json differ diff --git a/ContentLib/RecipePatches/Recipe_DS_Drive_240K_Fluid.json b/ContentLib/RecipePatches/Recipe_DS_Drive_240K_Fluid.json index e370610..2e7d1eb 100644 Binary files a/ContentLib/RecipePatches/Recipe_DS_Drive_240K_Fluid.json and b/ContentLib/RecipePatches/Recipe_DS_Drive_240K_Fluid.json differ diff --git a/ContentLib/RecipePatches/Recipe_DS_Drive_240K_Gas.json b/ContentLib/RecipePatches/Recipe_DS_Drive_240K_Gas.json index 0a61a9f..34644cd 100644 Binary files a/ContentLib/RecipePatches/Recipe_DS_Drive_240K_Gas.json and b/ContentLib/RecipePatches/Recipe_DS_Drive_240K_Gas.json differ diff --git a/ContentLib/RecipePatches/Recipe_DS_Drive_256.json b/ContentLib/RecipePatches/Recipe_DS_Drive_256.json index 5dfa428..bac804d 100644 --- a/ContentLib/RecipePatches/Recipe_DS_Drive_256.json +++ b/ContentLib/RecipePatches/Recipe_DS_Drive_256.json @@ -26,7 +26,7 @@ "UnlockedBy": [ "Schem_DS_T3_2_C" ], - "ManualManufacturingDuration": 45.0, + "ManualManufacturingMultiplier": 0.0, "VariablePowerConsumptionConstant": 0.0, "VariablePowerConsumptionFactor": 1.0, "ClearIngredients": true, diff --git a/ContentLib/RecipePatches/Recipe_DS_Drive_2K.json b/ContentLib/RecipePatches/Recipe_DS_Drive_2K.json index b7c7166..3bcd678 100644 --- a/ContentLib/RecipePatches/Recipe_DS_Drive_2K.json +++ b/ContentLib/RecipePatches/Recipe_DS_Drive_2K.json @@ -26,7 +26,7 @@ "UnlockedBy": [ "Schematic_DS_Mam_Drives_2K_C" ], - "ManualManufacturingDuration": 45.0, + "ManualManufacturingMultiplier": 0.0, "VariablePowerConsumptionConstant": 0.0, "VariablePowerConsumptionFactor": 1.0, "ClearIngredients": true, diff --git a/ContentLib/RecipePatches/Recipe_DS_Drive_32K.json b/ContentLib/RecipePatches/Recipe_DS_Drive_32K.json index d7a235b..fbcd043 100644 --- a/ContentLib/RecipePatches/Recipe_DS_Drive_32K.json +++ b/ContentLib/RecipePatches/Recipe_DS_Drive_32K.json @@ -34,7 +34,7 @@ "UnlockedBy": [ "Schematic_DS_Mam_Drives_32K_C" ], - "ManualManufacturingDuration": 60.0, + "ManualManufacturingMultiplier": 0.0, "VariablePowerConsumptionConstant": 0.0, "VariablePowerConsumptionFactor": 1.0, "ClearIngredients": true, diff --git a/ContentLib/RecipePatches/Recipe_DS_Drive_40K_Fluid.json b/ContentLib/RecipePatches/Recipe_DS_Drive_40K_Fluid.json index c3ade70..495561b 100644 Binary files a/ContentLib/RecipePatches/Recipe_DS_Drive_40K_Fluid.json and b/ContentLib/RecipePatches/Recipe_DS_Drive_40K_Fluid.json differ diff --git a/ContentLib/RecipePatches/Recipe_DS_Drive_40K_Gas.json b/ContentLib/RecipePatches/Recipe_DS_Drive_40K_Gas.json index 62b4fba..f6ff4f0 100644 Binary files a/ContentLib/RecipePatches/Recipe_DS_Drive_40K_Gas.json and b/ContentLib/RecipePatches/Recipe_DS_Drive_40K_Gas.json differ diff --git a/ContentLib/RecipePatches/Recipe_DS_Drive_4K.json b/ContentLib/RecipePatches/Recipe_DS_Drive_4K.json index 677d031..08a43ff 100644 --- a/ContentLib/RecipePatches/Recipe_DS_Drive_4K.json +++ b/ContentLib/RecipePatches/Recipe_DS_Drive_4K.json @@ -26,7 +26,7 @@ "UnlockedBy": [ "Schematic_DS_Mam_Drives_4K_C" ], - "ManualManufacturingDuration": 45.0, + "ManualManufacturingMultiplier": 0.0, "VariablePowerConsumptionConstant": 0.0, "VariablePowerConsumptionFactor": 1.0, "ClearIngredients": true, diff --git a/ContentLib/RecipePatches/Recipe_DS_Drive_512.json b/ContentLib/RecipePatches/Recipe_DS_Drive_512.json index 43476d5..991013a 100644 --- a/ContentLib/RecipePatches/Recipe_DS_Drive_512.json +++ b/ContentLib/RecipePatches/Recipe_DS_Drive_512.json @@ -26,7 +26,7 @@ "UnlockedBy": [ "Schematic_DS_Mam_Drives_512_C" ], - "ManualManufacturingDuration": 45.0, + "ManualManufacturingMultiplier": 0.0, "VariablePowerConsumptionConstant": 0.0, "VariablePowerConsumptionFactor": 1.0, "ClearIngredients": true, diff --git a/ContentLib/RecipePatches/Recipe_DS_Drive_60K_Fluid.json b/ContentLib/RecipePatches/Recipe_DS_Drive_60K_Fluid.json index 689e0bd..b7f6ec2 100644 Binary files a/ContentLib/RecipePatches/Recipe_DS_Drive_60K_Fluid.json and b/ContentLib/RecipePatches/Recipe_DS_Drive_60K_Fluid.json differ diff --git a/ContentLib/RecipePatches/Recipe_DS_Drive_60K_Gas.json b/ContentLib/RecipePatches/Recipe_DS_Drive_60K_Gas.json index fd3f616..6316a40 100644 Binary files a/ContentLib/RecipePatches/Recipe_DS_Drive_60K_Gas.json and b/ContentLib/RecipePatches/Recipe_DS_Drive_60K_Gas.json differ diff --git a/ContentLib/RecipePatches/Recipe_DS_Drive_64K.json b/ContentLib/RecipePatches/Recipe_DS_Drive_64K.json index 54b8521..fc7fa12 100644 --- a/ContentLib/RecipePatches/Recipe_DS_Drive_64K.json +++ b/ContentLib/RecipePatches/Recipe_DS_Drive_64K.json @@ -38,7 +38,7 @@ "UnlockedBy": [ "Schematic_DS_Mam_Drives_64K_C" ], - "ManualManufacturingDuration": 60.0, + "ManualManufacturingMultiplier": 0.0, "VariablePowerConsumptionConstant": 0.0, "VariablePowerConsumptionFactor": 1.0, "ClearIngredients": true, diff --git a/ContentLib/RecipePatches/Recipe_DS_Drive_8K.json b/ContentLib/RecipePatches/Recipe_DS_Drive_8K.json index ce324d8..9a98ff3 100644 --- a/ContentLib/RecipePatches/Recipe_DS_Drive_8K.json +++ b/ContentLib/RecipePatches/Recipe_DS_Drive_8K.json @@ -30,7 +30,7 @@ "UnlockedBy": [ "Schematic_DS_Mam_Drives_8K_C" ], - "ManualManufacturingDuration": 45.0, + "ManualManufacturingMultiplier": 0.0, "VariablePowerConsumptionConstant": 0.0, "VariablePowerConsumptionFactor": 1.0, "ClearIngredients": true, diff --git a/ContentLib/RecipePatches/Recipe_DS_Multitool.json b/ContentLib/RecipePatches/Recipe_DS_Multitool.json index 4ebb4bf..2f2ae28 100644 --- a/ContentLib/RecipePatches/Recipe_DS_Multitool.json +++ b/ContentLib/RecipePatches/Recipe_DS_Multitool.json @@ -30,7 +30,7 @@ "UnlockedBy": [ "Schematic_DS_Mam_Adapters_Universal_C" ], - "ManualManufacturingDuration": 1.0, + "ManualManufacturingMultiplier": 0.0, "VariablePowerConsumptionConstant": 0.0, "VariablePowerConsumptionFactor": 1.0, "ClearIngredients": true, diff --git a/ContentLib/RecipePatches/Recipe_DS_NetworkCable_Item.json b/ContentLib/RecipePatches/Recipe_DS_NetworkCable_Item.json index d61f1aa..a9bbb7e 100644 --- a/ContentLib/RecipePatches/Recipe_DS_NetworkCable_Item.json +++ b/ContentLib/RecipePatches/Recipe_DS_NetworkCable_Item.json @@ -26,7 +26,7 @@ "UnlockedBy": [ "Schem_DS_T3_1_C" ], - "ManualManufacturingDuration": 5.0, + "ManualManufacturingMultiplier": 0.0, "VariablePowerConsumptionConstant": 0.0, "VariablePowerConsumptionFactor": 1.0, "ClearIngredients": true, diff --git a/ContentLib/RecipePatches/Recipe_DS_WirelessExtender.json b/ContentLib/RecipePatches/Recipe_DS_WirelessExtender.json index bf4e896..7f09dd3 100644 --- a/ContentLib/RecipePatches/Recipe_DS_WirelessExtender.json +++ b/ContentLib/RecipePatches/Recipe_DS_WirelessExtender.json @@ -30,7 +30,7 @@ "UnlockedBy": [ "Schem_DS_T6_2_C" ], - "ManualManufacturingDuration": 1.0, + "ManualManufacturingMultiplier": 0.0, "VariablePowerConsumptionConstant": 0.0, "VariablePowerConsumptionFactor": 1.0, "ClearIngredients": true, diff --git a/ContentLib/RecipePatches/Recipe_DS_WirelessTablet.json b/ContentLib/RecipePatches/Recipe_DS_WirelessTablet.json index 694f4c3..f2bbc92 100644 --- a/ContentLib/RecipePatches/Recipe_DS_WirelessTablet.json +++ b/ContentLib/RecipePatches/Recipe_DS_WirelessTablet.json @@ -30,7 +30,7 @@ "UnlockedBy": [ "Schem_DS_T6_2_C" ], - "ManualManufacturingDuration": 1.0, + "ManualManufacturingMultiplier": 0.0, "VariablePowerConsumptionConstant": 0.0, "VariablePowerConsumptionFactor": 1.0, "ClearIngredients": true, diff --git a/magefile.go b/magefile.go index 888981c..3992d38 100644 --- a/magefile.go +++ b/magefile.go @@ -21,34 +21,27 @@ import ( ) const ( - zipName = "DigitalStorageTweaks.zip" - contentDir = "./ContentLib" - pluginFile = "./DigitalStorageTweaks.uplugin" - schemaPath = "./schema/CL_Recipe.json" // Local schema path + zipName = "DigitalStorageTweaks.zip" + contentDir = "./ContentLib" + pluginFile = "./DigitalStorageTweaks.uplugin" + schemaBaseURL = "https://raw.githubusercontent.com/budak7273/ContentLib_Documentation/main/JsonSchemas/" + schemaDir = "./schema" ) -var ( - // Binary file extensions to skip line ending conversion - binaryExtensions = map[string]bool{ - ".png": true, ".jpg": true, ".jpeg": true, ".bmp": true, ".gif": true, - ".dds": true, ".tga": true, ".psd": true, ".fbx": true, ".uasset": true, - ".umap": true, - } -) +var binaryExtensions = map[string]bool{ + ".png": true, ".jpg": true, ".jpeg": true, ".bmp": true, ".gif": true, + ".dds": true, ".tga": true, ".psd": true, ".fbx": true, ".uasset": true, + ".umap": true, +} -// Default target var Default = Build -// Build runs the full pipeline func Build() { mg.SerialDeps(Validate, Package) } -// Package creates the distribution zip func Package() error { fmt.Println("Packaging files...") - - // Create target directories for _, dir := range []string{"Windows", "WindowsServer", "LinuxServer"} { if err := os.MkdirAll(dir, 0755); err != nil { return fmt.Errorf("creating %s: %w", dir, err) @@ -57,140 +50,126 @@ func Package() error { return fmt.Errorf("copying to %s: %w", dir, err) } } - - // Create zip file if err := createZip(zipName, "Windows", "WindowsServer", "LinuxServer"); err != nil { return fmt.Errorf("creating zip: %w", err) } - - // Clean temp dirs return Clean("Windows", "WindowsServer", "LinuxServer") } + func Validate() error { fmt.Println("Validating files...") - - const ( - baseSchemaURL = "https://raw.githubusercontent.com/budak7273/ContentLib_Documentation/main/JsonSchemas/" - localSchemaDir = "./schema" - ) - compiler := jsonschema.NewCompiler() - - // Disable remote fetching compiler.LoadURL = func(url string) (io.ReadCloser, error) { return nil, fmt.Errorf("remote schema loading disabled: %s", url) } - // Walk through the schema directory and add all .json files - err := filepath.Walk(localSchemaDir, func(path string, info os.FileInfo, err error) error { - if err != nil || info.IsDir() { + if err := filepath.Walk(schemaDir, func(path string, info os.FileInfo, err error) error { + if err != nil || info.IsDir() || filepath.Ext(path) != ".json" { return err } - - if filepath.Ext(path) != ".json" { - return nil - } - - relPath, err := filepath.Rel(localSchemaDir, path) + relPath, err := filepath.Rel(schemaDir, path) if err != nil { - return fmt.Errorf("resolving relative path: %w", err) + return err } - - // Build the full schema ID - schemaID := baseSchemaURL + filepath.ToSlash(relPath) - + id := schemaBaseURL + filepath.ToSlash(relPath) data, err := ioutil.ReadFile(path) if err != nil { - return fmt.Errorf("reading %s: %w", path, err) + return err } - - err = compiler.AddResource(schemaID, bytes.NewReader(data)) - if err != nil { - return fmt.Errorf("adding schema %s: %w", schemaID, err) - } - - return nil - }) - if err != nil { + return compiler.AddResource(id, bytes.NewReader(data)) + }); err != nil { return fmt.Errorf("failed to load schemas: %w", err) } - // Main schema (must match its $id) - mainSchemaID := baseSchemaURL + "CL_Recipe.json" - - // Compile the main schema - schema, err := compiler.Compile(mainSchemaID) + schema, err := compiler.Compile(schemaBaseURL + "CL_Recipe.json") if err != nil { return fmt.Errorf("invalid main schema: %w", err) } - // Validate plugin file - if err := validateFile(pluginFile, schema); err != nil { - return fmt.Errorf("plugin file: %w", err) + var failed []string + paths := []string{pluginFile} + _ = filepath.Walk(contentDir, func(path string, info os.FileInfo, err error) error { + if err == nil && !info.IsDir() { + paths = append(paths, path) + } + return nil + }) + + for _, path := range paths { + if err := validateFile(path, schema); err != nil { + failed = append(failed, fmt.Sprintf("%s: %v", path, err)) + } } - // Validate all JSON files in the content directory - return filepath.Walk(contentDir, func(path string, info os.FileInfo, err error) error { - if err != nil || info.IsDir() { - return err + if len(failed) > 0 { + fmt.Println("Validation errors:") + for _, msg := range failed { + fmt.Println(" -", msg) } - return validateFile(path, schema) - }) + return fmt.Errorf("%d file(s) failed validation", len(failed)) + } + + fmt.Println("All files validated successfully.") + return nil } func validateFile(path string, schema *jsonschema.Schema) error { - // Skip binary files - ext := filepath.Ext(path) - if binaryExtensions[ext] { + if binaryExtensions[filepath.Ext(path)] { return nil } - - if ext == ".json" { + if filepath.Ext(path) == ".json" { return validateJSON(path, schema) } return validateEncoding(path) } func validateJSON(path string, schema *jsonschema.Schema) error { - content, err := ioutil.ReadFile(path) - if err != nil { - return fmt.Errorf("failed to read JSON file: %w", err) - } - - // Remove comment lines (starting with //) - var filteredContent []byte - for _, line := range bytes.Split(content, []byte("\n")) { - trimmed := bytes.TrimSpace(line) - if !bytes.HasPrefix(trimmed, []byte("//")) && len(trimmed) > 0 { - filteredContent = append(filteredContent, line...) - filteredContent = append(filteredContent, '\n') - } - } - - // Validate JSON - var v interface{} - if err := json.Unmarshal(filteredContent, &v); err != nil { - return fmt.Errorf("invalid JSON in %s: %w", path, err) - } - - if err := schema.Validate(v); err != nil { - return fmt.Errorf("schema validation failed for %s: %w", path, err) - } - - return nil -} - -func validateEncoding(path string) error { - content, err := ioutil.ReadFile(path) + data, err := ioutil.ReadFile(path) if err != nil { return err } + data = sanitizeJSONBytes(data) - // Check for non-ASCII - for i := 0; i < len(content); { - b := content[i] - if b > 127 { // Non-ASCII - r, _ := utf8.DecodeRune(content[i:]) + var lines [][]byte + for _, line := range bytes.Split(data, []byte("\n")) { + trim := bytes.TrimSpace(line) + if !bytes.HasPrefix(trim, []byte("//")) && len(trim) > 0 { + lines = append(lines, line) + } + } + clean := bytes.Join(lines, []byte("\n")) + + var v interface{} + if err := json.Unmarshal(clean, &v); err != nil { + return err + } + return schema.Validate(v) +} + +func sanitizeJSONBytes(data []byte) []byte { + data = bytes.TrimPrefix(data, []byte{0xEF, 0xBB, 0xBF}) + var out bytes.Buffer + for len(data) > 0 { + r, size := utf8.DecodeRune(data) + if r == utf8.RuneError && size == 1 || r == '\x00' { + data = data[1:] + continue + } + out.WriteRune(r) + data = data[size:] + } + return out.Bytes() +} + +func validateEncoding(path string) error { + data, err := ioutil.ReadFile(path) + if err != nil { + return err + } + for i := 0; i < len(data); { + if data[i] > 127 { + // DO NOT declare "size", it is unused and will cause a compiler error + r, _ := utf8.DecodeRune(data[i:]) if r == utf8.RuneError { return fmt.Errorf("invalid UTF-8 sequence at position %d", i) } @@ -201,7 +180,6 @@ func validateEncoding(path string) error { return nil } -// Clean removes all build artifacts func Clean(list ...string) error { fmt.Println("Cleaning up...") for _, f := range list { @@ -212,100 +190,67 @@ func Clean(list ...string) error { return nil } -// createZip creates a zip file from the specified directories func createZip(zipPath string, dirs ...string) error { - // Create zip file zipFile, err := os.Create(zipPath) if err != nil { return fmt.Errorf("creating zip file: %w", err) } defer zipFile.Close() - - // Create zip writer zipWriter := zip.NewWriter(zipFile) defer zipWriter.Close() - - // Add each directory to the zip for _, dir := range dirs { if err := addDirToZip(zipWriter, dir); err != nil { return fmt.Errorf("adding %s to zip: %w", dir, err) } } - return nil } -// addDirToZip adds a directory to the zip, converting text files to DOS line endings func addDirToZip(zipWriter *zip.Writer, dirPath string) error { return filepath.Walk(dirPath, func(filePath string, info os.FileInfo, err error) error { if err != nil { return err } - - // Create relative path for the zip file relPath, err := filepath.Rel(dirPath, filePath) if err != nil { return err } zipPath := filepath.Join(filepath.Base(dirPath), relPath) - - // Create zip file header header, err := zip.FileInfoHeader(info) if err != nil { return err } - header.Name = filepath.ToSlash(zipPath) // Use forward slashes for zip compatibility - - // Use compression for all files + header.Name = filepath.ToSlash(zipPath) header.Method = zip.Deflate - - // Handle directories if info.IsDir() { header.Name += "/" _, err := zipWriter.CreateHeader(header) return err } - - // Open source file file, err := os.Open(filePath) if err != nil { return err } defer file.Close() - - // Create writer in zip writer, err := zipWriter.CreateHeader(header) if err != nil { return err } - - // Process based on file type - ext := strings.ToLower(filepath.Ext(filePath)) - if binaryExtensions[ext] { - // Binary file - copy directly + if binaryExtensions[strings.ToLower(filepath.Ext(filePath))] { _, err = io.Copy(writer, file) return err - } else { - // Text file - read content and convert line endings - content, err := ioutil.ReadAll(file) - if err != nil { - return err - } - - // Convert LF to CRLF - content = convertToDOSLineEndings(content) - - // Write to zip - _, err = writer.Write(content) + } + content, err := ioutil.ReadAll(file) + if err != nil { return err } + content = convertToDOSLineEndings(content) + _, err = writer.Write(content) + return err }) } -// convertToDOSLineEndings converts LF to CRLF while preserving existing CRLF func convertToDOSLineEndings(content []byte) []byte { - // First normalize to LF normalized := bytes.ReplaceAll(content, []byte("\r\n"), []byte("\n")) - // Then convert to CRLF return bytes.ReplaceAll(normalized, []byte("\n"), []byte("\r\n")) } diff --git a/makefile b/makefile deleted file mode 100644 index 4cf4348..0000000 --- a/makefile +++ /dev/null @@ -1,9 +0,0 @@ -ZIP_NAME = DigitalStorageTweaks.zip -FILES = ./ContentLib ./DigitalStorageTweaks.uplugin - -all: - mkdir -p Windows WindowsServer LinuxServer - cp -r $(FILES) Windows/ - cp -r $(FILES) WindowsServer/ - cp -r $(FILES) LinuxServer/ - 7z a -r $(ZIP_NAME) Windows/ LinuxServer/ WindowsServer/ \ No newline at end of file