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