Asset - Extension SafeSave - INI and Data Structure Encryption

Discussion in 'Marketplace' started by zbox, Oct 26, 2016.

  1. zbox

    zbox Member GMC Elder

    Joined:
    Jun 21, 2016
    Posts:
    793
    [​IMG]
    Safe Save

    Itch.io: https://taylordale.itch.io/safesave-ini
    Marketplace: https://marketplace.yoyogames.com/assets/4685/safesave-ini-more
    Category: Extensions
    Price: $5.99 $3.99
    Modules: All

    Description:
    Securely save data on your user's devices!

    Whether you are used to using INI files or are looking for a more complicated solution to encrypt data structures this extension will cater to your needs.

    Features

    • Fully encrypted and unrecognizable INI files or regular data (see modes below)
    • Tamper proof mechanism ensures data cannot be changed
    • Prevents users from sharing save files & data
    • Can be used to store IAP data and much more
    • More compressed than an INI (smaller filesize)
    • Compatible with all exports
    • Choice of RC4 (default) or 128-bit AES (sold separately) for encryption!
    INI MODE
    To fully encrypt and make your INI files tamper-proof, all you have to do is prefix all "ini_*" functions in your project with "safe_", and change ini_open(filename) to safe_ini_open(filename, key) where key is a random string of letters that is used to encrypt your INI file. This is all that is required.

    ADVANCED MODE
    If you need more flexibility when you are saving data, safe_save will take a ds_map and a filename and write it to the disk fully encrypted with built in tamper-proofing. It will fully save and reconstitute all embedded data structures as well.

    There are examples of both modes in use included.

    Screenshots
    [​IMG]
    [​IMG]
    [​IMG]
     
    Last edited: Mar 6, 2017
  2. Ethanicus

    Ethanicus Ethan L!

    Joined:
    Jun 26, 2016
    Posts:
    230
    Very cool! Does the developer have any control over encryption keys or anything?
     
    zbox likes this.
  3. zbox

    zbox Member GMC Elder

    Joined:
    Jun 21, 2016
    Posts:
    793
    Absolutely :) You supply a key for both modes, an arbitrarily long string you can decide on.

    INI Mode
    safe_ini_open(fname,key);

    Or for advanced mode
    safe_save(filename,data,key)
    safe_load(filename,key)
     
    Ethanicus likes this.
  4. zbox

    zbox Member GMC Elder

    Joined:
    Jun 21, 2016
    Posts:
    793
    New update
    • Optional: Support for 128-bit AES to strengthen the encryption of your save files for storage and transmission (available separately)
    • Filesize reduction of up to 33%
    • Typos fixed and Readme updated
    • General bug fixes
     
  5. dadio

    dadio Potato Overlord Forum Staff Moderator

    Joined:
    Apr 15, 2016
    Posts:
    218
    Very nice zbox. :)
     
    zbox likes this.
  6. Coded Games

    Coded Games Member

    Joined:
    Jun 20, 2016
    Posts:
    412
    Looks good!
     
    zbox likes this.
  7. Lycanphoenix

    Lycanphoenix Guest

    I assume there's an option to just compress the save files, without encrypting them?
     
  8. zbox

    zbox Member GMC Elder

    Joined:
    Jun 21, 2016
    Posts:
    793
    The main point of the extension is to encrypt the save file, without it the encryption functionality most of the functions already exist in GM, however I guess I can add no encryption on request??

    There is no compression involved, the filesize reduction I mention is relative to old versions of SafeSave in which I've optimized the format :)
     
  9. Lycanphoenix

    Lycanphoenix Guest

    Oh. Right >.<
    How about File Integrity Verification? I do know that some encryption schemes are often used to hash files, and check to see of the files are damaged or incomplete.
     
  10. zbox

    zbox Member GMC Elder

    Joined:
    Jun 21, 2016
    Posts:
    793
    Absolutely - the save files have a built in integrity check and there is functionality to report whether the file has been tampered with or not. By default if the file is tampered with then it will fail to load. The save files are also device-specific as to not allow transfer between devices.
     
    Ethanicus likes this.
  11. zbox

    zbox Member GMC Elder

    Joined:
    Jun 21, 2016
    Posts:
    793
  12. zbox

    zbox Member GMC Elder

    Joined:
    Jun 21, 2016
    Posts:
    793
    33% Off sale :)!
     
  13. Ednei

    Ednei Member

    Joined:
    Jun 22, 2016
    Posts:
    178
    I'm having this strange error:

    Code:
    ############################################################################################
    FATAL ERROR in
    action number 1
    of Key Press Event for <Space> Key
    for object obSYS_Save_Game:
    
    Safe Save INI: This character is not allowed in Section or Key names: _
     at gml_Script_safe_ini_write_real (line 6) -     show_error("Safe Save INI: This character is not allowed in Section or Key names: " + string(global._safe_ini_sep), true);
    ############################################################################################
    --------------------------------------------------------------------------------------------
    stack frame is
    gml_Script_safe_ini_write_real (line 6)
    called from - gml_Script_scSYS_Save (line 34) -  safe_ini_write_real("OVER_Data","A_"+string(i),data) //levels
    called from - gml_Object_obSYS_Save_Game_KeyPress_32 (line 62) -   scSYS_Save(menu+1)
    My save script:

    Code:
    // Inicia argumentos
    var i, arg;
    for (i = 0; i < 5; i += 1;)
       {
       if argument_count > i
          {
          arg[i] = argument[i]
          }
       else
          {
          arg[i] = -1;
          }
       };
    var file, data, a, b, c, size;
    
    file = working_directory+"/OVER_Data/OVER_Save0"+string(arg[0])+".ini"
    
    size = ds_list_size(global.Player_Party)
    
    if (file_exists(file) = true)
    {
     file_delete(file)
    }
    
    safe_ini_open(file, global.key_pass)
    
    a = floor(random(150))+75
    b = floor(random(300))+100
    c = floor(random(50))+50
    
    for (i=0; i<size; i+=1)
    {
     data = (global.Character_Level[ds_list_find_value(global.Player_Party,i)])*(c*i+1)-b
     safe_ini_write_real("OVER_Data","A_"+string(i),data) //levels
    }
    
    for (i=0; i<size; i+=1)
    {
     data = (global.Character_Energy[ds_list_find_value(global.Player_Party,i)])*(i*10+1)+150-a
     safe_ini_write_real("OVER_Data","B_"+string(i),data) //energy
    }
    
    for (i=0; i<size; i+=1)
    {
     data = (global.Character_Health[ds_list_find_value(global.Player_Party,i)])*(i+(i*25)+1)-c
     safe_ini_write_real("OVER_Data","C_"+string(i),data) //health
    }
    
    safe_ini_write_real("OVER_Data","D_0",c+42) //random c
    data = (global.last_room)*b-c
    safe_ini_write_real("OVER_Data","D_1",data) //last room
    safe_ini_write_real("OVER_Data","D_2",a+17) // random a
    data = (global.Player_x)*(c+5)-b
    safe_ini_write_real("OVER_Data","D_3",data) //player x pos
    
    data = (global.Player_y)*(b+2)-a
    safe_ini_write_real("OVER_Data","E_0",data) //player y pos
    safe_ini_write_real("OVER_Data","E_1",b+24) //random b
    data = (global.Minute)*c-a
    safe_ini_write_real("OVER_Data","E_2",data) //timer: minute
    data = (global.Hour)*b+(a*2)
    safe_ini_write_real("OVER_Data","E_3",data) //timber: hour
    
    data = (global.Second)*(a+3)-c
    safe_ini_write_real("OVER_Data","F_0",data) //timer: second
    data = (global.Area_Name)*c+100
    safe_ini_write_real("OVER_Data","F_1",data) //current area
    data = (global.Currency[1])*b-(a+c)
    safe_ini_write_real("OVER_Data","F_2",data) //magic currency
    data = (global.Currency[0])*c+b
    safe_ini_write_real("OVER_Data","F_3",data) //money
    
    for (i=0; i<size; i+=1)
    {
     data = (global.Character_Speed[ds_list_find_value(global.Player_Party,i)])*(i+c)-(i*b)
     safe_ini_write_real("OVER_Data","G_"+string(i),data) //character speed
    }
    
    for (i=0; i<size; i+=1)
    {
     data = ((global.Character_Class[ds_list_find_value(global.Player_Party,i)])-b-(i*a))*c
     safe_ini_write_real("OVER_Data","H_"+string(i),data) //character current class
    }
    
    for (i=0; i<size; i+=1)
    {
     data = (global.Character_EXP_Current[ds_list_find_value(global.Player_Party,i)])*(i*b-a)
     safe_ini_write_real("OVER_Data","I_"+string(i),data) //character current EXP
    }
    
    for (i=0; i<size; i+=1)
    {
     data = (global.Character_EXP_Max[ds_list_find_value(global.Player_Party,i)])*(b+(i*5))
     safe_ini_write_real("OVER_Data","J_"+string(i),data) //character EXP max
    }
    
    for (i=0; i<size; i+=1)
    {
     data = (global.Item_Slot[ds_list_find_value(global.Player_Party,i),0])*(i*b+c-50)
     safe_ini_write_real("OVER_Data","K_"+string(i),data) //Helm
    }
    
    for (i=0; i<size; i+=1)
    {
     data = (global.Item_Slot[ds_list_find_value(global.Player_Party,i),1])*b+(c*i)
     safe_ini_write_real("OVER_Data","L_"+string(i),data) //Chest
    }
    
    for (i=0; i<size; i+=1)
    {
     data = (global.Item_Slot[ds_list_find_value(global.Player_Party,i),2])*(c+25*i)
     safe_ini_write_real("OVER_Data","M_"+string(i),data) //Weapon
    }
    
    for (i=0; i<size; i+=1)
    {
     data = (global.Item_Slot[ds_list_find_value(global.Player_Party,i),3])+c*(i*b-a)
     safe_ini_write_real("OVER_Data","N_"+string(i),data) //Accessory
    }
    
    for (i=0; i<size; i+=1)
    {
     data = (global.Item_Slot[ds_list_find_value(global.Player_Party,i),4])-c*(i*2+10)
     safe_ini_write_real("OVER_Data","O_"+string(i),data) //Special
    }
    
    for (i=0; i<size; i+=1)
    {
     data = (global.Character_Rune_Slot[ds_list_find_value(global.Player_Party,i),0])*((i*b)+17)
     safe_ini_write_real("OVER_Data","P_"+string(i),data) //Rune 1
    }
    
    for (i=0; i<size; i+=1)
    {
     data = (global.Character_Rune_Slot[ds_list_find_value(global.Player_Party,i),1])-b*(i*7+1)
     safe_ini_write_real("OVER_Data","Q_"+string(i),data) //Rune 2
    }
    
    for (i=0; i<size; i+=1)
    {
     data = (global.Character_Rune_Slot[ds_list_find_value(global.Player_Party,i),2])+43*(b*i+23)
     safe_ini_write_real("OVER_Data","R_"+string(i),data) //Rune 3
    }
    
    for (i=0; i<ds_list_size(global.Player_Iventory); i+=1)
    {
     data = (ds_list_find_value(global.Player_Iventory,i))*(i*b-c)
     safe_ini_write_real("OVER_Data","S_"+string(i),data) //ITEMS - for inventory order
    }
    
    data = (global.Total_Items)*c+b
    safe_ini_write_real("OVER_Data","T_0",data) //total ITEMS in game as of this version
    data = (global.Total_Creatures)*b-(c*21)
    safe_ini_write_real("OVER_Data","T_1",data) //total CREATURES in game as of this version
    data = (global.Total_Classes)*a-c
    safe_ini_write_real("OVER_Data","T_2",data) //total CLASSES in game as of this version
    data = (global.total_switches)*17+c
    safe_ini_write_real("OVER_Data","T_3",data) //total SWITCHES in game as of this version
    data = (ds_list_size(global.Player_Party))*c-b
    safe_ini_write_real("OVER_Data","T_4",data) //total PARTY MEMBERS with the player
    data = (ds_list_size(global.Player_Iventory))*a+b
    safe_ini_write_real("OVER_Data","T_5",data) //size of Player's inventory
    data = (global.Save_Disabled)*b+c
    safe_ini_write_real("OVER_Data","T_6",data) //player can save the game or not in the current room
    data = string_length(global.Character_Name[0])-(a-b)
    safe_ini_write_real("OVER_Data","T_7",data) //player name length
    
    
    for (i=0; i<ds_list_size(global.Player_Iventory); i+=1)
    {
     data = (global.Item_Quan[ds_list_find_value(global.Player_Iventory,i)])*(i+c)-b
     safe_ini_write_real("OVER_Data","U_"+string(i),data) //ITEM quantities
    }
    
    for (i=0; i<global.Total_Creatures; i+=1)
    {
     data = (global.Creature_Seen[i])*c-(b*i+3)
     safe_ini_write_real("OVER_Data","V_"+string(i),data) //CREATURES SEEN LIST
    }
    
    for (i=0; i<size; i+=1)
    {
     data = (ds_list_find_value(global.Player_Party,i))*b-c+(i*5)
     safe_ini_write_real("OVER_Data","W_"+string(i),data) //CHARACTER PARTY POSES YO
    }
    
    if (ds_list_size(global.Player_Party) > 0)
    {
     for (i=0; i<global.Total_Classes; i+=1)
     {
      data = (global.Character_Class_Level[ds_list_find_value(global.Player_Party,0),i])*(i+c)
      safe_ini_write_real("OVER_Data","X_"+string(i),data) //CLASS LEVELS
     }
    }
    
    if (ds_list_size(global.Player_Party) > 1)
    {
     for (i=0; i<global.Total_Classes; i+=1)
     {
      data = (global.Character_Class_Level[ds_list_find_value(global.Player_Party,1),i])*((i+1)-b)
      safe_ini_write_real("OVER_Data","Y_"+string(i),data) //CLASS LEVELS
     }
    }
    
    if (ds_list_size(global.Player_Party) > 2)
    {
     for (i=0; i<global.Total_Classes; i+=1)
     {
      data = (global.Character_Class_Level[ds_list_find_value(global.Player_Party,2),i])*(i+b)
      safe_ini_write_real("OVER_Data","Z_"+string(i),data) //CLASS LEVELS
     }
    }
    
    if (ds_list_size(global.Player_Party) > 3)
    {
     for (i=0; i<global.Total_Classes; i+=1)
     {
      data = (global.Character_Class_Level[ds_list_find_value(global.Player_Party,3),i])*(b+(i*3))
      safe_ini_write_real("OVER_Data","AA_"+string(i),data) //CLASS LEVELS
     }
    }
    
    if (ds_list_size(global.Player_Party) > 0)
    {
     for (i=0; i<global.Total_Classes; i+=1)
     {
      data = (global.Character_Class_EXP[ds_list_find_value(global.Player_Party,0),i])*(10+(i*b))
      safe_ini_write_real("OVER_Data","BB_"+string(i),data) //CLASS LEVELS
     }
    }
    
    if (ds_list_size(global.Player_Party) > 1)
    {
     for (i=0; i<global.Total_Classes; i+=1)
     {
      data = (global.Character_Class_EXP[ds_list_find_value(global.Player_Party,1),i])*(b+(i*c)-a)
      safe_ini_write_real("OVER_Data","CC_"+string(i),data) //CLASS LEVELS
     }
    }
    
    if (ds_list_size(global.Player_Party) > 2)
    {
     for (i=0; i<global.Total_Classes; i+=1)
     {
      data = (global.Character_Class_EXP[ds_list_find_value(global.Player_Party,2),i])*b-(i*3)
      safe_ini_write_real("OVER_Data","DD_"+string(i),data) //CLASS LEVELS
     }
    }
    
    if (ds_list_size(global.Player_Party) > 3)
    {
     for (i=0; i<global.Total_Classes; i+=1)
     {
      data = (global.Character_Class_EXP[ds_list_find_value(global.Player_Party,3),i])*(i+b-a)
      safe_ini_write_real("OVER_Data","OVER_"+string(i),data) //CLASS LEVELS
     }
    }
    
    
    //SKILL INICIO
    for (i=0; i<size; i+=1)
    {
     data = (ds_list_size(global.Character_Skills[ds_list_find_value(global.Player_Party,i)]))*(i+(i*b)+1)
     safe_ini_write_real("OVER_Data","FF_"+string(i),data) //CHARACTER SKILL LIST SIZES
    }
    
    if (ds_list_size(global.Player_Party) > 0)
    {
     for (i=0; i<ds_list_size(global.Character_Skills[ds_list_find_value(global.Player_Party,0)]); i+=1)
     {
      data = (ds_list_find_value(global.Character_Skills[ds_list_find_value(global.Player_Party,0)],i))*((i+c)*b-31)
      safe_ini_write_real("OVER_Data","GG_"+string(i),data) //CHARACTER SKILL LIST
     }
    }
    
    if (ds_list_size(global.Player_Party) > 1)
    {
     for (i=0; i<ds_list_size(global.Character_Skills[ds_list_find_value(global.Player_Party,1)]); i+=1)
     {
      data = (ds_list_find_value(global.Character_Skills[ds_list_find_value(global.Player_Party,1)],i))*(b+(c+i))
      safe_ini_write_real("OVER_Data","HH_"+string(i),data) //CHARACTER SKILL LIST
     }
    }
    
    if (ds_list_size(global.Player_Party) > 2)
    {
     for (i=0; i<ds_list_size(global.Character_Skills[ds_list_find_value(global.Player_Party,2)]); i+=1)
     {
      data = (ds_list_find_value(global.Character_Skills[ds_list_find_value(global.Player_Party,2)],i))*b+((i+1)*c)
      safe_ini_write_real("OVER_Data","II_"+string(i),data) //CHARACTER SKILL LIST
     }
    }
    
    if (ds_list_size(global.Player_Party) > 3)
    {
     for (i=0; i<ds_list_size(global.Character_Skills[ds_list_find_value(global.Player_Party,3)]); i+=1)
     {
      data = (ds_list_find_value(global.Character_Skills[ds_list_find_value(global.Player_Party,3)],i))*((i*a)-b)
      safe_ini_write_real("OVER_Data","JJ_"+string(i),data) //CHARACTER SKILL LIST
     }
    }
    
    // SKILL FIM
    //SITUATION INICIO
    for (i=0; i<size; i+=1)
    {
     data = (ds_list_size(global.Character_Situation[ds_list_find_value(global.Player_Party,i)]))*(i+(i*b)+1)
     safe_ini_write_real("OVER_Data","FFS_"+string(i),data) //CHARACTER SITUATION LIST SIZES
    }
    
    if (ds_list_size(global.Player_Party) > 0)
    {
     for (i=0; i<ds_list_size(global.Character_Situation[ds_list_find_value(global.Player_Party,0)]); i+=1)
     {
      data = (ds_list_find_value(global.Character_Situation[ds_list_find_value(global.Player_Party,0)],i))*((i+c)*b-31)
      safe_ini_write_real("OVER_Data","GGS_"+string(i),data) //CHARACTER SITUATION LIST
     }
    }
    
    if (ds_list_size(global.Player_Party) > 1)
    {
     for (i=0; i<ds_list_size(global.Character_Situation[ds_list_find_value(global.Player_Party,1)]); i+=1)
     {
      data = (ds_list_find_value(global.Character_Situation[ds_list_find_value(global.Player_Party,1)],i))*(b+(c+i))
      safe_ini_write_real("OVER_Data","HHS_"+string(i),data) //CHARACTER SITUATION LIST
     }
    }
    
    if (ds_list_size(global.Player_Party) > 2)
    {
     for (i=0; i<ds_list_size(global.Character_Situation[ds_list_find_value(global.Player_Party,2)]); i+=1)
     {
      data = (ds_list_find_value(global.Character_Situation[ds_list_find_value(global.Player_Party,2)],i))*b+((i+1)*c)
      safe_ini_write_real("OVER_Data","IIS_"+string(i),data) //CHARACTER SITUATION LIST
     }
    }
    
    if (ds_list_size(global.Player_Party) > 3)
    {
     for (i=0; i<ds_list_size(global.Character_Situation[ds_list_find_value(global.Player_Party,3)]); i+=1)
     {
      data = (ds_list_find_value(global.Character_Situation[ds_list_find_value(global.Player_Party,3)],i))*((i*a)-b)
      safe_ini_write_real("OVER_Data","JJS_"+string(i),data) //CHARACTER SITUATION LIST
     }
    }
    
    // SITUATION FIM
    
    
    
    
    for (i=0; i<global.total_switches; i+=1)
    {
     data = (global.Switch[i])*(i*c-a)
     safe_ini_write_real("OVER_Data","KK_"+string(i),data) //SWITCH STATUS
    }
    
    for (i=0; i<string_length(global.Character_Name[0]); i+=1)
    {
     temp = string_char_at(global.Character_Name[0],i+1)
     data = ord(temp)+(i+1*a)
     safe_ini_write_real("OVER_Data","LL_"+string(i),data) //Player Name
    }
    
    if (ds_list_size(global.Player_Party) > 0)
    {
     for (i=0; i<global.Total_Classes; i+=1)
     {
      data = (global.Class_Unlocked[i,ds_list_find_value(global.Player_Party,0)])*(10+(i*b))
      safe_ini_write_real("OVER_Data","MM_"+string(i),data) //CLASS UNLOCKS
     }
    }
    
    if (ds_list_size(global.Player_Party) > 1)
    {
     for (i=0; i<global.Total_Classes; i+=1)
     {
      data = (global.Class_Unlocked[i,ds_list_find_value(global.Player_Party,1)])*(b+(i*c)-a)
      safe_ini_write_real("OVER_Data","NN_"+string(i),data) //CLASS UNLOCKS
     }
    }
    
    if (ds_list_size(global.Player_Party) > 2)
    {
     for (i=0; i<global.Total_Classes; i+=1)
     {
      data = (global.Class_Unlocked[i,ds_list_find_value(global.Player_Party,2)])*b-(i*3)
      safe_ini_write_real("OVER_Data","OO_"+string(i),data) //CLASS UNLOCKS
     }
    }
    
    if (ds_list_size(global.Player_Party) > 3)
    {
     for (i=0; i<global.Total_Classes; i+=1)
     {
      data = (global.Class_Unlocked[i,ds_list_find_value(global.Player_Party,3)])*(i+b-a)
      safe_ini_write_real("OVER_Data","PP_"+string(i),data) //CLASS UNLOCKS
     }
    }
    // INCLUIDOS INICIO
    
    safe_ini_write_real( "SAVE", "combat_animation_save", global.combat_animation );// true
    safe_ini_write_real( "SAVE", "textbox_style_save", global.textbox_style  );//0
    safe_ini_write_real( "SAVE", "textbox_dialog_save", global.textbox_dialog );//0
    safe_ini_write_string( "SAVE", "language_save", global.language );// string
    safe_ini_write_real( "SAVE", "curWeekday_save", global.curWeekday );// 5
    safe_ini_write_real( "SAVE", "curMonth_save", global.curMonth );// 7
    safe_ini_write_real( "SAVE", "curDay_save", global.curDay );//23
    safe_ini_write_real( "SAVE", "curYear_save", global.curYear );//2997
    safe_ini_write_real( "SAVE", "secondsx_save", global.secondsx );//99999
    safe_ini_write_real( "SAVE", "showcalendar_save", global.showcalendar );//false
    safe_ini_write_real( "SAVE", "clockspeed_save", global.clockspeed );//99999
    
    safe_ini_write_real( "SAVE", "caterpillar_save", global.caterpillar );// true
    safe_ini_write_real( "SAVE", "lightsystem_save", global.lightsystem );// true
    safe_ini_write_real( "SAVE", "custom_battle_save", global.custom_battle );// true
    safe_ini_write_real( "SAVE", "input_number_save", global.input_number );//0
    safe_ini_write_real( "SAVE", "Player_Disabled_save", global.Player_Disabled );// false
    safe_ini_write_real( "SAVE", "Player_Movement_Speed_save", global.Player_Movement_Speed );// 4
    safe_ini_write_real( "SAVE", "current_account_save", global.current_account );// 0
    safe_ini_write_real( "SAVE", "savings_account_save", global.savings_account );// 0
    safe_ini_write_real( "SAVE", "CDB_account_save", global.CDB_account );// 0
    safe_ini_write_real( "SAVE", "RDB_account_save", global.RDB_account );// 0
    safe_ini_write_real( "SAVE", "stock_exchange_actions_save", global.stock_exchange_actions );// 0
    
    
    
    
    // INCLUIDOS FIM
    
    with (obSYS_Save_Game)
    {
     section = 3
     alarm[7]  = 30
    }
    
    safe_ini_close()
    
    

    My script was working perfectly, I just made small minor changes to "ini_open" and changed the prefixes "ini_" to "safe_ini".

    Can someone help me ?

    Ednei
     
  14. zbox

    zbox Member GMC Elder

    Joined:
    Jun 21, 2016
    Posts:
    793
    Hi there, the error is as describes if you have a look at the middle line: "Safe Save INI: This character is not allowed in Section or Key names: _"

    Just change the underscore to a hyphen "-" or some other symbol and you'll be be fine. :)
     
  15. Ednei

    Ednei Member

    Joined:
    Jun 22, 2016
    Posts:
    178
    Is it serious?
    Would not it be possible to make a change in its extension so that it can use "_" in the variables?
    The original functions (ini_write_real) allow "_" to be used.
    I'll have to practically rewrite my game to be able to use the safe save extension.
     
  16. zbox

    zbox Member GMC Elder

    Joined:
    Jun 21, 2016
    Posts:
    793
    You could probably set
    global._safe_ini_sep
    to "." or something but I wouldn't recommend it and this behavior is not supported. If you've got any more queries feel free to send me an email though the marketplace and that way I'll be able to get back quicker :)
     
  17. Ednei

    Ednei Member

    Joined:
    Jun 22, 2016
    Posts:
    178
    It's working correctly. Thank you for your support.
     
    zbox likes this.
  18. djrain

    djrain Member

    Joined:
    Aug 13, 2017
    Posts:
    139
    Installing this extension causes my game to immediately crash on iOS when compiled with YYC (works with VM though). Any reason why that would be happening? I really need it to work with YYC...
     
  19. zbox

    zbox Member GMC Elder

    Joined:
    Jun 21, 2016
    Posts:
    793
    Hi there - This extension is written in pure GML so I am skeptical as to whether it is the sole cause of the crash - Can you email me using the contact publisher button on the Marketplace and send me a copy of your logs when you run the game??
     
  20. djrain

    djrain Member

    Joined:
    Aug 13, 2017
    Posts:
    139
    Sure. Just to be clear, you want everything that Gamemaker generates (under "output") when I build it for iOS?
     
  21. zbox

    zbox Member GMC Elder

    Joined:
    Jun 21, 2016
    Posts:
    793
    Indeed - email it to me, in a separate text file.

    Edit: was a problem with another extension, not Safesave :)
     
    Last edited: Jan 2, 2018
  22. rIKmAN

    rIKmAN Member

    Joined:
    Sep 6, 2016
    Posts:
    4,510
    Are we able to choose whether the saves are device specific?
     
  23. zbox

    zbox Member GMC Elder

    Joined:
    Jun 21, 2016
    Posts:
    793
    Currently not - however a quick hack you could do would be to change the "device_get_id()" function to always return the same value - then saves would be transferrable :)
     
    rIKmAN likes this.
  24. Tornado

    Tornado Member

    Joined:
    Sep 9, 2016
    Posts:
    471
    @zbox
    Hi!
    Your asset "AES GML" looks very nice. Looks like probably the best out there for encryption.
    I'm trying to go deeper into this topic these days, but it is a hard topic for a layman. I saw some extensions which operates on Strings and there it is said it exists this problem with null string termination. So people keep saying that using buffers is the way to go. I saw in your documentation that you use arrays. So I guess arrays like buffers also do not have this null string problem?
    What is then the difference between implementation with buffers vs implementation with arrays? Just speed? Or more? Or actually nothing really?
    Thank you!
     
  25. Tornado

    Tornado Member

    Joined:
    Sep 9, 2016
    Posts:
    471
    @zbox
    One more question:
    are "AES GML" and "SafeSave - INI & More" more or less the same regarding the functionality, just another encryption algorith is used?
     
  26. zbox

    zbox Member GMC Elder

    Joined:
    Jun 21, 2016
    Posts:
    793
    Hi! The arrays are there to solve exactly the problem you mention, null string termination. (Which however once you're done decrypting you can turn it back into a string, given that your strings don't contain nulls (which they probably don't and if they do, you're most likely dealing with binary data blobs which would be better handled with the write to file of base64 encoding options))

    I find with this many operations on each char of the string the speed improvement buffers would give is not important, whilst they probably take a bit less memory, at this scale, the difference is negligible to modern computers. So I went with the portability of arrays instead - The outcome is the same.

    The two assets perform different functions - SafeSave is to store and encrypt data easily (With basic RC4 encryption), and the AES extension is a tool to help with encryption. SafeSave offers the functionality to switch out the normal RC4 encryption with AES encryption if it suits your purposes :)
     
    Tornado likes this.
  27. Tornado

    Tornado Member

    Joined:
    Sep 9, 2016
    Posts:
    471
    I'm still not sure if I understand. SafeSave contains big functionality with all the features listed on the marketplace. But if I want it to use AES encryption within it, I have to have AES GML extension too? AES GML can be seen either as a smaller standalone product and as well as a "plugin" for SafeSave?
    So if I want to encrypt ini files plus checksum checking, etc...but with strong AES encryption, then I have to have both assets?
    Is that understanding correct?

    Thank you very much for your kind support!
     
  28. zbox

    zbox Member GMC Elder

    Joined:
    Jun 21, 2016
    Posts:
    793
    Correct. If for your purposes RC4 is fine, then there is no need to buy the AES extension (however if you want to encrypt something else unrelated you can still use the AES extension!). Adding the AES integration just changes the type of encryption used in SafeSave, the rest of the functionality remains the same. Google is your friend in deciding which one is right for you :)
     
    Tornado likes this.
  29. Tornado

    Tornado Member

    Joined:
    Sep 9, 2016
    Posts:
    471
    The extensions are cheap for the value and it is only one time buy. I can buy both, this will be no problem. I aim for the maximal security of the ini file.

    I will be glad If you can recommend the algorithm.

    This will save me hours of layman surfing and digging for info which I might not even understand. Encryption is not an easy topic. I did quick googling it seems that AES is stronger!
    THX
     
    Last edited: Jan 30, 2018
    zbox likes this.
  30. RyanC

    RyanC Member

    Joined:
    Jun 24, 2016
    Posts:
    373
    This asset looks very nice!
    I'm a little concerned about the device_id function though. It looks likes it's a gml function? in which case may just return -1 for an unknown device or "android_tablet", which would still allows many users to share the file.
     
  31. zbox

    zbox Member GMC Elder

    Joined:
    Jun 21, 2016
    Posts:
    793
    Where do you get that impression :)?

    It would be literally impossible for the function to return either of those values, given it actually returns a checksum of the unique features of each device concatenated to eachother. You're also more than free to replace the function with your own device ID getter, if it suits you more.
     
    RyanC likes this.
  32. Tornado

    Tornado Member

    Joined:
    Sep 9, 2016
    Posts:
    471
    @zbox
    I bought both "SafeSave INI" and "AES GML". I can run both separately, both demos are working. I also ran SafeSave using AES.

    1) How can I check that the produced ini file is really AES encrypted?

    2)
    Why when importing those extensions some scripts are doubled and the duplicates get the suffix "_new". Then I have to go and delete all _new files.
    I'm using GMS v1.4.1772

    3) after importing into GMSG, can I remove everything from extensions folder? Or some stuff is needed from there too?

    Thx
     
    Last edited: Feb 7, 2018
  33. zbox

    zbox Member GMC Elder

    Joined:
    Jun 21, 2016
    Posts:
    793
    Great :)

    1) Take your key you are using to encrypt your data, and an example of the saved data file, decrypt it using the key. You'll see your data structure you saved
    2) There may be some overlap in scripts I use, delete the _new ones.
    3) The extensions are needed

    Cheers
     
    Tornado likes this.
  34. Zuljaras

    Zuljaras Member

    Joined:
    Jan 16, 2017
    Posts:
    414
    Hello.
    I have the following errors after I try to compile my game:
    "
    Error : gml_Script_safe_save(17) : unknown function or script safe_device_id
    Error : gml_Script_safe_save(27) : unknown function or script safe_save_use_aes
    Error : gml_Script_safe_save(27) : unknown function or script safe_save_use_aes
    Error : gml_Script_safe_save(41) : unknown function or script array_to_list
    Error : gml_Script_safe_load(22) : unknown function or script safe_save_use_aes
    Error : gml_Script_safe_load(22) : unknown function or script safe_save_use_aes
    Error : gml_Script_safe_load(31) : unknown function or script list_to_array
    Error : gml_Script_safe_load(32) : unknown function or script array_to_list
    Error : gml_Script_safe_load(43) : unknown function or script string_trim_trail
    Error : gml_Script_safe_load(61) : unknown function or script safe_device_id
    "

    I did not even used the encryption yet. I just imported it into my project and I cant start my game. This is GMS1.4.
     
  35. zbox

    zbox Member GMC Elder

    Joined:
    Jun 21, 2016
    Posts:
    793
    Hi - looks like you have not imported the scripts that come with the extension.
     
  36. Zuljaras

    Zuljaras Member

    Joined:
    Jan 16, 2017
    Posts:
    414
    https://i.imgur.com/1GhqTV3.png

    I did not get any scripts added.
     
  37. Zuljaras

    Zuljaras Member

    Joined:
    Jan 16, 2017
    Posts:
    414
    I managed to start it but the saving is too slow.
    Any other users get the same problems?

    I must save 2 arrays with 1000 positions. When I save them I get a freeze that lasts around 20-30 seconds. Every time.
     
  38. ei8htbit

    ei8htbit Member

    Joined:
    May 30, 2019
    Posts:
    1
    I purchased both the Safe Save and AES extension - great product, very useful - thanks for making this. I have a few question regarding best practices for storing Keys (and/or IVs for AES) is it advisable to:
    A) keep the Key and/or IV values stored as a variable within the script that gets compiled with the game package (ie. is there an easy way for anyone to search through compiled game code to sniff out a key string variable stored in game script?)
    B) obfuscate and save the key variable within a separate text file or elsewhere and then pull it into code in order to decrypt as needed during runtime?
    C) Bonus question - do you recommend generating individual Keys/IVs for every single value being encrypted with AES or is that overkill (essentially triples the amount of variables required for each encrypted data asset)?
    D) Last question: I checked the readme and looked through all script comments in the package for Safe Save and was not able to find an answer - how do you activate AES as the default encryption method for Safe Save (as opposed to RC4). It seems to mention on the marketplace page but the readme does not include that info - just thought I'd ask..

    Thanks again for your time and efforts, much appreciated.
     
    Last edited: May 30, 2019
  39. Hikati Games

    Hikati Games Member

    Joined:
    Jul 30, 2017
    Posts:
    1
    @zbox

    I am also looking for the same answers. The readme file no longer contains any mention of how to switch to aes and the file: save_save.gml looks like it only references rc4? I also checked the readme on the AES extension just in case but saw no mention of safesave in it.

    I do want to say: SafeSave and AES are great extensions, I have not had any problems using either one by itself.
     
  40. zbox

    zbox Member GMC Elder

    Joined:
    Jun 21, 2016
    Posts:
    793
    Hi all - please contact me through marketplace so I can follow up and be notified!
     

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice