1/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
2/* If you are missing that file, acquire a complete release at teeworlds.com. */
3
4#include <algorithm>
5#include <base/math.h>
6
7#include <engine/storage.h>
8
9#include "filecollection.h"
10
11void CFileCollection::Init(IStorage *pStorage, const char *pPath, const char *pFileDesc, const char *pFileExt, int MaxEntries)
12{
13 m_vFileEntries.clear();
14 str_copy(dst&: m_aFileDesc, src: pFileDesc);
15 m_FileDescLength = str_length(str: m_aFileDesc);
16 str_copy(dst&: m_aFileExt, src: pFileExt);
17 m_FileExtLength = str_length(str: m_aFileExt);
18 str_copy(dst&: m_aPath, src: pPath);
19 m_pStorage = pStorage;
20
21 m_pStorage->ListDirectory(Type: IStorage::TYPE_SAVE, pPath: m_aPath, pfnCallback: FilelistCallback, pUser: this);
22 std::sort(first: m_vFileEntries.begin(), last: m_vFileEntries.end(), comp: [](const CFileEntry &lhs, const CFileEntry &rhs) { return lhs.m_Timestamp < rhs.m_Timestamp; });
23
24 int FilesDeleted = 0;
25 for(auto FileEntry : m_vFileEntries)
26 {
27 if((int)m_vFileEntries.size() - FilesDeleted <= MaxEntries)
28 break;
29
30 char aBuf[IO_MAX_PATH_LENGTH];
31 if(m_aFileDesc[0] == '\0')
32 {
33 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "%s/%s", m_aPath, FileEntry.m_aFilename);
34 }
35 else
36 {
37 char aTimestring[TIMESTAMP_LENGTH];
38 str_timestamp_ex(time: FileEntry.m_Timestamp, buffer: aTimestring, buffer_size: sizeof(aBuf), FORMAT_NOSPACE);
39 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "%s/%s_%s%s", m_aPath, m_aFileDesc, aTimestring, m_aFileExt);
40 }
41
42 m_pStorage->RemoveFile(pFilename: aBuf, Type: IStorage::TYPE_SAVE);
43 FilesDeleted++;
44 }
45}
46
47bool CFileCollection::ExtractTimestamp(const char *pTimestring, time_t *pTimestamp)
48{
49 // Discard anything after timestamp length from pTimestring (most likely the extension)
50 char aStrippedTimestring[TIMESTAMP_LENGTH];
51 str_copy(dst&: aStrippedTimestring, src: pTimestring);
52 return timestamp_from_str(string: aStrippedTimestring, FORMAT_NOSPACE, timestamp: pTimestamp);
53}
54
55bool CFileCollection::ParseFilename(const char *pFilename, time_t *pTimestamp)
56{
57 // Check if filename is valid
58 if(!str_endswith(str: pFilename, suffix: m_aFileExt))
59 return false;
60
61 const char *pTimestring = pFilename;
62
63 if(m_aFileDesc[0] == '\0')
64 {
65 int FilenameLength = str_length(str: pFilename);
66 if(m_FileExtLength + TIMESTAMP_LENGTH > FilenameLength)
67 {
68 return false;
69 }
70
71 pTimestring += FilenameLength - m_FileExtLength - TIMESTAMP_LENGTH + 1;
72 }
73 else
74 {
75 if(str_length(str: pFilename) != m_FileDescLength + TIMESTAMP_LENGTH + m_FileExtLength ||
76 !str_startswith(str: pFilename, prefix: m_aFileDesc))
77 return false;
78
79 pTimestring += m_FileDescLength + 1;
80 }
81
82 // Extract timestamp
83 if(!ExtractTimestamp(pTimestring, pTimestamp))
84 return false;
85
86 return true;
87}
88
89int CFileCollection::FilelistCallback(const char *pFilename, int IsDir, int StorageType, void *pUser)
90{
91 CFileCollection *pThis = static_cast<CFileCollection *>(pUser);
92
93 // Try to parse filename and extract timestamp
94 time_t Timestamp;
95 if(IsDir || !pThis->ParseFilename(pFilename, pTimestamp: &Timestamp))
96 return 0;
97
98 // Add the entry
99 pThis->m_vFileEntries.emplace_back(args&: Timestamp, args&: pFilename);
100
101 return 0;
102}
103